@vybestack/llxprt-code-core 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 (168) hide show
  1. package/dist/src/config/config.d.ts +20 -0
  2. package/dist/src/config/config.js +143 -23
  3. package/dist/src/config/config.js.map +1 -1
  4. package/dist/src/config/configBaseCore.d.ts +6 -2
  5. package/dist/src/config/configBaseCore.js +8 -0
  6. package/dist/src/config/configBaseCore.js.map +1 -1
  7. package/dist/src/config/configConstructor.d.ts +2 -0
  8. package/dist/src/config/configConstructor.js +9 -1
  9. package/dist/src/config/configConstructor.js.map +1 -1
  10. package/dist/src/config/configTestHarness.d.ts +8 -6
  11. package/dist/src/config/configTestHarness.js +10 -4
  12. package/dist/src/config/configTestHarness.js.map +1 -1
  13. package/dist/src/config/configTypes.d.ts +7 -2
  14. package/dist/src/config/configTypes.js +2 -0
  15. package/dist/src/config/configTypes.js.map +1 -1
  16. package/dist/src/config/liveTrustTransitionLifecycle.d.ts +35 -0
  17. package/dist/src/config/liveTrustTransitionLifecycle.js +180 -0
  18. package/dist/src/config/liveTrustTransitionLifecycle.js.map +1 -0
  19. package/dist/src/config/schedulerSingleton.d.ts +2 -2
  20. package/dist/src/config/schedulerSingleton.js.map +1 -1
  21. package/dist/src/core/compression/types.d.ts +48 -1
  22. package/dist/src/core/compression/types.js +41 -0
  23. package/dist/src/core/compression/types.js.map +1 -1
  24. package/dist/src/core/model-limits.json +96 -0
  25. package/dist/src/core/model-limits.schema.d.ts +123 -0
  26. package/dist/src/core/model-limits.schema.js +75 -0
  27. package/dist/src/core/model-limits.schema.js.map +1 -0
  28. package/dist/src/core/tokenLimits.d.ts +6 -8
  29. package/dist/src/core/tokenLimits.js +56 -132
  30. package/dist/src/core/tokenLimits.js.map +1 -1
  31. package/dist/src/core/turn.d.ts +20 -3
  32. package/dist/src/core/turn.js +20 -3
  33. package/dist/src/core/turn.js.map +1 -1
  34. package/dist/src/hooks/hookRegistry.d.ts +1 -1
  35. package/dist/src/hooks/hookRegistry.js +3 -1
  36. package/dist/src/hooks/hookRegistry.js.map +1 -1
  37. package/dist/src/hooks/hookRunner.d.ts +6 -0
  38. package/dist/src/hooks/hookRunner.js +22 -6
  39. package/dist/src/hooks/hookRunner.js.map +1 -1
  40. package/dist/src/hooks/hookSystem.d.ts +10 -1
  41. package/dist/src/hooks/hookSystem.js +69 -12
  42. package/dist/src/hooks/hookSystem.js.map +1 -1
  43. package/dist/src/index.d.ts +1 -0
  44. package/dist/src/index.js +1 -0
  45. package/dist/src/index.js.map +1 -1
  46. package/dist/src/llm-types/geminiContent.d.ts +3 -0
  47. package/dist/src/policy/config.d.ts +4 -2
  48. package/dist/src/policy/config.js +22 -18
  49. package/dist/src/policy/config.js.map +1 -1
  50. package/dist/src/recording/ReplayEngine.js +39 -2
  51. package/dist/src/recording/ReplayEngine.js.map +1 -1
  52. package/dist/src/recording/SessionDiscovery.d.ts +8 -0
  53. package/dist/src/recording/SessionDiscovery.js +88 -3
  54. package/dist/src/recording/SessionDiscovery.js.map +1 -1
  55. package/dist/src/recording/SessionLockManager.d.ts +3 -0
  56. package/dist/src/recording/SessionLockManager.js +9 -3
  57. package/dist/src/recording/SessionLockManager.js.map +1 -1
  58. package/dist/src/recording/SessionRecordingService.d.ts +14 -1
  59. package/dist/src/recording/SessionRecordingService.js +27 -3
  60. package/dist/src/recording/SessionRecordingService.js.map +1 -1
  61. package/dist/src/recording/index.d.ts +4 -3
  62. package/dist/src/recording/index.js +4 -3
  63. package/dist/src/recording/index.js.map +1 -1
  64. package/dist/src/recording/replay-test-helpers.d.ts +5 -0
  65. package/dist/src/recording/replay-test-helpers.js +13 -0
  66. package/dist/src/recording/replay-test-helpers.js.map +1 -1
  67. package/dist/src/recording/resumeNotFoundMessages.d.ts +56 -0
  68. package/dist/src/recording/resumeNotFoundMessages.js +64 -0
  69. package/dist/src/recording/resumeNotFoundMessages.js.map +1 -0
  70. package/dist/src/recording/resumeSession.js +3 -2
  71. package/dist/src/recording/resumeSession.js.map +1 -1
  72. package/dist/src/recording/sessionManagement.d.ts +3 -0
  73. package/dist/src/recording/sessionManagement.js +38 -19
  74. package/dist/src/recording/sessionManagement.js.map +1 -1
  75. package/dist/src/recording/types.d.ts +42 -3
  76. package/dist/src/recording/types.js +1 -1
  77. package/dist/src/recording/types.js.map +1 -1
  78. package/dist/src/runtime/AgentRuntimeLoader.js +2 -1
  79. package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -1
  80. package/dist/src/scheduler/liveOutput.d.ts +10 -19
  81. package/dist/src/scheduler/liveOutput.js +21 -21
  82. package/dist/src/scheduler/liveOutput.js.map +1 -1
  83. package/dist/src/scheduler/types.d.ts +2 -2
  84. package/dist/src/services/history/ContentConverters.d.ts +4 -0
  85. package/dist/src/services/history/ContentConverters.js +77 -59
  86. package/dist/src/services/history/ContentConverters.js.map +1 -1
  87. package/dist/src/services/history/HistoryService.d.ts +13 -1
  88. package/dist/src/services/history/HistoryService.js +71 -0
  89. package/dist/src/services/history/HistoryService.js.map +1 -1
  90. package/dist/src/services/history/IContent.d.ts +10 -0
  91. package/dist/src/services/history/IContent.js.map +1 -1
  92. package/dist/src/skills/skillManager.d.ts +11 -1
  93. package/dist/src/skills/skillManager.js +21 -1
  94. package/dist/src/skills/skillManager.js.map +1 -1
  95. package/dist/src/test-utils/mock-tool.d.ts +3 -3
  96. package/dist/src/test-utils/mock-tool.js.map +1 -1
  97. package/dist/src/test-utils/tools.d.ts +2 -2
  98. package/dist/src/test-utils/tools.js.map +1 -1
  99. package/dist/src/tools-adapters/CoreShellToolHostAdapter.js +1 -6
  100. package/dist/src/tools-adapters/CoreShellToolHostAdapter.js.map +1 -1
  101. package/dist/src/utils/browser-profile-discovery.d.ts +40 -0
  102. package/dist/src/utils/browser-profile-discovery.js +251 -0
  103. package/dist/src/utils/browser-profile-discovery.js.map +1 -0
  104. package/dist/src/utils/env.d.ts +12 -0
  105. package/dist/src/utils/env.js +15 -0
  106. package/dist/src/utils/env.js.map +1 -0
  107. package/dist/src/utils/events.d.ts +10 -1
  108. package/dist/src/utils/events.js +22 -0
  109. package/dist/src/utils/events.js.map +1 -1
  110. package/dist/src/utils/fileUtils.js +8 -1
  111. package/dist/src/utils/fileUtils.js.map +1 -1
  112. package/dist/src/utils/retry.js +3 -4
  113. package/dist/src/utils/retry.js.map +1 -1
  114. package/dist/src/utils/secure-browser-launcher.d.ts +35 -1
  115. package/dist/src/utils/secure-browser-launcher.js +341 -1
  116. package/dist/src/utils/secure-browser-launcher.js.map +1 -1
  117. package/dist/src/utils/terminalSerializer.d.ts +12 -0
  118. package/dist/src/utils/terminalSerializer.js.map +1 -1
  119. package/dist/src/utils/thoughtUtils.d.ts +2 -0
  120. package/dist/src/utils/thoughtUtils.js.map +1 -1
  121. package/package.json +13 -9
  122. package/src/config/config.ts +161 -22
  123. package/src/config/configBaseCore.ts +10 -2
  124. package/src/config/configConstructor.ts +11 -0
  125. package/src/config/configTestHarness.ts +10 -4
  126. package/src/config/configTypes.ts +6 -1
  127. package/src/config/liveTrustTransitionLifecycle.ts +250 -0
  128. package/src/config/schedulerSingleton.ts +2 -5
  129. package/src/core/compression/types.ts +81 -1
  130. package/src/core/legacy-model-limits.expected.txt +96 -0
  131. package/src/core/model-limits.json +96 -0
  132. package/src/core/model-limits.schema.ts +87 -0
  133. package/src/core/tokenLimits.ts +73 -147
  134. package/src/core/turn.ts +20 -3
  135. package/src/hooks/hookRegistry.ts +3 -1
  136. package/src/hooks/hookRunner.ts +24 -7
  137. package/src/hooks/hookSystem.ts +73 -13
  138. package/src/index.ts +1 -0
  139. package/src/llm-types/geminiContent.ts +3 -0
  140. package/src/policy/config.ts +32 -19
  141. package/src/recording/ReplayEngine.ts +51 -2
  142. package/src/recording/SessionDiscovery.ts +99 -4
  143. package/src/recording/SessionLockManager.ts +10 -3
  144. package/src/recording/SessionRecordingService.ts +36 -3
  145. package/src/recording/index.ts +19 -2
  146. package/src/recording/replay-test-helpers.ts +14 -0
  147. package/src/recording/resumeNotFoundMessages.ts +75 -0
  148. package/src/recording/resumeSession.ts +3 -1
  149. package/src/recording/sessionManagement.ts +51 -21
  150. package/src/recording/types.ts +44 -2
  151. package/src/runtime/AgentRuntimeLoader.ts +3 -1
  152. package/src/scheduler/liveOutput.ts +25 -22
  153. package/src/scheduler/types.ts +5 -2
  154. package/src/services/history/ContentConverters.ts +91 -68
  155. package/src/services/history/HistoryService.ts +92 -1
  156. package/src/services/history/IContent.ts +13 -0
  157. package/src/skills/skillManager.ts +26 -1
  158. package/src/test-utils/mock-tool.ts +4 -3
  159. package/src/test-utils/tools.ts +4 -3
  160. package/src/tools-adapters/CoreShellToolHostAdapter.ts +1 -7
  161. package/src/utils/browser-profile-discovery.ts +358 -0
  162. package/src/utils/env.ts +15 -0
  163. package/src/utils/events.ts +31 -0
  164. package/src/utils/fileUtils.ts +11 -1
  165. package/src/utils/retry.ts +3 -4
  166. package/src/utils/secure-browser-launcher.ts +500 -2
  167. package/src/utils/terminalSerializer.ts +9 -0
  168. package/src/utils/thoughtUtils.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vybestack/llxprt-code-core",
