@gajae-code/coding-agent 0.13.2 → 0.14.0

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 (610) hide show
  1. package/CHANGELOG.md +215 -8
  2. package/README.md +2 -0
  3. package/bin/gajaessi.js +4 -0
  4. package/dist/types/async/job-manager.d.ts +55 -2
  5. package/dist/types/async/support.d.ts +14 -0
  6. package/dist/types/capability/mcp.d.ts +6 -0
  7. package/dist/types/capability/types.d.ts +6 -0
  8. package/dist/types/cli/accounts-cli.d.ts +19 -0
  9. package/dist/types/cli/args.d.ts +3 -0
  10. package/dist/types/cli/config-cli.d.ts +6 -0
  11. package/dist/types/cli/crash-cli.d.ts +10 -0
  12. package/dist/types/cli/customize-doctor.d.ts +145 -0
  13. package/dist/types/cli/daemon-cli.d.ts +3 -1
  14. package/dist/types/cli/mcp-cli.d.ts +11 -0
  15. package/dist/types/cli/notify-cli.d.ts +1 -1
  16. package/dist/types/cli/root-flags.d.ts +12 -0
  17. package/dist/types/cli/setup-cli.d.ts +3 -1
  18. package/dist/types/cli/skills-cli.d.ts +2 -1
  19. package/dist/types/cli/update-cli.d.ts +19 -1
  20. package/dist/types/cli.d.ts +6 -0
  21. package/dist/types/commands/accounts.d.ts +40 -0
  22. package/dist/types/commands/crash.d.ts +24 -0
  23. package/dist/types/commands/customize.d.ts +22 -0
  24. package/dist/types/commands/daemon.d.ts +1 -1
  25. package/dist/types/commands/launch.d.ts +6 -0
  26. package/dist/types/commands/setup.d.ts +6 -0
  27. package/dist/types/commands/skills.d.ts +6 -1
  28. package/dist/types/commands/update.d.ts +7 -0
  29. package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
  30. package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
  31. package/dist/types/config/config-schema-version.d.ts +10 -0
  32. package/dist/types/config/file-lock.d.ts +18 -0
  33. package/dist/types/config/model-profile-activation.d.ts +3 -1
  34. package/dist/types/config/model-registry.d.ts +10 -1
  35. package/dist/types/config/models-config-schema.d.ts +2 -0
  36. package/dist/types/config/settings-schema.d.ts +73 -9
  37. package/dist/types/config/settings.d.ts +63 -0
  38. package/dist/types/config/skill-settings-defaults.d.ts +26 -0
  39. package/dist/types/config/task-delegation.d.ts +39 -0
  40. package/dist/types/config/update-channel.d.ts +10 -0
  41. package/dist/types/coordinator-mcp/server.d.ts +5 -1
  42. package/dist/types/crash/index-store.d.ts +87 -0
  43. package/dist/types/crash/nudge.d.ts +50 -0
  44. package/dist/types/crash/record-loader.d.ts +17 -0
  45. package/dist/types/crash/report.d.ts +134 -0
  46. package/dist/types/crash/sanitize.d.ts +25 -0
  47. package/dist/types/cursor.d.ts +13 -2
  48. package/dist/types/customization/import.d.ts +37 -0
  49. package/dist/types/customization/index.d.ts +4 -0
  50. package/dist/types/customization/inventory.d.ts +25 -0
  51. package/dist/types/customization/mutations.d.ts +32 -0
  52. package/dist/types/customization/types.d.ts +115 -0
  53. package/dist/types/daemon/control-types.d.ts +8 -1
  54. package/dist/types/daemon/operator-contract.d.ts +4 -4
  55. package/dist/types/discovery/claude.d.ts +39 -1
  56. package/dist/types/discovery/codex.d.ts +39 -1
  57. package/dist/types/discovery/index.d.ts +2 -0
  58. package/dist/types/discovery/mcp-compat.d.ts +30 -0
  59. package/dist/types/edit/renderer.d.ts +23 -0
  60. package/dist/types/extensibility/extensions/runner.d.ts +3 -0
  61. package/dist/types/extensibility/extensions/types.d.ts +21 -4
  62. package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
  63. package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
  64. package/dist/types/extensibility/hooks/loader.d.ts +19 -5
  65. package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
  66. package/dist/types/extensibility/skill-management.d.ts +107 -0
  67. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
  68. package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
  69. package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
  70. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
  71. package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
  72. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
  73. package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
  74. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
  75. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
  76. package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
  77. package/dist/types/hooks/events.d.ts +60 -0
  78. package/dist/types/hooks/normalize.d.ts +83 -0
  79. package/dist/types/hooks/skill-state.d.ts +9 -2
  80. package/dist/types/main.d.ts +2 -1
  81. package/dist/types/modes/acp/abort-scope.d.ts +13 -0
  82. package/dist/types/modes/acp/acp-agent.d.ts +7 -4
  83. package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
  84. package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
  85. package/dist/types/modes/components/customization/index.d.ts +1 -0
  86. package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
  87. package/dist/types/modes/components/gajae-pet-widget.d.ts +6 -28
  88. package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
  89. package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
  90. package/dist/types/modes/components/oauth-selector.d.ts +10 -4
  91. package/dist/types/modes/components/pet-capability.d.ts +7 -21
  92. package/dist/types/modes/components/settings-selector.d.ts +5 -0
  93. package/dist/types/modes/components/status-line/gh.d.ts +6 -2
  94. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  95. package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
  96. package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
  97. package/dist/types/modes/interactive-mode.d.ts +1 -0
  98. package/dist/types/modes/theme/defaults/index.d.ts +118 -0
  99. package/dist/types/modes/types.d.ts +1 -0
  100. package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
  101. package/dist/types/runtime-mcp/config.d.ts +9 -0
  102. package/dist/types/runtime-mcp/manager.d.ts +26 -1
  103. package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
  104. package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
  105. package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
  106. package/dist/types/runtime-mcp/protocol.d.ts +278 -0
  107. package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
  108. package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
  109. package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
  110. package/dist/types/runtime-mcp/types.d.ts +102 -10
  111. package/dist/types/sdk/acp/adapter.d.ts +26 -5
  112. package/dist/types/sdk/broker/broker.d.ts +3 -55
  113. package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
  114. package/dist/types/sdk/broker/session-index.d.ts +14 -5
  115. package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
  116. package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
  117. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
  118. package/dist/types/sdk/bus/config.d.ts +11 -0
  119. package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
  120. package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
  121. package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
  122. package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
  123. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
  124. package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
  125. package/dist/types/sdk/bus/index.d.ts +85 -99
  126. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
  127. package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
  128. package/dist/types/sdk/bus/notification-service.d.ts +52 -0
  129. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
  130. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
  131. package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
  132. package/dist/types/sdk/bus/session-control.d.ts +25 -1
  133. package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
  134. package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
  135. package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
  136. package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
  137. package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
  138. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
  139. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
  140. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
  141. package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
  142. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
  143. package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
  144. package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
  145. package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
  146. package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
  147. package/dist/types/sdk/cli/rows.d.ts +2 -11
  148. package/dist/types/sdk/cli/session-cli.d.ts +15 -25
  149. package/dist/types/sdk/client/client.d.ts +97 -1
  150. package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
  151. package/dist/types/sdk/host/control/index.d.ts +1 -1
  152. package/dist/types/sdk/host/control/operations.d.ts +18 -5
  153. package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
  154. package/dist/types/sdk/host/host.d.ts +15 -1
  155. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  156. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  157. package/dist/types/sdk/host/session-runtime.d.ts +127 -7
  158. package/dist/types/sdk/host/types.d.ts +1 -1
  159. package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
  160. package/dist/types/sdk/index.d.ts +2 -1
  161. package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
  162. package/dist/types/sdk/lifecycle/client.d.ts +59 -0
  163. package/dist/types/sdk/lifecycle/index.d.ts +3 -0
  164. package/dist/types/sdk/lifecycle/service.d.ts +232 -0
  165. package/dist/types/sdk/mcp/server.d.ts +9 -6
  166. package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
  167. package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
  168. package/dist/types/sdk/prompt-status.d.ts +10 -3
  169. package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
  170. package/dist/types/sdk/receipt-state.d.ts +17 -0
  171. package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
  172. package/dist/types/sdk/router/index.d.ts +1 -0
  173. package/dist/types/sdk/router/session-router.d.ts +152 -0
  174. package/dist/types/sdk/session-list.d.ts +22 -0
  175. package/dist/types/sdk/session-reconnect.d.ts +43 -1
  176. package/dist/types/sdk/session.d.ts +34 -1
  177. package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
  178. package/dist/types/session/account-inventory.d.ts +86 -0
  179. package/dist/types/session/agent-session.d.ts +145 -6
  180. package/dist/types/session/agent-storage.d.ts +10 -0
  181. package/dist/types/session/coordinator-tool-label.d.ts +28 -0
  182. package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
  183. package/dist/types/session/messages.d.ts +1 -1
  184. package/dist/types/session/secure-token-file.d.ts +3 -0
  185. package/dist/types/session/session-manager.d.ts +47 -1
  186. package/dist/types/session/startup-auth-config.d.ts +44 -0
  187. package/dist/types/session/terminal-abort.d.ts +378 -0
  188. package/dist/types/session/yield-queue.d.ts +7 -0
  189. package/dist/types/session-import/claude.d.ts +26 -0
  190. package/dist/types/session-import/codex.d.ts +88 -0
  191. package/dist/types/session-import/command.d.ts +35 -0
  192. package/dist/types/session-import/detect.d.ts +22 -0
  193. package/dist/types/session-import/index.d.ts +7 -0
  194. package/dist/types/session-import/provider-codex.d.ts +22 -0
  195. package/dist/types/session-import/provider-service.d.ts +38 -0
  196. package/dist/types/session-import/redact.d.ts +23 -0
  197. package/dist/types/session-import/service.d.ts +47 -0
  198. package/dist/types/session-import/types.d.ts +120 -0
  199. package/dist/types/setup/paseo/check.d.ts +16 -0
  200. package/dist/types/setup/paseo/install-saga.d.ts +115 -0
  201. package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
  202. package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
  203. package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
  204. package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
  205. package/dist/types/setup/paseo/provider-config.d.ts +35 -0
  206. package/dist/types/setup/paseo/remove.d.ts +14 -0
  207. package/dist/types/setup/paseo/result-types.d.ts +59 -0
  208. package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
  209. package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
  210. package/dist/types/skill-state/active-state.d.ts +5 -1
  211. package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
  212. package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
  213. package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
  214. package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
  215. package/dist/types/slash-commands/types.d.ts +7 -1
  216. package/dist/types/task/executor.d.ts +10 -0
  217. package/dist/types/task/index.d.ts +3 -1
  218. package/dist/types/tools/ask-answer-registry.d.ts +6 -3
  219. package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
  220. package/dist/types/tools/bash.d.ts +2 -6
  221. package/dist/types/tools/browser/attach.d.ts +11 -0
  222. package/dist/types/tools/browser/launch.d.ts +20 -0
  223. package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
  224. package/dist/types/tools/browser/render.d.ts +1 -0
  225. package/dist/types/tools/browser.d.ts +3 -1
  226. package/dist/types/tools/image-gen.d.ts +10 -2
  227. package/dist/types/tools/index.d.ts +8 -0
  228. package/dist/types/tools/monitor.d.ts +1 -1
  229. package/dist/types/tools/skill-discovery.d.ts +8 -1
  230. package/dist/types/tools/subagent.d.ts +2 -0
  231. package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
  232. package/dist/types/utils/herdr-pane.d.ts +76 -0
  233. package/package.json +19 -9
  234. package/scripts/build-binary.ts +2 -1
  235. package/scripts/build-sdk-package-smoke.ts +2 -13
  236. package/scripts/capture-sticky-viewport-showcase.ts +0 -1
  237. package/scripts/compile-args.ts +2 -0
  238. package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
  239. package/scripts/generate-sdk-operation-inventory.ts +28 -0
  240. package/scripts/generate-telegram-baseline-manifest.ts +1 -0
  241. package/scripts/generate-tool-catalog.ts +6 -4
  242. package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
  243. package/src/async/job-manager.ts +352 -17
  244. package/src/async/support.ts +25 -0
  245. package/src/autoresearch/dashboard.ts +28 -13
  246. package/src/capability/index.ts +3 -1
  247. package/src/capability/mcp.ts +6 -0
  248. package/src/capability/types.ts +6 -0
  249. package/src/cli/accounts-cli.ts +395 -0
  250. package/src/cli/args.ts +23 -0
  251. package/src/cli/auth-broker-cli.ts +136 -66
  252. package/src/cli/auth-gateway-cli.ts +94 -70
  253. package/src/cli/config-cli.ts +27 -2
  254. package/src/cli/crash-cli.ts +78 -0
  255. package/src/cli/customize-doctor.ts +1605 -0
  256. package/src/cli/daemon-cli.ts +12 -3
  257. package/src/cli/fast-help.ts +1 -0
  258. package/src/cli/mcp-cli.ts +128 -60
  259. package/src/cli/notify-cli.ts +8 -14
  260. package/src/cli/root-flags.ts +8 -0
  261. package/src/cli/setup-cli.ts +145 -0
  262. package/src/cli/skills-cli.ts +38 -2
  263. package/src/cli/update-cli.ts +167 -12
  264. package/src/cli.ts +4 -0
  265. package/src/commands/accounts.ts +71 -0
  266. package/src/commands/crash.ts +35 -0
  267. package/src/commands/customize.ts +84 -0
  268. package/src/commands/daemon.ts +2 -2
  269. package/src/commands/deep-interview.ts +2 -0
  270. package/src/commands/launch.ts +1 -0
  271. package/src/commands/mcp.ts +12 -7
  272. package/src/commands/ralplan.ts +2 -0
  273. package/src/commands/sdk.ts +65 -32
  274. package/src/commands/setup.ts +5 -0
  275. package/src/commands/skills.ts +14 -4
  276. package/src/commands/ultragoal.ts +10 -1
  277. package/src/commands/update.ts +26 -15
  278. package/src/config/atomic-yaml-patch-worker.ts +57 -0
  279. package/src/config/atomic-yaml-patch.ts +769 -29
  280. package/src/config/config-schema-version.ts +10 -0
  281. package/src/config/file-lock.ts +70 -1
  282. package/src/config/mcp-schema.json +52 -14
  283. package/src/config/model-profile-activation.ts +23 -2
  284. package/src/config/model-profiles.ts +88 -21
  285. package/src/config/model-registry.ts +147 -21
  286. package/src/config/models-config-schema.ts +1 -1
  287. package/src/config/settings-schema.ts +77 -7
  288. package/src/config/settings.ts +4038 -199
  289. package/src/config/skill-settings-defaults.ts +41 -3
  290. package/src/config/task-delegation.ts +87 -0
  291. package/src/config/update-channel.ts +23 -0
  292. package/src/coordinator-mcp/server.ts +377 -308
  293. package/src/crash/index-store.ts +723 -0
  294. package/src/crash/nudge.ts +89 -0
  295. package/src/crash/record-loader.ts +149 -0
  296. package/src/crash/report.ts +551 -0
  297. package/src/crash/sanitize.ts +129 -0
  298. package/src/cursor.ts +36 -10
  299. package/src/customization/import.ts +946 -0
  300. package/src/customization/index.ts +4 -0
  301. package/src/customization/inventory.ts +335 -0
  302. package/src/customization/mutations.ts +141 -0
  303. package/src/customization/types.ts +223 -0
  304. package/src/daemon/control-types.ts +8 -1
  305. package/src/daemon/operator-contract.ts +6 -6
  306. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
  307. package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
  308. package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
  309. package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
  310. package/src/discovery/claude-hooks.ts +38 -0
  311. package/src/discovery/claude.ts +142 -209
  312. package/src/discovery/codex-hooks.ts +38 -0
  313. package/src/discovery/codex.ts +132 -198
  314. package/src/discovery/helpers.ts +8 -1
  315. package/src/discovery/index.ts +2 -0
  316. package/src/discovery/mcp-compat.ts +234 -0
  317. package/src/discovery/mcp-json.ts +6 -0
  318. package/src/edit/renderer.ts +40 -0
  319. package/src/edit/streaming.ts +4 -2
  320. package/src/exec/bash-executor.ts +24 -20
  321. package/src/export/html/index.ts +51 -2
  322. package/src/extensibility/extensions/loader.ts +9 -1
  323. package/src/extensibility/extensions/runner.ts +17 -4
  324. package/src/extensibility/extensions/types.ts +31 -6
  325. package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
  326. package/src/extensibility/gjc-plugins/installer.ts +9 -5
  327. package/src/extensibility/gjc-plugins/observability.ts +8 -2
  328. package/src/extensibility/gjc-plugins/registry.ts +13 -2
  329. package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
  330. package/src/extensibility/gjc-plugins/schema.ts +222 -39
  331. package/src/extensibility/hooks/loader.ts +154 -10
  332. package/src/extensibility/runtime-skill-discovery.ts +257 -60
  333. package/src/extensibility/skill-management.ts +368 -0
  334. package/src/extensibility/skills.ts +52 -16
  335. package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
  336. package/src/gjc-runtime/launch-worktree.ts +56 -2
  337. package/src/gjc-runtime/linux-proc.ts +7 -1
  338. package/src/gjc-runtime/ralplan-runtime.ts +100 -164
  339. package/src/gjc-runtime/session-state-lock.ts +992 -0
  340. package/src/gjc-runtime/session-state-sidecar.ts +644 -142
  341. package/src/gjc-runtime/state-writer.ts +39 -14
  342. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
  343. package/src/gjc-runtime/ultragoal-guard.ts +11 -4
  344. package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
  345. package/src/gjc-runtime/workflow-settings.ts +596 -0
  346. package/src/goals/tools/goal-tool.ts +6 -1
  347. package/src/hooks/events.ts +291 -0
  348. package/src/hooks/native-skill-hook.ts +4 -0
  349. package/src/hooks/normalize.ts +369 -0
  350. package/src/hooks/skill-state.ts +165 -46
  351. package/src/internal-urls/docs-index.generated.ts +41 -32
  352. package/src/main.ts +36 -5
  353. package/src/migrate/mcp-mapper.ts +25 -2
  354. package/src/modes/acp/abort-scope.ts +29 -0
  355. package/src/modes/acp/acp-agent.ts +423 -98
  356. package/src/modes/components/assistant-message.ts +6 -1
  357. package/src/modes/components/chrome.ts +4 -3
  358. package/src/modes/components/customization/customization-dashboard.ts +443 -0
  359. package/src/modes/components/customization/import-wizard.ts +372 -0
  360. package/src/modes/components/customization/index.ts +1 -0
  361. package/src/modes/components/dynamic-theme-text.ts +22 -0
  362. package/src/modes/components/gajae-pet-widget.ts +607 -193
  363. package/src/modes/components/irc-sidebar.ts +11 -0
  364. package/src/modes/components/iterm-pet-transport.ts +585 -0
  365. package/src/modes/components/model-selector.ts +1 -6
  366. package/src/modes/components/oauth-selector.ts +277 -86
  367. package/src/modes/components/pet-capability.ts +35 -31
  368. package/src/modes/components/settings-selector.ts +29 -5
  369. package/src/modes/components/status-line/gh.ts +45 -1
  370. package/src/modes/components/status-line/segments.ts +19 -4
  371. package/src/modes/components/tool-status-header.ts +13 -3
  372. package/src/modes/controllers/command-controller.ts +20 -8
  373. package/src/modes/controllers/extension-ui-controller.ts +56 -257
  374. package/src/modes/controllers/input-controller.ts +10 -0
  375. package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
  376. package/src/modes/controllers/selector-controller.ts +238 -77
  377. package/src/modes/controllers/stt-controller.ts +3 -3
  378. package/src/modes/interactive-mode.ts +88 -16
  379. package/src/modes/print-mode.ts +35 -6
  380. package/src/modes/runtime-init.ts +21 -68
  381. package/src/modes/theme/defaults/index.ts +2 -0
  382. package/src/modes/theme/defaults/ouroboros.json +118 -0
  383. package/src/modes/types.ts +1 -0
  384. package/src/modes/utils/injected-user-submission.ts +25 -0
  385. package/src/modes/utils/terminal-bell.ts +15 -1
  386. package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
  387. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  388. package/src/prompts/agents/critic.md +1 -1
  389. package/src/prompts/system/rlm-research.md +1 -1
  390. package/src/prompts/system/system-prompt.md +0 -2
  391. package/src/prompts/tools/bash.md +7 -1
  392. package/src/prompts/tools/browser.md +1 -1
  393. package/src/prompts/tools/cron.md +6 -6
  394. package/src/prompts/tools/read.md +0 -2
  395. package/src/prompts/tools/skill-discovery.md +4 -2
  396. package/src/runtime-mcp/client.ts +427 -23
  397. package/src/runtime-mcp/config.ts +37 -5
  398. package/src/runtime-mcp/manager-pool.test.ts +56 -0
  399. package/src/runtime-mcp/manager.ts +52 -2
  400. package/src/runtime-mcp/oauth-discovery.ts +13 -0
  401. package/src/runtime-mcp/oauth-flow.ts +46 -1
  402. package/src/runtime-mcp/pool-key.ts +5 -1
  403. package/src/runtime-mcp/pool.test.ts +9 -0
  404. package/src/runtime-mcp/protocol.test.ts +353 -0
  405. package/src/runtime-mcp/protocol.ts +624 -0
  406. package/src/runtime-mcp/scope-config.ts +78 -0
  407. package/src/runtime-mcp/tool-bridge.ts +44 -10
  408. package/src/runtime-mcp/transports/http.ts +84 -13
  409. package/src/runtime-mcp/types.ts +123 -12
  410. package/src/sdk/acp/adapter.ts +328 -97
  411. package/src/sdk/broker/broker.ts +160 -741
  412. package/src/sdk/broker/ensure.ts +9 -1
  413. package/src/sdk/broker/lifecycle.ts +510 -116
  414. package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
  415. package/src/sdk/broker/process-incarnation.ts +19 -0
  416. package/src/sdk/broker/session-index.ts +516 -117
  417. package/src/sdk/broker/startup-failure.ts +28 -5
  418. package/src/sdk/broker/transport.ts +1 -13
  419. package/src/sdk/bus/chat-daemon-cli.ts +4 -2
  420. package/src/sdk/bus/chat-daemon-control.ts +67 -6
  421. package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
  422. package/src/sdk/bus/config.ts +13 -0
  423. package/src/sdk/bus/conversation-store.ts +59 -171
  424. package/src/sdk/bus/daemon-paths.ts +20 -0
  425. package/src/sdk/bus/discord-conversation.ts +2 -0
  426. package/src/sdk/bus/discord-daemon.ts +524 -126
  427. package/src/sdk/bus/discord-live-provider.ts +206 -79
  428. package/src/sdk/bus/existing-thread-readiness.ts +4 -97
  429. package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
  430. package/src/sdk/bus/index.ts +2480 -456
  431. package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
  432. package/src/sdk/bus/lifecycle-commands.ts +2 -2
  433. package/src/sdk/bus/managed-daemon.ts +2 -145
  434. package/src/sdk/bus/notification-service.ts +196 -0
  435. package/src/sdk/bus/operator-runtime.ts +1 -1
  436. package/src/sdk/bus/prompt-reconciliation.ts +24 -15
  437. package/src/sdk/bus/reconciliation-store.ts +553 -24
  438. package/src/sdk/bus/session-control.ts +49 -3
  439. package/src/sdk/bus/slack-conversation.ts +4 -0
  440. package/src/sdk/bus/slack-daemon.ts +733 -134
  441. package/src/sdk/bus/slack-live-provider.ts +65 -15
  442. package/src/sdk/bus/slack-provider.ts +4 -0
  443. package/src/sdk/bus/slack-thread-binding.ts +9 -104
  444. package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
  445. package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
  446. package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
  447. package/src/sdk/bus/telegram-daemon-control.ts +3 -13
  448. package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
  449. package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
  450. package/src/sdk/bus/telegram-daemon.ts +2552 -2280
  451. package/src/sdk/bus/telegram-reference.ts +1 -226
  452. package/src/sdk/bus/telegram-setup.ts +2 -2
  453. package/src/sdk/bus/threaded-render.ts +1 -0
  454. package/src/sdk/bus/topic-registry.ts +279 -146
  455. package/src/sdk/cli/rows.ts +2 -35
  456. package/src/sdk/cli/session-cli.ts +776 -700
  457. package/src/sdk/client/client.ts +892 -1
  458. package/src/sdk/host/control/dispatch.ts +93 -58
  459. package/src/sdk/host/control/index.ts +1 -1
  460. package/src/sdk/host/control/operations.ts +18 -10
  461. package/src/sdk/host/control/runtime-gate.ts +40 -0
  462. package/src/sdk/host/host.ts +63 -12
  463. package/src/sdk/host/query/handlers.ts +66 -7
  464. package/src/sdk/host/reverse-leases.ts +2 -2
  465. package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
  466. package/src/sdk/host/session-runtime.test.ts +2787 -34
  467. package/src/sdk/host/session-runtime.ts +1489 -101
  468. package/src/sdk/host/surface-policy.ts +12 -8
  469. package/src/sdk/host/types.ts +1 -1
  470. package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
  471. package/src/sdk/host/websocket-transport.ts +85 -50
  472. package/src/sdk/index.ts +2 -1
  473. package/src/sdk/lifecycle/broker-client.ts +53 -0
  474. package/src/sdk/lifecycle/client.ts +234 -0
  475. package/src/sdk/lifecycle/index.ts +3 -0
  476. package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
  477. package/src/sdk/lifecycle/service.ts +720 -0
  478. package/src/sdk/mcp/server.ts +226 -188
  479. package/src/sdk/prompt-deadline-lease.ts +55 -0
  480. package/src/sdk/prompt-deadline-manager.ts +148 -0
  481. package/src/sdk/prompt-status.ts +11 -3
  482. package/src/sdk/protocol/operation-inventory.generated.json +272 -129
  483. package/src/sdk/protocol/operation-registry.ts +71 -8
  484. package/src/sdk/receipt-state.ts +43 -0
  485. package/src/sdk/reconciliation-extensions.ts +14 -0
  486. package/src/sdk/router/index.ts +1 -0
  487. package/src/sdk/router/session-router.ts +1658 -0
  488. package/src/sdk/session-list.ts +93 -0
  489. package/src/sdk/session-reconnect.ts +45 -1
  490. package/src/sdk/session.ts +716 -120
  491. package/src/sdk/transport/serve-cli.ts +18 -34
  492. package/src/session/account-inventory.ts +603 -0
  493. package/src/session/agent-session.ts +2873 -559
  494. package/src/session/agent-storage.ts +35 -0
  495. package/src/session/blob-store.ts +32 -1
  496. package/src/session/coordinator-tool-label.ts +40 -0
  497. package/src/session/internal/managed-session-scope.ts +4 -0
  498. package/src/session/internal/managed-session-storage.ts +199 -31
  499. package/src/session/messages.ts +1 -1
  500. package/src/session/secure-token-file.ts +80 -0
  501. package/src/session/session-manager.ts +757 -57
  502. package/src/session/session-storage.ts +25 -16
  503. package/src/session/startup-auth-config.ts +297 -0
  504. package/src/session/terminal-abort.ts +1253 -0
  505. package/src/session/yield-queue.ts +57 -15
  506. package/src/session-import/claude.ts +334 -0
  507. package/src/session-import/codex.ts +1033 -0
  508. package/src/session-import/command.ts +77 -0
  509. package/src/session-import/detect.ts +133 -0
  510. package/src/session-import/index.ts +7 -0
  511. package/src/session-import/provider-codex.ts +327 -0
  512. package/src/session-import/provider-service.ts +457 -0
  513. package/src/session-import/redact.ts +128 -0
  514. package/src/session-import/service.ts +1264 -0
  515. package/src/session-import/types.ts +148 -0
  516. package/src/setup/paseo/check.ts +213 -0
  517. package/src/setup/paseo/install-saga.ts +293 -0
  518. package/src/setup/paseo/json-publisher.ts +263 -0
  519. package/src/setup/paseo/orchestration-preferences.ts +86 -0
  520. package/src/setup/paseo/paseo-ownership.ts +224 -0
  521. package/src/setup/paseo/paseo-setup.ts +253 -0
  522. package/src/setup/paseo/provider-config.ts +139 -0
  523. package/src/setup/paseo/remove.ts +183 -0
  524. package/src/setup/paseo/result-types.ts +79 -0
  525. package/src/setup/paseo/setup-deps.ts +175 -0
  526. package/src/setup/paseo/skills-bridge.ts +260 -0
  527. package/src/skill-state/active-state.ts +21 -11
  528. package/src/slash-commands/acp-builtins.ts +9 -1
  529. package/src/slash-commands/builtin-registry.ts +117 -19
  530. package/src/slash-commands/helpers/credential-switch.ts +143 -0
  531. package/src/slash-commands/helpers/mcp.ts +9 -3
  532. package/src/slash-commands/helpers/usage-report.ts +61 -56
  533. package/src/slash-commands/types.ts +7 -2
  534. package/src/task/executor.ts +30 -11
  535. package/src/task/index.ts +66 -7
  536. package/src/task/render.ts +23 -9
  537. package/src/tools/ask-answer-registry.ts +23 -7
  538. package/src/tools/ask.ts +8 -4
  539. package/src/tools/bash-sleep-advisory.ts +115 -0
  540. package/src/tools/bash.ts +98 -18
  541. package/src/tools/browser/attach.ts +71 -3
  542. package/src/tools/browser/launch.ts +86 -13
  543. package/src/tools/browser/profile-discovery.ts +57 -7
  544. package/src/tools/browser/render.ts +8 -1
  545. package/src/tools/browser.ts +130 -21
  546. package/src/tools/computer.enforcement.test.ts +1 -1
  547. package/src/tools/computer.ts +3 -2
  548. package/src/tools/cron.ts +12 -1
  549. package/src/tools/image-gen.ts +349 -142
  550. package/src/tools/index.ts +8 -0
  551. package/src/tools/job.ts +33 -6
  552. package/src/tools/monitor.ts +41 -2
  553. package/src/tools/skill-discovery.ts +21 -29
  554. package/src/tools/subagent.ts +142 -24
  555. package/src/tools/todo-contract.ts +55 -3
  556. package/src/tools/todo-write.ts +4 -3
  557. package/src/tools/tool-catalog.generated.ts +6 -6
  558. package/src/tools/ultragoal-ask-guard.ts +10 -3
  559. package/src/utils/herdr-pane.ts +345 -0
  560. package/src/utils/title-generator.ts +2 -0
  561. package/src/web/scrapers/index.ts +6 -4
  562. package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
  563. package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
  564. package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
  565. package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
  566. package/dist/types/runtime/lazy-service.test.d.ts +0 -1
  567. package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
  568. package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
  569. package/dist/types/runtime-mcp/config.test.d.ts +0 -1
  570. package/dist/types/runtime-mcp/http.test.d.ts +0 -1
  571. package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
  572. package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
  573. package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
  574. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
  575. package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
  576. package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
  577. package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
  578. package/dist/types/sdk/elevation/audit.d.ts +0 -93
  579. package/dist/types/sdk/elevation/capability.d.ts +0 -3
  580. package/dist/types/sdk/elevation/digest.d.ts +0 -72
  581. package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
  582. package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
  583. package/dist/types/sdk/elevation/owner.d.ts +0 -48
  584. package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
  585. package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
  586. package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
  587. package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
  588. package/dist/types/session/auth-broker-config.d.ts +0 -13
  589. package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
  590. package/dist/types/skills/skills.test.d.ts +0 -1
  591. package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
  592. package/dist/types/task/token-log.test.d.ts +0 -1
  593. package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
  594. package/dist/types/tools/descriptors.test.d.ts +0 -1
  595. package/scripts/g005-daemon-smoke.ts +0 -180
  596. package/scripts/g011-daemon-path-smoke.ts +0 -153
  597. package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
  598. package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
  599. package/src/sdk/bus/telegram-cli.ts +0 -212
  600. package/src/sdk/elevation/allowlist.ts +0 -54
  601. package/src/sdk/elevation/audit.ts +0 -208
  602. package/src/sdk/elevation/capability.ts +0 -41
  603. package/src/sdk/elevation/digest.ts +0 -129
  604. package/src/sdk/elevation/dispatch-receipt.ts +0 -184
  605. package/src/sdk/elevation/grant-ledger.ts +0 -1142
  606. package/src/sdk/elevation/owner.ts +0 -109
  607. package/src/session/auth-broker-config.ts +0 -109
  608. /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
  609. /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
  610. /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
