@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.
Files changed (126) hide show
  1. package/package.json +13 -13
  2. package/src/commands/mcp/add.ts +8 -4
  3. package/src/config/cliArgParser.ts +2 -0
  4. package/src/config/config.ts +2 -0
  5. package/src/config/configBuilder.ts +3 -0
  6. package/src/config/configError.ts +23 -0
  7. package/src/config/extensions/extensionEnablement.ts +62 -1
  8. package/src/config/interactiveContext.ts +2 -1
  9. package/src/config/intermediateConfig.ts +4 -0
  10. package/src/config/keyBindings.ts +29 -3
  11. package/src/config/policy.ts +7 -1
  12. package/src/config/postConfigRuntime.ts +20 -14
  13. package/src/config/settings-schema/schema-ui.ts +9 -0
  14. package/src/config/settingsLoader.ts +21 -17
  15. package/src/config/settingsSchema.ts +2 -2
  16. package/src/config/trustedFolders.ts +275 -58
  17. package/src/config/yargsOptions.ts +7 -0
  18. package/src/generated/git-commit.json +1 -1
  19. package/src/nonInteractiveCli.ts +1 -0
  20. package/src/nonInteractiveCliSupport.ts +171 -51
  21. package/src/runtime/interactiveToolScheduler.ts +6 -6
  22. package/src/services/BuiltinCommandLoader.ts +33 -25
  23. package/src/services/FileCommandLoader.ts +34 -7
  24. package/src/test-utils/async.ts +20 -0
  25. package/src/test-utils/render.tsx +10 -1
  26. package/src/ui/App.tsx +17 -10
  27. package/src/ui/AppContainerRuntime.tsx +7 -9
  28. package/src/ui/cliUiRuntime.ts +28 -0
  29. package/src/ui/commands/authCommand.ts +240 -3
  30. package/src/ui/commands/compressCommand.ts +9 -1
  31. package/src/ui/commands/directoryCommand.tsx +1 -1
  32. package/src/ui/commands/logoutCommand.ts +2 -2
  33. package/src/ui/commands/permissionsCommand.ts +77 -20
  34. package/src/ui/commands/policiesCommand.ts +6 -1
  35. package/src/ui/commands/quotaCommand.ts +501 -0
  36. package/src/ui/commands/statsCommand.ts +2 -2
  37. package/src/ui/commands/statsQuota.ts +1 -1
  38. package/src/ui/commands/subagentCommand.ts +59 -27
  39. package/src/ui/components/AuthDialog.tsx +3 -3
  40. package/src/ui/components/Composer.tsx +1 -0
  41. package/src/ui/components/DialogManager.tsx +9 -2
  42. package/src/ui/components/FolderTrustDialog.tsx +71 -60
  43. package/src/ui/components/PermissionsModifyTrustDialog.tsx +211 -146
  44. package/src/ui/components/ProfileCreateWizard/constants.ts +13 -1
  45. package/src/ui/components/QueuedMessagesPanel.tsx +388 -0
  46. package/src/ui/components/inputPromptHooks.ts +2 -0
  47. package/src/ui/components/inputPromptKeyHandlers.ts +14 -0
  48. package/src/ui/components/inputPromptTypes.ts +6 -0
  49. package/src/ui/components/messages/AiMessage.tsx +24 -12
  50. package/src/ui/components/messages/ToolMessage.tsx +26 -40
  51. package/src/ui/containers/AppContainer/builders/buildUIActions.ts +3 -1
  52. package/src/ui/containers/AppContainer/builders/buildUIState.ts +5 -4
  53. package/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts +3 -33
  54. package/src/ui/containers/AppContainer/hooks/useAppDialogs.ts +2 -14
  55. package/src/ui/containers/AppContainer/hooks/useAppInput.ts +31 -25
  56. package/src/ui/containers/AppContainer/hooks/useAppLayout.ts +90 -60
  57. package/src/ui/containers/AppContainer/hooks/useDisplayPreferences.ts +10 -0
  58. package/src/ui/containers/AppContainer/hooks/useInputHandling.ts +3 -17
  59. package/src/ui/containers/AppContainer/hooks/useKeybindings.ts +51 -38
  60. package/src/ui/containers/AppContainer/hooks/useSteer.ts +51 -0
  61. package/src/ui/contexts/KeypressContext.tsx +13 -2
  62. package/src/ui/contexts/ShellCommandDisplayContext.tsx +103 -0
  63. package/src/ui/contexts/TodoContext.tsx +0 -13
  64. package/src/ui/contexts/TodoProvider.tsx +1 -34
  65. package/src/ui/contexts/UIActionsContext.tsx +2 -1
  66. package/src/ui/contexts/UIStateContext.tsx +3 -2
  67. package/src/ui/hooks/agentStream/streamUtils.ts +0 -24
  68. package/src/ui/hooks/agentStream/thoughtState.ts +110 -30
  69. package/src/ui/hooks/agentStream/types.ts +1 -0
  70. package/src/ui/hooks/agentStream/useAgentEventStream.ts +8 -17
  71. package/src/ui/hooks/agentStream/useAgentStream.ts +2 -2
  72. package/src/ui/hooks/agentStream/useAgentStreamLifecycle.ts +4 -5
  73. package/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts +12 -5
  74. package/src/ui/hooks/agentStream/useQueuedSubmissions.ts +99 -0
  75. package/src/ui/hooks/agentStream/useStreamEventHandlers.ts +4 -13
  76. package/src/ui/hooks/agentStream/useStreamState.ts +60 -41
  77. package/src/ui/hooks/agentStream/useSubmitQuery.ts +284 -59
  78. package/src/ui/hooks/slashCommandProcessorSupport.ts +4 -0
  79. package/src/ui/hooks/useAgentStream-test-helpers.ts +1 -0
  80. package/src/ui/hooks/useAutoAcceptIndicator.ts +13 -0
  81. package/src/ui/hooks/useFolderTrust.ts +118 -81
  82. package/src/ui/hooks/useGitBranchName.ts +79 -33
  83. package/src/ui/hooks/useIdeTrustListener.ts +5 -19
  84. package/src/ui/hooks/usePermissionsModifyTrust.ts +223 -85
  85. package/src/ui/hooks/useReactToolScheduler.ts +13 -17
  86. package/src/ui/hooks/useTodoPausePreserver.ts +0 -55
  87. package/src/ui/layouts/DefaultAppLayout.tsx +2 -0
  88. package/src/ui/layouts/DefaultAppLayoutHelpers.tsx +37 -162
  89. package/src/ui/layouts/InlineContent.tsx +198 -0
  90. package/src/ui/trustDialogHelpers.ts +190 -0
  91. package/src/utils/cleanup-state.ts +115 -0
  92. package/src/utils/cleanup.ts +32 -34
  93. package/src/utils/sandbox-containers.ts +183 -53
  94. package/src/utils/sandbox-exec.ts +9 -3
  95. package/src/zed-integration/acp-terminal-shell-host.ts +112 -0
  96. package/src/zed-integration/acp-types.ts +35 -0
  97. package/src/zed-integration/fileSystemService.ts +1 -1
  98. package/src/zed-integration/runZedIntegration.ts +142 -0
  99. package/src/zed-integration/zed-agent-event-handler.ts +179 -0
  100. package/src/zed-integration/zed-agent-setup.ts +43 -0
  101. package/src/zed-integration/zed-command-registry.ts +167 -0
  102. package/src/zed-integration/zed-config-options.ts +280 -0
  103. package/src/zed-integration/zed-content-utils.ts +2 -2
  104. package/src/zed-integration/zed-helpers.ts +132 -0
  105. package/src/zed-integration/zed-initialize.ts +64 -0
  106. package/src/zed-integration/zed-plan-update.ts +20 -0
  107. package/src/zed-integration/zed-prompt-command.ts +77 -0
  108. package/src/zed-integration/zed-session-config.ts +119 -0
  109. package/src/zed-integration/zed-session-errors.ts +337 -0
  110. package/src/zed-integration/zed-session-events.ts +216 -0
  111. package/src/zed-integration/zed-session-info.ts +255 -0
  112. package/src/zed-integration/zed-session-lifecycle.ts +186 -0
  113. package/src/zed-integration/zed-session-listing.ts +153 -0
  114. package/src/zed-integration/zed-session-loader.ts +193 -0
  115. package/src/zed-integration/zed-session-pagination.ts +240 -0
  116. package/src/zed-integration/zed-session-replay.ts +564 -0
  117. package/src/zed-integration/zed-stream-batcher.ts +236 -0
  118. package/src/zed-integration/zed-terminal-manager.ts +416 -0
  119. package/src/zed-integration/zed-terminal-setup.ts +72 -0
  120. package/src/zed-integration/zed-test-helpers.ts +349 -4
  121. package/src/zed-integration/zed-tool-handler.ts +47 -5
  122. package/src/zed-integration/zedIntegration.ts +538 -587
  123. package/src/services/todo-continuation/todoContinuationService.ts +0 -610
  124. package/src/ui/containers/AppContainer/hooks/useIdeRestartHotkey.ts +0 -33
  125. package/src/ui/containers/AppContainer/hooks/useTodoContinuationFlow.ts +0 -160
  126. package/src/ui/hooks/useTodoContinuation.ts +0 -299