3
- "version": "0.10.0",
3
+ "version": "0.11.0-nightly.260728.ffaa8d5d7",
4
4
  "description": "LLxprt Code Core",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -355,6 +355,10 @@
355
355
  "bun": "./src/tools-adapters/CoreMessageBusAdapter.ts",
356
356
  "import": "./dist/src/tools-adapters/CoreMessageBusAdapter.js"
357
357
  },
358
+ "./tools-adapters/CoreShellToolHostAdapter.js": {
359
+ "bun": "./src/tools-adapters/CoreShellToolHostAdapter.ts",
360
+ "import": "./dist/src/tools-adapters/CoreShellToolHostAdapter.js"
361
+ },
358
362
  "./tools-adapters/CoreToolRegistryHostAdapter.js": {
359
363
  "bun": "./src/tools-adapters/CoreToolRegistryHostAdapter.ts",
360
364
  "import": "./dist/src/tools-adapters/CoreToolRegistryHostAdapter.js"
@@ -513,13 +517,13 @@
513
517
  "dist"
514
518
  ],
515
519
  "dependencies": {
516
- "@vybestack/llxprt-code-auth": "0.10.0",
517
- "@vybestack/llxprt-code-ide-integration": "0.10.0",
518
- "@vybestack/llxprt-code-storage": "0.10.0",
519
- "@vybestack/llxprt-code-mcp": "0.10.0",
520
- "@vybestack/llxprt-code-policy": "0.10.0",
521
- "@vybestack/llxprt-code-settings": "0.10.0",
522
- "@vybestack/llxprt-code-telemetry": "0.10.0",
520
+ "@vybestack/llxprt-code-auth": "0.11.0-nightly.260728.ffaa8d5d7",
521
+ "@vybestack/llxprt-code-ide-integration": "0.11.0-nightly.260728.ffaa8d5d7",
522
+ "@vybestack/llxprt-code-storage": "0.11.0-nightly.260728.ffaa8d5d7",
523
+ "@vybestack/llxprt-code-mcp": "0.11.0-nightly.260728.ffaa8d5d7",
524
+ "@vybestack/llxprt-code-policy": "0.11.0-nightly.260728.ffaa8d5d7",
525
+ "@vybestack/llxprt-code-settings": "0.11.0-nightly.260728.ffaa8d5d7",
526
+ "@vybestack/llxprt-code-telemetry": "0.11.0-nightly.260728.ffaa8d5d7",
523
527
  "@ai-sdk/openai": "^2.0.74",
524
528
  "@anthropic-ai/sdk": "^0.55.1",
525
529
  "@ast-grep/lang-c": "^0.0.5",
@@ -540,7 +544,7 @@
540
544
  "@google/genai": "1.30.0",
541
545
  "@iarna/toml": "^2.2.5",
542
546
  "@modelcontextprotocol/sdk": "^1.25.2",
543
- "@vybestack/llxprt-code-tools": "0.10.0",
547
+ "@vybestack/llxprt-code-tools": "0.11.0-nightly.260728.ffaa8d5d7",
544
548
  "@xterm/headless": "5.5.0",
545
549
  "ai": "^5.0.104",
546
550
  "ajv": "^8.17.1",
@@ -50,6 +50,7 @@ import {
50
50
  getOrCreateAsyncTaskReminderService,
51
51
  setupAsyncTaskAutoTrigger,
52
52
  } from './asyncTaskServices.js';
53
+ import { LiveTrustTransitionLifecycle } from './liveTrustTransitionLifecycle.js';
53
54
  import { parseSettingsSubagentDefinitions } from './subagentSettingsParser.js';
54
55
 
55
56
  import {
@@ -104,6 +105,10 @@ import type { MessageBus } from '../confirmation-bus/message-bus.js';
104
105
  import { coreEvents, CoreEvent } from '../utils/events.js';
105
106
  import { McpClientManager } from '@vybestack/llxprt-code-mcp';
106
107
  import { getCoreVersion } from '../utils/version.js';
108
+ import {
109
+ buildMcpTrustedRules,
110
+ MCP_TRUSTED_POLICY_SOURCE,
111
+ } from '../policy/config.js';
107
112
 
108
113
  import type { ShellExecutionConfig } from '../services/shellExecutionService.js';
109
114
  import type { ToolSchedulerContract } from '../core/toolSchedulerContract.js';
@@ -111,10 +116,39 @@ import type { ToolSchedulerContract } from '../core/toolSchedulerContract.js';
111
116
  export class Config extends ConfigBase {
112
117
  private static readonly logger = new DebugLogger('llxprt:config');
113
118
 
119
+ private readonly liveTrustTransitionLifecycle: LiveTrustTransitionLifecycle;
120
+
114
121
  constructor(params: ConfigParameters) {
115
122
  super();
116
123
  applyConfigParams(this as unknown as ConfigConstructorTarget, params);
117
124
  this.syncModeDerivedPolicyRules(this.approvalMode);
125
+ this.cachedEffectiveTrust = this.isTrustedFolder();
126
+ this.liveTrustTransitionLifecycle = new LiveTrustTransitionLifecycle({
127
+ downgradeApprovalMode: () => {
128
+ if (this.approvalMode !== ApprovalMode.DEFAULT) {
129
+ this.approvalMode = ApprovalMode.DEFAULT;
130
+ }
131
+ },
132
+ removeTrustedPolicyRules: () =>
133
+ this.policyEngine.removeRulesBySource(MCP_TRUSTED_POLICY_SOURCE),
134
+ updateTrustPolicy: (trusted) => {
135
+ if (trusted) {
136
+ for (const rule of buildMcpTrustedRules({
137
+ mcpServers: this.getMcpServers(),
138
+ })) {
139
+ this.policyEngine.addRule(rule);
140
+ }
141
+ } else {
142
+ this.mcpClientManager?.quarantineForTrustRevocation();
143
+ }
144
+ },
145
+ transitionMcp: (trusted) =>
146
+ trusted
147
+ ? this.mcpClientManager?.onFolderTrustGained()
148
+ : this.mcpClientManager?.onFolderTrustRevoked(),
149
+ initializeHooks: (signal) => this.getHookSystem()?.initialize(signal),
150
+ emitTrustChanged: (trusted) => coreEvents.emitFolderTrustChanged(trusted),
151
+ });
118
152
  }
119
153
 
120
154
  // Issue #2325: Background MCP discovery promise started in initialize() so
@@ -163,7 +197,7 @@ export class Config extends ConfigBase {
163
197
  'Config.initialize requires an explicit session/runtime MessageBus dependency.',
164
198
  );
165
199
  }
166
- this.initialized = true;
200
+ this.cachedEffectiveTrust = this.isTrustedFolder();
167
201
  this.ideClient = await IdeClient.getInstance();
168
202
  // Initialize centralized FileDiscoveryService
169
203
  this.getFileService();
@@ -179,6 +213,8 @@ export class Config extends ConfigBase {
179
213
  this,
180
214
  this.eventEmitter,
181
215
  );
216
+ this.registerIdeTrustListener();
217
+ this.initialized = true;
182
218
  // Issue #2325: Fire MCP discovery in the background — don't block startup.
183
219
  // Tools are gated before model turns via McpClientManager.whenDiscoverySettled().
184
220
  this.mcpDiscoveryPromise =
@@ -551,22 +587,37 @@ export class Config extends ConfigBase {
551
587
  * 'false' for untrusted.
552
588
  */
553
589
  isTrustedFolder(): boolean {
554
- // isWorkspaceTrusted in cli/src/config/trustedFolder.js returns undefined
555
- // when the file based trust value is unavailable, since it is mainly used
556
- // in the initialization for trust dialogs, etc. Here we return true since
557
- // config.isTrustedFolder() is used for the main business logic of blocking
558
- // tool calls etc in the rest of the application.
559
- //
560
- // Default value is true since we load with trusted settings to avoid
561
- // restarts in the more common path. If the user chooses to mark the folder
562
- // as untrusted, the CLI will restart and we will have the trust value
563
- // reloaded.
564
- const context = ideContext.getIdeContext();
565
- if (context?.workspaceState?.isTrusted !== undefined) {
566
- return context.workspaceState.isTrusted;
590
+ return this.getIdeTrust() ?? this.trustedFolder ?? true;
591
+ }
592
+
593
+ getIdeTrust(): boolean | undefined {
594
+ return (
595
+ this.ideTrust ?? ideContext.getIdeContext()?.workspaceState?.isTrusted
596
+ );
597
+ }
598
+
599
+ /**
600
+ * Updates the local trust fallback. IDE trust remains authoritative, so this
601
+ * only triggers a transition when the effective trust value changes.
602
+ */
603
+ setTrustedFolderLive(trusted: boolean): Promise<void> {
604
+ const previousEffectiveTrust = this.isTrustedFolder();
605
+ this.trustedFolder = trusted;
606
+ if (!this.initialized) {
607
+ return Promise.resolve();
567
608
  }
609
+ return this.reconcileEffectiveTrust(previousEffectiveTrust);
610
+ }
568
611
 
569
- return this.trustedFolder ?? true;
612
+ private reconcileEffectiveTrust(
613
+ previousEffectiveTrust: boolean,
614
+ ): Promise<void> {
615
+ const effectiveTrust = this.isTrustedFolder();
616
+ this.cachedEffectiveTrust = effectiveTrust;
617
+ if (previousEffectiveTrust !== effectiveTrust) {
618
+ return this.liveTrustTransitionLifecycle.apply(effectiveTrust);
619
+ }
620
+ return Promise.resolve();
570
621
  }
571
622
 
572
623
  setPtyTerminalSize(
@@ -822,10 +873,92 @@ export class Config extends ConfigBase {
822
873
  return this.hookSystem;
823
874
  }
824
875
 
876
+ private ideTrust: boolean | undefined;
877
+ private cachedEffectiveTrust: boolean;
878
+
879
+ /**
880
+ * Resolves once any pending trust-transition side-effects have settled.
881
+ */
882
+ async whenTrustTransitionSettled(): Promise<void> {
883
+ await this.liveTrustTransitionLifecycle.whenSettled();
884
+ }
885
+
886
+ private ideTrustChangeListener:
887
+ | ((isTrusted: boolean | undefined) => void)
888
+ | undefined;
889
+
890
+ /**
891
+ * Subscribes to live IDE trust changes so that MCP servers, hooks, and
892
+ * approval mode are updated immediately without requiring a restart.
893
+ */
894
+ private registerIdeTrustListener(): void {
895
+ const client = this.ideClient;
896
+ const previousEffectiveTrust = this.cachedEffectiveTrust;
897
+ this.ideTrust = ideContext.getIdeContext()?.workspaceState?.isTrusted;
898
+ if (client && typeof client.addTrustChangeListener === 'function') {
899
+ this.ideTrustChangeListener = (isTrusted: boolean | undefined) => {
900
+ const priorEffectiveTrust = this.cachedEffectiveTrust;
901
+ this.ideTrust = isTrusted;
902
+ void this.reconcileEffectiveTrust(priorEffectiveTrust).catch(
903
+ (error) => {
904
+ Config.logger.error(
905
+ `IDE trust reconciliation failed: ${getErrorMessage(error)}`,
906
+ );
907
+ },
908
+ );
909
+ };
910
+ client.addTrustChangeListener(this.ideTrustChangeListener);
911
+ }
912
+ void this.reconcileEffectiveTrust(previousEffectiveTrust).catch((error) => {
913
+ Config.logger.error(
914
+ `IDE trust reconciliation failed: ${getErrorMessage(error)}`,
915
+ );
916
+ });
917
+ }
918
+
825
919
  async dispose(): Promise<void> {
920
+ const failures: unknown[] = [];
921
+ try {
922
+ this.liveTrustTransitionLifecycle.beginDisposal();
923
+ } catch (error) {
924
+ failures.push(error);
925
+ }
926
+ if (
927
+ this.ideTrustChangeListener &&
928
+ this.ideClient &&
929
+ typeof this.ideClient.removeTrustChangeListener === 'function'
930
+ ) {
931
+ try {
932
+ this.ideClient.removeTrustChangeListener(this.ideTrustChangeListener);
933
+ } catch (error) {
934
+ failures.push(error);
935
+ }
936
+ this.ideTrustChangeListener = undefined;
937
+ }
938
+ // Initiate MCP shutdown before awaiting trust-transition settlement so
939
+ // in-flight MCP work is cancelled and cannot block disposal.
940
+ const stopPromise = this.mcpClientManager?.stop();
941
+ try {
942
+ await this.whenTrustTransitionSettled();
943
+ } catch (error) {
944
+ failures.push(error);
945
+ }
826
946
  const client = this.agentClient as AgentClientContract | undefined;
827
947
  if (client !== undefined) {
828
- client.dispose();
948
+ try {
949
+ client.dispose();
950
+ } catch (error) {
951
+ failures.push(error);
952
+ }
953
+ }
954
+ if (this.hookSystem !== undefined) {
955
+ const hookSystem = this.hookSystem;
956
+ this.hookSystem = undefined;
957
+ try {
958
+ hookSystem.dispose();
959
+ } catch (error) {
960
+ failures.push(error);
961
+ }
829
962
  }
830
963
  if (this.mcpDiscoveryPromise !== undefined) {
831
964
  try {
@@ -836,12 +969,18 @@ export class Config extends ConfigBase {
836
969
  );
837
970
  }
838
971
  }
839
- // Issue #2325: Extension server discoveries fired by startExtension are
840
- // tracked by whenDiscoverySettled(). Await it to avoid tearing down
841
- // clients mid-connection.
842
- if (this.mcpClientManager !== undefined) {
843
- await this.mcpClientManager.whenDiscoverySettled();
844
- await this.mcpClientManager.stop();
972
+ if (stopPromise !== undefined) {
973
+ try {
974
+ await stopPromise;
975
+ } catch (error) {
976
+ failures.push(error);
977
+ }
978
+ }
979
+ if (failures.length === 1) {
980
+ throw failures[0];
981
+ }
982
+ if (failures.length > 1) {
983
+ throw new AggregateError(failures, 'Config disposal failed');
845
984
  }
846
985
  }
847
986
  }
@@ -102,6 +102,7 @@ export abstract class ConfigBaseCore {
102
102
  protected workspaceContext!: WorkspaceContext;
103
103
  protected readonly debugMode!: boolean;
104
104
  protected readonly outputFormat!: OutputFormat;
105
+ protected readonly quiet!: boolean;
105
106
  protected readonly question: string | undefined;
106
107
  /**
107
108
  * @plan PLAN-20250212-LSP.P33
@@ -153,7 +154,7 @@ export abstract class ConfigBaseCore {
153
154
  protected readonly originalModel!: string;
154
155
  protected readonly extensionContextFilePaths!: string[];
155
156
  protected readonly noBrowser!: boolean;
156
- protected readonly folderTrust!: boolean;
157
+ protected folderTrust!: boolean;
157
158
  protected ideMode!: boolean;
158
159
  protected ideClient: IdeClient | undefined;
159
160
  protected inFallbackMode = false;
@@ -182,7 +183,7 @@ export abstract class ConfigBaseCore {
182
183
  protected readonly loadMemoryFromIncludeDirectories: boolean = false;
183
184
  protected readonly chatCompression: ChatCompressionSettings | undefined;
184
185
  protected readonly interactive!: boolean;
185
- protected readonly trustedFolder: boolean | undefined;
186
+ protected trustedFolder: boolean | undefined;
186
187
  protected readonly useRipgrep!: boolean;
187
188
  protected readonly shouldUseNodePtyShell!: boolean;
188
189
  protected readonly allowPtyThemeOverride!: boolean;
@@ -204,6 +205,7 @@ export abstract class ConfigBaseCore {
204
205
  enableToolOutputTruncation!: boolean;
205
206
 
206
207
  protected readonly continueOnFailedApiCall!: boolean;
208
+ protected readonly imagePayloadBudgetBytes!: number;
207
209
  protected readonly enableShellOutputEfficiency!: boolean;
208
210
  protected readonly continueSession!: boolean | string;
209
211
  protected readonly disableYoloMode!: boolean;
@@ -488,6 +490,9 @@ export abstract class ConfigBaseCore {
488
490
  getOutputFormat(): OutputFormat {
489
491
  return this.outputFormat;
490
492
  }
493
+ getQuiet(): boolean {
494
+ return this.quiet;
495
+ }
491
496
  getQuestion(): string | undefined {
492
497
  return this.question;
493
498
  }
@@ -769,6 +774,9 @@ export abstract class ConfigBaseCore {
769
774
  getContinueOnFailedApiCall(): boolean {
770
775
  return this.continueOnFailedApiCall;
771
776
  }
777
+ getImagePayloadBudgetBytes(): number {
778
+ return this.imagePayloadBudgetBytes;
779
+ }
772
780
  getEnableShellOutputEfficiency(): boolean {
773
781
  return this.enableShellOutputEfficiency;
774
782
  }
@@ -21,6 +21,7 @@ import {
21
21
  normalizeShellReplacement,
22
22
  DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
23
23
  DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
24
+ DEFAULT_IMAGE_PAYLOAD_BUDGET_BYTES,
24
25
  type AccessibilitySettings,
25
26
  type BugCommandSettings,
26
27
  type ChatCompressionSettings,
@@ -96,6 +97,7 @@ export interface ConfigConstructorTarget {
96
97
  debugMode: boolean;
97
98
  outputFormat: OutputFormat;
98
99
  question: string | undefined;
100
+ quiet: boolean;
99
101
 
100
102
  // Tool governance
101
103
  coreTools: string[] | undefined;
@@ -169,6 +171,7 @@ export interface ConfigConstructorTarget {
169
171
  truncateToolOutputLines: number;
170
172
  enableToolOutputTruncation: boolean;
171
173
  continueOnFailedApiCall: boolean;
174
+ imagePayloadBudgetBytes: number;
172
175
  enableShellOutputEfficiency: boolean;
173
176
  continueSession: boolean | string;
174
177
  extensionManagement: boolean;
@@ -256,6 +259,7 @@ function applyCoreIdentity(
256
259
  config.debugMode = params.debugMode;
257
260
  config.outputFormat = params.outputFormat ?? OutputFormat.TEXT;
258
261
  config.question = params.question;
262
+ config.quiet = params.quiet ?? false;
259
263
  }
260
264
 
261
265
  function applyToolGovernance(
@@ -436,6 +440,13 @@ function applySessionFlags(
436
440
  params: ConfigParameters,
437
441
  ): void {
438
442
  config.continueOnFailedApiCall = params.continueOnFailedApiCall ?? true;
443
+ const imagePayloadBudgetBytes = params.imagePayloadBudgetBytes;
444
+ config.imagePayloadBudgetBytes =
445
+ typeof imagePayloadBudgetBytes === 'number' &&
446
+ Number.isSafeInteger(imagePayloadBudgetBytes) &&
447
+ imagePayloadBudgetBytes >= 0
448
+ ? imagePayloadBudgetBytes
449
+ : DEFAULT_IMAGE_PAYLOAD_BUDGET_BYTES;
439
450
  config.continueSession = params.continueSession ?? false;
440
451
  config.storage = new Storage(config.targetDir);
441
452
  config.fileExclusions = new FileExclusions(config as unknown as Config);
@@ -181,6 +181,8 @@ export function buildIdeIntegrationMockBody(
181
181
  getConnectionStatus: vi.fn(),
182
182
  initialize: vi.fn(),
183
183
  shutdown: vi.fn(),
184
+ addTrustChangeListener: vi.fn(),
185
+ removeTrustChangeListener: vi.fn(),
184
186
  }),
185
187
  },
186
188
  };
@@ -206,12 +208,16 @@ export function buildEventsMockBody(
206
208
  actual: unknown,
207
209
  hoisted: HoistedConfigMocks,
208
210
  ) {
211
+ const eventsModule = actual as { coreEvents?: object };
212
+ const CoreEventsConstructor = eventsModule.coreEvents?.constructor;
213
+ if (typeof CoreEventsConstructor !== 'function') {
214
+ throw new TypeError('Expected coreEvents to expose a callable constructor');
215
+ }
216
+ const coreEvents = Reflect.construct(CoreEventsConstructor, []);
217
+ Object.assign(coreEvents, hoisted.coreEvents, { emit: vi.fn() });
209
218
  return {
210
219
  ...(actual as object),
211
- coreEvents: {
212
- ...hoisted.coreEvents,
213
- emit: vi.fn(),
214
- },
220
+ coreEvents,
215
221
  };
216
222
  }
217
223
 
@@ -209,6 +209,8 @@ export function normalizeShellReplacement(
209
209
  export const DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 4_000_000;
210
210
  export const DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES = 1000;
211
211
 
212
+ export const DEFAULT_IMAGE_PAYLOAD_BUDGET_BYTES = 15 * 1024 * 1024;
213
+
212
214
  export class MCPServerConfig {
213
215
  constructor(
214
216
  // For stdio transport
@@ -227,6 +229,7 @@ export class MCPServerConfig {
227
229
  * Transport type for URL-based servers.
228
230
  * When set, disables automatic HTTP→SSE fallback.
229
231
  * - 'http' → StreamableHTTPClientTransport
232
+ * - 'streamable-http' → alias for 'http' (StreamableHTTPClientTransport)
230
233
  * - 'sse' → SSEClientTransport
231
234
  * - omitted → defaults to HTTP with SSE fallback (deprecated; add type explicitly)
232
235
  *
@@ -234,7 +237,7 @@ export class MCPServerConfig {
234
237
  * @plan PLAN-20250219-GMERGE021.R3.P03
235
238
  * @requirement REQ-GMERGE021-R3-001
236
239
  */
237
- readonly type?: 'sse' | 'http',
240
+ readonly type?: 'sse' | 'http' | 'streamable-http',
238
241
  // Common
239
242
  readonly timeout?: number,
240
243
  readonly trust?: boolean,
@@ -373,6 +376,7 @@ export interface ConfigParameters {
373
376
  debugMode: boolean;
374
377
  outputFormat?: OutputFormat;
375
378
  question?: string;
379
+ quiet?: boolean;
376
380
 
377
381
  coreTools?: string[];
378
382
  allowedTools?: string[];
@@ -447,6 +451,7 @@ export interface ConfigParameters {
447
451
  truncateToolOutputLines?: number;
448
452
  enableToolOutputTruncation?: boolean;
449
453
  continueOnFailedApiCall?: boolean;
454
+ imagePayloadBudgetBytes?: number;
450
455
  enableShellOutputEfficiency?: boolean;
451
456
  continueSession?: boolean | string;
452
457
  disableYoloMode?: boolean;