@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
@@ -7,19 +7,32 @@
7
7
  import { useState, useCallback, useEffect, useRef } from 'react';
8
8
  import { ExitCodes } from '@vybestack/llxprt-code-core';
9
9
  import { DebugLogger } from '@vybestack/llxprt-code-telemetry';
10
- import type { LoadedSettings, Settings } from '../../config/settings.js';
10
+ import type { LoadedSettings } from '../../config/settings.js';
11
11
  import { FolderTrustChoice } from '../components/FolderTrustDialog.js';
12
12
  import {
13
13
  loadTrustedFolders,
14
+ resolveLocalWorkspaceTrust,
14
15
  TrustLevel,
15
16
  isWorkspaceTrusted,
17
+ type TrustedFolderSnapshot,
16
18
  } from '../../config/trustedFolders.js';
17
19
  import { type HistoryItemWithoutId, MessageType } from '../types.js';
20
+ import type { CliUiRuntime } from '../cliUiRuntime.js';
18
21
  import process from 'node:process';
22
+ import {
23
+ combineTrustUpdateFailure,
24
+ getTrustCommitErrorMessage,
25
+ } from '../trustDialogHelpers.js';
26
+
27
+ export type FolderTrustRuntime = Pick<
28
+ CliUiRuntime,
29
+ 'getWorkingDir' | 'setTrustedFolderLive' | 'isTrustedFolder'
30
+ >;
19
31
 
20
32
  const debug = new DebugLogger('llxprt:ui:useFolderTrust');
21
33
 
22
34
  type AddItemFn = (item: HistoryItemWithoutId, timestamp: number) => number;
35
+ type SetDialogOpenFn = (open: boolean) => void;
23
36
 
