shortcutxl 0.3.80 → 0.3.82

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 (207) hide show
  1. package/BINARY-INVENTORY.json +14 -14
  2. package/CHANGELOG.md +11 -0
  3. package/dist/ai/models.d.ts +2 -0
  4. package/dist/ai/models.js +23 -0
  5. package/dist/ai/providers/anthropic.js +18 -13
  6. package/dist/ai/providers/openai-reasoning.d.ts +4 -1
  7. package/dist/ai/providers/openai-reasoning.js +10 -4
  8. package/dist/ai/providers/provider-error-normalization.js +1 -1
  9. package/dist/app/agent-session.d.ts +0 -1
  10. package/dist/app/background/tool-summaries.d.ts +3 -3
  11. package/dist/app/background/tool-summaries.js +5 -3
  12. package/dist/app/background-agents/durable-memory/coordinator.js +2 -2
  13. package/dist/app/extensions/durable-memory/constants.d.ts +3 -1
  14. package/dist/app/extensions/durable-memory/constants.js +4 -1
  15. package/dist/app/extensions/wrapper.js +3 -3
  16. package/dist/app/mcp/index.js +54 -16
  17. package/dist/app/mcp/mcp-describe-tool.d.ts +1 -1
  18. package/dist/app/mcp/mcp-describe-tool.js +4 -1
  19. package/dist/app/mcp/mcp-execute-tool.d.ts +1 -1
  20. package/dist/app/mcp/mcp-execute-tool.js +4 -1
  21. package/dist/app/modes/action/agent.d.ts +1 -1
  22. package/dist/app/modes/index.js +12 -13
  23. package/dist/app/observability/client-logging/client-logger.d.ts +2 -3
  24. package/dist/app/observability/client-logging/client-logger.js +2 -3
  25. package/dist/app/observability/client-logging/startup-events.d.ts +1 -1
  26. package/dist/app/observability/client-logging/startup-events.js +2 -2
  27. package/dist/app/observability/client-logging/types.d.ts +0 -1
  28. package/dist/app/prompts/spreadjs-api-reference.json +1 -1
  29. package/dist/app/resources/skills.js +3 -0
  30. package/dist/app/session/compaction-observability.d.ts +0 -1
  31. package/dist/app/session/run-agent-observability.d.ts +0 -1
  32. package/dist/app/session/run-agent-observability.js +0 -1
  33. package/dist/app/session/session-catalog.d.ts +4 -2
  34. package/dist/app/session/session-catalog.js +15 -89
  35. package/dist/app/session/session-manager.d.ts +2 -0
  36. package/dist/app/session/session-manager.js +4 -0
  37. package/dist/app/session/session-metadata.d.ts +16 -0
  38. package/dist/app/session/session-metadata.js +75 -0
  39. package/dist/app/session/session-reader.d.ts +9 -0
  40. package/dist/app/session/session-reader.js +16 -0
  41. package/dist/app/session/session-store-types.d.ts +2 -0
  42. package/dist/app/subagents/defaults.d.ts +2 -2
  43. package/dist/app/sync/remote-skill-files.d.ts +6 -0
  44. package/dist/app/sync/remote-skill-files.js +24 -0
  45. package/dist/app/sync/skills-download.d.ts +4 -2
  46. package/dist/app/sync/skills-download.js +44 -3
  47. package/dist/app/sync/skills-status.js +14 -4
  48. package/dist/app/sync/skills-upload.d.ts +1 -0
  49. package/dist/app/sync/skills-upload.js +13 -3
  50. package/dist/app/tools/task/index.d.ts +1 -0
  51. package/dist/app/tools/task/task.d.ts +10 -1
  52. package/dist/app/tools/task/task.js +28 -11
  53. package/dist/cli/args.d.ts +3 -0
  54. package/dist/cli/args.js +15 -1
  55. package/dist/cli/runtime-args.d.ts +12 -0
  56. package/dist/cli/runtime-args.js +25 -0
  57. package/dist/cli/runtime-launch-profile.d.ts +27 -0
  58. package/dist/cli/runtime-launch-profile.js +26 -0
  59. package/dist/cli.js +4745 -3600
  60. package/dist/constants.d.ts +4 -0
  61. package/dist/constants.js +11 -0
  62. package/dist/contracts/agent-api.d.ts +93 -3
  63. package/dist/contracts/agent-api.js +95 -0
  64. package/dist/contracts/agent-session-store.d.ts +12 -0
  65. package/dist/contracts/host-tool-api.d.ts +1 -0
  66. package/dist/contracts/host-tool-api.js +1 -0
  67. package/dist/contracts/host-tool-request-id.d.ts +7 -0
  68. package/dist/contracts/host-tool-request-id.js +13 -0
  69. package/dist/contracts/model-stream.js +7 -1
  70. package/dist/core/agent-engine.d.ts +0 -7
  71. package/dist/core/agent-engine.js +0 -3
  72. package/dist/core/agent-loop.js +0 -10
  73. package/dist/core/core-types.d.ts +0 -8
  74. package/dist/core/embedded-agent-facade.d.ts +6 -2
  75. package/dist/core/embedded-agent-facade.js +26 -5
  76. package/dist/core/index.d.ts +1 -0
  77. package/dist/core/index.js +1 -0
  78. package/dist/core/pending-host-tool-requests.d.ts +22 -1
  79. package/dist/core/pending-host-tool-requests.js +23 -1
  80. package/dist/core/session/compaction/compaction.d.ts +2 -8
  81. package/dist/core/session/compaction/compaction.js +2 -16
  82. package/dist/core/session/compaction-bridge.d.ts +1 -17
  83. package/dist/core/session/compaction-bridge.js +1 -23
  84. package/dist/core/session/session-compaction.js +1 -1
  85. package/dist/core/tool-definition.d.ts +4 -0
  86. package/dist/core/tool-definition.js +6 -0
  87. package/dist/embedded-agent/compaction-wiring.d.ts +0 -6
  88. package/dist/embedded-agent/compaction-wiring.js +6 -58
  89. package/dist/embedded-agent/compose.js +257 -20
  90. package/dist/embedded-agent/context-review/activity.d.ts +4 -0
  91. package/dist/embedded-agent/context-review/activity.js +22 -0
  92. package/dist/embedded-agent/context-review/agent.d.ts +14 -0
  93. package/dist/embedded-agent/context-review/agent.js +90 -0
  94. package/dist/embedded-agent/context-review/result.d.ts +7 -0
  95. package/dist/embedded-agent/context-review/result.js +17 -0
  96. package/dist/embedded-agent/host-runtime-options.d.ts +4 -1
  97. package/dist/embedded-agent/host-runtime-options.js +156 -1
  98. package/dist/embedded-agent/host-tools/build-tool-list.d.ts +5 -3
  99. package/dist/embedded-agent/host-tools/build-tool-list.js +10 -3
  100. package/dist/embedded-agent/host-tools/create-website/contract.d.ts +5 -3
  101. package/dist/embedded-agent/host-tools/create-website/contract.js +34 -14
  102. package/dist/embedded-agent/host-tools/create-website/index.d.ts +1 -1
  103. package/dist/embedded-agent/host-tools/create-website/index.js +1 -1
  104. package/dist/embedded-agent/host-tools/get-tool-info/shared.d.ts +1 -1
  105. package/dist/embedded-agent/host-tools/mode-host-tools.d.ts +1 -0
  106. package/dist/embedded-agent/host-tools/mode-host-tools.js +1 -0
  107. package/dist/embedded-agent/host-tools/read-skill/contract.d.ts +12 -5
  108. package/dist/embedded-agent/host-tools/read-skill/contract.js +28 -15
  109. package/dist/embedded-agent/host-tools/task/contract.d.ts +2 -0
  110. package/dist/embedded-agent/host-tools/task/contract.js +27 -12
  111. package/dist/embedded-agent/host-tools/task/lifecycle.js +7 -5
  112. package/dist/embedded-agent/host-tools/task/runner.d.ts +8 -2
  113. package/dist/embedded-agent/host-tools/task/runner.js +26 -9
  114. package/dist/embedded-agent/index.d.ts +41 -21
  115. package/dist/embedded-agent/review/agent.d.ts +14 -0
  116. package/dist/embedded-agent/review/agent.js +43 -0
  117. package/dist/embedded-agent/review/coordinator.d.ts +89 -0
  118. package/dist/embedded-agent/review/coordinator.js +233 -0
  119. package/dist/embedded-agent/review/findings.d.ts +7 -0
  120. package/dist/embedded-agent/review/findings.js +63 -0
  121. package/dist/embedded-agent/suggestions.js +9 -6
  122. package/dist/embedded-agent/transport-errors.js +5 -2
  123. package/dist/embedded-agent/worker-bridge/dispatch.d.ts +2 -2
  124. package/dist/embedded-agent/worker-bridge/dispatch.js +31 -25
  125. package/dist/embedded-agent/worker-bridge/host-client.d.ts +5 -3
  126. package/dist/embedded-agent/worker-bridge/host-client.js +58 -45
  127. package/dist/embedded-agent/worker-bridge/worker-port.d.ts +1 -1
  128. package/dist/embedded-agent/worker-bridge/worker-port.js +6 -5
  129. package/dist/launcher/native-app.d.ts +18 -0
  130. package/dist/launcher/native-app.js +17 -0
  131. package/dist/main-helpers.d.ts +2 -1
  132. package/dist/main-helpers.js +4 -4
  133. package/dist/main.js +48 -60
  134. package/dist/mode-names.d.ts +48 -0
  135. package/dist/mode-names.js +31 -0
  136. package/dist/model-ids.d.ts +2 -8
  137. package/dist/model-ids.js +2 -10
  138. package/dist/remote-control/session-registry.js +2 -2
  139. package/dist/rpc/index.d.ts +1 -1
  140. package/dist/rpc/rpc-client.js +7 -1
  141. package/dist/rpc/rpc-host.d.ts +11 -0
  142. package/dist/rpc/rpc-host.js +127 -12
  143. package/dist/rpc/rpc-mode.d.ts +1 -1
  144. package/dist/rpc/rpc-types.d.ts +43 -2
  145. package/dist/setup.d.ts +26 -0
  146. package/dist/setup.js +79 -0
  147. package/dist/shell/clipboard/clipboard.js +6 -1
  148. package/dist/shell/components/selectors/session-selector.js +1 -1
  149. package/dist/shell/interactive/interactive-mode-options.d.ts +1 -0
  150. package/dist/shell/interactive/skills-workflow.d.ts +2 -0
  151. package/dist/shell/interactive/skills-workflow.js +26 -7
  152. package/dist/shell/status/update-reminder.d.ts +1 -0
  153. package/dist/shell/status/update-reminder.js +2 -1
  154. package/dist/shortcut-model-catalog.js +11 -2
  155. package/dist/startup/interactive-commands.js +2 -0
  156. package/dist/startup/preflight/index.d.ts +11 -1
  157. package/dist/startup/preflight/index.js +5 -6
  158. package/dist/startup/preflight/utils.d.ts +8 -0
  159. package/dist/startup/preflight/utils.js +14 -0
  160. package/dist/startup/preflight/windows.d.ts +11 -3
  161. package/dist/startup/preflight/windows.js +12 -7
  162. package/dist/startup/runtime-routing.d.ts +7 -4
  163. package/dist/startup/runtime-routing.js +16 -24
  164. package/dist/startup/terminal-update-flow.d.ts +20 -0
  165. package/dist/startup/terminal-update-flow.js +13 -0
  166. package/dist/startup/update-execution.d.ts +11 -0
  167. package/dist/startup/update-execution.js +30 -0
  168. package/dist/startup/update-prompt.js +2 -1
  169. package/dist/startup/windows-excel-runtime-upgrade.d.ts +5 -1
  170. package/dist/startup/windows-excel-runtime-upgrade.js +6 -5
  171. package/dist/startup/xll-requirements.d.ts +8 -0
  172. package/dist/startup/xll-requirements.js +9 -0
  173. package/dist/subagent-model-policy.d.ts +11 -5
  174. package/dist/subagent-model-policy.js +13 -3
  175. package/dist/subagent-thinking-policy.d.ts +3 -3
  176. package/dist/subagent-thinking-policy.js +4 -4
  177. package/dist/update.d.ts +1 -0
  178. package/dist/update.js +1 -0
  179. package/dist/utils/api-reference.d.ts +2 -1
  180. package/dist/utils/api-reference.js +7 -1
  181. package/dist/utils/mog-headless-api-reference.js +4 -2
  182. package/native-app/main/chunks/linux-C_fdDfT1.js +43 -0
  183. package/native-app/main/chunks/mac-B6uq7hmv.js +36 -0
  184. package/native-app/main/chunks/{photon_rs-BibpYw4G.js → photon_rs-DzyDysqa.js} +2 -2
  185. package/native-app/main/chunks/setup-worker-protocol-PnJOIZVR.js +661 -0
  186. package/native-app/main/chunks/shared-CXjreP6V.js +190 -0
  187. package/native-app/main/index.js +4142 -997
  188. package/native-app/main/setup-worker.js +1236 -0
  189. package/native-app/package.json +1 -1
  190. package/native-app/preload/index.cjs +48 -15
  191. package/native-app/renderer/assets/{index-BCDBJbGm.js → index-BObJc0Rl.js} +1764 -800
  192. package/native-app/renderer/assets/{index-i7q59YlN.css → index-Beh8sTpE.css} +662 -182
  193. package/native-app/renderer/index.html +2 -2
  194. package/package.json +36 -2
  195. package/xll/ShortcutXL.xll +0 -0
  196. package/xll/python/Lib/site-packages/httpx-0.28.1.dist-info/RECORD +1 -1
  197. package/xll/python/Lib/site-packages/idna-3.18.dist-info/RECORD +1 -1
  198. package/xll/python/Lib/site-packages/pip-26.2.dist-info/RECORD +3 -3
  199. package/xll/python/Lib/site-packages/pywin32-311.dist-info/RECORD +2 -2
  200. package/xll/python/Scripts/httpx.exe +0 -0
  201. package/xll/python/Scripts/idna.exe +0 -0
  202. package/xll/python/Scripts/pip.exe +0 -0
  203. package/xll/python/Scripts/pip3.13.exe +0 -0
  204. package/xll/python/Scripts/pip3.exe +0 -0
  205. package/xll/python/Scripts/pywin32_postinstall.exe +0 -0
  206. package/xll/python/Scripts/pywin32_testall.exe +0 -0
  207. package/native-app/main/update-helper.js +0 -92
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-08-07T00:26:16.459Z",
3
+ "generatedAt": "2026-08-14T02:24:29.936Z",
4
4
  "package": "shortcutxl",