@@ -4,28 +4,71 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { useState, useCallback, useMemo } from 'react';
7
+ import { useState, useCallback, useEffect, useMemo, useRef } from 'react';
8
8
  import * as path from 'node:path';
9
- import type { LoadedTrustedFolders } from '../../config/trustedFolders.js';
10
- import { loadTrustedFolders, TrustLevel } from '../../config/trustedFolders.js';
11
- import { useSettings } from '../contexts/SettingsContext.js';
12
- import { getIdeTrust } from '@vybestack/llxprt-code-core';
9
+ import {
10
+ loadTrustedFolders,
11
+ resolveLocalWorkspaceTrust,
12
+ type LoadedTrustedFolders,
13
+ TrustLevel,
14
+ type TrustedFolderSnapshot,
15
+ } from '../../config/trustedFolders.js';
16
+ import type { CliUiRuntime } from '../cliUiRuntime.js';
17
+ import { useIdeTrustListener } from './useIdeTrustListener.js';
18
+ import { combineTrustUpdateFailure } from '../trustDialogHelpers.js';
19
+ import process from 'node:process';
20
+
21
+ export type PermissionsTrustRuntime = Pick<
22
+ CliUiRuntime,
23
+ | 'getWorkingDir'
24
+ | 'getFolderTrust'
25
+ | 'getIdeClient'
26
+ | 'isTrustedFolder'
27
+ | 'setTrustedFolderLive'
28
+ >;
29
+
30
+ const emptyIdeState: Pick<PermissionsTrustRuntime, 'getIdeClient'> = {
31
+ getIdeClient: () => undefined,
32
+ };
33
+
34
+ function useMountedRef() {
35
+ const mountedRef = useRef(true);
36
+ useEffect(() => {
37
+ mountedRef.current = true;
38
+ return () => {
39
+ mountedRef.current = false;
40
+ };
41
+ }, []);
42
+ return mountedRef;
43
+ }
13
44
 