24
37
  function getTrustLevelFromChoice(choice: FolderTrustChoice): TrustLevel | null {
25
38
  switch (choice) {
@@ -34,39 +47,6 @@ function getTrustLevelFromChoice(choice: FolderTrustChoice): TrustLevel | null {
34
47
  }
35
48
  }
36
49
 
37
- function saveTrustLevel(
38
- cwd: string,
39
- trustLevel: TrustLevel,
40
- addItem?: AddItemFn,
41
- ): boolean {
42
- try {
43
- const trustedFolders = loadTrustedFolders();
44
- trustedFolders.setValue(cwd, trustLevel);
45
- return true;
46
- } catch {
47
- if (addItem) {
48
- addItem(
49
- {
50
- type: MessageType.ERROR,
51
- text: 'Failed to save trust settings. Exiting LLxprt Code.',
52
- },
53
- Date.now(),
54
- );
55
- }
56
- setTimeout(() => {
57
- process.exit(ExitCodes.FATAL_CONFIG_ERROR);
58
- }, 100);
59
- return false;
60
- }
61
- }
62
-
63
- function computeNewTrustedState(trustLevel: TrustLevel): boolean {
64
- return (
65
- trustLevel === TrustLevel.TRUST_FOLDER ||
66
- trustLevel === TrustLevel.TRUST_PARENT
67
- );
68
- }
69
-
70
50
  function showStartupMessage(
71
51
  trusted: boolean | undefined = undefined,
72
52
  addItem: AddItemFn | undefined,
@@ -76,77 +56,134 @@ function showStartupMessage(
76
56
  debug.log(
77
57
  'Folder is untrusted - displaying permissions command hint on startup',
78
58
  );
79
- if (addItem) {
80
- addItem(
81
- {
82
- type: MessageType.INFO,
83
- text: 'This folder is not trusted. Some features may be disabled. Use the `/permissions` command to change the trust level.',
84
- },
85
- Date.now(),
86
- );
87
- }
59
+ addItem?.(
60
+ {
61
+ type: MessageType.INFO,
62
+ text: 'This folder is not trusted. Some features may be disabled. Use the `/permissions` command to change the trust level.',
63
+ },
64
+ Date.now(),
65
+ );
88
66
  startupMessageSent.current = true;
89
67
  }
90
68
  }
91
69
 
70
+ async function applyFolderTrustChoice(
71
+ choice: FolderTrustChoice,
72
+ settings: LoadedSettings,
73
+ config: FolderTrustRuntime | undefined,
74
+ addItem: AddItemFn | undefined,
75
+ setDialogOpen: SetDialogOpenFn,
76
+ mountedRef: React.MutableRefObject<boolean>,
77
+ ): Promise<void> {
78
+ const trustLevel = getTrustLevelFromChoice(choice);
79
+ if (trustLevel === null) {
80
+ return;
81
+ }
82
+ const workingDirectory = config?.getWorkingDir() ?? process.cwd();
83
+ let trustedFolders: ReturnType<typeof loadTrustedFolders> | undefined;
84
+ let savedSnapshot: TrustedFolderSnapshot | undefined;
85
+ let previousLiveTrust = false;
86
+ let failedPhase: 'persistence' | 'live' = 'persistence';
87
+ try {
88
+ trustedFolders = loadTrustedFolders();
89
+ savedSnapshot = trustedFolders.snapshotValue(workingDirectory);
90
+ previousLiveTrust = config?.isTrustedFolder() ?? false;
91
+ trustedFolders.setValue(workingDirectory, trustLevel);
92
+ const newIsTrusted =
93
+ resolveLocalWorkspaceTrust(
94
+ settings.merged,
95
+ trustedFolders,
96
+ workingDirectory,
97
+ ) ?? false;
98
+ failedPhase = 'live';
99
+ await config?.setTrustedFolderLive(newIsTrusted);
100
+ if (mountedRef.current) {
101
+ setDialogOpen(false);
102
+ }
103
+ } catch (error) {
104
+ const rollbackFailures: unknown[] = [];
105
+ if (trustedFolders !== undefined && savedSnapshot !== undefined) {
106
+ try {
107
+ trustedFolders.restoreSnapshot(savedSnapshot);
108
+ } catch (rollbackError) {
109
+ rollbackFailures.push(rollbackError);
110
+ }
111
+ }
112
+ if (failedPhase === 'live') {
113
+ try {
114
+ await config?.setTrustedFolderLive(previousLiveTrust);
115
+ } catch (rollbackError) {
116
+ rollbackFailures.push(rollbackError);
117
+ }
118
+ }
119
+ if (!mountedRef.current) {
120
+ return;
121
+ }
122
+ const failure = combineTrustUpdateFailure(
123
+ error,
124
+ rollbackFailures,
125
+ 'Trust update and rollback failed',
126
+ );
127
+ const message = `${getTrustCommitErrorMessage(
128
+ failedPhase,
129
+ failure.error,
130
+ failure.rollbackSucceeded,
131
+ )} Exiting LLxprt Code.`;
132
+ debug.error(message);
133
+ addItem?.({ type: MessageType.ERROR, text: message }, Date.now());
134
+ setTimeout(() => process.exit(ExitCodes.FATAL_CONFIG_ERROR), 100);
135
+ }
136
+ }
137
+
92
138
  export const useFolderTrust = (
93
139
  settings: LoadedSettings,
94
140
  addItem?: AddItemFn,
141
+ config?: FolderTrustRuntime,
95
142
  ) => {
96
- // Folder trust feature flag removed - now using settings directly
97
143
  const { folderTrust } = settings.merged;
98
- const initialTrust = isWorkspaceTrusted({ folderTrust } as Settings);
99
- const [isTrusted, setIsTrusted] = useState<boolean | undefined>(initialTrust);
144
+ const initialTrust = isWorkspaceTrusted(
145
+ settings.merged,
146
+ config?.getWorkingDir() ?? process.cwd(),
147
+ );
100
148
  const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = useState(
101
149
  initialTrust === undefined,
102
150
  );
103
- const [isRestarting, setIsRestarting] = useState(false);
104
151
  const startupMessageSent = useRef(false);
105
152
  const previousFolderTrust = useRef(folderTrust);
153
+ const mountedRef = useRef(true);
154
+
155
+ useEffect(() => {
156
+ mountedRef.current = true;
157
+ return () => {
158
+ mountedRef.current = false;
159
+ };
160
+ }, []);
106
161
 
107
162
  useEffect(() => {
108
163
  const folderTrustChanged = previousFolderTrust.current !== folderTrust;
109
164
  previousFolderTrust.current = folderTrust;
110
- const trusted = isWorkspaceTrusted({ folderTrust } as Settings);
165
+ const trusted = isWorkspaceTrusted(
166
+ settings.merged,
167
+ config?.getWorkingDir() ?? process.cwd(),
168
+ );
111
169
  if (folderTrustChanged) {
112
- setIsTrusted(trusted);
113
170
  setIsFolderTrustDialogOpen(trusted === undefined);
114
171
  }
115
-
116
172
  showStartupMessage(trusted, addItem, startupMessageSent);
117
- }, [folderTrust, addItem]);
173
+ }, [folderTrust, addItem, config, settings.merged]);
118
174
 
