mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 14:25:51 +00:00
feat: 完成大部分操作
This commit is contained in:
@@ -1,2 +1,9 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export function useFrustrationDetection(
|
||||
_messages: unknown[],
|
||||
_isLoading: boolean,
|
||||
_hasActivePrompt: boolean,
|
||||
_otherSurveyOpen: boolean,
|
||||
): { state: 'closed' | 'open'; handleTranscriptSelect: () => void } {
|
||||
return { state: 'closed', handleTranscriptSelect: () => {} };
|
||||
}
|
||||
|
||||
@@ -294,8 +294,8 @@ function PromptInput({
|
||||
// otherwise bridge becomes an invisible selection stop.
|
||||
const bridgeFooterVisible = replBridgeConnected && (replBridgeExplicit || replBridgeReconnecting);
|
||||
// Tmux pill (ant-only) — visible when there's an active tungsten session
|
||||
const hasTungstenSession = useAppState(s => "external" === 'ant' && s.tungstenActiveSession !== undefined);
|
||||
const tmuxFooterVisible = "external" === 'ant' && hasTungstenSession;
|
||||
const hasTungstenSession = useAppState(s => ("external" as string) === 'ant' && s.tungstenActiveSession !== undefined);
|
||||
const tmuxFooterVisible = ("external" as string) === 'ant' && hasTungstenSession;
|
||||
// WebBrowser pill — visible when a browser is open
|
||||
const bagelFooterVisible = useAppState(s => false);
|
||||
const teamContext = useAppState(s => s.teamContext);
|
||||
@@ -391,7 +391,7 @@ function PromptInput({
|
||||
// exist. When only local_agent tasks are running (coordinator/fork mode), the
|
||||
// pill is absent, so the -1 sentinel would leave nothing visually selected.
|
||||
// In that case, skip -1 and treat 0 as the minimum selectable index.
|
||||
const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !("external" === 'ant' && isPanelAgentTask(t))), [tasks]);
|
||||
const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !(("external" as string) === 'ant' && isPanelAgentTask(t))), [tasks]);
|
||||
const minCoordinatorIndex = hasBgTaskPill ? -1 : 0;
|
||||
// Clamp index when tasks complete and the list shrinks beneath the cursor
|
||||
useEffect(() => {
|
||||
@@ -455,7 +455,7 @@ function PromptInput({
|
||||
// Panel shows retained-completed agents too (getVisibleAgentTasks), so the
|
||||
// pill must stay navigable whenever the panel has rows — not just when
|
||||
// something is running.
|
||||
const tasksFooterVisible = (runningTaskCount > 0 || "external" === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree);
|
||||
const tasksFooterVisible = (runningTaskCount > 0 || ("external" as string) === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree);
|
||||
const teamsFooterVisible = cachedTeams.length > 0;
|
||||
const footerItems = useMemo(() => [tasksFooterVisible && 'tasks', tmuxFooterVisible && 'tmux', bagelFooterVisible && 'bagel', teamsFooterVisible && 'teams', bridgeFooterVisible && 'bridge', companionFooterVisible && 'companion'].filter(Boolean) as FooterItem[], [tasksFooterVisible, tmuxFooterVisible, bagelFooterVisible, teamsFooterVisible, bridgeFooterVisible, companionFooterVisible]);
|
||||
|
||||
@@ -1054,7 +1054,7 @@ function PromptInput({
|
||||
clearBuffer();
|
||||
resetHistory();
|
||||
return;
|
||||
} else if (result.error === 'no_team_context') {
|
||||
} else if ('error' in result && result.error === 'no_team_context') {
|
||||
// No team context - fall through to normal prompt submission
|
||||
} else {
|
||||
// Unknown recipient - fall through to normal prompt submission
|
||||
@@ -1742,7 +1742,7 @@ function PromptInput({
|
||||
useKeybindings({
|
||||
'footer:up': () => {
|
||||
// ↑ scrolls within the coordinator task list before leaving the pill
|
||||
if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) {
|
||||
if (tasksSelected && ("external" as string) === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) {
|
||||
setCoordinatorTaskIndex(prev => prev - 1);
|
||||
return;
|
||||
}
|
||||
@@ -1750,7 +1750,7 @@ function PromptInput({
|
||||
},
|
||||
'footer:down': () => {
|
||||
// ↓ scrolls within the coordinator task list, never leaves the pill
|
||||
if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0) {
|
||||
if (tasksSelected && ("external" as string) === 'ant' && coordinatorTaskCount > 0) {
|
||||
if (coordinatorTaskIndex < coordinatorTaskCount - 1) {
|
||||
setCoordinatorTaskIndex(prev => prev + 1);
|
||||
}
|
||||
@@ -1813,7 +1813,7 @@ function PromptInput({
|
||||
}
|
||||
break;
|
||||
case 'tmux':
|
||||
if ("external" === 'ant') {
|
||||
if (("external" as string) === 'ant') {
|
||||
setAppState(prev => prev.tungstenPanelAutoHidden ? {
|
||||
...prev,
|
||||
tungstenPanelAutoHidden: false
|
||||
@@ -2306,7 +2306,7 @@ function getInitialPasteId(messages: Message[]): number {
|
||||
if (message.type === 'user') {
|
||||
// Check image paste IDs
|
||||
if (message.imagePasteIds) {
|
||||
for (const id of message.imagePasteIds) {
|
||||
for (const id of message.imagePasteIds as number[]) {
|
||||
if (id > maxId) maxId = id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,11 +143,11 @@ function PromptInputFooter({
|
||||
</Box>
|
||||
<Box flexShrink={1} gap={1}>
|
||||
{isFullscreen ? null : <Notifications apiKeyStatus={apiKeyStatus} autoUpdaterResult={autoUpdaterResult} debug={debug} isAutoUpdating={isAutoUpdating} verbose={verbose} messages={messages} onAutoUpdaterResult={onAutoUpdaterResult} onChangeIsUpdating={onChangeIsUpdating} ideSelection={ideSelection} mcpClients={mcpClients} isInputWrapped={isInputWrapped} isNarrow={isNarrow} />}
|
||||
{"external" === 'ant' && isUndercover() && <Text dimColor>undercover</Text>}
|
||||
{("external" as string) === 'ant' && isUndercover() && <Text dimColor>undercover</Text>}
|
||||
<BridgeStatusIndicator bridgeSelected={bridgeSelected} />
|
||||
</Box>
|
||||
</Box>
|
||||
{"external" === 'ant' && <CoordinatorTaskPanel />}
|
||||
{("external" as string) === 'ant' && <CoordinatorTaskPanel />}
|
||||
</>;
|
||||
}
|
||||
export default memo(PromptInputFooter);
|
||||
|
||||
@@ -260,7 +260,7 @@ function ModeIndicator({
|
||||
const expandedView = useAppState(s_3 => s_3.expandedView);
|
||||
const showSpinnerTree = expandedView === 'teammates';
|
||||
const prStatus = usePrStatus(isLoading, isPrStatusEnabled());
|
||||
const hasTmuxSession = useAppState(s_4 => "external" === 'ant' && s_4.tungstenActiveSession !== undefined);
|
||||
const hasTmuxSession = useAppState(s_4 => ("external" as string) === 'ant' && s_4.tungstenActiveSession !== undefined);
|
||||
const nextTickAt = useSyncExternalStore(proactiveModule?.subscribeToProactiveChanges ?? NO_OP_SUBSCRIBE, proactiveModule?.getNextTickAt ?? NULL, NULL);
|
||||
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||
const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false;
|
||||
@@ -274,7 +274,7 @@ function ModeIndicator({
|
||||
const selGetState = useSelection().getState;
|
||||
const hasNextTick = nextTickAt !== null;
|
||||
const isCoordinator = feature('COORDINATOR_MODE') ? coordinatorModule?.isCoordinatorMode() === true : false;
|
||||
const runningTaskCount = useMemo(() => count(Object.values(tasks), t => isBackgroundTask(t) && !("external" === 'ant' && isPanelAgentTask(t))), [tasks]);
|
||||
const runningTaskCount = useMemo(() => count(Object.values(tasks), t => isBackgroundTask(t) && !(("external" as string) === 'ant' && isPanelAgentTask(t))), [tasks]);
|
||||
const tasksV2 = useTasksV2();
|
||||
const hasTaskItems = tasksV2 !== undefined && tasksV2.length > 0;
|
||||
const escShortcut = useShortcutDisplay('chat:cancel', 'Chat', 'esc').toLowerCase();
|
||||
@@ -365,7 +365,7 @@ function ModeIndicator({
|
||||
// its click-target Box isn't nested inside the <Text wrap="truncate">
|
||||
// wrapper (reconciler throws on Box-in-Text).
|
||||
// Tmux pill (ant-only) — appears right after tasks in nav order
|
||||
...("external" === 'ant' && hasTmuxSession ? [<TungstenPill key="tmux" selected={tmuxSelected} />] : []), ...(isAgentSwarmsEnabled() && hasTeams ? [<TeamStatus key="teams" teamsSelected={teamsSelected} showHint={showHint && !hasBackgroundTasks} />] : []), ...(shouldShowPrStatus ? [<PrBadge key="pr-status" number={prStatus.number!} url={prStatus.url!} reviewState={prStatus.reviewState!} />] : [])];
|
||||
...(("external" as string) === 'ant' && hasTmuxSession ? [<TungstenPill key="tmux" selected={tmuxSelected} />] : []), ...(isAgentSwarmsEnabled() && hasTeams ? [<TeamStatus key="teams" teamsSelected={teamsSelected} showHint={showHint && !hasBackgroundTasks} />] : []), ...(shouldShowPrStatus ? [<PrBadge key="pr-status" number={prStatus.number!} url={prStatus.url!} reviewState={prStatus.reviewState!} />] : [])];
|
||||
|
||||
// Check if any in-process teammates exist (for hint text cycling)
|
||||
const hasAnyInProcessTeammates = Object.values(tasks).some(t_2 => t_2.type === 'in_process_teammate' && t_2.status === 'running');
|
||||
@@ -399,7 +399,7 @@ function ModeIndicator({
|
||||
}
|
||||
|
||||
// Add "↓ to manage tasks" hint when panel has visible rows
|
||||
const hasCoordinatorTasks = "external" === 'ant' && getVisibleAgentTasks(tasks).length > 0;
|
||||
const hasCoordinatorTasks = ("external" as string) === 'ant' && getVisibleAgentTasks(tasks).length > 0;
|
||||
|
||||
// Tasks pill renders as a Box sibling (not a parts entry) so its
|
||||
// click-target Box isn't nested inside <Text wrap="truncate"> — the
|
||||
|
||||
@@ -34,7 +34,7 @@ function getIcon(itemId: string): string {
|
||||
function isUnifiedSuggestion(itemId: string): boolean {
|
||||
return itemId.startsWith('file-') || itemId.startsWith('mcp-resource-') || itemId.startsWith('agent-');
|
||||
}
|
||||
const SuggestionItemRow = memo(function SuggestionItemRow(t0) {
|
||||
const SuggestionItemRow = memo(function SuggestionItemRow(t0: { item: SuggestionItem; maxColumnWidth: number; isSelected: boolean }) {
|
||||
const $ = _c(36);
|
||||
const {
|
||||
item,
|
||||
|
||||
@@ -74,8 +74,8 @@ export function HighlightedInput(t0) {
|
||||
$[8] = lo;
|
||||
$[9] = hi;
|
||||
} else {
|
||||
lo = $[8];
|
||||
hi = $[9];
|
||||
lo = $[8] as number;
|
||||
hi = $[9] as number;
|
||||
}
|
||||
sweepStart = lo - 10;
|
||||
cycleLength = hi - lo + 20;
|
||||
|
||||
@@ -11,6 +11,10 @@ if (typeof globalThis.MACRO === "undefined") {
|
||||
VERSION_CHANGELOG: "",
|
||||
};
|
||||
}
|
||||
// Build-time constants — normally replaced by Bun bundler at compile time
|
||||
(globalThis as any).BUILD_TARGET = "external";
|
||||
(globalThis as any).BUILD_ENV = "production";
|
||||
(globalThis as any).INTERFACE_TYPE = "stdio";
|
||||
|
||||
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
|
||||
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
||||
|
||||
@@ -98,7 +98,8 @@ export type SDKMessage = { type: string; [key: string]: unknown }
|
||||
export type SDKUserMessage = { type: "user"; content: unknown; uuid: string; [key: string]: unknown }
|
||||
export type SDKUserMessageReplay = SDKUserMessage
|
||||
export type SDKAssistantMessage = { type: "assistant"; content: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantMessageError = { type: "assistant_error"; error: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantErrorMessage = { type: "assistant_error"; error: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantMessageError = 'authentication_failed' | 'billing_error' | 'rate_limit' | 'invalid_request' | 'server_error' | 'unknown' | 'max_output_tokens'
|
||||
export type SDKPartialAssistantMessage = { type: "partial_assistant"; [key: string]: unknown }
|
||||
export type SDKResultMessage = { type: "result"; [key: string]: unknown }
|
||||
export type SDKResultSuccess = { type: "result_success"; [key: string]: unknown }
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export function useAntOrgWarningNotification(): void {}
|
||||
|
||||
@@ -104,13 +104,13 @@ const VoiceKeybindingHandler: typeof import('../hooks/useVoiceIntegration.js').V
|
||||
// Frustration detection is ant-only (dogfooding). Conditional require so external
|
||||
// builds eliminate the module entirely (including its two O(n) useMemos that run
|
||||
// on every messages change, plus the GrowthBook fetch).
|
||||
const useFrustrationDetection: typeof import('../components/FeedbackSurvey/useFrustrationDetection.js').useFrustrationDetection = "external" === 'ant' ? require('../components/FeedbackSurvey/useFrustrationDetection.js').useFrustrationDetection : () => ({
|
||||
const useFrustrationDetection: typeof import('../components/FeedbackSurvey/useFrustrationDetection.js').useFrustrationDetection = ("external" as string) === 'ant' ? require('../components/FeedbackSurvey/useFrustrationDetection.js').useFrustrationDetection : () => ({
|
||||
state: 'closed',
|
||||
handleTranscriptSelect: () => {}
|
||||
});
|
||||
// Ant-only org warning. Conditional require so the org UUID list is
|
||||
// eliminated from external builds (one UUID is on excluded-strings).
|
||||
const useAntOrgWarningNotification: typeof import('../hooks/notifs/useAntOrgWarningNotification.js').useAntOrgWarningNotification = "external" === 'ant' ? require('../hooks/notifs/useAntOrgWarningNotification.js').useAntOrgWarningNotification : () => {};
|
||||
const useAntOrgWarningNotification: typeof import('../hooks/notifs/useAntOrgWarningNotification.js').useAntOrgWarningNotification = ("external" as string) === 'ant' ? require('../hooks/notifs/useAntOrgWarningNotification.js').useAntOrgWarningNotification : () => {};
|
||||
// Dead code elimination: conditional import for coordinator mode
|
||||
const getCoordinatorUserContext: (mcpClients: ReadonlyArray<{
|
||||
name: string;
|
||||
@@ -218,9 +218,9 @@ import { EffortCallout, shouldShowEffortCallout } from '../components/EffortCall
|
||||
import type { EffortValue } from '../utils/effort.js';
|
||||
import { RemoteCallout } from '../components/RemoteCallout.js';
|
||||
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
||||
const AntModelSwitchCallout = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').AntModelSwitchCallout : null;
|
||||
const shouldShowAntModelSwitch = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').shouldShowModelSwitchCallout : (): boolean => false;
|
||||
const UndercoverAutoCallout = "external" === 'ant' ? require('../components/UndercoverAutoCallout.js').UndercoverAutoCallout : null;
|
||||
const AntModelSwitchCallout = ("external" as string) === 'ant' ? require('../components/AntModelSwitchCallout.js').AntModelSwitchCallout : null;
|
||||
const shouldShowAntModelSwitch = ("external" as string) === 'ant' ? require('../components/AntModelSwitchCallout.js').shouldShowModelSwitchCallout : (): boolean => false;
|
||||
const UndercoverAutoCallout = ("external" as string) === 'ant' ? require('../components/UndercoverAutoCallout.js').UndercoverAutoCallout : null;
|
||||
/* eslint-enable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
||||
import { activityManager } from '../utils/activityManager.js';
|
||||
import { createAbortController } from '../utils/abortController.js';
|
||||
@@ -601,7 +601,7 @@ export function REPL({
|
||||
// Env-var gates hoisted to mount-time — isEnvTruthy does toLowerCase+trim+
|
||||
// includes, and these were on the render path (hot during PageUp spam).
|
||||
const titleDisabled = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE), []);
|
||||
const moreRightEnabled = useMemo(() => "external" === 'ant' && isEnvTruthy(process.env.CLAUDE_MORERIGHT), []);
|
||||
const moreRightEnabled = useMemo(() => ("external" as string) === 'ant' && isEnvTruthy(process.env.CLAUDE_MORERIGHT), []);
|
||||
const disableVirtualScroll = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL), []);
|
||||
const disableMessageActions = feature('MESSAGE_ACTIONS') ?
|
||||
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||
@@ -733,7 +733,7 @@ export function REPL({
|
||||
const [showIdeOnboarding, setShowIdeOnboarding] = useState(false);
|
||||
// Dead code elimination: model switch callout state (ant-only)
|
||||
const [showModelSwitchCallout, setShowModelSwitchCallout] = useState(() => {
|
||||
if ("external" === 'ant') {
|
||||
if (("external" as string) === 'ant') {
|
||||
return shouldShowAntModelSwitch();
|
||||
}
|
||||
return false;
|
||||
@@ -1012,7 +1012,7 @@ export function REPL({
|
||||
}, []);
|
||||
const [showUndercoverCallout, setShowUndercoverCallout] = useState(false);
|
||||
useEffect(() => {
|
||||
if ("external" === 'ant') {
|
||||
if (("external" as string) === 'ant') {
|
||||
void (async () => {
|
||||
// Wait for repo classification to settle (memoized, no-op if primed).
|
||||
const {
|
||||
@@ -2041,10 +2041,10 @@ export function REPL({
|
||||
if (allowDialogsWithAnimation && showIdeOnboarding) return 'ide-onboarding';
|
||||
|
||||
// Model switch callout (ant-only, eliminated from external builds)
|
||||
if ("external" === 'ant' && allowDialogsWithAnimation && showModelSwitchCallout) return 'model-switch';
|
||||
if (("external" as string) === 'ant' && allowDialogsWithAnimation && showModelSwitchCallout) return 'model-switch';
|
||||
|
||||
// Undercover auto-enable explainer (ant-only, eliminated from external builds)
|
||||
if ("external" === 'ant' && allowDialogsWithAnimation && showUndercoverCallout) return 'undercover-callout';
|
||||
if (("external" as string) === 'ant' && allowDialogsWithAnimation && showUndercoverCallout) return 'undercover-callout';
|
||||
|
||||
// Effort callout (shown once for Opus 4.6 users when effort is enabled)
|
||||
if (allowDialogsWithAnimation && showEffortCallout) return 'effort-callout';
|
||||
@@ -2482,7 +2482,7 @@ export function REPL({
|
||||
dynamicSkillDirTriggers: new Set<string>(),
|
||||
discoveredSkillNames: discoveredSkillNamesRef.current,
|
||||
setResponseLength,
|
||||
pushApiMetricsEntry: "external" === 'ant' ? (ttftMs: number) => {
|
||||
pushApiMetricsEntry: ("external" as string) === 'ant' ? (ttftMs: number) => {
|
||||
const now = Date.now();
|
||||
const baseline = responseLengthRef.current;
|
||||
apiMetricsRef.current.push({
|
||||
@@ -2605,7 +2605,7 @@ export function REPL({
|
||||
if (feature('PROACTIVE') || feature('KAIROS')) {
|
||||
proactiveModule?.setContextBlocked(false);
|
||||
}
|
||||
} else if (newMessage.type === 'progress' && isEphemeralToolProgress(newMessage.data.type)) {
|
||||
} else if ((newMessage as MessageType).type === 'progress' && isEphemeralToolProgress((newMessage as ProgressMessage<unknown>).data.type)) {
|
||||
// Replace the previous ephemeral progress tick for the same tool
|
||||
// call instead of appending. Sleep/Bash emit a tick per second and
|
||||
// only the last one is rendered; appending blows up the messages
|
||||
@@ -2618,7 +2618,7 @@ export function REPL({
|
||||
// "Initializing…" because it renders the full progress trail.
|
||||
setMessages(oldMessages => {
|
||||
const last = oldMessages.at(-1);
|
||||
if (last?.type === 'progress' && last.parentToolUseID === newMessage.parentToolUseID && last.data.type === newMessage.data.type) {
|
||||
if (last?.type === 'progress' && last.parentToolUseID === (newMessage as MessageType).parentToolUseID && last.data.type === (newMessage as ProgressMessage<unknown>).data.type) {
|
||||
const copy = oldMessages.slice();
|
||||
copy[copy.length - 1] = newMessage;
|
||||
return copy;
|
||||
@@ -2804,14 +2804,14 @@ export function REPL({
|
||||
if (feature('BUDDY')) {
|
||||
void fireCompanionObserver(messagesRef.current, reaction => setAppState(prev => prev.companionReaction === reaction ? prev : {
|
||||
...prev,
|
||||
companionReaction: reaction
|
||||
companionReaction: reaction as string | undefined
|
||||
}));
|
||||
}
|
||||
queryCheckpoint('query_end');
|
||||
|
||||
// Capture ant-only API metrics before resetLoadingState clears the ref.
|
||||
// For multi-request turns (tool use loops), compute P50 across all requests.
|
||||
if ("external" === 'ant' && apiMetricsRef.current.length > 0) {
|
||||
if (("external" as string) === 'ant' && apiMetricsRef.current.length > 0) {
|
||||
const entries = apiMetricsRef.current;
|
||||
const ttfts = entries.map(e => e.ttftMs);
|
||||
// Compute per-request OTPS using only active streaming time and
|
||||
@@ -2939,7 +2939,7 @@ export function REPL({
|
||||
// minutes — wiping the session made the pill disappear entirely, forcing
|
||||
// the user to re-invoke Tmux just to peek. Skip on abort so the panel
|
||||
// stays open for inspection (matches the turn-duration guard below).
|
||||
if ("external" === 'ant' && !abortController.signal.aborted) {
|
||||
if (("external" as string) === 'ant' && !abortController.signal.aborted) {
|
||||
setAppState(prev => {
|
||||
if (prev.tungstenActiveSession === undefined) return prev;
|
||||
if (prev.tungstenPanelAutoHidden === true) return prev;
|
||||
@@ -3062,7 +3062,7 @@ export function REPL({
|
||||
}
|
||||
|
||||
// Atomically: clear initial message, set permission mode and rules, and store plan for verification
|
||||
const shouldStorePlanForVerification = initialMsg.message.planContent && "external" === 'ant' && isEnvTruthy(undefined);
|
||||
const shouldStorePlanForVerification = initialMsg.message.planContent && ("external" as string) === 'ant' && isEnvTruthy(undefined);
|
||||
setAppState(prev => {
|
||||
// Build and apply permission updates (mode + allowedPrompts rules)
|
||||
let updatedToolPermissionContext = initialMsg.mode ? applyPermissionUpdates(prev.toolPermissionContext, buildPermissionUpdates(initialMsg.mode, initialMsg.allowedPrompts)) : prev.toolPermissionContext;
|
||||
@@ -3595,7 +3595,7 @@ export function REPL({
|
||||
|
||||
// Handler for when user presses 1 on survey thanks screen to share details
|
||||
const handleSurveyRequestFeedback = useCallback(() => {
|
||||
const command = "external" === 'ant' ? '/issue' : '/feedback';
|
||||
const command = ("external" as string) === 'ant' ? '/issue' : '/feedback';
|
||||
onSubmit(command, {
|
||||
setCursorOffset: () => {},
|
||||
clearBuffer: () => {},
|
||||
@@ -4063,7 +4063,7 @@ export function REPL({
|
||||
// - Workers receive permission responses via mailbox messages
|
||||
// - Leaders receive permission requests via mailbox messages
|
||||
|
||||
if ("external" === 'ant') {
|
||||
if (("external" as string) === 'ant') {
|
||||
// Tasks mode: watch for tasks and auto-process them
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
// biome-ignore lint/correctness/useHookAtTopLevel: conditional for dead code elimination in external builds
|
||||
@@ -4172,7 +4172,7 @@ export function REPL({
|
||||
|
||||
// Fall back to default behavior
|
||||
const hookType = currentHooks[0]?.data.hookEvent === 'SubagentStop' ? 'subagent stop' : 'stop';
|
||||
if ("external" === 'ant') {
|
||||
if (("external" as string) === 'ant') {
|
||||
const cmd = currentHooks[completedCount]?.data.command;
|
||||
const label = cmd ? ` '${truncateToWidth(cmd, 40)}'` : '';
|
||||
return total === 1 ? `running ${hookType} hook${label}` : `running ${hookType} hook${label}\u2026 ${completedCount}/${total}`;
|
||||
@@ -4581,7 +4581,7 @@ export function REPL({
|
||||
{toolJSX && !(toolJSX.isLocalJSXCommand && toolJSX.isImmediate) && !toolJsxCentered && <Box flexDirection="column" width="100%">
|
||||
{toolJSX.jsx}
|
||||
</Box>}
|
||||
{"external" === 'ant' && <TungstenLiveMonitor />}
|
||||
{("external" as string) === 'ant' && <TungstenLiveMonitor />}
|
||||
{feature('WEB_BROWSER_TOOL') ? WebBrowserPanelModule && <WebBrowserPanelModule.WebBrowserPanel /> : null}
|
||||
<Box flexGrow={1} />
|
||||
{showSpinner && <SpinnerWithVerb mode={streamMode} spinnerTip={spinnerTip} responseLengthRef={responseLengthRef} apiMetricsRef={apiMetricsRef} overrideMessage={spinnerMessage} spinnerSuffix={stopHookSpinnerSuffix} verbose={verbose} loadingStartTimeRef={loadingStartTimeRef} totalPausedMsRef={totalPausedMsRef} pauseStartTimeRef={pauseStartTimeRef} overrideColor={spinnerColor} overrideShimmerColor={spinnerShimmerColor} hasActiveTools={inProgressToolUseIDs.size > 0} leaderIsIdle={!isLoading} />}
|
||||
@@ -4804,7 +4804,7 @@ export function REPL({
|
||||
});
|
||||
}} />}
|
||||
{focusedInputDialog === 'ide-onboarding' && <IdeOnboardingDialog onDone={() => setShowIdeOnboarding(false)} installationStatus={ideInstallationStatus} />}
|
||||
{"external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && <AntModelSwitchCallout onDone={(selection: string, modelAlias?: string) => {
|
||||
{("external" as string) === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && <AntModelSwitchCallout onDone={(selection: string, modelAlias?: string) => {
|
||||
setShowModelSwitchCallout(false);
|
||||
if (selection === 'switch' && modelAlias) {
|
||||
setAppState(prev => ({
|
||||
@@ -4814,7 +4814,7 @@ export function REPL({
|
||||
}));
|
||||
}
|
||||
}} />}
|
||||
{"external" === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && <UndercoverAutoCallout onDone={() => setShowUndercoverCallout(false)} />}
|
||||
{("external" as string) === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && <UndercoverAutoCallout onDone={() => setShowUndercoverCallout(false)} />}
|
||||
{focusedInputDialog === 'effort-callout' && <EffortCallout model={mainLoopModel} onDone={selection => {
|
||||
setShowEffortCallout(false);
|
||||
if (selection !== 'dismiss') {
|
||||
@@ -4897,7 +4897,7 @@ export function REPL({
|
||||
{/* Frustration-triggered transcript sharing prompt */}
|
||||
{frustrationDetection.state !== 'closed' && <FeedbackSurvey state={frustrationDetection.state} lastResponse={null} handleSelect={() => {}} handleTranscriptSelect={frustrationDetection.handleTranscriptSelect} inputValue={inputValue} setInputValue={setInputValue} />}
|
||||
{/* Skill improvement survey - appears when improvements detected (ant-only) */}
|
||||
{"external" === 'ant' && skillImprovementSurvey.suggestion && <SkillImprovementSurvey isOpen={skillImprovementSurvey.isOpen} skillName={skillImprovementSurvey.suggestion.skillName} updates={skillImprovementSurvey.suggestion.updates} handleSelect={skillImprovementSurvey.handleSelect} inputValue={inputValue} setInputValue={setInputValue} />}
|
||||
{("external" as string) === 'ant' && skillImprovementSurvey.suggestion && <SkillImprovementSurvey isOpen={skillImprovementSurvey.isOpen} skillName={skillImprovementSurvey.suggestion.skillName} updates={skillImprovementSurvey.suggestion.updates} handleSelect={skillImprovementSurvey.handleSelect} inputValue={inputValue} setInputValue={setInputValue} />}
|
||||
{showIssueFlagBanner && <IssueFlagBanner />}
|
||||
{}
|
||||
<PromptInput debug={debug} ideSelection={ideSelection} hasSuppressedDialogs={!!hasSuppressedDialogs} isLocalJSXCommandActive={isShowingLocalJSXCommand} getToolUseContext={getToolUseContext} toolPermissionContext={toolPermissionContext} setToolPermissionContext={setToolPermissionContext} apiKeyStatus={apiKeyStatus} commands={commands} agents={agentDefinitions.activeAgents} isLoading={isLoading} onExit={handleExit} verbose={verbose} messages={messages} onAutoUpdaterResult={setAutoUpdaterResult} autoUpdaterResult={autoUpdaterResult} input={inputValue} onInputChange={setInputValue} mode={inputMode} onModeChange={setInputMode} stashedPrompt={stashedPrompt} setStashedPrompt={setStashedPrompt} submitCount={submitCount} onShowMessageSelector={handleShowMessageSelector} onMessageActionsEnter={
|
||||
@@ -4990,7 +4990,7 @@ export function REPL({
|
||||
setIsMessageSelectorVisible(false);
|
||||
setMessageSelectorPreselect(undefined);
|
||||
}} />}
|
||||
{"external" === 'ant' && <DevBar />}
|
||||
{("external" as string) === 'ant' && <DevBar />}
|
||||
</Box>
|
||||
{feature('BUDDY') && !(companionNarrow && isFullscreenEnvEnabled()) && companionVisible ? <CompanionSprite /> : null}
|
||||
</Box>} />
|
||||
|
||||
@@ -139,7 +139,7 @@ function useAppStore(): AppStateStore {
|
||||
* const { text, promptId } = useAppState(s => s.promptSuggestion) // good
|
||||
* ```
|
||||
*/
|
||||
export function useAppState(selector) {
|
||||
export function useAppState<R>(selector: (state: AppState) => R): R {
|
||||
const $ = _c(3);
|
||||
const store = useAppStore();
|
||||
let t0;
|
||||
@@ -183,7 +183,7 @@ const NOOP_SUBSCRIBE = () => () => {};
|
||||
* Safe version of useAppState that returns undefined if called outside of AppStateProvider.
|
||||
* Useful for components that may be rendered in contexts where AppStateProvider isn't available.
|
||||
*/
|
||||
export function useAppStateMaybeOutsideOfProvider(selector) {
|
||||
export function useAppStateMaybeOutsideOfProvider<R>(selector: (state: AppState) => R): R | undefined {
|
||||
const $ = _c(3);
|
||||
const store = useContext(AppStoreContext);
|
||||
let t0;
|
||||
|
||||
28
src/types/global.d.ts
vendored
28
src/types/global.d.ts
vendored
@@ -58,4 +58,30 @@ declare function launchUltraplan(...args: unknown[]): void
|
||||
declare type T = any
|
||||
|
||||
// Tungsten (internal)
|
||||
declare function TungstenPill(): JSX.Element | null
|
||||
declare function TungstenPill(props?: { key?: string; selected?: boolean }): JSX.Element | null
|
||||
|
||||
// ============================================================================
|
||||
// Build-time constants — replaced by Bun bundler, polyfilled at runtime
|
||||
// Using `string` (not literal types) so comparisons don't produce TS2367
|
||||
declare const BUILD_TARGET: string
|
||||
declare const BUILD_ENV: string
|
||||
declare const INTERFACE_TYPE: string
|
||||
|
||||
// ============================================================================
|
||||
// Bun text/file loaders — allow importing non-TS assets as strings
|
||||
declare module '*.md' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
declare module '*.txt' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
declare module '*.html' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
declare module '*.css' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
|
||||
@@ -1,40 +1,62 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export type Message = any;
|
||||
export type AssistantMessage = any;
|
||||
export type AttachmentMessage<T = any> = any;
|
||||
export type ProgressMessage<T = any> = any;
|
||||
export type SystemLocalCommandMessage = any;
|
||||
export type SystemMessage = any;
|
||||
export type UserMessage = any;
|
||||
export type NormalizedUserMessage = any;
|
||||
export type RequestStartEvent = any;
|
||||
export type StreamEvent = any;
|
||||
export type SystemCompactBoundaryMessage = any;
|
||||
export type TombstoneMessage = any;
|
||||
export type ToolUseSummaryMessage = any;
|
||||
export type MessageOrigin = any;
|
||||
export type CompactMetadata = any;
|
||||
export type SystemAPIErrorMessage = any;
|
||||
export type SystemFileSnapshotMessage = any;
|
||||
export type NormalizedAssistantMessage<T = any> = any;
|
||||
export type NormalizedMessage = any;
|
||||
export type PartialCompactDirection = any;
|
||||
export type StopHookInfo = any;
|
||||
export type SystemAgentsKilledMessage = any;
|
||||
export type SystemApiMetricsMessage = any;
|
||||
export type SystemAwaySummaryMessage = any;
|
||||
export type SystemBridgeStatusMessage = any;
|
||||
export type SystemInformationalMessage = any;
|
||||
export type SystemMemorySavedMessage = any;
|
||||
export type SystemMessageLevel = any;
|
||||
export type SystemMicrocompactBoundaryMessage = any;
|
||||
export type SystemPermissionRetryMessage = any;
|
||||
export type SystemScheduledTaskFireMessage = any;
|
||||
export type SystemStopHookSummaryMessage = any;
|
||||
export type SystemTurnDurationMessage = any;
|
||||
export type GroupedToolUseMessage = any;
|
||||
export type RenderableMessage = any;
|
||||
export type CollapsedReadSearchGroup = any;
|
||||
export type CollapsibleMessage = any;
|
||||
export type HookResultMessage = any;
|
||||
export type SystemThinkingMessage = any;
|
||||
import type { UUID } from 'crypto'
|
||||
|
||||
/**
|
||||
* Base message type with discriminant `type` field and common properties.
|
||||
* Individual message subtypes (UserMessage, AssistantMessage, etc.) extend
|
||||
* this with narrower `type` literals and additional fields.
|
||||
*/
|
||||
export type MessageType = 'user' | 'assistant' | 'system' | 'attachment' | 'progress'
|
||||
export type Message = {
|
||||
type: MessageType
|
||||
uuid: UUID
|
||||
isMeta?: boolean
|
||||
isCompactSummary?: boolean
|
||||
toolUseResult?: unknown
|
||||
isVisibleInTranscriptOnly?: boolean
|
||||
message?: {
|
||||
role?: string
|
||||
content?: string | Array<{ type: string; text?: string; [key: string]: unknown }>
|
||||
usage?: Record<string, unknown>
|
||||
[key: string]: unknown
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
export type AssistantMessage = Message & { type: 'assistant' };
|
||||
export type AttachmentMessage<T = unknown> = Message & { type: 'attachment' };
|
||||
export type ProgressMessage<T = unknown> = Message & { type: 'progress' };
|
||||
export type SystemLocalCommandMessage = Message & { type: 'system' };
|
||||
export type SystemMessage = Message & { type: 'system' };
|
||||
export type UserMessage = Message & { type: 'user' };
|
||||
export type NormalizedUserMessage = UserMessage;
|
||||
export type RequestStartEvent = { type: string; [key: string]: unknown };
|
||||
export type StreamEvent = { type: string; [key: string]: unknown };
|
||||
export type SystemCompactBoundaryMessage = Message & { type: 'system' };
|
||||
export type TombstoneMessage = Message;
|
||||
export type ToolUseSummaryMessage = Message;
|
||||
export type MessageOrigin = string;
|
||||
export type CompactMetadata = Record<string, unknown>;
|
||||
export type SystemAPIErrorMessage = Message & { type: 'system' };
|
||||
export type SystemFileSnapshotMessage = Message & { type: 'system' };
|
||||
export type NormalizedAssistantMessage<T = unknown> = AssistantMessage;
|
||||
export type NormalizedMessage = Message;
|
||||
export type PartialCompactDirection = string;
|
||||
export type StopHookInfo = Record<string, unknown>;
|
||||
export type SystemAgentsKilledMessage = Message & { type: 'system' };
|
||||
export type SystemApiMetricsMessage = Message & { type: 'system' };
|
||||
export type SystemAwaySummaryMessage = Message & { type: 'system' };
|
||||
export type SystemBridgeStatusMessage = Message & { type: 'system' };
|
||||
export type SystemInformationalMessage = Message & { type: 'system' };
|
||||
export type SystemMemorySavedMessage = Message & { type: 'system' };
|
||||
export type SystemMessageLevel = string;
|
||||
export type SystemMicrocompactBoundaryMessage = Message & { type: 'system' };
|
||||
export type SystemPermissionRetryMessage = Message & { type: 'system' };
|
||||
export type SystemScheduledTaskFireMessage = Message & { type: 'system' };
|
||||
export type SystemStopHookSummaryMessage = Message & { type: 'system' };
|
||||
export type SystemTurnDurationMessage = Message & { type: 'system' };
|
||||
export type GroupedToolUseMessage = Message;
|
||||
export type RenderableMessage = Message;
|
||||
export type CollapsedReadSearchGroup = Message;
|
||||
export type CollapsibleMessage = Message;
|
||||
export type HookResultMessage = Message;
|
||||
export type SystemThinkingMessage = Message & { type: 'system' };
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export type QueueOperationMessage = any;
|
||||
export type QueueOperation = any;
|
||||
export type QueueOperationMessage = {
|
||||
type: 'queue-operation'
|
||||
operation: QueueOperation
|
||||
timestamp: string
|
||||
sessionId: string
|
||||
content?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
export type QueueOperation = 'enqueue' | 'dequeue' | 'remove' | string;
|
||||
|
||||
3
src/types/sdk-stubs.d.ts
vendored
3
src/types/sdk-stubs.d.ts
vendored
@@ -94,7 +94,8 @@ declare module "*/sdk/coreTypes.generated.js" {
|
||||
export type SDKUserMessage = { type: "user"; content: unknown; uuid: string; [key: string]: unknown }
|
||||
export type SDKUserMessageReplay = SDKUserMessage
|
||||
export type SDKAssistantMessage = { type: "assistant"; content: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantMessageError = { type: "assistant_error"; error: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantErrorMessage = { type: "assistant_error"; error: unknown; [key: string]: unknown }
|
||||
export type SDKAssistantMessageError = 'authentication_failed' | 'billing_error' | 'rate_limit' | 'invalid_request' | 'server_error' | 'unknown' | 'max_output_tokens'
|
||||
export type SDKPartialAssistantMessage = { type: "partial_assistant"; [key: string]: unknown }
|
||||
export type SDKResultMessage = { type: "result"; [key: string]: unknown }
|
||||
export type SDKResultSuccess = { type: "result_success"; [key: string]: unknown }
|
||||
|
||||
@@ -486,8 +486,87 @@ function parseHttpHookOutput(body: string): {
|
||||
}
|
||||
}
|
||||
|
||||
/** Typed representation of sync hook JSON output, matching the syncHookResponseSchema Zod schema. */
|
||||
interface TypedSyncHookOutput {
|
||||
continue?: boolean
|
||||
suppressOutput?: boolean
|
||||
stopReason?: string
|
||||
decision?: 'approve' | 'block'
|
||||
reason?: string
|
||||
systemMessage?: string
|
||||
hookSpecificOutput?:
|
||||
| {
|
||||
hookEventName: 'PreToolUse'
|
||||
permissionDecision?: 'ask' | 'deny' | 'allow' | 'passthrough'
|
||||
permissionDecisionReason?: string
|
||||
updatedInput?: Record<string, unknown>
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'UserPromptSubmit'
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'SessionStart'
|
||||
additionalContext?: string
|
||||
initialUserMessage?: string
|
||||
watchPaths?: string[]
|
||||
}
|
||||
| {
|
||||
hookEventName: 'Setup'
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'SubagentStart'
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'PostToolUse'
|
||||
additionalContext?: string
|
||||
updatedMCPToolOutput?: unknown
|
||||
}
|
||||
| {
|
||||
hookEventName: 'PostToolUseFailure'
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'PermissionDenied'
|
||||
retry?: boolean
|
||||
}
|
||||
| {
|
||||
hookEventName: 'Notification'
|
||||
additionalContext?: string
|
||||
}
|
||||
| {
|
||||
hookEventName: 'PermissionRequest'
|
||||
decision?: PermissionRequestResult
|
||||
}
|
||||
| {
|
||||
hookEventName: 'Elicitation'
|
||||
action?: 'accept' | 'decline' | 'cancel'
|
||||
content?: Record<string, unknown>
|
||||
}
|
||||
| {
|
||||
hookEventName: 'ElicitationResult'
|
||||
action?: 'accept' | 'decline' | 'cancel'
|
||||
content?: Record<string, unknown>
|
||||
}
|
||||
| {
|
||||
hookEventName: 'CwdChanged'
|
||||
watchPaths?: string[]
|
||||
}
|
||||
| {
|
||||
hookEventName: 'FileChanged'
|
||||
watchPaths?: string[]
|
||||
}
|
||||
| {
|
||||
hookEventName: 'WorktreeCreate'
|
||||
worktreePath: string
|
||||
}
|
||||
}
|
||||
|
||||
function processHookJSONOutput({
|
||||
json,
|
||||
json: rawJson,
|
||||
command,
|
||||
hookName,
|
||||
toolUseID,
|
||||
@@ -511,6 +590,9 @@ function processHookJSONOutput({
|
||||
}): Partial<HookResult> {
|
||||
const result: Partial<HookResult> = {}
|
||||
|
||||
// Cast to typed interface for type-safe property access
|
||||
const json = rawJson as TypedSyncHookOutput
|
||||
|
||||
// At this point we know it's a sync response
|
||||
const syncJson = json
|
||||
|
||||
|
||||
@@ -1033,7 +1033,7 @@ class Project {
|
||||
'sourceToolAssistantUUID' in message &&
|
||||
message.sourceToolAssistantUUID
|
||||
) {
|
||||
effectiveParentUuid = message.sourceToolAssistantUUID
|
||||
effectiveParentUuid = message.sourceToolAssistantUUID as UUID
|
||||
}
|
||||
|
||||
const transcriptMessage: TranscriptMessage = {
|
||||
@@ -2120,7 +2120,7 @@ function recoverOrphanedParallelToolResults(
|
||||
chain: TranscriptMessage[],
|
||||
seen: Set<UUID>,
|
||||
): TranscriptMessage[] {
|
||||
type ChainAssistant = Extract<TranscriptMessage, { type: 'assistant' }>
|
||||
type ChainAssistant = TranscriptMessage & { type: 'assistant' }
|
||||
const chainAssistants = chain.filter(
|
||||
(m): m is ChainAssistant => m.type === 'assistant',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user