14
45
  export interface UsePermissionsModifyTrustReturn {
15
- /** Current trust level for the working directory */
16
- currentTrustLevel: TrustLevel | undefined;
17
46
  /** Pending trust level change (before commit) */
18
47
  pendingTrustLevel: TrustLevel | undefined;
19
48
  /** Set a pending trust level change */
20
49
  setPendingTrustLevel: (level: TrustLevel) => void;
21
50
  /** Commit the pending trust level change */
22
- commitTrustLevel: (level?: TrustLevel) => void;
51
+ commitTrustLevel: (level?: TrustLevel) => Promise<
52
+ | { success: true }
53
+ | {
54
+ success: false;
55
+ phase: 'persistence' | 'live';
56
+ error: unknown;
57
+ rollbackSucceeded: boolean;
58
+ }
59
+ >;
60
+ /** Effective local trust level, including inherited rules */
61
+ effectiveLocalTrustLevel: TrustLevel | undefined;
23
62
  /** Whether the workspace is trusted through IDE */
24
- isIdeTrusted: boolean;
25
- /** Whether the workspace is trusted through parent folder */
63
+ isIdeTrusted: boolean | undefined;
64
+ /** Whether the winning local rule is inherited */
26
65
  isParentTrusted: boolean;
27
- /** Whether a restart is required after committing */
28
- requiresRestart: boolean;
66
+ /** Whether a trust change was committed */
67
+ trustChanged: boolean;
68
+ /** The local trust level saved during this dialog session */
69
+ committedTrustLevel: TrustLevel | undefined;
70
+ /** The live effective trust state after the change */
71
+ effectiveTrust: boolean | undefined;
29
72
  /** The current working directory */
30
73
  workingDirectory: string;
31
74
  /** The parent folder name */
@@ -34,101 +77,196 @@ export interface UsePermissionsModifyTrustReturn {
34
77
  trustedFolders: LoadedTrustedFolders;
35
78
  }
36
79
 
37
- /**
38
- * Hook that manages folder trust settings for the permissions dialog.
39
- * Handles current trust level state, pending changes, inherited trust detection,
40
- * and restart requirement detection.
41
- */
42
- export function usePermissionsModifyTrust(): UsePermissionsModifyTrustReturn {
43
- const settings = useSettings();
44
- const cwd = process.cwd();
45
- const parentFolderName = path.basename(path.dirname(cwd));
46
-
47
- const trustedFolders = useMemo(() => loadTrustedFolders(), []);
48
-
49
- // Determine current trust level for the working directory
50
- const currentTrustLevel = useMemo((): TrustLevel | undefined => {
51
- // Find exact match for current directory
52
- const rule = trustedFolders.rules.find(
53
- (r) => path.normalize(r.path) === path.normalize(cwd),
54
- );
55
- return rule?.trustLevel;
56
- }, [trustedFolders, cwd]);
80
+ function restoreSavedTrustLevel(
81
+ trustedFolders: LoadedTrustedFolders,
82
+ snapshot: TrustedFolderSnapshot,
83
+ ): void {
84
+ trustedFolders.restoreSnapshot(snapshot);
85
+ }
57
86
 
58
- // Check if trusted through IDE
59
- const isIdeTrusted = useMemo(() => {
60
- const ideTrust = getIdeTrust();
61
- return ideTrust === true;
62
- }, []);
87
+ async function applyLiveTrustLevel(
88
+ config: PermissionsTrustRuntime,
89
+ trustedFolders: LoadedTrustedFolders,
90
+ folderPath: string,
91
+ ): Promise<boolean> {
92
+ await config.setTrustedFolderLive(
93
+ resolveLocalWorkspaceTrust(
94
+ { folderTrust: config.getFolderTrust() },
95
+ trustedFolders,
96
+ folderPath,
97
+ ) ?? false,
98
+ );
99
+ return config.isTrustedFolder();
100
+ }
63
101
 
64
- // Check if trusted through parent folder
65
- const isParentTrusted = useMemo(() => {
66
- if (isIdeTrusted) return false;
102
+ type TrustCommitResult =
103
+ | { success: true }
104
+ | {
105
+ success: false;
106
+ phase: 'persistence' | 'live';
107
+ error: unknown;
108
+ rollbackSucceeded: boolean;
109
+ };
67
110
 
68
- // Check if any parent rule would trust this directory
69
- for (const rule of trustedFolders.rules) {
70
- if (rule.trustLevel === TrustLevel.TRUST_PARENT) {
71
- const parentPath = path.dirname(rule.path);
72
- if (path.normalize(cwd).startsWith(path.normalize(parentPath))) {
73
- return true;
74
- }
75
- }
76
- if (rule.trustLevel === TrustLevel.TRUST_FOLDER) {
77
- const normalizedRulePath = path.normalize(rule.path);
78
- const normalizedCwd = path.normalize(cwd);
79
- if (
80
- normalizedCwd.startsWith(normalizedRulePath) &&
81
- normalizedCwd !== normalizedRulePath
82
- ) {
83
- return true;
84
- }
85
- }
111
+ async function commitSavedTrustLevel(
112
+ config: PermissionsTrustRuntime | undefined,
113
+ trustedFolders: LoadedTrustedFolders,
114
+ folderPath: string,
115
+ nextLevel: TrustLevel,
116
+ ): Promise<{ result: TrustCommitResult; effectiveTrust?: boolean }> {
117
+ let savedSnapshot: TrustedFolderSnapshot;
118
+ try {
119
+ savedSnapshot = trustedFolders.snapshotValue(folderPath);
120
+ } catch (error) {
121
+ return {
122
+ result: {
123
+ success: false,
124
+ phase: 'persistence',
125
+ error,
126
+ rollbackSucceeded: true,
127
+ },
128
+ };
129
+ }
130
+ const previousLiveTrust = config?.isTrustedFolder() ?? false;
131
+ try {
132
+ trustedFolders.setValue(folderPath, nextLevel);
133
+ } catch (error) {
134
+ return {
135
+ result: {
136
+ success: false,
137
+ phase: 'persistence',
138
+ error,
139
+ rollbackSucceeded: true,
140
+ },
141
+ };
142
+ }
143
+ if (config === undefined) {
144
+ return {
145
+ result: { success: true },
146
+ effectiveTrust: nextLevel !== TrustLevel.DO_NOT_TRUST,
147
+ };
148
+ }
149
+ try {
150
+ return {
151
+ result: { success: true },
152
+ effectiveTrust: await applyLiveTrustLevel(
153
+ config,
154
+ trustedFolders,
155
+ folderPath,
156
+ ),
157
+ };
158
+ } catch (error) {
159
+ const rollbackFailures: unknown[] = [];
160
+ try {
161
+ restoreSavedTrustLevel(trustedFolders, savedSnapshot);
162
+ } catch (rollbackError) {
163
+ rollbackFailures.push(rollbackError);
86
164
  }
87
- return false;
88
- }, [trustedFolders, cwd, isIdeTrusted]);
165
+ try {
166
+ await config.setTrustedFolderLive(previousLiveTrust);
167
+ } catch (rollbackError) {
168
+ rollbackFailures.push(rollbackError);
169
+ }
170
+ const failure = combineTrustUpdateFailure(
171
+ error,
172
+ rollbackFailures,
173
+ 'Live trust update and rollback failed',
174
+ );
175
+ return {
176
+ result: {
177
+ success: false,
178
+ phase: 'live',
179
+ error: failure.error,
180
+ rollbackSucceeded: failure.rollbackSucceeded,
181
+ },
182
+ };
183
+ }
184
+ }
89
185
 
186
+ /**
187
+ * Hook that manages folder trust settings for the permissions dialog.
188
+ * Handles current trust level state, pending changes, inherited trust detection,
189
+ * and live Config updates via setTrustedFolderLive.
190
+ */
191
+ export function usePermissionsModifyTrust(
192
+ config?: PermissionsTrustRuntime,
193
+ ): UsePermissionsModifyTrustReturn {
194
+ const normalizedCwd = path.resolve(config?.getWorkingDir() ?? process.cwd());
195
+ const trustedFolders = useMemo(() => loadTrustedFolders(), []);
196
+ const winningRule = trustedFolders.resolvePathTrust(normalizedCwd);
197
+ const currentEffectiveTrust =
198
+ config?.isTrustedFolder() ?? winningRule?.trusted;
199
+ const { isIdeTrusted } = useIdeTrustListener(config ?? emptyIdeState);
200
+ const isParentTrusted =
201
+ isIdeTrusted === undefined && winningRule?.provenance === 'inherited';
90
202
  const [pendingTrustLevel, setPendingTrustLevel] = useState<
91
203
  TrustLevel | undefined
92
- >(currentTrustLevel);
93
-
94
- const [hasCommitted, setHasCommitted] = useState(false);
95
-
204
+ >(() => trustedFolders.getValue(normalizedCwd));
205
+ const [committedLevel, setCommittedLevel] = useState<TrustLevel>();
206
+ const [effectiveTrust, setEffectiveTrust] = useState(currentEffectiveTrust);
207
+ const commitQueueRef = useRef<Promise<void>>(Promise.resolve());
208
+ const mountedRef = useMountedRef();
209
+ const currentWorkingDirectoryRef = useRef(normalizedCwd);
210
+ currentWorkingDirectoryRef.current = normalizedCwd;
211
+ useEffect(() => {
212
+ setPendingTrustLevel(trustedFolders.getValue(normalizedCwd));
213
+ setCommittedLevel(undefined);
214
+ }, [normalizedCwd, trustedFolders]);
215
+ useEffect(
216
+ () => setEffectiveTrust(currentEffectiveTrust),
217
+ [currentEffectiveTrust],
218
+ );
96
219
  const commitTrustLevel = useCallback(
97
- (level?: TrustLevel) => {
220
+ (level?: TrustLevel): Promise<TrustCommitResult> => {
98
221
  const nextLevel = level ?? pendingTrustLevel;
99
222
  if (nextLevel === undefined) {
100
- return;
223
+ return Promise.resolve({ success: true });
101
224
  }
102
225
 
103
- setPendingTrustLevel(nextLevel);
104
- trustedFolders.setValue(cwd, nextLevel);
105
- setHasCommitted(true);
226
+ const runCommit = async (): Promise<TrustCommitResult> => {
227
+ const commit = await commitSavedTrustLevel(
228
+ config,
229
+ trustedFolders,
230
+ normalizedCwd,
231
+ nextLevel,
232
+ );
233
+ if (!commit.result.success) {
234
+ return commit.result;
235
+ }
236
+ if (
237
+ mountedRef.current &&
238
+ currentWorkingDirectoryRef.current === normalizedCwd
239
+ ) {
240
+ setEffectiveTrust(commit.effectiveTrust);
241
+ setPendingTrustLevel(nextLevel);
242
+ setCommittedLevel(nextLevel);
243
+ }
244
+ return { success: true };
245
+ };
246
+ const commit = commitQueueRef.current.then(runCommit, runCommit);
247
+ commitQueueRef.current = commit.then(
248
+ () => undefined,
249
+ () => undefined,
250
+ );
251
+ return commit;
106
252
  },
107
- [pendingTrustLevel, trustedFolders, cwd],
253
+ [pendingTrustLevel, trustedFolders, normalizedCwd, config, mountedRef],
108
254
  );
109
255
 
110
- // Determine if restart is required after committing
111
- const requiresRestart = useMemo(() => {
112
- if (!hasCommitted) return false;
113
-
114
- // Check if folder trust feature is enabled
115
- const folderTrustEnabled = settings.merged.folderTrust ?? false;
116
- if (!folderTrustEnabled) return false;
117
-
118
- // A restart is required if we changed the trust level
119
- return pendingTrustLevel !== currentTrustLevel;
120
- }, [hasCommitted, pendingTrustLevel, currentTrustLevel, settings]);
256
+ const trustChanged = committedLevel !== undefined;
121
257
 
122
258
  return {
123
- currentTrustLevel,
124
259
  pendingTrustLevel,
125
260
  setPendingTrustLevel,
126
261
  commitTrustLevel,
262
+ effectiveLocalTrustLevel: winningRule?.rule.trustLevel,
127
263
  isIdeTrusted,
128
264
  isParentTrusted,
129
- requiresRestart,
130
- workingDirectory: cwd,
131
- parentFolderName,
265
+ trustChanged,
266
+ committedTrustLevel: committedLevel,
267
+ effectiveTrust,
268
+ workingDirectory: normalizedCwd,
269
+ parentFolderName: path.basename(path.dirname(normalizedCwd)),
132
270
  trustedFolders,
133
271
  };
134
272
  }
@@ -14,7 +14,7 @@ import {
14
14
  type CancelledToolCall,
15
15
  type ToolCall,
16
16
  type EditorType,
17
- type AnsiOutput,
17
+ type LiveOutputUpdate,
18
18
  type MessageBus,
19
19
  accumulateLiveOutput,
20
20
  } from '@vybestack/llxprt-code-core';
@@ -86,7 +86,7 @@ export type CancelAllFn = () => void;
86
86
  export type ReplaceToolCallsFn = (calls: ToolCall[]) => void;
87
87
  export type UpdateToolOutputFn = (
88
88
  toolCallId: string,
89
- chunk: string | AnsiOutput,
89
+ update: LiveOutputUpdate,
90
90
  ) => void;
91
91
  export type ReactToolSchedulerResult = readonly [
92
92
  TrackedToolCall[],
@@ -104,7 +104,7 @@ export type ReactToolSchedulerResult = readonly [
104
104
  function updatePendingItemWithOutput(
105
105
  prevItem: HistoryItemWithoutId | null,
106
106
  toolCallId: string,
107
- outputChunk: string | AnsiOutput,
107
+ update: LiveOutputUpdate,
108
108
  ): HistoryItemWithoutId | null {
109
109
  if (prevItem?.type !== 'tool_group') return prevItem;
110
110
  return {
@@ -116,7 +116,7 @@ function updatePendingItemWithOutput(
116
116
  ...toolDisplay,
117
117
  resultDisplay: accumulateLiveOutput(
118
118
  toolDisplay.resultDisplay,
119
- outputChunk,
119
+ update,
120
120
  ),
121
121
  }
122
122
  : toolDisplay,
@@ -148,13 +148,13 @@ function mapCallsWithDisplayClearedFlag(
148
148
  function updateCallsWithLiveOutput(
149
149
  prevCalls: TrackedToolCall[],
150
150
  toolCallId: string,
151
- outputChunk: string | AnsiOutput,
151
+ update: LiveOutputUpdate,
152
152
  ): TrackedToolCall[] {
153
153
  return prevCalls.map((call) =>
154
154
  call.request.callId === toolCallId && call.status === 'executing'
155
155
  ? {
156
156
  ...call,
157
- liveOutput: accumulateLiveOutput(call.liveOutput, outputChunk),
157
+ liveOutput: accumulateLiveOutput(call.liveOutput, update),
158
158
  }
159
159
  : call,
160
160
  );
@@ -209,7 +209,7 @@ function useToolCallUpdaters(
209
209
  updateToolCallOutput: (
210
210
  schedulerId: symbol,
211
211
  toolCallId: string,
212
- outputChunk: string | AnsiOutput,
212
+ update: LiveOutputUpdate,
213
213
  ) => void;
214
214
  replaceToolCallsForScheduler: (
215
215
  schedulerId: symbol,
@@ -240,23 +240,19 @@ function useToolCallUpdaters(
240
240
  );
241
241
 
242
242
  const updateToolCallOutput = useCallback(
243
- (
244
- schedulerId: symbol,
245
- toolCallId: string,
246
- outputChunk: string | AnsiOutput,
247
- ) => {
243
+ (schedulerId: symbol, toolCallId: string, update: LiveOutputUpdate) => {
248
244
  updateToolCallsForScheduler(schedulerId, (prevCalls) => {
249
245
  const nextCalls = updateCallsWithLiveOutput(
250
246
  prevCalls,
251
247
  toolCallId,
252
- outputChunk,
248
+ update,
253
249
  );
254
250
  return nextCalls.some((call, index) => call !== prevCalls[index])
255
251
  ? nextCalls
256
252
  : prevCalls;
257
253
  });
258
254
  setPendingHistoryItem((prev) =>
259
- updatePendingItemWithOutput(prev, toolCallId, outputChunk),
255
+ updatePendingItemWithOutput(prev, toolCallId, update),
260
256
  );
261
257
  },
262
258
  [updateToolCallsForScheduler, setPendingHistoryItem],
@@ -378,7 +374,7 @@ function useSchedulerRefs(
378
374
  updateToolCallOutput: (
379
375
  schedulerId: symbol,
380
376
  toolCallId: string,
381
- chunk: string | AnsiOutput,
377
+ update: LiveOutputUpdate,
382
378
  ) => void;
383
379
  replaceToolCallsForScheduler: (
384
380
  schedulerId: symbol,
@@ -470,8 +466,8 @@ function useBoundDisplayUpdaters(
470
466
  [replaceToolCallsForScheduler, mainSchedulerId],
471
467
  );
472
468
  const updateToolOutput = useCallback(
473
- (toolCallId: string, chunk: string | AnsiOutput) =>
474
- updateToolCallOutput(mainSchedulerId, toolCallId, chunk),
469
+ (toolCallId: string, update: LiveOutputUpdate) =>
470
+ updateToolCallOutput(mainSchedulerId, toolCallId, update),
475
471
  [updateToolCallOutput, mainSchedulerId],
476
472
  );
477
473
  return { replaceToolCalls, updateToolOutput };
@@ -4,7 +4,6 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { useCallback } from 'react';
8
7
  import { type Todo } from '@vybestack/llxprt-code-core';
9
8
 
10
9
  /**
@@ -20,57 +19,3 @@ export function shouldClearTodos(todos: Todo[]): boolean {
20
19
  }
21
20
  return todos.every((t) => t.status === 'completed');
22
21
  }
23
-
24
- export class TodoPausePreserver {
25
- private preserveNextSubmission = false;
26
-
27
- /**
28
- * @plan PLAN-20260129-TODOPERSIST.P01
29
- * Modified to accept todos parameter for conditional clearing logic.
30
- */
31
- handleSubmit(onClear: () => void, todos: Todo[]): void {
32
- if (this.preserveNextSubmission) {
33
- this.preserveNextSubmission = false;
34
- return;
35
- }
36
- if (shouldClearTodos(todos)) {
37
- onClear();
38
- }
39
- }
40
-
41
- registerTodoPause(): void {
42
- this.preserveNextSubmission = true;
43
- }
44
- }
45
-
46
- interface UseTodoPausePreserverOptions {
47
- controller: TodoPausePreserver;
48
- updateTodos: (todos: Todo[]) => void;
49
- handleFinalSubmit: (submittedValue: string) => void;
50
- /**
51
- * @plan PLAN-20260129-TODOPERSIST.P01
52
- * Current todos list required for conditional clearing.
53
- */
54
- todos: Todo[];
55
- }
56
-
57
- export const useTodoPausePreserver = ({
58
- controller,
59
- updateTodos,
60
- handleFinalSubmit,
61
- todos,
62
- }: UseTodoPausePreserverOptions) => {
63
- const handleUserInputSubmit = useCallback(
64
- (submittedValue: string) => {
65
- controller.handleSubmit(() => {
66
- updateTodos([]);
67
- }, todos);
68
- handleFinalSubmit(submittedValue);
69
- },
70
- [controller, updateTodos, handleFinalSubmit, todos],
71
- );
72
-
73
- return {
74
- handleUserInputSubmit,
75
- };
76
- };
@@ -224,6 +224,8 @@ function buildMainControlsProps(
224
224
  history: uiState.history,
225
225
  inputWidth: uiState.inputWidth,
226
226
  isTodoPanelCollapsed: uiState.isTodoPanelCollapsed,
227
+ isQueuedMessagesPanelCollapsed: uiState.isQueuedMessagesPanelCollapsed,
228
+ queuedSubmissions: uiState.queuedSubmissions,
227
229
  showTodoPanelSetting: layoutSettings.showTodoPanelSetting,
228
230
  dialogsVisible: hasActiveDialog(uiState),
229
231
  hideContextSummary: layoutSettings.hideContextSummary,