119
175
  const handleFolderTrustSelect = useCallback(
120
- (choice: FolderTrustChoice) => {
121
- const trustLevel = getTrustLevelFromChoice(choice);
122
- if (trustLevel === null) {
123
- return;
124
- }
125
-
126
- const cwd = process.cwd();
127
- const wasTrusted = isTrusted ?? false;
128
-
129
- if (!saveTrustLevel(cwd, trustLevel, addItem)) {
130
- return;
131
- }
132
-
133
- const newIsTrusted = computeNewTrustedState(trustLevel);
134
- setIsTrusted(newIsTrusted);
135
-
136
- const needsRestart = wasTrusted !== newIsTrusted;
137
- if (needsRestart) {
138
- setIsRestarting(true);
139
- setIsFolderTrustDialogOpen(true);
140
- } else {
141
- setIsFolderTrustDialogOpen(false);
142
- }
143
- },
144
- [isTrusted, addItem],
176
+ (choice: FolderTrustChoice): Promise<void> =>
177
+ applyFolderTrustChoice(
178
+ choice,
179
+ settings,
180
+ config,
181
+ addItem,
182
+ setIsFolderTrustDialogOpen,
183
+ mountedRef,
184
+ ),
185
+ [addItem, config, settings],
145
186
  );
146
187
 
147
- return {
148
- isFolderTrustDialogOpen,
149
- handleFolderTrustSelect,
150
- isRestarting,
151
- };
188
+ return { isFolderTrustDialogOpen, handleFolderTrustSelect };
152
189
  };
@@ -4,56 +4,97 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { useState, useEffect, useCallback } from 'react';
7
+ import { useState, useEffect, useCallback, useRef } from 'react';
8
8
  import { exec } from 'node:child_process';
9
9
  import fs from 'node:fs';
10
10
  import fsPromises from 'node:fs/promises';
11
11
  import path from 'path';
12
12
 
