@vybestack/llxprt-code 0.10.0 → 0.11.0-nightly.260728.ffaa8d5d7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/commands/mcp/add.ts +8 -4
- package/src/config/cliArgParser.ts +2 -0
- package/src/config/config.ts +2 -0
- package/src/config/configBuilder.ts +3 -0
- package/src/config/configError.ts +23 -0
- package/src/config/extensions/extensionEnablement.ts +62 -1
- package/src/config/interactiveContext.ts +2 -1
- package/src/config/intermediateConfig.ts +4 -0
- package/src/config/keyBindings.ts +29 -3
- package/src/config/policy.ts +7 -1
- package/src/config/postConfigRuntime.ts +20 -14
- package/src/config/settings-schema/schema-ui.ts +9 -0
- package/src/config/settingsLoader.ts +21 -17
- package/src/config/settingsSchema.ts +2 -2
- package/src/config/trustedFolders.ts +275 -58
- package/src/config/yargsOptions.ts +7 -0
- package/src/generated/git-commit.json +1 -1
- package/src/nonInteractiveCli.ts +1 -0
- package/src/nonInteractiveCliSupport.ts +171 -51
- package/src/runtime/interactiveToolScheduler.ts +6 -6
- package/src/services/BuiltinCommandLoader.ts +33 -25
- package/src/services/FileCommandLoader.ts +34 -7
- package/src/test-utils/async.ts +20 -0
- package/src/test-utils/render.tsx +10 -1
- package/src/ui/App.tsx +17 -10
- package/src/ui/AppContainerRuntime.tsx +7 -9
- package/src/ui/cliUiRuntime.ts +28 -0
- package/src/ui/commands/authCommand.ts +240 -3
- package/src/ui/commands/compressCommand.ts +9 -1
- package/src/ui/commands/directoryCommand.tsx +1 -1
- package/src/ui/commands/logoutCommand.ts +2 -2
- package/src/ui/commands/permissionsCommand.ts +77 -20
- package/src/ui/commands/policiesCommand.ts +6 -1
- package/src/ui/commands/quotaCommand.ts +501 -0
- package/src/ui/commands/statsCommand.ts +2 -2
- package/src/ui/commands/statsQuota.ts +1 -1
- package/src/ui/commands/subagentCommand.ts +59 -27
- package/src/ui/components/AuthDialog.tsx +3 -3
- package/src/ui/components/Composer.tsx +1 -0
- package/src/ui/components/DialogManager.tsx +9 -2
- package/src/ui/components/FolderTrustDialog.tsx +71 -60
- package/src/ui/components/PermissionsModifyTrustDialog.tsx +211 -146
- package/src/ui/components/ProfileCreateWizard/constants.ts +13 -1
- package/src/ui/components/QueuedMessagesPanel.tsx +388 -0
- package/src/ui/components/inputPromptHooks.ts +2 -0
- package/src/ui/components/inputPromptKeyHandlers.ts +14 -0
- package/src/ui/components/inputPromptTypes.ts +6 -0
- package/src/ui/components/messages/AiMessage.tsx +24 -12
- package/src/ui/components/messages/ToolMessage.tsx +26 -40
- package/src/ui/containers/AppContainer/builders/buildUIActions.ts +3 -1
- package/src/ui/containers/AppContainer/builders/buildUIState.ts +5 -4
- package/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts +3 -33
- package/src/ui/containers/AppContainer/hooks/useAppDialogs.ts +2 -14
- package/src/ui/containers/AppContainer/hooks/useAppInput.ts +31 -25
- package/src/ui/containers/AppContainer/hooks/useAppLayout.ts +90 -60
- package/src/ui/containers/AppContainer/hooks/useDisplayPreferences.ts +10 -0
- package/src/ui/containers/AppContainer/hooks/useInputHandling.ts +3 -17
- package/src/ui/containers/AppContainer/hooks/useKeybindings.ts +51 -38
- package/src/ui/containers/AppContainer/hooks/useSteer.ts +51 -0
- package/src/ui/contexts/KeypressContext.tsx +13 -2
- package/src/ui/contexts/ShellCommandDisplayContext.tsx +103 -0
- package/src/ui/contexts/TodoContext.tsx +0 -13
- package/src/ui/contexts/TodoProvider.tsx +1 -34
- package/src/ui/contexts/UIActionsContext.tsx +2 -1
- package/src/ui/contexts/UIStateContext.tsx +3 -2
- package/src/ui/hooks/agentStream/streamUtils.ts +0 -24
- package/src/ui/hooks/agentStream/thoughtState.ts +110 -30
- package/src/ui/hooks/agentStream/types.ts +1 -0
- package/src/ui/hooks/agentStream/useAgentEventStream.ts +8 -17
- package/src/ui/hooks/agentStream/useAgentStream.ts +2 -2
- package/src/ui/hooks/agentStream/useAgentStreamLifecycle.ts +4 -5
- package/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts +12 -5
- package/src/ui/hooks/agentStream/useQueuedSubmissions.ts +99 -0
- package/src/ui/hooks/agentStream/useStreamEventHandlers.ts +4 -13
- package/src/ui/hooks/agentStream/useStreamState.ts +60 -41
- package/src/ui/hooks/agentStream/useSubmitQuery.ts +284 -59
- package/src/ui/hooks/slashCommandProcessorSupport.ts +4 -0
- package/src/ui/hooks/useAgentStream-test-helpers.ts +1 -0
- package/src/ui/hooks/useAutoAcceptIndicator.ts +13 -0
- package/src/ui/hooks/useFolderTrust.ts +118 -81
- package/src/ui/hooks/useGitBranchName.ts +79 -33
- package/src/ui/hooks/useIdeTrustListener.ts +5 -19
- package/src/ui/hooks/usePermissionsModifyTrust.ts +223 -85
- package/src/ui/hooks/useReactToolScheduler.ts +13 -17
- package/src/ui/hooks/useTodoPausePreserver.ts +0 -55
- package/src/ui/layouts/DefaultAppLayout.tsx +2 -0
- package/src/ui/layouts/DefaultAppLayoutHelpers.tsx +37 -162
- package/src/ui/layouts/InlineContent.tsx +198 -0
- package/src/ui/trustDialogHelpers.ts +190 -0
- package/src/utils/cleanup-state.ts +115 -0
- package/src/utils/cleanup.ts +32 -34
- package/src/utils/sandbox-containers.ts +183 -53
- package/src/utils/sandbox-exec.ts +9 -3
- package/src/zed-integration/acp-terminal-shell-host.ts +112 -0
- package/src/zed-integration/acp-types.ts +35 -0
- package/src/zed-integration/fileSystemService.ts +1 -1
- package/src/zed-integration/runZedIntegration.ts +142 -0
- package/src/zed-integration/zed-agent-event-handler.ts +179 -0
- package/src/zed-integration/zed-agent-setup.ts +43 -0
- package/src/zed-integration/zed-command-registry.ts +167 -0
- package/src/zed-integration/zed-config-options.ts +280 -0
- package/src/zed-integration/zed-content-utils.ts +2 -2
- package/src/zed-integration/zed-helpers.ts +132 -0
- package/src/zed-integration/zed-initialize.ts +64 -0
- package/src/zed-integration/zed-plan-update.ts +20 -0
- package/src/zed-integration/zed-prompt-command.ts +77 -0
- package/src/zed-integration/zed-session-config.ts +119 -0
- package/src/zed-integration/zed-session-errors.ts +337 -0
- package/src/zed-integration/zed-session-events.ts +216 -0
- package/src/zed-integration/zed-session-info.ts +255 -0
- package/src/zed-integration/zed-session-lifecycle.ts +186 -0
- package/src/zed-integration/zed-session-listing.ts +153 -0
- package/src/zed-integration/zed-session-loader.ts +193 -0
- package/src/zed-integration/zed-session-pagination.ts +240 -0
- package/src/zed-integration/zed-session-replay.ts +564 -0
- package/src/zed-integration/zed-stream-batcher.ts +236 -0
- package/src/zed-integration/zed-terminal-manager.ts +416 -0
- package/src/zed-integration/zed-terminal-setup.ts +72 -0
- package/src/zed-integration/zed-test-helpers.ts +349 -4
- package/src/zed-integration/zed-tool-handler.ts +47 -5
- package/src/zed-integration/zedIntegration.ts +538 -587
- package/src/services/todo-continuation/todoContinuationService.ts +0 -610
- package/src/ui/containers/AppContainer/hooks/useIdeRestartHotkey.ts +0 -33
- package/src/ui/containers/AppContainer/hooks/useTodoContinuationFlow.ts +0 -160
- package/src/ui/hooks/useTodoContinuation.ts +0 -299
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Vybestack LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @hook useTodoContinuationFlow
|
|
9
|
-
* @description Task-list continuation detection and prompts
|
|
10
|
-
* @inputs uiRuntime, streamingState, history, pendingHistoryItems, setDebugMessage, todoContinuationRef, hadToolCallsRef
|
|
11
|
-
* @outputs todoContinuationRef
|
|
12
|
-
* @sideEffects Effect watching streaming state
|
|
13
|
-
* @cleanup N/A
|
|
14
|
-
* @strictMode Safe - effect deps are stable
|
|
15
|
-
* @subscriptionStrategy Resubscribe
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { useEffect, useMemo, useRef } from 'react';
|
|
19
|
-
import {
|
|
20
|
-
StreamingState,
|
|
21
|
-
type HistoryItem,
|
|
22
|
-
type HistoryItemWithoutId,
|
|
23
|
-
} from '../../../types.js';
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
useTodoContinuation,
|
|
27
|
-
type TodoContinuationHook,
|
|
28
|
-
} from '../../../hooks/useTodoContinuation.js';
|
|
29
|
-
import type { UiRuntime } from '../../../cliUiRuntime.js';
|
|
30
|
-
import type { Agent } from '@vybestack/llxprt-code-agents';
|
|
31
|
-
|
|
32
|
-
export type { TodoContinuationHook };
|
|
33
|
-
|
|
34
|
-
interface UseTodoContinuationFlowOptions {
|
|
35
|
-
uiRuntime: UiRuntime;
|
|
36
|
-
agent: Agent;
|
|
37
|
-
streamingState: StreamingState;
|
|
38
|
-
history: HistoryItem[];
|
|
39
|
-
pendingHistoryItems: HistoryItemWithoutId[];
|
|
40
|
-
setDebugMessage: (message: string) => void;
|
|
41
|
-
todoContinuationRef?: React.MutableRefObject<Pick<
|
|
42
|
-
TodoContinuationHook,
|
|
43
|
-
'handleTodoPause' | 'clearPause'
|
|
44
|
-
> | null>;
|
|
45
|
-
hadToolCallsRef?: React.MutableRefObject<boolean>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface UseTodoContinuationFlowResult {
|
|
49
|
-
todoContinuationRef: React.MutableRefObject<Pick<
|
|
50
|
-
TodoContinuationHook,
|
|
51
|
-
'handleTodoPause' | 'clearPause'
|
|
52
|
-
> | null>;
|
|
53
|
-
hadToolCallsRef: React.MutableRefObject<boolean>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function useTodoContinuationFlow({
|
|
57
|
-
uiRuntime,
|
|
58
|
-
agent,
|
|
59
|
-
streamingState,
|
|
60
|
-
history,
|
|
61
|
-
pendingHistoryItems,
|
|
62
|
-
setDebugMessage,
|
|
63
|
-
todoContinuationRef: externalTodoContinuationRef,
|
|
64
|
-
hadToolCallsRef: externalHadToolCallsRef,
|
|
65
|
-
}: UseTodoContinuationFlowOptions): UseTodoContinuationFlowResult {
|
|
66
|
-
const internalTodoContinuationRef = useRef<Pick<
|
|
67
|
-
TodoContinuationHook,
|
|
68
|
-
'handleTodoPause' | 'clearPause'
|
|
69
|
-
> | null>(null);
|
|
70
|
-
const todoContinuationRef =
|
|
71
|
-
externalTodoContinuationRef ?? internalTodoContinuationRef;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @plan PLAN-20260129-TODOPERSIST.P12
|
|
75
|
-
* Wire up task-list continuation detection to trigger continuation prompts
|
|
76
|
-
* when streams complete without tool calls and active tasks exist.
|
|
77
|
-
*/
|
|
78
|
-
const continuationRuntime = useMemo(
|
|
79
|
-
() => ({
|
|
80
|
-
getEphemeralSettings: () => uiRuntime.app.getEphemeralSettings(),
|
|
81
|
-
getApprovalMode: () => uiRuntime.approval.getApprovalMode(),
|
|
82
|
-
}),
|
|
83
|
-
[uiRuntime.app, uiRuntime.approval],
|
|
84
|
-
);
|
|
85
|
-
const todoContinuation = useTodoContinuation(
|
|
86
|
-
agent,
|
|
87
|
-
continuationRuntime,
|
|
88
|
-
streamingState === StreamingState.Responding ||
|
|
89
|
-
streamingState === StreamingState.WaitingForConfirmation,
|
|
90
|
-
setDebugMessage,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
todoContinuationRef.current = todoContinuation;
|
|
94
|
-
|
|
95
|
-
// Track previous streaming state to detect turn completion
|
|
96
|
-
const prevStreamingStateRef = useRef<StreamingState>(streamingState);
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* @plan PLAN-20260129-TODOPERSIST.P12
|
|
100
|
-
* Track whether tool calls were made during the turn for continuation decision.
|
|
101
|
-
* Tool calls signal the AI made progress, so we don't need continuation.
|
|
102
|
-
*/
|
|
103
|
-
const internalHadToolCallsRef = useRef<boolean>(false);
|
|
104
|
-
const hadToolCallsRef = externalHadToolCallsRef ?? internalHadToolCallsRef;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* @plan PLAN-20260129-TODOPERSIST.P12
|
|
108
|
-
* Track tool calls by detecting tool_group items in history and pending items.
|
|
109
|
-
* Reset hadToolCallsRef when a new turn starts (streamingState transitions TO Responding)
|
|
110
|
-
* so that tool calls from previous turns don't suppress continuation prompts.
|
|
111
|
-
*/
|
|
112
|
-
useEffect(() => {
|
|
113
|
-
// Detect transition into Responding (new turn start) and reset the flag.
|
|
114
|
-
const wasIdle = prevStreamingStateRef.current === StreamingState.Idle;
|
|
115
|
-
const isNowResponding = streamingState === StreamingState.Responding;
|
|
116
|
-
if (wasIdle && isNowResponding) {
|
|
117
|
-
hadToolCallsRef.current = false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const hasToolCalls =
|
|
121
|
-
history.some((item) => item.type === 'tool_group') ||
|
|
122
|
-
pendingHistoryItems.some((item) => item.type === 'tool_group');
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
hasToolCalls &&
|
|
126
|
-
(streamingState === StreamingState.Responding ||
|
|
127
|
-
streamingState === StreamingState.WaitingForConfirmation)
|
|
128
|
-
) {
|
|
129
|
-
hadToolCallsRef.current = true;
|
|
130
|
-
}
|
|
131
|
-
}, [history, pendingHistoryItems, streamingState, hadToolCallsRef]);
|
|
132
|
-
|
|
133
|
-
// Detect turn completion (streaming goes idle) for continuation logic
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
const wasActive =
|
|
136
|
-
prevStreamingStateRef.current === StreamingState.Responding ||
|
|
137
|
-
prevStreamingStateRef.current === StreamingState.WaitingForConfirmation;
|
|
138
|
-
const isNowIdle = streamingState === StreamingState.Idle;
|
|
139
|
-
prevStreamingStateRef.current = streamingState;
|
|
140
|
-
|
|
141
|
-
if (!wasActive || !isNowIdle) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @plan PLAN-20260129-TODOPERSIST.P12
|
|
147
|
-
* Notify continuation logic that stream completed.
|
|
148
|
-
* Pass hadToolCalls to determine if continuation is needed.
|
|
149
|
-
*/
|
|
150
|
-
todoContinuation.handleStreamCompleted(hadToolCallsRef.current);
|
|
151
|
-
|
|
152
|
-
// Reset for next turn
|
|
153
|
-
hadToolCallsRef.current = false;
|
|
154
|
-
}, [streamingState, todoContinuation, hadToolCallsRef]);
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
todoContinuationRef,
|
|
158
|
-
hadToolCallsRef,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Vybestack LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
8
|
-
import type { Todo } from '@vybestack/llxprt-code-core';
|
|
9
|
-
import { ApprovalMode } from '@vybestack/llxprt-code-core';
|
|
10
|
-
import type { Agent } from '@vybestack/llxprt-code-agents';
|
|
11
|
-
import { useTodoContext } from '../contexts/TodoContext.js';
|
|
12
|
-
|
|
13
|
-
export interface TodoContinuationRuntime {
|
|
14
|
-
getEphemeralSettings(): Record<string, unknown>;
|
|
15
|
-
getApprovalMode(): ApprovalMode;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ContinuationState {
|
|
19
|
-
isActive: boolean;
|
|
20
|
-
taskDescription?: string;
|
|
21
|
-
attemptCount: number;
|
|
22
|
-
lastPromptTime?: Date;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ContinuationOptions {
|
|
26
|
-
taskDescription: string;
|
|
27
|
-
isYoloMode: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface ContinuationConditions {
|
|
31
|
-
streamCompleted: boolean;
|
|
32
|
-
noToolCallsMade: boolean;
|
|
33
|
-
hasActiveTodos: boolean;
|
|
34
|
-
continuationEnabled: boolean;
|
|
35
|
-
notAlreadyContinuing: boolean;
|
|
36
|
-
todoPaused: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface TodoContinuationHook {
|
|
40
|
-
handleStreamCompleted: (hadToolCalls: boolean) => void;
|
|
41
|
-
continuationState: ContinuationState;
|
|
42
|
-
handleTodoPause: (reason: string) => {
|
|
43
|
-
type: 'pause';
|
|
44
|
-
reason: string;
|
|
45
|
-
message: string;
|
|
46
|
-
};
|
|
47
|
-
clearPause: () => void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function evaluateConditions(
|
|
51
|
-
hadToolCalls: boolean,
|
|
52
|
-
todoPaused: boolean,
|
|
53
|
-
config: TodoContinuationRuntime,
|
|
54
|
-
todos: Todo[],
|
|
55
|
-
isActive: boolean,
|
|
56
|
-
): ContinuationConditions {
|
|
57
|
-
const ephemeralSettings = config.getEphemeralSettings();
|
|
58
|
-
const isEnabled = ephemeralSettings['todo-continuation'] === true;
|
|
59
|
-
const hasActiveTodos = todos.some(
|
|
60
|
-
(todo) => todo.status === 'pending' || todo.status === 'in_progress',
|
|
61
|
-
);
|
|
62
|
-
const hadBlockingToolCalls = hadToolCalls && (todoPaused || !hasActiveTodos);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
streamCompleted: true,
|
|
66
|
-
noToolCallsMade: !hadBlockingToolCalls,
|
|
67
|
-
hasActiveTodos,
|
|
68
|
-
continuationEnabled: isEnabled,
|
|
69
|
-
notAlreadyContinuing: !isActive,
|
|
70
|
-
todoPaused,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function shouldTrigger(
|
|
75
|
-
conditions: ContinuationConditions,
|
|
76
|
-
isResponding: boolean,
|
|
77
|
-
): boolean {
|
|
78
|
-
if (isResponding) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const baseConditionsMet =
|
|
83
|
-
conditions.streamCompleted &&
|
|
84
|
-
conditions.noToolCallsMade &&
|
|
85
|
-
conditions.hasActiveTodos;
|
|
86
|
-
const continuationReady =
|
|
87
|
-
conditions.continuationEnabled && conditions.notAlreadyContinuing;
|
|
88
|
-
return baseConditionsMet && continuationReady && !conditions.todoPaused;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function findMostRelevantTodo(todos: Todo[]): Todo | null {
|
|
92
|
-
const inProgressTodos = todos.filter((todo) => todo.status === 'in_progress');
|
|
93
|
-
|
|
94
|
-
if (inProgressTodos.length > 0) {
|
|
95
|
-
return inProgressTodos[0];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const pendingTodos = todos.filter((todo) => todo.status === 'pending');
|
|
99
|
-
|
|
100
|
-
if (pendingTodos.length > 0) {
|
|
101
|
-
return pendingTodos[0];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function generatePrompt(todo: Todo, config: TodoContinuationRuntime): string {
|
|
108
|
-
const isYoloMode = config.getApprovalMode() === ApprovalMode.YOLO;
|
|
109
|
-
|
|
110
|
-
if (isYoloMode) {
|
|
111
|
-
return `Continue to proceed with the active task without waiting for confirmation: "${todo.content}"`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return `Please continue working on the following task: "${todo.content}"`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function sendContinuationPrompt(
|
|
118
|
-
agent: Agent,
|
|
119
|
-
taskDescription: string,
|
|
120
|
-
continuationPrompt: string,
|
|
121
|
-
onDebugMessage: (message: string) => void,
|
|
122
|
-
setContinuationState: React.Dispatch<React.SetStateAction<ContinuationState>>,
|
|
123
|
-
continuationInProgressRef: React.MutableRefObject<boolean>,
|
|
124
|
-
abortControllerRef: React.MutableRefObject<AbortController | undefined>,
|
|
125
|
-
): void {
|
|
126
|
-
if (continuationInProgressRef.current) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
continuationInProgressRef.current = true;
|
|
131
|
-
|
|
132
|
-
setContinuationState((prev) => ({
|
|
133
|
-
isActive: true,
|
|
134
|
-
taskDescription,
|
|
135
|
-
attemptCount: prev.attemptCount + 1,
|
|
136
|
-
lastPromptTime: new Date(),
|
|
137
|
-
}));
|
|
138
|
-
|
|
139
|
-
const abortController = new AbortController();
|
|
140
|
-
abortControllerRef.current = abortController;
|
|
141
|
-
const promptId = `todo-continuation-${Date.now().toString(16)}`;
|
|
142
|
-
|
|
143
|
-
void (async () => {
|
|
144
|
-
try {
|
|
145
|
-
const stream = agent.stream(continuationPrompt, {
|
|
146
|
-
signal: abortController.signal,
|
|
147
|
-
promptId,
|
|
148
|
-
});
|
|
149
|
-
for await (const _event of stream) {
|
|
150
|
-
// Continuation prompts are submitted for side effects; the main stream
|
|
151
|
-
// UI owns rendering for user-initiated turns.
|
|
152
|
-
}
|
|
153
|
-
} catch (error: unknown) {
|
|
154
|
-
if (!abortController.signal.aborted) {
|
|
155
|
-
onDebugMessage(
|
|
156
|
-
`[TodoContinuation] Error sending continuation prompt: ${error instanceof Error ? error.message : String(error)}`,
|
|
157
|
-
);
|
|
158
|
-
setContinuationState((prev) => ({
|
|
159
|
-
isActive: false,
|
|
160
|
-
attemptCount: prev.attemptCount,
|
|
161
|
-
lastPromptTime: prev.lastPromptTime,
|
|
162
|
-
taskDescription: prev.taskDescription,
|
|
163
|
-
}));
|
|
164
|
-
}
|
|
165
|
-
} finally {
|
|
166
|
-
continuationInProgressRef.current = false;
|
|
167
|
-
if (abortControllerRef.current === abortController) {
|
|
168
|
-
abortControllerRef.current = undefined;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
})();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function processContinuation(
|
|
175
|
-
hadToolCalls: boolean,
|
|
176
|
-
todoContext: { todos: Todo[]; paused: boolean },
|
|
177
|
-
config: TodoContinuationRuntime,
|
|
178
|
-
isResponding: boolean,
|
|
179
|
-
continuationState: ContinuationState,
|
|
180
|
-
continuationInProgressRef: React.MutableRefObject<boolean>,
|
|
181
|
-
abortControllerRef: React.MutableRefObject<AbortController | undefined>,
|
|
182
|
-
agent: Agent,
|
|
183
|
-
onDebugMessage: (message: string) => void,
|
|
184
|
-
setContinuationState: React.Dispatch<React.SetStateAction<ContinuationState>>,
|
|
185
|
-
): void {
|
|
186
|
-
if (hadToolCalls) {
|
|
187
|
-
setContinuationState((prev) => ({ ...prev, isActive: false }));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const conditions = evaluateConditions(
|
|
191
|
-
hadToolCalls,
|
|
192
|
-
todoContext.paused,
|
|
193
|
-
config,
|
|
194
|
-
todoContext.todos,
|
|
195
|
-
continuationState.isActive,
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
if (!shouldTrigger(conditions, isResponding)) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const activeTodo = findMostRelevantTodo(todoContext.todos);
|
|
203
|
-
|
|
204
|
-
if (!activeTodo?.content || activeTodo.content.trim() === '') {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (continuationState.isActive || continuationInProgressRef.current) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const continuationPrompt = generatePrompt(activeTodo, config);
|
|
213
|
-
sendContinuationPrompt(
|
|
214
|
-
agent,
|
|
215
|
-
activeTodo.content,
|
|
216
|
-
continuationPrompt,
|
|
217
|
-
onDebugMessage,
|
|
218
|
-
setContinuationState,
|
|
219
|
-
continuationInProgressRef,
|
|
220
|
-
abortControllerRef,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* React hook for task continuation - monitors stream completion and triggers continuation
|
|
226
|
-
* prompts when active tasks exist but no tool calls were made.
|
|
227
|
-
* [REQ-001] Task Continuation Detection, [REQ-002] Continuation Prompting
|
|
228
|
-
*/
|
|
229
|
-
export const useTodoContinuation = (
|
|
230
|
-
agent: Agent,
|
|
231
|
-
config: TodoContinuationRuntime,
|
|
232
|
-
isResponding: boolean,
|
|
233
|
-
onDebugMessage: (message: string) => void,
|
|
234
|
-
): TodoContinuationHook => {
|
|
235
|
-
const [continuationState, setContinuationState] = useState<ContinuationState>(
|
|
236
|
-
{ isActive: false, attemptCount: 0 },
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
const continuationInProgressRef = useRef<boolean>(false);
|
|
240
|
-
const todoContext = useTodoContext();
|
|
241
|
-
const abortControllerRef = useRef<AbortController | undefined>(undefined);
|
|
242
|
-
const continuationTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
|
243
|
-
|
|
244
|
-
const handleStreamCompleted = useCallback(
|
|
245
|
-
(hadToolCalls: boolean): void => {
|
|
246
|
-
processContinuation(
|
|
247
|
-
hadToolCalls,
|
|
248
|
-
todoContext,
|
|
249
|
-
config,
|
|
250
|
-
isResponding,
|
|
251
|
-
continuationState,
|
|
252
|
-
continuationInProgressRef,
|
|
253
|
-
abortControllerRef,
|
|
254
|
-
agent,
|
|
255
|
-
onDebugMessage,
|
|
256
|
-
setContinuationState,
|
|
257
|
-
);
|
|
258
|
-
},
|
|
259
|
-
[
|
|
260
|
-
config,
|
|
261
|
-
todoContext,
|
|
262
|
-
continuationState,
|
|
263
|
-
isResponding,
|
|
264
|
-
agent,
|
|
265
|
-
onDebugMessage,
|
|
266
|
-
],
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const handleTodoPause = useCallback(
|
|
270
|
-
(reason: string): { type: 'pause'; reason: string; message: string } => {
|
|
271
|
-
todoContext.setPaused(true);
|
|
272
|
-
return {
|
|
273
|
-
type: 'pause' as const,
|
|
274
|
-
reason,
|
|
275
|
-
message: `Task paused: ${reason}`,
|
|
276
|
-
};
|
|
277
|
-
},
|
|
278
|
-
[todoContext],
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
const clearPause = useCallback(() => {
|
|
282
|
-
todoContext.setPaused(false);
|
|
283
|
-
}, [todoContext]);
|
|
284
|
-
|
|
285
|
-
useEffect(
|
|
286
|
-
() => () => {
|
|
287
|
-
abortControllerRef.current?.abort();
|
|
288
|
-
clearTimeout(continuationTimeoutRef.current);
|
|
289
|
-
},
|
|
290
|
-
[],
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
handleStreamCompleted,
|
|
295
|
-
continuationState,
|
|
296
|
-
handleTodoPause,
|
|
297
|
-
clearPause,
|
|
298
|
-
};
|
|
299
|
-
};
|