@@ -30,6 +30,7 @@ interface MCPConfigFile {
30
30
  enabled?: boolean;
31
31
  autoload?: boolean;
32
32
  sharing?: "per-session" | "shared";
33
+ protocol?: "auto" | "2026-07-28" | "legacy";
33
34
  timeout?: number;
34
35
  command?: string;
35
36
  args?: string[];
@@ -104,6 +105,10 @@ function isValidExactServerConfig(value: unknown): boolean {
104
105
  return (
105
106
  isOptionalBoolean(value.enabled) &&
106
107
  (value.sharing === undefined || value.sharing === "per-session" || value.sharing === "shared") &&
108
+ (value.protocol === undefined ||
109
+ value.protocol === "auto" ||
110
+ value.protocol === "2026-07-28" ||
111
+ value.protocol === "legacy") &&
107
112
  isOptionalBoolean(value.autoload) &&
108
113
  isOptionalBoolean(value.noInheritEnv) &&
109
114
  (value.timeout === undefined ||
@@ -203,6 +208,7 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta, quiet = f
203
208
  enabled,
204
209
  autoload,
205
210
  sharing: serverConfig.sharing,
211
+ protocol: serverConfig.protocol,
206
212
  timeout,
207
213
  command: serverConfig.command,
208
214
  args: serverConfig.args,
@@ -2,6 +2,7 @@
2
2
  * Edit tool renderer and LSP batching helpers.
3
3
  */
4
4
 
5
+ import { createHash } from "node:crypto";
5
6
  import type { Component } from "@gajae-code/tui";
6
7
  import { Text, visibleWidth, wrapTextWithAnsi } from "@gajae-code/tui";
7
8
  import { sanitizeText } from "@gajae-code/utils";
@@ -85,6 +86,45 @@ export interface EditToolDetails {
85
86
  newText?: string;
86
87
  }
87
88
 
89
+ /**
90
+ * Bounded durable identity of one edit snapshot (#4566).
91
+ *
92
+ * Live edit results carry full `oldText`/`newText` file bodies so in-process
93
+ * consumers (ACP `diff` ToolCallContent, editors) keep working. Every persisted
94
+ * transcript entry replaces those bodies with this fixed-size receipt: byte
95
+ * length plus SHA-256 content digest, enough to detect source drift and to
96
+ * account for the edit durably without re-writing the whole file per edit.
97
+ */
98
+ export interface EditSnapshotReceipt {
99
+ /** UTF-8 byte length of the snapshot (`0` for create/delete-absent sides). */
100
+ bytes: number;
101
+ /** SHA-256 hex digest of the exact snapshot text (empty string for length 0). */
102
+ sha256: string;
103
+ }
104
+
105
+ /** Per-edit-mode cap on any single persisted edit-result string field (#4566). */
106
+ export const EDIT_PERSIST_FIELD_MAX_CHARS = 16 * 1024;
107
+
108
+ /** Fixed marker used when a snapshot receipt replaces a full body. */
109
+ export const EDIT_SNAPSHOT_EXTERNALIZED_NOTICE =
110
+ "[edit snapshot externalized: see oldTextDigest/newTextDigest; full body omitted from transcript]";
111
+
112
+ function sha256Hex(text: string): string {
113
+ if (text.length === 0) return "";
114
+ return createHash("sha256").update(Buffer.from(text, "utf-8")).digest("hex");
115
+ }
116
+
117
+ /** Build the bounded durable receipt for one snapshot body. */
118
+ export function editSnapshotReceipt(text: string | undefined): EditSnapshotReceipt | undefined {
119
+ if (text === undefined) return undefined;
120
+ return { bytes: Buffer.byteLength(text, "utf-8"), sha256: sha256Hex(text) };
121
+ }
122
+
123
+ /** True when a snapshot body is small enough to persist inline without amplification. */
124
+ export function editSnapshotPersistableInline(text: string | undefined): boolean {
125
+ return text !== undefined && text.length <= EDIT_PERSIST_FIELD_MAX_CHARS;
126
+ }
127
+
88
128
  // ═══════════════════════════════════════════════════════════════════════════
89
129
  // TUI Renderer
90
130
  // ═══════════════════════════════════════════════════════════════════════════
@@ -82,7 +82,9 @@ export interface EditRequestTargetInventory {
82
82
  /** First envelope entry rename target, for free-form modes that carry no structured `edits`. */
83
83
  rename?: string;
84
84
  }
85
- const MISSING_APPLY_PATCH_END_ERROR = `The last line of the patch must be '${END_PATCH_MARKER}'`;
85
+ function missingApplyPatchEndError(): string {
86
+ return `The last line of the patch must be '${END_PATCH_MARKER}'`;
87
+ }
86
88
 
87
89
  export function orderedDistinctPaths(paths: readonly (string | undefined)[]): string[] {
88
90
  const seen = new Set<string>();
@@ -181,7 +183,7 @@ export function getEditRequestTargetInventory(
181
183
  } catch (err) {
182
184
  const parseError = err instanceof Error ? err.message : String(err);
183
185
  const inventory = applyPatchEntryInventory(expandApplyPatchToPreviewEntries({ input }));
184
- if (options.isPartial && parseError === MISSING_APPLY_PATCH_END_ERROR) return inventory;
186
+ if (options.isPartial && parseError === missingApplyPatchEndError()) return inventory;
185
187
  return { ...inventory, parseError };
186
188
  }
187
189
  }
@@ -184,7 +184,6 @@ export interface BashResult {
184
184
  }
185
185
 
186
186
  const shellSessions = new Map<string, Shell>();
187
- const brokenShellSessions = new Set<string>();
188
187
  const retiringShellSessions = new Set<Shell>();
189
188
  // Cover pi-shell's normal cancellation kill waves without turning a stalled
190
189
  // native cleanup into a multi-second JavaScript tool stall.
@@ -204,16 +203,18 @@ export function getShellSessionCount(): number {
204
203
  */
205
204
  export async function disposeAllShellSessions(): Promise<void> {
206
205
  // Snapshot and drop strong references up front so concurrent callers cannot
207
- // reuse a session that is being torn down, then await every native abort so
206
+ // reuse a session that is being torn down, then await every native close so
208
207
  // shutdown/signal cleanup does not return before resources are released.
209
208
  // Include retiring shells whose JS call returned after bounded abort cleanup
210
209
  // while the native run is still unwinding; they are no longer reusable but
211
210
  // remain owned until their run promise settles.
211
+ // `close` rather than `abort`: aborting only cancels in-flight commands and
212
+ // leaves a completed session retained for reuse, which keeps the native shell
213
+ // alive for the rest of the process lifetime.
212
214
  const sessions = new Set([...shellSessions.values(), ...retiringShellSessions]);
213
215
  shellSessions.clear();
214
216
  retiringShellSessions.clear();
215
- brokenShellSessions.clear();
216
- await Promise.allSettled([...sessions].map(session => session.abort()));
217
+ await Promise.allSettled([...sessions].map(session => session.close()));
217
218
  }
218
219
 
219
220
  postmortem.register("bash-executor:shell-sessions", () => disposeAllShellSessions());
@@ -292,10 +293,9 @@ export async function executeBash(command: string, options?: BashExecutorOptions
292
293
 
293
294
  const usePersistentShell = options?.oneShot !== true;
294
295
  const sessionKey = buildSessionKey(shell, configuredPrefix, snapshotPath, shellEnv, options?.sessionKey, minimizer);
295
- const persistentSessionBroken = usePersistentShell && brokenShellSessions.has(sessionKey);
296
296
 
297
- let shellSession = persistentSessionBroken || !usePersistentShell ? undefined : shellSessions.get(sessionKey);
298
- if (!shellSession && !persistentSessionBroken && usePersistentShell) {
297
+ let shellSession = usePersistentShell ? shellSessions.get(sessionKey) : undefined;
298
+ if (!shellSession && usePersistentShell) {
299
299
  shellSession = new Shell({
300
300
  sessionEnv: shellEnv,
301
301
  snapshotPath: snapshotPath ?? undefined,
@@ -306,14 +306,13 @@ export async function executeBash(command: string, options?: BashExecutorOptions
306
306
  // Non-persistent invocations still need an owned native Shell so its lifetime
307
307
  // can be ended explicitly. executeShell creates a native shell outside the
308
308
  // persistent registry, leaving its cleanup untrackable by the host process.
309
- const oneShotShell =
310
- !usePersistentShell || persistentSessionBroken
311
- ? new Shell({
312
- sessionEnv: shellEnv,
313
- snapshotPath: snapshotPath ?? undefined,
314
- minimizer,
315
- })
316
- : undefined;
309
+ const oneShotShell = !usePersistentShell
310
+ ? new Shell({
311
+ sessionEnv: shellEnv,
312
+ snapshotPath: snapshotPath ?? undefined,
313
+ minimizer,
314
+ })
315
+ : undefined;
317
316
  const activeShell = shellSession ?? oneShotShell;
318
317
  const userSignal = options?.signal;
319
318
  const runAbortController = new AbortController();
@@ -389,20 +388,23 @@ export async function executeBash(command: string, options?: BashExecutorOptions
389
388
  if (shellSession) {
390
389
  resetSession = true;
391
390
  retiringShellSessions.add(shellSession);
392
- brokenShellSessions.add(sessionKey);
393
- shellSessions.delete(sessionKey);
391
+ if (shellSessions.get(sessionKey) === shellSession) {
392
+ shellSessions.delete(sessionKey);
393
+ }
394
394
  runSettled = await awaitAbortCleanup(runPromise);
395
+ // A retired session is never reused, so release the native shell instead
396
+ // of leaving it retained for the rest of the process lifetime.
395
397
  if (runSettled) {
396
- brokenShellSessions.delete(sessionKey);
397
398
  retiringShellSessions.delete(shellSession);
399
+ void shellSession.close().catch(() => undefined);
398
400
  } else {
399
401
  void runPromise
400
402
  .finally(() => {
401
- brokenShellSessions.delete(sessionKey);
402
403
  retiringShellSessions.delete(shellSession);
403
404
  if (shellSessions.get(sessionKey) === shellSession) {
404
405
  shellSessions.delete(sessionKey);
405
406
  }
407
+ void shellSession.close().catch(() => undefined);
406
408
  })
407
409
  .catch(() => undefined);
408
410
  }
@@ -508,7 +510,9 @@ export async function executeBash(command: string, options?: BashExecutorOptions
508
510
  shellSessions.delete(sessionKey);
509
511
  }
510
512
  if (oneShotShell) {
511
- const disposePromise = abortPromise ?? oneShotShell.abort();
513
+ // Always close: a successful run keeps its session retained, so aborting
514
+ // alone would leak the native shell and hold the event loop open.
515
+ const disposePromise = (abortPromise ?? Promise.resolve()).then(() => oneShotShell.close());
512
516
  await Promise.race([disposePromise.catch(() => undefined), Bun.sleep(CANCEL_CLEANUP_WAIT_MS)]);
513
517
  }
514
518
  }
@@ -1,3 +1,4 @@
1
+ import * as syncFs from "node:fs";
1
2
  import * as fs from "node:fs/promises";
2
3
  import * as path from "node:path";
3
4
  import type { AgentState } from "@gajae-code/agent-core";
@@ -162,6 +163,51 @@ async function assertExportDoesNotAliasSource(sourcePath: string, outputPath: st
162
163
  throw new Error("HTML export output must not overwrite the source transcript");
163
164
  }
164
165
 
166
+ const HTML_EXPORT_SOURCE_OPEN_FLAGS =
167
+ syncFs.constants.O_RDONLY |
168
+ syncFs.constants.O_NONBLOCK |
169
+ (process.platform === "win32" ? 0 : (syncFs.constants.O_NOFOLLOW ?? 0));
170
+
171
+ const HTML_EXPORT_DESTINATION_OPEN_FLAGS =
172
+ syncFs.constants.O_WRONLY |
173
+ syncFs.constants.O_CREAT |
174
+ (process.platform === "win32" ? 0 : (syncFs.constants.O_NOFOLLOW ?? 0));
175
+
176
+ function sameFileIdentity(left: syncFs.BigIntStats, right: syncFs.BigIntStats): boolean {
177
+ return left.dev === right.dev && left.ino === right.ino;
178
+ }
179
+
180
+ async function openHtmlExportDescriptors(
181
+ sourcePath: string | undefined,
182
+ outputPath: string,
183
+ ): Promise<{ source: number | undefined; destination: number }> {
184
+ let source: number | undefined;
185
+ let destination: number | undefined;
186
+ try {
187
+ if (sourcePath) {
188
+ try {
189
+ source = syncFs.openSync(sourcePath, HTML_EXPORT_SOURCE_OPEN_FLAGS);
190
+ } catch (error) {
191
+ if (!isEnoent(error)) throw error;
192
+ }
193
+ await assertExportDoesNotAliasSource(sourcePath, outputPath);
194
+ }
195
+ destination = syncFs.openSync(outputPath, HTML_EXPORT_DESTINATION_OPEN_FLAGS, 0o666);
196
+ if (source !== undefined) {
197
+ const sourceIdentity = syncFs.fstatSync(source, { bigint: true });
198
+ const destinationIdentity = syncFs.fstatSync(destination, { bigint: true });
199
+ if (sameFileIdentity(sourceIdentity, destinationIdentity))
200
+ throw new Error("HTML export output must not overwrite the source transcript");
201
+ }
202
+ syncFs.ftruncateSync(destination, 0);
203
+ return { source, destination };
204
+ } catch (error) {
205
+ if (destination !== undefined) syncFs.closeSync(destination);
206
+ if (source !== undefined) syncFs.closeSync(source);
207
+ throw error;
208
+ }
209
+ }
210
+
165
211
  async function writeSessionHtml(
166
212
  sm: SessionManager,
167
213
  state: AgentState | undefined,
@@ -174,8 +220,8 @@ async function writeSessionHtml(
174
220
  const markerOffset = themedTemplate.indexOf(marker);
175
221
  if (markerOffset < 0) throw new Error("HTML export template is missing the session-data marker");
176
222
  const sourcePath = sm.getSessionFile();
177
- if (sourcePath) await assertExportDoesNotAliasSource(sourcePath, outputPath);
178
- const sink = Bun.file(outputPath).writer();
223
+ const descriptors = await openHtmlExportDescriptors(sourcePath, outputPath);
224
+ const sink = Bun.file(descriptors.destination).writer();
179
225
  const encoder = new Base64StreamEncoder();
180
226
  const writeEncoded = (value: string): void => {
181
227
  const encoded = encoder.write(value);
@@ -204,6 +250,9 @@ async function writeSessionHtml(
204
250
  } catch (error) {
205
251
  await sink.end();
206
252
  throw error;
253
+ } finally {
254
+ if (descriptors.source !== undefined) syncFs.closeSync(descriptors.source);
255
+ syncFs.closeSync(descriptors.destination);
207
256
  }
208
257
  }
209
258
 
@@ -240,7 +240,15 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
240
240
 
241
241
  sendUserMessage(
242
242
  content: string | (TextContent | ImageContent)[],
243
- options?: { deliverAs?: "steer" | "followUp"; onPreflightAccepted?: () => void },
243
+ options?: {
244
+ deliverAs?: "steer" | "followUp";
245
+ queuedAtDispatch?: boolean;
246
+ onPreflightAccepted?: () => void;
247
+ onPreflightAcceptCommit?: () => void | Promise<void>;
248
+ onQueuedPromoted?: () => void;
249
+ preflightSignal?: AbortSignal;
250
+ sdkRunToken?: string;
251
+ },
244
252
  ): Promise<void> {
245
253
  return Promise.resolve(this.runtime.sendUserMessage(content, options));
246
254
  }
@@ -69,12 +69,18 @@ interface BeforeAgentStartCombinedResult {
69
69
 
70
70
  export type ExtensionErrorListener = (error: ExtensionError) => void;
71
71
 
72
+ /** Bounded timeout for session_shutdown handlers — generous but never infinite. */
73
+ export const SESSION_SHUTDOWN_HANDLER_TIMEOUT_MS = 60_000;
72
74
  export const EXTENSION_HANDLER_TIMEOUT_MS = 30_000;
73
75
  let extensionHandlerTimeoutMs = EXTENSION_HANDLER_TIMEOUT_MS;
76
+ let sessionShutdownHandlerTimeoutMs = SESSION_SHUTDOWN_HANDLER_TIMEOUT_MS;
74
77
 
75
78
  export function testSetExtensionHandlerTimeoutMs(timeoutMs: number): void {
76
79
  extensionHandlerTimeoutMs = timeoutMs;
77
80
  }
81
+ export function testSetSessionShutdownHandlerTimeoutMs(timeoutMs: number): void {
82
+ sessionShutdownHandlerTimeoutMs = timeoutMs;
83
+ }
78
84
 
79
85
  const EXTENSION_HANDLER_TIMEOUT = Symbol("extensionHandlerTimeout");
80
86
 
@@ -201,7 +207,7 @@ export class ExtensionRunner {
201
207
  #isIdleFn: () => boolean = () => true;
202
208
  #getActivePromptHandleFn: () => string | undefined = () => undefined;
203
209
  #waitForIdleFn: () => Promise<void> = async () => {};
204
- #abortFn: () => void = () => {};
210
+ #abortFn: () => void | Promise<void> = () => {};
205
211
  #abortPromptAndWaitFn: NonNullable<ExtensionContextActions["abortPromptAndWait"]> = async () => {
206
212
  throw new Error("abortPromptAndWait binding is unavailable");
207
213
  };
@@ -637,7 +643,7 @@ export class ExtensionRunner {
637
643
  withSdkControlMutation: body => this.#withSdkControlMutationFn?.(body) ?? body(),
638
644
  cycleThinkingLevel: () => this.#cycleThinkingLevelFn?.(),
639
645
  setQueueMode: (kind, mode) => this.#setQueueModeFn?.(kind, mode) ?? false,
640
- invokeSkill: async (name, args) => await this.#invokeSkillFn?.(name, args),
646
+ invokeSkill: async (name, args, options) => await this.#invokeSkillFn?.(name, args, options),
641
647
 
642
648
  setPlanMode: on => this.#setPlanModeFn?.(on),
643
649
  operateGoal: async (op, objective) => await this.#operateGoalFn?.(op, objective),
@@ -715,12 +721,13 @@ export class ExtensionRunner {
715
721
  event: TEvent,
716
722
  ctx: ExtensionContext,
717
723
  ext: Extension,
718
- timeoutMs: number,
724
+ timeoutMs: number | undefined,
719
725
  ): Promise<TResult | undefined> {
720
726
  let timeout: NodeJS.Timeout | undefined;
721
727
  const abortController = new AbortController();
722
728
  const handlerContext = createHandlerContext(ctx, abortController.signal);
723
729
  try {
730
+ if (timeoutMs === undefined) return await handler(event, handlerContext);
724
731
  const timeoutPromise = new Promise<typeof EXTENSION_HANDLER_TIMEOUT>(resolve => {
725
732
  timeout = setTimeout(() => resolve(EXTENSION_HANDLER_TIMEOUT), timeoutMs);
726
733
  });
@@ -782,7 +789,13 @@ export class ExtensionRunner {
782
789
  this.#markAttemptExecuted(scope);
783
790
  marked = true;
784
791
  }
785
- const handlerResult = await this.#runHandlerWithTimeout(handler, event, ctx, ext, extensionHandlerTimeoutMs);
792
+ const handlerResult = await this.#runHandlerWithTimeout(
793
+ handler,
794
+ event,
795
+ ctx,
796
+ ext,
797
+ event.type === "session_shutdown" ? sessionShutdownHandlerTimeoutMs : extensionHandlerTimeoutMs,
798
+ );
786
799
  if (continueWhile && !continueWhile()) return result as RunnerEmitResult<TEvent>;
787
800
 
788
801
  if (this.#isSessionBeforeEvent(event) && handlerResult) {
@@ -65,7 +65,6 @@ import type {
65
65
  import type { EventBus } from "../../utils/event-bus";
66
66
  import type {
67
67
  AgentEndEvent,
68
- AgentStartEvent,
69
68
  AutoCompactionEndEvent,
70
69
  AutoCompactionStartEvent,
71
70
  AutoRetryEndEvent,
@@ -89,6 +88,7 @@ import type {
89
88
  SessionStartEvent,
90
89
  SessionSwitchEvent,
91
90
  SessionTreeEvent,
91
+ AgentStartEvent as SharedAgentStartEvent,
92
92
  TodoReminderEvent,
93
93
  ToolCallEventResult,
94
94
  ToolResultEventResult,
@@ -369,7 +369,7 @@ export interface ExtensionContext {
369
369
  /** Stable resource ownership identifier for the active prompt run. */
370
370
  getActivePromptHandle(): string | undefined;
371
371
  /** Abort the current agent operation */
372
- abort(): void;
372
+ abort(): void | Promise<void>;
373
373
  /** Abort and prove whether resources for a specific prompt settled. */
374
374
  abortPromptAndWait?(handle: string, options: { graceMs: number }): Promise<RunSettlementProof>;
375
375
  /** Whether there are queued messages waiting */
@@ -429,6 +429,7 @@ export interface ExtensionContext {
429
429
  onPreflightAccepted?: () => void;
430
430
  onPreflightAcceptCommit?: () => void | Promise<void>;
431
431
  onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
432
+ preflightSignal?: AbortSignal;
432
433
  },
433
434
  ): Promise<unknown>;
434
435
  setPlanMode?(on: boolean): unknown;
@@ -634,7 +635,12 @@ export interface BeforeAgentStartEvent {
634
635
  systemPrompt: string[];
635
636
  }
636
637
 
637
- export type { AgentEndEvent, AgentStartEvent, TurnEndEvent, TurnStartEvent } from "../shared-events";
638
+ export type { AgentEndEvent, TurnEndEvent, TurnStartEvent } from "../shared-events";
639
+
640
+ /** Fired when an agent loop starts. `sdkRunToken` is an internal SDK queue-owner binding. */
641
+ export interface AgentStartEvent extends SharedAgentStartEvent {
642
+ sdkRunToken?: string;
643
+ }
638
644
 
639
645
  /** Fired when a message starts (user, assistant, or toolResult) */
640
646
  export interface MessageStartEvent {
@@ -1169,7 +1175,10 @@ export interface ExtensionAPI {
1169
1175
  */
1170
1176
  sendMessage<T = unknown>(
1171
1177
  message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
1172
- options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
1178
+ options?: {
1179
+ triggerTurn?: boolean;
1180
+ deliverAs?: "steer" | "followUp" | "nextTurn";
1181
+ },
1173
1182
  ): void;
1174
1183
 
1175
1184
  /** Send a user message to the agent, or queue it when deliverAs is set. */
@@ -1177,8 +1186,14 @@ export interface ExtensionAPI {
1177
1186
  content: string | (TextContent | ImageContent)[],
1178
1187
  options?: {
1179
1188
  deliverAs?: "steer" | "followUp";
1189
+ /** Internal SDK signal preserving a busy dispatch across async admission fences. */
1190
+ queuedAtDispatch?: boolean;
1180
1191
  onPreflightAccepted?: () => void;
1181
1192
  onPreflightAcceptCommit?: () => void | Promise<void>;
1193
+ onQueuedPromoted?: () => void;
1194
+ preflightSignal?: AbortSignal;
1195
+ /** Internal SDK correlation owner for an exact queued follow-up. */
1196
+ sdkRunToken?: string;
1182
1197
  },
1183
1198
  ): Promise<void>;
1184
1199
 
@@ -1387,7 +1402,10 @@ export type SendMessageHandler = <T = unknown>(
1387
1402
  * When paired with `triggerTurn: true` during prompt teardown, the session schedules
1388
1403
  * an internal continuation without surfacing the message in the editable pending queue.
1389
1404
  */
1390
- options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
1405
+ options?: {
1406
+ triggerTurn?: boolean;
1407
+ deliverAs?: "steer" | "followUp" | "nextTurn";
1408
+ },
1391
1409
  ) => void;
1392
1410
 
1393
1411
  export type SendUserMessageHandler = (
@@ -1396,6 +1414,11 @@ export type SendUserMessageHandler = (
1396
1414
  deliverAs?: "steer" | "followUp";
1397
1415
  onPreflightAccepted?: () => void;
1398
1416
  onPreflightAcceptCommit?: () => void | Promise<void>;
1417
+ /** Fired when a queued submission (steering or follow-up) is promoted to its own run (SDK ownership correlation). */
1418
+ onQueuedPromoted?: () => void;
1419
+ preflightSignal?: AbortSignal;
1420
+ /** Internal SDK correlation owner for an exact queued follow-up. */
1421
+ sdkRunToken?: string;
1399
1422
  },
1400
1423
  ) => void | Promise<void>;
1401
1424
 
@@ -1485,8 +1508,9 @@ export interface ExtensionContextActions {
1485
1508
  isIdle: () => boolean;
1486
1509
  /** Stable resource ownership identifier for the active prompt run. */
1487
1510
  getActivePromptHandle?: () => string | undefined;
1488
- abort: () => void;
1511
+ abort: () => void | Promise<void>;
1489
1512
  abortPromptAndWait?: (handle: string, options: { graceMs: number }) => Promise<RunSettlementProof>;
1513
+
1490
1514
  hasPendingMessages: () => boolean;
1491
1515
  /** Typed pending-message counts per queue; optional for embedders without a counted queue. */
1492
1516
  getPendingMessageCounts?: () => { steering: number; followUp: number; nextTurn: number };
@@ -1555,6 +1579,7 @@ export interface ExtensionContextActions {
1555
1579
  onPreflightAccepted?: () => void;
1556
1580
  onPreflightAcceptCommit?: () => void | Promise<void>;
1557
1581
  onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
1582
+ preflightSignal?: AbortSignal;
1558
1583
  },
1559
1584
  ) => Promise<unknown>;
1560
1585
  setPlanMode?: (on: boolean) => unknown;
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { logger } from "@gajae-code/utils";
4
+ import { normalizePluginHook } from "../../hooks/normalize";
4
5
  import { bundleIdentity } from "./lifecycle-reconciliation";
5
6
  import { verifyImplementationHash } from "./metadata";
6
7
  import { resolveWithinRoot } from "./paths";
@@ -106,6 +107,19 @@ export class ConstrainedPluginHookDescriptor {
106
107
  }
107
108
 
108
109
  async load(): Promise<ConstrainedPluginHook> {
110
+ const normalized = normalizePluginHook({
111
+ declaredEvent: this.event,
112
+ target: this.target,
113
+ phase: this.phase,
114
+ plugin: this.plugin,
115
+ source: this.relativePath,
116
+ });
117
+ if (!normalized.hook) {
118
+ throw new GjcPluginLoadError(
119
+ "invalid_hook",
120
+ normalized.diagnostics.map(diagnostic => `${diagnostic.code}: ${diagnostic.message}`).join("; "),
121
+ );
122
+ }
109
123
  if (this.implementationHash) await verifyImplementationHash(this.relativePath, this.implementationHash);
110
124
  const registered: { event: string; handler: (...a: any[]) => unknown }[] = [];
111
125
  const deny = (method: string) => () => {
@@ -171,6 +185,25 @@ export async function loadConstrainedPluginHooks(input: { cwd: string }): Promis
171
185
  const invalidHookIds = new Set<string>();
172
186
  for (const entry of effective) {
173
187
  if (!entry.enabled) continue;
188
+ for (const hook of entry.surfaces.hooks) {
189
+ if (entry.disabledSurfaceIds.includes(hook.extensionId)) continue;
190
+ const normalized = normalizePluginHook({
191
+ declaredEvent: hook.event,
192
+ target: hook.target,
193
+ phase: hook.phase,
194
+ plugin: entry.name,
195
+ source: hook.relativePath,
196
+ });
197
+ if (normalized.hook) continue;
198
+ invalidHookIds.add(`${entry.scope}:${entry.name}:${hook.extensionId}`);
199
+ preQuarantine.push({
200
+ identity: bundleIdentity(entry.scope, entry.name),
201
+ plugin: entry.name,
202
+ surfaceId: hook.extensionId,
203
+ code: "invalid_hook",
204
+ message: normalized.diagnostics.map(diagnostic => `${diagnostic.code}: ${diagnostic.message}`).join("; "),
205
+ });
206
+ }
174
207
  const drift = await verifyEntryHashes(entry);
175
208
  if (drift) preQuarantine.push(drift);
176
209
  for (const hook of entry.surfaces.hooks) {
@@ -392,8 +392,12 @@ export async function runGjcBundleTransaction(
392
392
  ): Promise<GjcBundleTransactionResult> {
393
393
  const resolved = await resolveSource(source);
394
394
  try {
395
- // Compile + validate outside every lock (never imports plugin code).
395
+ // Compile + validate candidate-internal collisions and MCP policy outside
396
+ // every lock (never imports plugin code). Registry collisions are rechecked
397
+ // under both locks below, but a statically invalid candidate must not create
398
+ // lock roots or sweep installation remnants.
396
399
  const bundle = await compileGjcPluginBundle(resolved.dir);
400
+ validateInstallPlan(bundle, []);
397
401
  const dirName = safeDirSegment(bundle.name);
398
402
  const root = scopeRoot(options.scope, options.cwd);
399
403
  const finalDir = path.join(root, dirName);
@@ -450,10 +454,6 @@ export async function runGjcBundleTransaction(
450
454
  if (decision.kind === "abort") return { status: "aborted", error: decision.error, remnants: [] };
451
455
  if (decision.kind === "noop") return { status: "noop", entry: decision.entry, remnants: [] };
452
456
 
453
- // The decision committed, so mutation may begin.
454
- await fs.mkdir(root, { recursive: true });
455
- await cleanupOrphans(root, dirName);
456
-
457
457
  // Hard install-time collision + MCP security validation against the
458
458
  // effective registry across BOTH scopes. Surface IDs derive from the
459
459
  // surface name, not the bundle name, so a differently named bundle in
@@ -461,6 +461,10 @@ export async function runGjcBundleTransaction(
461
461
  // identity is excluded, since that is the entry being replaced.
462
462
  validateInstallPlan(bundle, effective);
463
463
 
464
+ // The decision and all validation committed, so mutation may begin.
465
+ await fs.mkdir(root, { recursive: true });
466
+ await cleanupOrphans(root, dirName);
467
+
464
468
  const unique = `${process.pid}-${randomBytes(6).toString("hex")}`;
465
469
  const stagingDir = `${finalDir}.installing-${unique}`;
466
470
  const backupDir = `${finalDir}.backup-${unique}`;
@@ -65,9 +65,15 @@ function rowsForEntry(entry: GjcPluginRegistryEntry, quarantinedIds: Map<string,
65
65
  /**
66
66
  * Build the observability summary for the effective registry at `cwd`, including
67
67
  * hash-drift and session-collision quarantine status.
68
+ *
69
+ * Pass `{ migrate: false }` from read-only inspection surfaces (for example
70
+ * `gjc customize doctor`) so reporting never persists registry migrations.
68
71
  */
69
- export async function summarizeGjcPluginObservability(cwd: string): Promise<PluginObservabilitySummary> {
70
- const effective = await loadEffectiveGjcPluginRegistry(cwd);
72
+ export async function summarizeGjcPluginObservability(
73
+ cwd: string,
74
+ options: { migrate?: boolean } = {},
75
+ ): Promise<PluginObservabilitySummary> {
76
+ const effective = await loadEffectiveGjcPluginRegistry(cwd, options);
71
77
  const preQuarantine: SessionQuarantine[] = [];
72
78
  for (const entry of effective) {
73
79
  if (!entry.enabled) continue;
@@ -319,9 +319,20 @@ export async function updateRegistry(
319
319
 
320
320
  /**
321
321
  * Effective registry for a cwd: user + project entries in deterministic order.
322
+ *
323
+ * Defaults to the startup semantics (legacy-entry discovery + migration, which
324
+ * may persist the migrated registry). Read-only inspection surfaces (for
325
+ * example `gjc customize doctor`) pass `{ migrate: false }` so reporting never
326
+ * writes to the registry.
322
327
  */
323
- export async function loadEffectiveGjcPluginRegistry(cwd: string): Promise<GjcPluginRegistryEntry[]> {
324
- const [user, project] = await Promise.all([readRegistry("user", cwd), readRegistry("project", cwd)]);
328
+ export async function loadEffectiveGjcPluginRegistry(
329
+ cwd: string,
330
+ options: { migrate?: boolean } = {},
331
+ ): Promise<GjcPluginRegistryEntry[]> {
332
+ const [user, project] = await Promise.all([
333
+ readRegistry("user", cwd, options),
334
+ readRegistry("project", cwd, options),
335
+ ]);
325
336
  return sortRegistryEntries([...user.plugins, ...project.plugins]);
326
337
  }
327
338
 
@@ -437,7 +437,9 @@ export async function buildPluginMcpConfigs(input: { cwd: string }): Promise<{
437
437
  const { assertMcpInstallPolicy, assertDnsResolvesPublic, assertUrlAllowed } = await import("./mcp-policy");
438
438
  const nodePath = await import("node:path");
439
439
 
440
- const configs: Record<string, any> = {};
440
+ // A manifest-controlled MCP name such as "constructor" or "toString" must
441
+ // remain an ordinary own key rather than interacting with Object.prototype.
442
+ const configs: Record<string, any> = Object.create(null) as Record<string, any>;
441
443
  for (const entry of active) {
442
444
  const disabled = new Set(entry.disabledSurfaceIds);
443
445
  for (const m of entry.surfaces.mcps) {