13
+ /**
14
+ * Debounce window for coalescing rapid reflog writes caused by compound
15
+ * git commands (e.g. `git checkout main && git fetch && git pull`). Without
16
+ * this, each write to `.git/logs/HEAD` spawns an overlapping `exec()`,
17
+ * increasing the chance that a stale result clobbers the latest branch name.
18
+ *
19
+ * 200ms was chosen empirically: compound git commands (checkout + fetch +
20
+ * pull) typically write the reflog 2-3 times within ~50ms. A 200ms window
21
+ * safely covers the burst while keeping the visible latency below a typical
22
+ * user's perception threshold. It is also well under the 3000ms fs.watchFile
23
+ * polling interval, so it never delays a legitimate single-command update
24
+ * beyond what the polling itself already imposes.
25
+ */
26
+ const FETCH_DEBOUNCE_MS = 200;
27
+
28
+ /** Exposed for tests so timing assertions track the production constant. */
29
+ export { FETCH_DEBOUNCE_MS };
30
+
13
31
  export function useGitBranchName(cwd: string): string | undefined {
14
32
  const [branchName, setBranchName] = useState<string | undefined>(undefined);
15
33
 
16
- const fetchBranchName = useCallback(
17
- () =>
18
- exec(
19
- 'git rev-parse --abbrev-ref HEAD',
20
- { cwd },
21
- (error, stdout, _stderr) => {
22
- if (error) {
23
- setBranchName(undefined);
24
- return;
25
- }
26
- const branch = stdout.toString().trim();
27
- if (branch && branch !== 'HEAD') {
28
- setBranchName(branch);
29
- } else {
30
- exec(
31
- 'git rev-parse --short HEAD',
32
- { cwd },
33
- (error, stdout, _stderr) => {
34
- if (error) {
35
- setBranchName(undefined);
36
- return;
37
- }
38
- setBranchName(stdout.toString().trim());
39
- },
40
- );
41
- }
42
- },
43
- ),
44
- [cwd, setBranchName],
45
- );
34
+ // Monotonic token that guards against stale exec callbacks. Each fetch
35
+ // increments the token; when an exec callback fires, it only applies the
36
+ // result if its token still matches the latest one. This prevents an
37
+ // earlier-started exec (whose callback resolves after a newer one) from
38
+ // overwriting the branch name with a stale value.
39
+ const fetchTokenRef = useRef(0);
40
+ const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
41
+
42
+ const cancelledRef = useRef(false);
43
+
44
+ const fetchBranchName = useCallback(() => {
45
+ const token = ++fetchTokenRef.current;
46
+ exec(
47
+ 'git rev-parse --abbrev-ref HEAD',
48
+ { cwd },
49
+ (error, stdout, _stderr) => {
50
+ if (cancelledRef.current || token !== fetchTokenRef.current) return;
51
+ if (error) {
52
+ setBranchName(undefined);
53
+ return;
54
+ }
55
+ const branch = stdout.toString().trim();
56
+ if (branch && branch !== 'HEAD') {
57
+ setBranchName(branch);
58
+ } else {
59
+ exec(
60
+ 'git rev-parse --short HEAD',
61
+ { cwd },
62
+ (error, stdout, _stderr) => {
63
+ if (cancelledRef.current || token !== fetchTokenRef.current)
64
+ return;
65
+ if (error) {
66
+ setBranchName(undefined);
67
+ return;
68
+ }
69
+ setBranchName(stdout.toString().trim());
70
+ },
71
+ );
72
+ }
73
+ },
74
+ );
75
+ }, [cwd]);
76
+
77
+ const scheduleFetch = useCallback(() => {
78
+ if (debounceRef.current) {
79
+ clearTimeout(debounceRef.current);
80
+ }
81
+ debounceRef.current = setTimeout(() => {
82
+ debounceRef.current = null;
83
+ fetchBranchName();
84
+ }, FETCH_DEBOUNCE_MS);
85
+ }, [fetchBranchName]);
46
86
 
47
87
  useEffect(() => {
48
88
  fetchBranchName(); // Initial fetch
49
89
 
50
90
  const gitLogsHeadPath = path.join(cwd, '.git', 'logs', 'HEAD');
51
91
  let cancelled = false;
92
+ cancelledRef.current = false;
52
93
 
53
94
  const onGitLogsHeadChange = (curr: fs.Stats, prev: fs.Stats) => {
54
95
  if (cancelled) return;
55
96
  if (curr.mtimeMs !== prev.mtimeMs || curr.size !== prev.size) {
56
- fetchBranchName();
97
+ scheduleFetch();
57
98
  }
58
99
  };
59
100
 
@@ -77,9 +118,14 @@ export function useGitBranchName(cwd: string): string | undefined {
77
118
 
78
119
  return () => {
79
120
  cancelled = true;
121
+ cancelledRef.current = true;
122
+ if (debounceRef.current) {
123
+ clearTimeout(debounceRef.current);
124
+ debounceRef.current = null;
125
+ }
80
126
  fs.unwatchFile(gitLogsHeadPath, onGitLogsHeadChange);
81
127
  };
82
- }, [cwd, fetchBranchName]);
128
+ }, [cwd, fetchBranchName, scheduleFetch]);
83
129
 
84
130
  return branchName;
85
131
  }
@@ -4,17 +4,16 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';
7
+ import { useCallback, useSyncExternalStore } from 'react';
8
8
 
9
9
  import { ideContext } from '@vybestack/llxprt-code-core';
10
10
  import type { IdeState } from '../cliUiRuntime.js';
11
11
 
12
12
  /**
13
- * This hook listens for trust status updates from the IDE companion extension.
14
- * It provides the current trust status from the IDE and a flag indicating
15
- * if a restart is needed because the trust state has changed.
13
+ * This hook listens for trust status updates from the IDE companion extension
14
+ * and provides the current IDE trust status.
16
15
  */
17
- export function useIdeTrustListener(ide: IdeState) {
16
+ export function useIdeTrustListener(ide: Pick<IdeState, 'getIdeClient'>) {
18
17
  const subscribe = useCallback(
19
18
  (onStoreChange: () => void) => {
20
19
  const ideClient = ide.getIdeClient();
@@ -34,18 +33,5 @@ export function useIdeTrustListener(ide: IdeState) {
34
33
 
35
34
  const isIdeTrusted = useSyncExternalStore(subscribe, getSnapshot);
36
35
 
37
- const [needsRestart, setNeedsRestart] = useState(false);
38
- const [initialTrustValue] = useState(isIdeTrusted);
39
-
40
- useEffect(() => {
41
- if (
42
- !needsRestart &&
43
- initialTrustValue !== undefined &&
44
- initialTrustValue !== isIdeTrusted
45
- ) {
46
- setNeedsRestart(true);
47
- }
48
- }, [isIdeTrusted, initialTrustValue, needsRestart]);
49
-
50
- return { isIdeTrusted, needsRestart };
36
+ return { isIdeTrusted };
51
37
  }