5
5
  "build": {
6
6
  "builder": "github-actions",
7
7
  "repository": "lyfegame/shortcut",
8
- "sourceRef": "refs/heads/codex/shortcutxl-0.3.80-changelog",
9
- "sourceCommit": "6a199532f36743871e3f650222a403fc3d207382",
10
- "workflowRef": "lyfegame/shortcut/.github/workflows/release-shortcutxl.yml@refs/heads/codex/shortcutxl-0.3.80-changelog",
11
- "runId": "31134569821",
8
+ "sourceRef": "refs/heads/fix/native-tab-lifecycle-e2e",
9
+ "sourceCommit": "9fd127dae6648769fa32fdb55830324ee5c4a697",
10
+ "workflowRef": "lyfegame/shortcut/.github/workflows/release-shortcutxl.yml@refs/heads/fix/native-tab-lifecycle-e2e",
11
+ "runId": "31763560794",
12
12
  "runnerOs": "Windows",
13
13
  "runnerImage": "win25-vs2026",
14
- "runnerImageVersion": "20260728.188.1"
14
+ "runnerImageVersion": "20260803.193.1"
15
15
  },
16
16
  "binaryExtensions": [
17
17
  ".dll",
@@ -22,7 +22,7 @@
22
22
  "files": [
23
23
  {
24
24
  "path": "xll/ShortcutXL.xll",
25
- "sha256": "03089f6b2f0567240d0839be1aff9cc60145348316509bf230e2a95381fa0455",
25
+ "sha256": "184bd94bf10a566d242761aed8036e3db819447dcf2f65c94b39a55d91985e68",
26
26
  "source": "ShortcutXL native XLL build",
27
27
  "version": "package",
28
28
  "builtBy": "shortcut",
@@ -534,7 +534,7 @@
534
534
  },
535
535
  {
536
536
  "path": "xll/python/Scripts/httpx.exe",
537
- "sha256": "492a00ed48d74a8ab29af275064dba124618a7528fb3d937394a9b34242d293c",
537
+ "sha256": "bad169cdd079f8fb3406a85f7ba16d0164016c97c0416b8ecfb7cb20556d3570",
538
538
  "source": "httpx console launcher installed into embedded Python",
539
539
  "version": "see packaged httpx distribution",
540
540
  "builtBy": "third-party",
@@ -542,7 +542,7 @@
542
542
  },
543
543
  {
544
544
  "path": "xll/python/Scripts/idna.exe",
545
- "sha256": "487383c788707ee5a2d1f9e28cdad95eecbd255216490de784892650b837b81c",
545
+ "sha256": "2de9014203c4efd41520931e924b8b616712380a14233b5d40cec24213844c32",
546
546
  "source": "Python package console launcher installed into embedded Python",
547
547
  "version": "see owning Python package metadata in site-packages",
548
548
  "builtBy": "third-party",
@@ -550,7 +550,7 @@
550
550
  },
551
551
  {
552
552
  "path": "xll/python/Scripts/pip.exe",
553
- "sha256": "69a873135f5af6cfdbbea25cc9cadc5b579cb48569ed3e751640c40fc1591807",
553
+ "sha256": "7290ede2cd0eb8df02cd886b2e4b6fddb4b37554f686409dede62561ceb15a14",
554
554
  "source": "pip console launcher installed into embedded Python",
555
555
  "version": "see packaged pip distribution",
556
556
  "builtBy": "third-party",
@@ -558,7 +558,7 @@
558
558
  },
559
559
  {
560
560
  "path": "xll/python/Scripts/pip3.13.exe",
561
- "sha256": "69a873135f5af6cfdbbea25cc9cadc5b579cb48569ed3e751640c40fc1591807",
561
+ "sha256": "7290ede2cd0eb8df02cd886b2e4b6fddb4b37554f686409dede62561ceb15a14",
562
562
  "source": "pip console launcher installed into embedded Python",
563
563
  "version": "see packaged pip distribution",
564
564
  "builtBy": "third-party",
@@ -566,7 +566,7 @@
566
566
  },
567
567
  {
568
568
  "path": "xll/python/Scripts/pip3.exe",
569
- "sha256": "69a873135f5af6cfdbbea25cc9cadc5b579cb48569ed3e751640c40fc1591807",
569
+ "sha256": "7290ede2cd0eb8df02cd886b2e4b6fddb4b37554f686409dede62561ceb15a14",
570
570
  "source": "pip console launcher installed into embedded Python",
571
571
  "version": "see packaged pip distribution",
572
572
  "builtBy": "third-party",
@@ -574,7 +574,7 @@
574
574
  },
575
575
  {
576
576
  "path": "xll/python/Scripts/pywin32_postinstall.exe",
577
- "sha256": "d44daeb018223b473588a1e704dcc4dba42f8ef39e06cc6cefc6bf28a8ed7e89",
577
+ "sha256": "34e35cb64770e068105ba8cb830af68da90aec289a89de86e723f75e2ab1f163",
578
578
  "source": "Python package console launcher installed into embedded Python",
579
579
  "version": "see owning Python package metadata in site-packages",
580
580
  "builtBy": "third-party",
@@ -582,7 +582,7 @@
582
582
  },
583
583
  {
584
584
  "path": "xll/python/Scripts/pywin32_testall.exe",
585
- "sha256": "942e759d93d1bba8f1d5bfb97bf5b76ad3b2cff978900425b04fccb2e427e25d",
585
+ "sha256": "5268485231eb0052632e91db2305af6f6a41ceac7b00b858528ae0326029299e",
586
586
  "source": "Python package console launcher installed into embedded Python",
587
587
  "version": "see owning Python package metadata in site-packages",
588
588
  "builtBy": "third-party",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.82]
4
+
5
+ - **Safer CLI and app updates**
6
+ - **More reliable native chats for app**
7
+ - **Drag and drop file attachments for app**
8
+
9
+ ## [0.3.81]
10
+
11
+ - **Smoother native setup and upgrades** - `shortcut app` now guides first-run prerequisites and required Excel integration updates directly in the app.
12
+ - **Native resources and MCP management** - The native app adds dedicated Skills, Memories, and MCP pages.
13
+
3
14
  ## [0.3.80]
4
15
 
5
16
  - **Shortcut app updates** - The native app now surfaces available updates, installs and restarts in place.
@@ -19,6 +19,8 @@ export declare function getModelReasoningLevels<TApi extends Api>(model: Model<T
19
19
  export declare function assertModelSupportsReasoningLevel<TApi extends Api>(model: Model<TApi>, level: ReasoningLevel): void;
20
20
  /** Resolve a stale/cross-model selection to the target model's declared default. */
21
21
  export declare function resolveModelReasoningLevel<TApi extends Api>(model: Model<TApi>, requested: ReasoningLevel | undefined): ReasoningLevel;
22
+ /** Clamp an explicit effort hint to the nearest level supported by the target model. */
23
+ export declare function clampModelReasoningLevel<TApi extends Api>(model: Model<TApi>, requested: ReasoningLevel | undefined): ReasoningLevel;
22
24
  /**
23
25
  * Check if two models are equal by comparing both their id and provider.
24
26
  * Returns false if either model is null or undefined.
package/dist/ai/models.js CHANGED
@@ -72,6 +72,29 @@ export function resolveModelReasoningLevel(model, requested) {
72
72
  return requested;
73
73
  return capabilities?.defaultLevel ?? 'off';
74
74
  }
75
+ const REASONING_LEVEL_ORDER = [
76
+ 'off',
77
+ 'minimal',
78
+ 'low',
79
+ 'medium',
80
+ 'high',
81
+ 'xhigh',
82
+ 'max'
83
+ ];
84
+ /** Clamp an explicit effort hint to the nearest level supported by the target model. */
85
+ export function clampModelReasoningLevel(model, requested) {
86
+ if (requested === undefined)
87
+ return resolveModelReasoningLevel(model, undefined);
88
+ const available = getModelReasoningLevels(model);
89
+ if (available.includes(requested))
90
+ return requested;
91
+ const requestedIndex = REASONING_LEVEL_ORDER.indexOf(requested);
92
+ return available.reduce((nearest, candidate) => {
93
+ const nearestDistance = Math.abs(REASONING_LEVEL_ORDER.indexOf(nearest) - requestedIndex);
94
+ const candidateDistance = Math.abs(REASONING_LEVEL_ORDER.indexOf(candidate) - requestedIndex);
95
+ return candidateDistance < nearestDistance ? candidate : nearest;
96
+ });
97
+ }
75
98
  /**
76
99
  * Check if two models are equal by comparing both their id and provider.
77
100
  * Returns false if either model is null or undefined.
@@ -158,7 +158,10 @@ export const streamAnthropic = (model, context, options) => {
158
158
  const client = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers, options?.fetchOptions, options?.fetch);
159
159
  const params = buildParams(model, context, options);
160
160
  options?.onPayload?.(params);
161
- const anthropicStream = client.messages.stream({ ...params, stream: true }, { signal: options?.signal });
161
+ // Consume raw events because this provider owns accumulation. The SDK's
162
+ // MessageStream accumulator assumes usage is always present, but private
163
+ // gateways can omit initial usage from otherwise valid response streams.
164
+ const anthropicStream = await client.messages.create({ ...params, stream: true }, { signal: options?.signal });
162
165
  stream.push({ type: 'start', partial: output });
163
166
  const blocks = output.content;
164
167
  let uncachedInputTokens = 0;
@@ -167,10 +170,11 @@ export const streamAnthropic = (model, context, options) => {
167
170
  if (event.type === 'message_start') {
168
171
  // Capture initial token usage from message_start event
169
172
  // This ensures we have input token counts even if the stream is aborted early
170
- uncachedInputTokens = event.message.usage.input_tokens || 0;
171
- output.usage.output = event.message.usage.output_tokens || 0;
172
- output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
173
- output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
173
+ const usage = event.message.usage;
174
+ uncachedInputTokens = usage?.input_tokens ?? 0;
175
+ output.usage.output = usage?.output_tokens ?? 0;
176
+ output.usage.cacheRead = usage?.cache_read_input_tokens ?? 0;
177
+ output.usage.cacheWrite = usage?.cache_creation_input_tokens ?? 0;
174
178
  // Anthropic doesn't provide total_tokens; usage.input is normalized
175
179
  // to total input, including cache buckets.
176
180
  output.usage.input =
@@ -328,17 +332,18 @@ export const streamAnthropic = (model, context, options) => {
328
332
  }
329
333
  // Only update usage fields if present (not null).
330
334
  // Preserves input_tokens from message_start when proxies omit it in message_delta.
331
- if (event.usage.output_tokens != null) {
332
- output.usage.output = event.usage.output_tokens;
335
+ const usage = event.usage;
336
+ if (usage?.output_tokens != null) {
337
+ output.usage.output = usage.output_tokens;
333
338
  }
334
- if (event.usage.cache_read_input_tokens != null) {
335
- output.usage.cacheRead = event.usage.cache_read_input_tokens;
339
+ if (usage?.cache_read_input_tokens != null) {
340
+ output.usage.cacheRead = usage.cache_read_input_tokens;
336
341
  }
337
- if (event.usage.cache_creation_input_tokens != null) {
338
- output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
342
+ if (usage?.cache_creation_input_tokens != null) {
343
+ output.usage.cacheWrite = usage.cache_creation_input_tokens;
339
344
  }
340
- if (event.usage.input_tokens != null) {
341
- uncachedInputTokens = event.usage.input_tokens;
345
+ if (usage?.input_tokens != null) {
346
+ uncachedInputTokens = usage.input_tokens;
342
347
  }
343
348
  // Anthropic doesn't provide total_tokens; usage.input is normalized
344
349
  // to total input, including cache buckets.
@@ -1,5 +1,8 @@
1
1
  import type { Api, Model, ReasoningLevel } from '../types.js';
2
2
  export type OpenAIReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
3
- /** Preserve exact native effort names; explicit `off` maps to OpenAI `none`. */
3
+ /**
4
+ * Preserve exact native effort names; explicit `off` maps to OpenAI `none`.
5
+ * Stale levels are clamped to the model contract before reaching the provider.
6
+ */
4
7
  export declare function resolveOpenAIReasoningEffort(model: Model<Api>, level: ReasoningLevel | undefined): OpenAIReasoningEffort | undefined;
5
8
  //# sourceMappingURL=openai-reasoning.d.ts.map
@@ -1,5 +1,8 @@
1
- import { assertModelSupportsReasoningLevel } from '../models.js';
2
- /** Preserve exact native effort names; explicit `off` maps to OpenAI `none`. */
1
+ import { clampModelReasoningLevel } from '../models.js';
2
+ /**
3
+ * Preserve exact native effort names; explicit `off` maps to OpenAI `none`.
4
+ * Stale levels are clamped to the model contract before reaching the provider.
5
+ */
3
6
  export function resolveOpenAIReasoningEffort(model, level) {
4
7
  if (level === undefined)
5
8
  return undefined;
@@ -7,7 +10,10 @@ export function resolveOpenAIReasoningEffort(model, level) {
7
10
  // configurable wire value requires an explicit model capability contract.
8
11
  if (!model.capabilities?.reasoning)
9
12
  return undefined;
10
- assertModelSupportsReasoningLevel(model, level);
11
- return level === 'off' ? 'none' : level;
13
+ // Internal calls commonly request a cheap effort such as `off` or `low`.
14
+ // When a substituted model does not offer it, use the nearest supported
15
+ // level instead of failing or promoting the call to the model default.
16
+ const resolvedLevel = clampModelReasoningLevel(model, level);
17
+ return resolvedLevel === 'off' ? 'none' : resolvedLevel;
12
18
  }
13
19
  //# sourceMappingURL=openai-reasoning.js.map
@@ -66,7 +66,7 @@ function normalizedSdkErrorDetails(error, rawMessage) {
66
66
  return undefined;
67
67
  const merged = { ...classified, ...headerDetails };
68
68
  const accessAware = applyModelAccessDeniedErrorDetails(merged, rawMessage);
69
- if (isContextOverflowErrorMessage(rawMessage)) {
69
+ if (status === 413 || isContextOverflowErrorMessage(rawMessage)) {
70
70
  return { ...accessAware, code: 'context_overflow' };
71
71
  }
72
72
  return accessAware;
@@ -121,7 +121,6 @@ export interface AgentSessionConfig {
121
121
  info?: (input: {
122
122
  module: string;
123
123
  event: string;
124
- analyticsEvent?: boolean;
125
124
  data?: Record<string, unknown>;
126
125
  duration?: number;
127
126
  }) => void;
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Fire-and-forget tool-call summarization via a cheap LLM (Haiku).
2
+ * Fire-and-forget tool-call summarization via a cheap LLM.
3
3
  *
4
4
  * Summaries are ephemeral UX — displayed to the user but never injected
5
5
  * into the LLM conversation context.
6
6
  */
7
7
  import { type ShortcutLlmProxyClient } from '../providers/shortcut-llm-proxy-client.js';
8
- export declare const TOOL_SUMMARY_MODEL = "claude-haiku-4-5-20251001";
8
+ export declare const TOOL_SUMMARY_MODEL: "gpt-5.6-luna";
9
9
  interface ToolSummaryInput {
10
10
  toolName: string;
11
11
  args: unknown;
@@ -17,7 +17,7 @@ export interface ToolSummaryGenerationResult {
17
17
  httpStatus?: number;
18
18
  }
19
19
  /**
20
- * Call the Shortcut LLM proxy (Haiku) to generate a one-line summary of a tool call.
20
+ * Call the Shortcut LLM proxy to generate a one-line summary of a tool call.
21
21
  *
22
22
  * Returns the summary string, or undefined if the call fails or is aborted.
23
23
  * Never throws — errors are silently swallowed since summaries are best-effort UX.
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Fire-and-forget tool-call summarization via a cheap LLM (Haiku).
2
+ * Fire-and-forget tool-call summarization via a cheap LLM.
3
3
  *
4
4
  * Summaries are ephemeral UX — displayed to the user but never injected
5
5
  * into the LLM conversation context.
6
6
  */
7
+ import { SHORTCUT_MODEL_ID } from '../../model-ids.js';
7
8
  import { ShortcutLlmProxyError } from '../providers/shortcut-llm-proxy-client.js';
8
9
  import { TOOL_SUMMARY_MAX_OUTPUT_TOKENS } from '../session/tool-summary-policy.js';
9
- export const TOOL_SUMMARY_MODEL = 'claude-haiku-4-5-20251001';
10
+ export const TOOL_SUMMARY_MODEL = SHORTCUT_MODEL_ID.Gpt56Luna;
10
11
  const MAX_ARGS_CHARS = 500;
11
12
  const MAX_RESULT_CHARS = 1000;
12
13
  const SUMMARY_PROMPT = 'Summarize this tool call in one sentence (under 15 words). Be specific about what was found/done, not what tool was used.';
@@ -15,7 +16,7 @@ function truncate(value, maxChars) {
15
16
  return str.length > maxChars ? str.slice(0, maxChars) + '…' : str;
16
17
  }
17
18
  /**
18
- * Call the Shortcut LLM proxy (Haiku) to generate a one-line summary of a tool call.
19
+ * Call the Shortcut LLM proxy to generate a one-line summary of a tool call.
19
20
  *
20
21
  * Returns the summary string, or undefined if the call fails or is aborted.
21
22
  * Never throws — errors are silently swallowed since summaries are best-effort UX.
@@ -35,6 +36,7 @@ export async function generateToolSummaryDetailed(input, shortcutLlmProxy, optio
35
36
  { role: 'human', content: userContent }
36
37
  ],
37
38
  max_output_tokens: maxOutputTokens,
39
+ reasoning: 'off',
38
40
  llmOperation: 'summarize'
39
41
  }, {
40
42
  signal
@@ -1,6 +1,6 @@
1
1
  import { SHORTCUT_LLM_PROXY_URL } from '../../../constants.js';
2
2
  import { getActiveUserMessageId } from '../../../core/user-message-id.js';
3
- import { DURABLE_MEMORY_AGENT_MODEL, DURABLE_MEMORY_AGENT_TOOLS, DURABLE_MEMORY_TURN_END_CADENCE } from '../../extensions/durable-memory/constants.js';
3
+ import { DURABLE_MEMORY_AGENT_EFFORT, DURABLE_MEMORY_AGENT_MODEL, DURABLE_MEMORY_AGENT_TOOLS, DURABLE_MEMORY_TURN_END_CADENCE } from '../../extensions/durable-memory/constants.js';
4
4
  import { buildDurableMemoryPromptFragment } from '../../extensions/durable-memory/retrieval.js';
5
5
  import { emitDurableMemoryStatus } from '../../extensions/durable-memory/status.js';
6
6
  import { validateWorkspaceState } from '../../extensions/durable-memory/validator.js';
@@ -342,7 +342,7 @@ export class DurableMemoryCoordinator {
342
342
  instruction: MEMORY_AGENT_INSTRUCTION,
343
343
  traceDir: resolveForkTraceDir(run.sessionDir, run.sessionFile),
344
344
  signal,
345
- effortLevel: 'low',
345
+ effortLevel: DURABLE_MEMORY_AGENT_EFFORT,
346
346
  onToolCall: emitRunningToolCall
347
347
  });
348
348
  agentResponse = summarizeAgentResponse(result.response);
@@ -5,6 +5,8 @@ export declare const DURABLE_MEMORY_HARD_MAX_TOPIC_FILES = 15;
5
5
  export declare const DURABLE_MEMORY_TURN_END_CADENCE = 4;
6
6
  export declare const DURABLE_MEMORY_AGENT_NAME = "durable-memory";
7
7
  export declare const DURABLE_MEMORY_AGENT_DESCRIPTION = "Maintains durable memory topic files from session checkpoints.";
8
- export declare const DURABLE_MEMORY_AGENT_MODEL = "claude-haiku-4-5-20251001";
8
+ export declare const DURABLE_MEMORY_AGENT_MODEL: "gpt-5.6-luna";
9
+ /** Keep maintenance inexpensive while retaining bounded reasoning over memory changes. */
10
+ export declare const DURABLE_MEMORY_AGENT_EFFORT: "low";
9
11
  export declare const DURABLE_MEMORY_AGENT_TOOLS: readonly ["read", "edit", "write", "grep"];
10
12
  //# sourceMappingURL=constants.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { SHORTCUT_MODEL_ID } from '../../../model-ids.js';
1
2
  import { EDIT, GREP, READ, WRITE } from '../../../tool-names.js';
2
3
  export const DURABLE_MEMORY_ROOT = 'memories';
3
4
  export const DURABLE_MEMORY_INDEX = 'MEMORY.md';
@@ -6,6 +7,8 @@ export const DURABLE_MEMORY_HARD_MAX_TOPIC_FILES = 15;
6
7
  export const DURABLE_MEMORY_TURN_END_CADENCE = 4;
7
8
  export const DURABLE_MEMORY_AGENT_NAME = 'durable-memory';
8
9
  export const DURABLE_MEMORY_AGENT_DESCRIPTION = 'Maintains durable memory topic files from session checkpoints.';
9
- export const DURABLE_MEMORY_AGENT_MODEL = 'claude-haiku-4-5-20251001';
10
+ export const DURABLE_MEMORY_AGENT_MODEL = SHORTCUT_MODEL_ID.Gpt56Luna;
11
+ /** Keep maintenance inexpensive while retaining bounded reasoning over memory changes. */
12
+ export const DURABLE_MEMORY_AGENT_EFFORT = 'low';
10
13
  export const DURABLE_MEMORY_AGENT_TOOLS = [READ, EDIT, WRITE, GREP];
11
14
  //# sourceMappingURL=constants.js.map
@@ -4,14 +4,14 @@
4
4
  * Wraps ToolDefinitions with tool_call / tool_result event hooks so
5
5
  * extensions can block or modify tool execution.
6
6
  */
7
+ import { cloneToolDefinition } from '../../core/tool-definition.js';
7
8
  /**
8
9
  * Wrap a tool with extension callbacks for interception.
9
10
  * - Emits tool_call event before execution (can block)
10
11
  * - Emits tool_result event after execution (can modify result)
11
12
  */
12
13
  export function wrapToolWithExtensions(tool, runner) {
13
- return {
14
- ...tool,
14
+ return cloneToolDefinition(tool, {
15
15
  execute: async (toolCallId, params, signal, onUpdate, ctx) => {
16
16
  // Emit tool_call event - extensions can block execution
17
17
  if (runner.hasHandlers('tool_call')) {
@@ -73,7 +73,7 @@ export function wrapToolWithExtensions(tool, runner) {
73
73
  throw err;
74
74
  }
75
75
  }
76
- };
76
+ });
77
77
  }
78
78
  /**
79
79
  * Wrap all tools with extension callbacks.
@@ -11,6 +11,15 @@ import { registerHostedMcpOAuthProviders, unregisterHostedMcpOAuthProviders } fr
11
11
  import { createMcpDescribeTool } from './mcp-describe-tool.js';
12
12
  import { createMcpExecuteTool } from './mcp-execute-tool.js';
13
13
  import { isHostedServerConfig } from './types.js';
14
+ function resolveDisplayedAuthStatus(connection, rawAuthStatus, failureKind) {
15
+ if (failureKind === 'auth_required' && rawAuthStatus !== 'configured') {
16
+ return 'not_logged_in';
17
+ }
18
+ if (connection && rawAuthStatus === 'not_logged_in') {
19
+ return 'unsupported';
20
+ }
21
+ return rawAuthStatus;
22
+ }
14
23
  export async function createMcpManager(options) {
15
24
  const servers = loadMcpConfig({
16
25
  cwd: options.cwd,
@@ -25,6 +34,7 @@ export async function createMcpManager(options) {
25
34
  const failures = new Map();
26
35
  const failureKinds = new Map();
27
36
  const authStatuses = new Map();
37
+ let shutDown = false;
28
38
  const results = await Promise.allSettled(serverNames.map(async (name) => {
29
39
  authStatuses.set(name, await resolveMcpAuthStatus(name, servers[name], {
30
40
  authStorage: options.authStorage,
@@ -39,10 +49,14 @@ export async function createMcpManager(options) {
39
49
  const config = servers[result.value.name];
40
50
  if (isHostedServerConfig(config)) {
41
51
  const reconnect = async () => {
42
- if (!options.authStorage) {
52
+ if (!options.authStorage || shutDown)
43
53
  return null;
44
- }
54
+ options.authStorage.reload();
45
55
  const nextConnection = await connectServer(result.value.name, config, options.cwd, options.authStorage, options.mcpOAuthStore);
56
+ if (shutDown) {
57
+ closeConnection(nextConnection);
58
+ return null;
59
+ }
46
60
  const previousConnection = connections.get(result.value.name);
47
61
  connections.set(result.value.name, nextConnection);
48
62
  nextConnection.reconnect = reconnect;
@@ -63,18 +77,10 @@ export async function createMcpManager(options) {
63
77
  log.warn('mcp', `MCP server '${name}' failed to start: ${reason}`);
64
78
  }
65
79
  }
66
- const catalog = buildCatalog([...connections.values()]);
67
- const tools = connections.size > 0
68
- ? [createMcpExecuteTool(connections, catalog), createMcpDescribeTool(connections)]
69
- : [];
70
80
  const statusLine = buildStatusLine(serverNames.map((name) => {
71
81
  const connection = connections.get(name);
72
82
  const rawAuthStatus = authStatuses.get(name) ?? 'unsupported';
73
- const authStatus = failureKinds.get(name) === 'auth_required'
74
- ? 'not_logged_in'
75
- : connection && rawAuthStatus === 'not_logged_in'
76
- ? 'unsupported'
77
- : rawAuthStatus;
83
+ const authStatus = resolveDisplayedAuthStatus(connection, rawAuthStatus, failureKinds.get(name));
78
84
  return {
79
85
  name,
80
86
  connected: connection,
@@ -92,11 +98,7 @@ export async function createMcpManager(options) {
92
98
  const failure = failures.get(name);
93
99
  const config = servers[name];
94
100
  const rawAuthStatus = authStatuses.get(name) ?? 'unsupported';
95
- const authStatus = failureKinds.get(name) === 'auth_required'
96
- ? 'not_logged_in'
97
- : connection && rawAuthStatus === 'not_logged_in'
98
- ? 'unsupported'
99
- : rawAuthStatus;
101
+ const authStatus = resolveDisplayedAuthStatus(connection, rawAuthStatus, failureKinds.get(name));
100
102
  return {
101
103
  name,
102
104
  status: connection ? 'connected' : 'failed',
@@ -114,12 +116,48 @@ export async function createMcpManager(options) {
114
116
  ...(failure ? { error: failure } : {})
115
117
  };
116
118
  });
119
+ const markAuthRequired = (status) => {
120
+ const connection = connections.get(status.name);
121
+ connections.delete(status.name);
122
+ if (connection)
123
+ closeConnection(connection);
124
+ status.status = 'failed';
125
+ status.authStatus = 'not_logged_in';
126
+ status.failureKind = 'auth_required';
127
+ status.toolCount = 0;
128
+ status.toolNames = [];
129
+ status.error = 'Login required';
130
+ };
131
+ const statusByName = new Map(serverStatuses.map((status) => [status.name, status]));
132
+ const prepareConnection = async (name) => {
133
+ if (shutDown)
134
+ return `Unknown MCP server '${name}'.`;
135
+ const status = statusByName.get(name);
136
+ if (!status)
137
+ return `Unknown MCP server '${name}'.`;
138
+ if (status.oauthProviderId) {
139
+ options.authStorage?.reload();
140
+ if (options.authStorage?.get(status.oauthProviderId)?.type !== 'oauth') {
141
+ markAuthRequired(status);
142
+ return `MCP server '${name}' requires re-authentication. Log in and try again.`;
143
+ }
144
+ }
145
+ return undefined;
146
+ };
147
+ const catalog = buildCatalog([...connections.values()]);
148
+ const tools = connections.size > 0
149
+ ? [
150
+ createMcpExecuteTool(connections, catalog, prepareConnection),
151
+ createMcpDescribeTool(connections, prepareConnection)
152
+ ]
153
+ : [];
117
154
  return {
118
155
  tools,
119
156
  statusLine,
120
157
  globalConfigDir: options.globalConfigDir,
121
158
  servers: serverStatuses,
122
159
  shutdown() {
160
+ shutDown = true;
123
161
  for (const conn of connections.values()) {
124
162
  closeConnection(conn);
125
163
  }
@@ -12,6 +12,6 @@ declare const schema: import("@sinclair/typebox").TObject<{
12
12
  tool: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
13
13
  }>;
14
14
  /** Create the `mcp_describe` tool that lists or details tools on a connected MCP server. */
15
- export declare function createMcpDescribeTool(connections: Map<string, McpConnection>): ToolDefinition<typeof schema>;
15
+ export declare function createMcpDescribeTool(connections: Map<string, McpConnection>, prepareConnection?: (serverName: string) => Promise<string | undefined>): ToolDefinition<typeof schema>;
16
16
  export {};
17
17
  //# sourceMappingURL=mcp-describe-tool.d.ts.map
@@ -15,7 +15,7 @@ const schema = Type.Object({
15
15
  }))
16
16
  });
17
17
  /** Create the `mcp_describe` tool that lists or details tools on a connected MCP server. */
18
- export function createMcpDescribeTool(connections) {
18
+ export function createMcpDescribeTool(connections, prepareConnection) {
19
19
  return {
20
20
  name: 'mcp_describe',
21
21
  label: 'MCP Describe',
@@ -24,6 +24,9 @@ export function createMcpDescribeTool(connections) {
24
24
  '- mcp_describe(server, tool) → full JSON schema for one tool',
25
25
  parameters: schema,
26
26
  async execute(_toolCallId, params) {
27
+ const preparationError = await prepareConnection?.(params.server);
28
+ if (preparationError)
29
+ return errorResult(preparationError);
27
30
  const conn = resolveServer(connections, params.server);
28
31
  if (typeof conn === 'string')
29
32
  return errorResult(conn);
@@ -9,6 +9,6 @@ declare const schema: import("@sinclair/typebox").TObject<{
9
9
  arguments: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnknown>>;
10
10
  }>;
11
11
  /** Create the `mcp` gateway tool that dispatches calls to connected MCP servers. */
12
- export declare function createMcpExecuteTool(connections: Map<string, McpConnection>, catalog: string): ToolDefinition<typeof schema>;
12
+ export declare function createMcpExecuteTool(connections: Map<string, McpConnection>, catalog: string, prepareConnection?: (serverName: string) => Promise<string | undefined>): ToolDefinition<typeof schema>;
13
13
  export {};
14
14
  //# sourceMappingURL=mcp-execute-tool.d.ts.map
@@ -12,13 +12,16 @@ const schema = Type.Object({
12
12
  }))
13
13
  });
14
14
  /** Create the `mcp` gateway tool that dispatches calls to connected MCP servers. */
15
- export function createMcpExecuteTool(connections, catalog) {
15
+ export function createMcpExecuteTool(connections, catalog, prepareConnection) {
16
16
  return {
17
17
  name: 'mcp',
18
18
  label: 'MCP',
19
19
  description: `Call a tool on a connected MCP server.\n\n${catalog}`,
20
20
  parameters: schema,
21
21
  async execute(_toolCallId, params, signal) {
22
+ const preparationError = await prepareConnection?.(params.server);
23
+ if (preparationError)
24
+ return errorResult(preparationError);
22
25
  const conn = resolveServer(connections, params.server);
23
26
  if (typeof conn === 'string')
24
27
  return errorResult(conn);
@@ -5,7 +5,7 @@
5
5
  * Can switch to plan or installation mode.
6
6
  */
7
7
  import type { AgentDefinition } from '../../agent-definition.js';
8
- export declare const ACTION_AGENT_TOOL_NAMES: ("write" | "execute_code" | "execute_tool" | "get_tool_info" | "mcp" | "refresh_context" | "send_message" | "take_screenshot" | "task" | "bash" | "switch_from_action_mode")[];
8
+ export declare const ACTION_AGENT_TOOL_NAMES: ("bash" | "write" | "execute_code" | "execute_tool" | "get_tool_info" | "mcp" | "refresh_context" | "send_message" | "take_screenshot" | "task" | "switch_from_action_mode")[];
9
9
  /** Construct the action-mode agent definition with the full execution toolset. */
10
10
  export declare function actionAgent(): AgentDefinition;
11
11
  //# sourceMappingURL=agent.d.ts.map
@@ -10,7 +10,7 @@ export { installationAgent } from './installation/agent.js';
10
10
  export { manageAgent } from './manage/agent.js';
11
11
  export { planAgent } from './plan/agent.js';
12
12
  export { reviewAgent } from './review/agent.js';
13
- import { MODE } from '../../mode-names.js';
13
+ import { isAgentMode, MODE } from '../../mode-names.js';
14
14
  import { actionAgent } from './action/agent.js';
15
15
  import { askAgent } from './ask/agent.js';
16
16
  import { installationAgent } from './installation/agent.js';
@@ -18,25 +18,24 @@ import { manageAgent } from './manage/agent.js';
18
18
  import { MODE_METADATA } from './metadata.js';
19
19
  import { planAgent } from './plan/agent.js';
20
20
  import { reviewAgent } from './review/agent.js';
21
- const MODE_FACTORIES = new Map([
22
- [MODE.INSTALLATION, installationAgent],
23
- [MODE.ACTION, actionAgent],
24
- [MODE.PLAN, planAgent],
25
- [MODE.ASK, askAgent],
26
- [MODE.MANAGE, manageAgent],
27
- [MODE.REVIEW, reviewAgent]
28
- ]);
21
+ const MODE_FACTORIES = {
22
+ [MODE.INSTALLATION]: installationAgent,
23
+ [MODE.ACTION]: actionAgent,
24
+ [MODE.PLAN]: planAgent,
25
+ [MODE.ASK]: askAgent,
26
+ [MODE.MANAGE]: manageAgent,
27
+ [MODE.REVIEW]: reviewAgent
28
+ };
29
29
  export function getModes() {
30
30
  return MODE_METADATA.map((metadata) => {
31
- const factory = MODE_FACTORIES.get(metadata.name);
32
- if (!factory) {
31
+ if (!isAgentMode(metadata.name)) {
33
32
  throw new Error(`No mode factory registered for "${metadata.name}".`);
34
33
  }
35
- return factory();
34
+ return MODE_FACTORIES[metadata.name]();
36
35
  });
37
36
  }
38
37
  export function getMode(name) {
39
- return MODE_FACTORIES.get(name)?.();
38
+ return isAgentMode(name) ? MODE_FACTORIES[name]() : undefined;
40
39
  }
41
40
  export function getModeNames() {
42
41
  return MODE_METADATA.map((mode) => mode.name);