@gajae-code/coding-agent 0.18.7 → 0.18.9

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 (344) hide show
  1. package/CHANGELOG.md +377 -1
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/job-manager.d.ts +1 -1
  4. package/dist/types/async/support.d.ts +0 -14
  5. package/dist/types/capability/mcp.d.ts +2 -0
  6. package/dist/types/capability/skill.d.ts +2 -0
  7. package/dist/types/capability/types.d.ts +2 -0
  8. package/dist/types/cli/public-command-errors.d.ts +3 -1
  9. package/dist/types/cli/setup-cli.d.ts +1 -1
  10. package/dist/types/commands/deep-interview.d.ts +3 -0
  11. package/dist/types/commands/mcp-serve.d.ts +4 -0
  12. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  13. package/dist/types/config/file-lock.d.ts +1 -0
  14. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  15. package/dist/types/config/model-profile-activation.d.ts +11 -3
  16. package/dist/types/config/model-registry.d.ts +4 -9
  17. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  18. package/dist/types/cursor-delete-path.d.ts +1 -0
  19. package/dist/types/cursor-mcp-name.d.ts +8 -0
  20. package/dist/types/customization/inventory.d.ts +2 -0
  21. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  24. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  25. package/dist/types/discovery/helpers.d.ts +6 -1
  26. package/dist/types/discovery/mcp-json.d.ts +1 -0
  27. package/dist/types/eval/py/kernel.d.ts +18 -0
  28. package/dist/types/export/html/css-color.d.ts +2 -0
  29. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  32. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  33. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  34. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  35. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  36. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  37. package/dist/types/extensibility/skill-management.d.ts +4 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  40. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  41. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  42. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  44. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  45. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  46. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  47. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  48. package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
  49. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  50. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  51. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  52. package/dist/types/lsp/config.d.ts +8 -5
  53. package/dist/types/lsp/workspace-path.d.ts +1 -0
  54. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  55. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  56. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  57. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  58. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  59. package/dist/types/modes/interactive-mode.d.ts +1 -0
  60. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  61. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  62. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  63. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  64. package/dist/types/progress/progress-contract.d.ts +154 -0
  65. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  66. package/dist/types/progress/project-progress.d.ts +141 -0
  67. package/dist/types/progress/render-progress.d.ts +28 -0
  68. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  69. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  70. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
  71. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  72. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  73. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  74. package/dist/types/runtime-mcp/types.d.ts +6 -0
  75. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  76. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  77. package/dist/types/sdk/broker/broker.d.ts +24 -2
  78. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  79. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  80. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  81. package/dist/types/sdk/bus/index.d.ts +12 -1
  82. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  83. package/dist/types/sdk/client/client.d.ts +2 -0
  84. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  85. package/dist/types/sdk/index.d.ts +1 -0
  86. package/dist/types/sdk/router/session-router.d.ts +2 -0
  87. package/dist/types/sdk/session-list.d.ts +1 -1
  88. package/dist/types/sdk/session.d.ts +8 -0
  89. package/dist/types/session/agent-session.d.ts +38 -9
  90. package/dist/types/session/artifacts.d.ts +2 -2
  91. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  92. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  93. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  94. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  95. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  96. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  97. package/dist/types/session/session-manager.d.ts +31 -5
  98. package/dist/types/session/session-retirement.d.ts +31 -7
  99. package/dist/types/session/session-storage.d.ts +69 -2
  100. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  101. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  102. package/dist/types/setup/credential-import.d.ts +6 -4
  103. package/dist/types/skill-state/active-state.d.ts +38 -0
  104. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  105. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  106. package/dist/types/task/executor.d.ts +3 -0
  107. package/dist/types/task/isolation-dir.d.ts +26 -0
  108. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  109. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  110. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  111. package/dist/types/tools/github-side-effect.d.ts +22 -0
  112. package/dist/types/tools/index.d.ts +3 -0
  113. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  114. package/dist/types/tools/python.d.ts +9 -1
  115. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  116. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  117. package/dist/types/tools/todo-write.d.ts +6 -0
  118. package/dist/types/utils/tools-checksum.d.ts +7 -0
  119. package/package.json +7 -7
  120. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  121. package/scripts/verify-managed-empty-stop.ts +28 -0
  122. package/src/async/endpoint-id.ts +26 -0
  123. package/src/async/job-manager.ts +247 -132
  124. package/src/async/support.ts +0 -25
  125. package/src/capability/index.ts +15 -0
  126. package/src/capability/mcp.ts +2 -0
  127. package/src/capability/skill.ts +2 -0
  128. package/src/capability/types.ts +2 -0
  129. package/src/cli/auth-broker-cli.ts +1 -1
  130. package/src/cli/customize-doctor.ts +15 -2
  131. package/src/cli/doctor/repairs.ts +4 -1
  132. package/src/cli/doctor-supervisor.ts +3 -0
  133. package/src/cli/public-command-errors.ts +21 -1
  134. package/src/cli/public-command-registry.ts +28 -6
  135. package/src/cli/setup-cli.ts +4 -4
  136. package/src/cli/skills-cli.ts +5 -0
  137. package/src/commands/deep-interview.ts +47 -8
  138. package/src/commands/mcp-serve.ts +7 -0
  139. package/src/commands/sdk.ts +455 -397
  140. package/src/config/autorouting-tier-map.ts +117 -7
  141. package/src/config/file-lock.ts +316 -79
  142. package/src/config/mcp-schema.json +4 -0
  143. package/src/config/model-bindings-applier.ts +20 -0
  144. package/src/config/model-profile-activation.ts +240 -46
  145. package/src/config/model-registry.ts +47 -153
  146. package/src/config/skill-settings-defaults.ts +7 -0
  147. package/src/coordinator-mcp/policy.ts +3 -2
  148. package/src/coordinator-mcp/server.ts +2 -0
  149. package/src/cursor-delete-path.ts +10 -0
  150. package/src/cursor-mcp-name.ts +15 -0
  151. package/src/cursor.ts +13 -2
  152. package/src/customization/inventory.ts +4 -13
  153. package/src/dap/config.ts +2 -2
  154. package/src/debug/report-bundle-parent.ts +22 -0
  155. package/src/debug/report-bundle.ts +3 -0
  156. package/src/debug/system-info.ts +13 -1
  157. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  158. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  159. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  160. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  161. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  162. package/src/defaults/gjc-defaults.test.ts +18 -1
  163. package/src/defaults/gjc-defaults.ts +3 -0
  164. package/src/defaults/gjc-skills.generated.ts +58 -36
  165. package/src/discovery/agents.ts +6 -1
  166. package/src/discovery/builtin.ts +13 -1
  167. package/src/discovery/helpers.ts +171 -29
  168. package/src/discovery/mcp-json.ts +26 -12
  169. package/src/discovery/ssh.ts +1 -1
  170. package/src/discovery/substitute-plugin-root.ts +3 -1
  171. package/src/edit/file-read-cache.ts +1 -1
  172. package/src/eval/py/executor.ts +796 -154
  173. package/src/eval/py/kernel.ts +79 -13
  174. package/src/eval/py/tool-bridge.ts +4 -0
  175. package/src/export/html/css-color.ts +9 -0
  176. package/src/export/html/html-export-destination.ts +16 -0
  177. package/src/export/html/index.ts +7 -10
  178. package/src/export/html/template.generated.ts +1 -1
  179. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  180. package/src/extensibility/extensions/runner.ts +15 -1
  181. package/src/extensibility/extensions/types.ts +7 -0
  182. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  183. package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -8
  184. package/src/extensibility/plugins/manager.ts +3 -2
  185. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  186. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  187. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  188. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  189. package/src/extensibility/skill-management.ts +9 -2
  190. package/src/extensibility/skills.ts +6 -1
  191. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  192. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  193. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  194. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  195. package/src/gjc-runtime/gc-runtime.ts +206 -9
  196. package/src/gjc-runtime/launch-worktree.ts +70 -9
  197. package/src/gjc-runtime/psmux-detect.ts +28 -4
  198. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  199. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  200. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  201. package/src/gjc-runtime/state-argv.ts +2 -0
  202. package/src/gjc-runtime/state-migrations.ts +91 -29
  203. package/src/gjc-runtime/state-runtime.ts +3716 -135
  204. package/src/gjc-runtime/state-writer.ts +105 -16
  205. package/src/gjc-runtime/tmux-common.ts +8 -5
  206. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  207. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  208. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  209. package/src/harness-control-plane/operate.ts +12 -4
  210. package/src/harness-control-plane/owner.ts +5 -1
  211. package/src/hashline/parser.ts +48 -9
  212. package/src/hashline/recovery.ts +13 -4
  213. package/src/internal-urls/docs-index.generated.ts +20 -19
  214. package/src/internal-urls/skill-path.ts +19 -0
  215. package/src/internal-urls/skill-protocol.ts +2 -0
  216. package/src/lsp/config.ts +12 -63
  217. package/src/lsp/edits.ts +6 -0
  218. package/src/lsp/workspace-path.ts +51 -0
  219. package/src/main.ts +195 -31
  220. package/src/memories/index.ts +88 -7
  221. package/src/modes/acp/acp-agent.ts +88 -4
  222. package/src/modes/components/custom-editor.ts +14 -0
  223. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  224. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  225. package/src/modes/components/extensions/state-manager.ts +15 -5
  226. package/src/modes/components/hook-editor.ts +1 -1
  227. package/src/modes/components/hook-selector.ts +1 -1
  228. package/src/modes/components/model-selector.ts +1 -4
  229. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  230. package/src/modes/components/status-line/segments.ts +4 -0
  231. package/src/modes/controllers/event-controller.ts +11 -4
  232. package/src/modes/controllers/input-controller.ts +76 -31
  233. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
  234. package/src/modes/controllers/selector-controller.ts +24 -15
  235. package/src/modes/controllers/todo-command-controller.ts +1 -1
  236. package/src/modes/interactive-mode.ts +5 -1
  237. package/src/modes/runtime-init.ts +4 -0
  238. package/src/modes/utils/abort-message.ts +3 -1
  239. package/src/modes/utils/interrupt-ownership.ts +12 -0
  240. package/src/modes/utils/keybinding-matchers.ts +4 -0
  241. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  242. package/src/progress/collect-project-progress.ts +153 -0
  243. package/src/progress/progress-contract.ts +172 -0
  244. package/src/progress/progress-snapshot.ts +140 -0
  245. package/src/progress/project-progress.ts +446 -0
  246. package/src/progress/render-progress.ts +209 -0
  247. package/src/prompts/system/master-mode.md +1 -0
  248. package/src/prompts/tools/skill-discovery.md +1 -1
  249. package/src/runtime-mcp/client.ts +4 -2
  250. package/src/runtime-mcp/config-writer.ts +10 -6
  251. package/src/runtime-mcp/config.ts +1 -0
  252. package/src/runtime-mcp/json-rpc.ts +2 -1
  253. package/src/runtime-mcp/manager.ts +29 -11
  254. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  255. package/src/runtime-mcp/oauth-flow.ts +14 -2
  256. package/src/runtime-mcp/oauth-public-url.ts +14 -0
  257. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  258. package/src/runtime-mcp/pool.ts +6 -0
  259. package/src/runtime-mcp/project-host-values.ts +46 -0
  260. package/src/runtime-mcp/smithery-registry.ts +22 -3
  261. package/src/runtime-mcp/transports/http.ts +4 -3
  262. package/src/runtime-mcp/types.ts +6 -0
  263. package/src/sdk/acp/adapter.ts +95 -8
  264. package/src/sdk/broker/broker-exit.ts +52 -23
  265. package/src/sdk/broker/broker.ts +81 -6
  266. package/src/sdk/broker/doctor-restart.ts +4 -0
  267. package/src/sdk/broker/ensure.ts +31 -2
  268. package/src/sdk/broker/lifecycle.ts +711 -66
  269. package/src/sdk/broker/session-index.ts +191 -81
  270. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  271. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  272. package/src/sdk/bus/index.ts +238 -43
  273. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  274. package/src/sdk/cli/session-cli.ts +17 -3
  275. package/src/sdk/client/client.ts +28 -8
  276. package/src/sdk/host/query/handlers.ts +9 -1
  277. package/src/sdk/host/session-runtime.test.ts +2705 -303
  278. package/src/sdk/host/session-runtime.ts +490 -143
  279. package/src/sdk/host/surface-policy.ts +1 -0
  280. package/src/sdk/index.ts +8 -0
  281. package/src/sdk/lifecycle-session.ts +3 -1
  282. package/src/sdk/prompt-deadline-manager.ts +15 -0
  283. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  284. package/src/sdk/protocol/operation-registry.ts +9 -2
  285. package/src/sdk/router/session-router.ts +82 -18
  286. package/src/sdk/session-list.ts +1 -0
  287. package/src/sdk/session.ts +69 -45
  288. package/src/session/agent-session.ts +679 -238
  289. package/src/session/artifacts.ts +176 -71
  290. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  291. package/src/session/internal/managed-session-scope.ts +3779 -462
  292. package/src/session/internal/managed-session-storage.ts +501 -78
  293. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  294. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  295. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  296. package/src/session/session-manager.ts +951 -133
  297. package/src/session/session-retirement.ts +573 -14
  298. package/src/session/session-storage.ts +447 -15
  299. package/src/session/task-artifact-owner-codec.ts +71 -8
  300. package/src/session/task-artifact-owner-retirement.ts +21 -19
  301. package/src/setup/credential-auto-import.ts +12 -3
  302. package/src/setup/credential-import.ts +176 -5
  303. package/src/setup/paseo/announce.ts +6 -1
  304. package/src/skill-state/active-state.ts +145 -2
  305. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  306. package/src/slash-commands/builtin-registry.ts +39 -9
  307. package/src/slash-commands/helpers/mcp.ts +8 -4
  308. package/src/slash-commands/helpers/progress-report.ts +23 -0
  309. package/src/stt/setup.ts +3 -5
  310. package/src/task/commands.ts +3 -1
  311. package/src/task/executor.ts +4 -0
  312. package/src/task/index.ts +6 -0
  313. package/src/task/isolation-dir.ts +143 -0
  314. package/src/task/worktree.ts +16 -7
  315. package/src/tools/ask.ts +205 -6
  316. package/src/tools/ast-edit-write-path.ts +68 -0
  317. package/src/tools/ast-edit.ts +10 -1
  318. package/src/tools/bash-allowed-prefixes.ts +15 -0
  319. package/src/tools/bash-skill-urls.ts +56 -3
  320. package/src/tools/bash.ts +48 -79
  321. package/src/tools/browser/cdp-url.ts +18 -0
  322. package/src/tools/browser.ts +8 -1
  323. package/src/tools/descriptors.ts +5 -1
  324. package/src/tools/eval.ts +56 -36
  325. package/src/tools/gh.ts +10 -0
  326. package/src/tools/github-side-effect.ts +112 -0
  327. package/src/tools/image-gen.ts +1 -1
  328. package/src/tools/index.ts +3 -0
  329. package/src/tools/monitor-mutation.ts +27 -0
  330. package/src/tools/monitor.ts +7 -0
  331. package/src/tools/python.ts +188 -66
  332. package/src/tools/recipe/index.ts +9 -0
  333. package/src/tools/recipe/recipe-guard.ts +19 -0
  334. package/src/tools/restricted-cwd.ts +10 -0
  335. package/src/tools/skill-discovery.ts +5 -0
  336. package/src/tools/skill.ts +28 -1
  337. package/src/tools/todo-write.ts +10 -1
  338. package/src/tools/tool-catalog.generated.ts +1 -1
  339. package/src/tools/write.ts +1 -0
  340. package/src/utils/external-editor.ts +3 -2
  341. package/src/utils/tools-checksum.ts +34 -0
  342. package/src/utils/tools-manager.ts +24 -6
  343. package/src/web/insane/url-guard.ts +2 -1
  344. package/src/web/search/providers/codex.ts +3 -2
@@ -77,6 +77,26 @@ interface KernelStartOptions extends KernelLifecycleOptions {
77
77
  env?: Record<string, string | undefined>;
78
78
  runtimeOptions?: PythonRuntimeOptions;
79
79
  settings?: Settings;
80
+ /** @internal Synchronously registers the actual spawned kernel before startup callbacks run. */
81
+ onKernelCreated?: (kernel: PythonKernel) => void;
82
+ /** @internal Routes partial-start cleanup through the executor's retained ownership. */
83
+ onStartupFailure?: (kernel: PythonKernel) => Promise<void>;
84
+ }
85
+
86
+ export type PythonKernelStartupCleanupFailure =
87
+ | { kind: "unconfirmed"; result: KernelShutdownResult }
88
+ | { kind: "rejected"; error: unknown };
89
+
90
+ /** Startup failed after a real runner was created and physical cleanup failed. */
91
+ export class PythonKernelStartError extends Error {
92
+ constructor(
93
+ readonly kernel: PythonKernel,
94
+ readonly startupError: unknown,
95
+ readonly cleanupFailure: PythonKernelStartupCleanupFailure,
96
+ ) {
97
+ super("Python kernel startup failed and runner cleanup was not confirmed", { cause: startupError });
98
+ this.name = "PythonKernelStartError";
99
+ }
80
100
  }
81
101
 
82
102
  interface KernelShutdownOptions {
@@ -108,6 +128,13 @@ function throwIfAborted(signal: AbortSignal | undefined, fallbackReason: string)
108
128
  throw createAbortError("AbortError", typeof reason === "string" ? reason : fallbackReason);
109
129
  }
110
130
 
131
+ function throwIfLifecycleEnded(lifecycle: KernelLifecycleOptions, message: string): void {
132
+ throwIfAborted(lifecycle.signal, message);
133
+ if (lifecycle.deadlineMs !== undefined && lifecycle.deadlineMs <= Date.now()) {
134
+ throw createAbortError("TimeoutError", `${message} timed out`);
135
+ }
136
+ }
137
+
111
138
  async function waitForLifecycle<T>(
112
139
  value: Promise<T>,
113
140
  lifecycle: PythonRuntimeLifecycleOptions | undefined,
@@ -244,6 +271,7 @@ export class PythonKernel {
244
271
  #alive = true;
245
272
  #disposed = false;
246
273
  #shutdownConfirmed = false;
274
+ #shutdownPromise: Promise<KernelShutdownResult> | null = null;
247
275
  #exitedPromise: Promise<number> | null = null;
248
276
  #exitCode: number | null = null;
249
277
  #stderrTail = "";
@@ -255,6 +283,7 @@ export class PythonKernel {
255
283
  }
256
284
 
257
285
  static async start(options: KernelStartOptions): Promise<PythonKernel> {
286
+ throwIfLifecycleEnded(options, "Python kernel startup");
258
287
  const availability = await logger.time(
259
288
  "PythonKernel.start:availabilityCheck",
260
289
  checkPythonKernelAvailability,
@@ -263,17 +292,20 @@ export class PythonKernel {
263
292
  { signal: options.signal, deadlineMs: options.deadlineMs },
264
293
  options.settings,
265
294
  );
295
+ throwIfLifecycleEnded(options, "Python kernel startup");
266
296
  if (!availability.ok) {
267
297
  throw new Error(availability.reason ?? "Python kernel unavailable");
268
298
  }
269
299
 
270
300
  const settings = options.settings ?? (await Settings.init());
301
+ throwIfLifecycleEnded(options, "Python kernel startup");
271
302
  const { env: shellEnv } = settings.getShellConfig();
272
303
  const baseEnv = filterEnv(shellEnv);
273
304
  const runtime = await ensurePythonRuntime(options.cwd, baseEnv, options.runtimeOptions, {
274
305
  signal: options.signal,
275
306
  deadlineMs: options.deadlineMs,
276
307
  });
308
+ throwIfLifecycleEnded(options, "Python kernel startup");
277
309
  const spawnEnv: Record<string, string> = {};
278
310
  for (const [key, value] of Object.entries(runtime.env)) {
279
311
  if (typeof value === "string") spawnEnv[key] = value;
@@ -286,6 +318,7 @@ export class PythonKernel {
286
318
  spawnEnv.PYTHONIOENCODING = "utf-8";
287
319
 
288
320
  const scriptPath = await ensureRunnerScript();
321
+ throwIfLifecycleEnded(options, "Python kernel startup");
289
322
  const kernel = new PythonKernel(Snowflake.next());
290
323
 
291
324
  const proc = Bun.spawn([runtime.pythonPath, "-u", scriptPath], {
@@ -302,25 +335,40 @@ export class PythonKernel {
302
335
  kernel.#proc = proc;
303
336
  kernel.#stdin = proc.stdin;
304
337
  kernel.#exitedPromise = proc.exited;
305
- void kernel.#exitedPromise.then(code => {
306
- kernel.#exitCode = code;
307
- kernel.#alive = false;
308
- kernel.#abortPendingExecutions(`Python kernel exited with code ${code}`, { kernelKilled: true });
309
- });
310
338
 
311
- kernel.#startReader(proc.stdout as ReadableStream<Uint8Array>);
312
- kernel.#startStderrDrain(proc.stderr as ReadableStream<Uint8Array>);
339
+ try {
340
+ options.onKernelCreated?.(kernel);
341
+ void kernel.#exitedPromise.then(code => {
342
+ kernel.#exitCode = code;
343
+ kernel.#alive = false;
344
+ kernel.#abortPendingExecutions(`Python kernel exited with code ${code}`, { kernelKilled: true });
345
+ });
313
346
 
314
- const startup = { signal: options.signal, deadlineMs: options.deadlineMs };
315
- const startupBudget = Math.min(getRemainingTimeMs(startup.deadlineMs) ?? STARTUP_TIMEOUT_MS, STARTUP_TIMEOUT_MS);
347
+ kernel.#startReader(proc.stdout as ReadableStream<Uint8Array>);
348
+ kernel.#startStderrDrain(proc.stderr as ReadableStream<Uint8Array>);
316
349
 
317
- try {
350
+ const startup = { signal: options.signal, deadlineMs: options.deadlineMs };
351
+ const startupBudget = Math.min(
352
+ getRemainingTimeMs(startup.deadlineMs) ?? STARTUP_TIMEOUT_MS,
353
+ STARTUP_TIMEOUT_MS,
354
+ );
318
355
  const initScript = buildInitScript(options.cwd, options.env);
319
356
  await kernel.#executeWithBudget(initScript, startup.signal, startupBudget, "Python kernel init");
320
357
  await kernel.#executeWithBudget(PYTHON_PRELUDE, startup.signal, startupBudget, "Python kernel prelude");
321
358
  return kernel;
322
359
  } catch (err) {
323
- await kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS }).catch(() => {});
360
+ let cleanupFailure: PythonKernelStartupCleanupFailure | undefined;
361
+ try {
362
+ if (options.onStartupFailure) {
363
+ await options.onStartupFailure(kernel);
364
+ } else {
365
+ const result = await kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS });
366
+ if (!result.confirmed) cleanupFailure = { kind: "unconfirmed", result };
367
+ }
368
+ } catch (cleanupError) {
369
+ cleanupFailure = { kind: "rejected", error: cleanupError };
370
+ }
371
+ if (cleanupFailure) throw new PythonKernelStartError(kernel, err, cleanupFailure);
324
372
  throw err;
325
373
  }
326
374
  }
@@ -475,9 +523,27 @@ export class PythonKernel {
475
523
  }
476
524
  }
477
525
 
478
- async shutdown(options?: KernelShutdownOptions): Promise<KernelShutdownResult> {
479
- if (this.#shutdownConfirmed) return { confirmed: true };
526
+ shutdown(options?: KernelShutdownOptions): Promise<KernelShutdownResult> {
527
+ if (this.#shutdownConfirmed) return Promise.resolve({ confirmed: true });
528
+ if (this.#shutdownPromise) return this.#shutdownPromise;
529
+
530
+ const { promise, resolve, reject } = Promise.withResolvers<KernelShutdownResult>();
531
+ this.#shutdownPromise = promise;
532
+ void promise.then(
533
+ () => {
534
+ if (this.#shutdownPromise === promise) this.#shutdownPromise = null;
535
+ },
536
+ () => {
537
+ if (this.#shutdownPromise === promise) this.#shutdownPromise = null;
538
+ },
539
+ );
540
+ void Promise.resolve()
541
+ .then(() => this.#shutdown(options))
542
+ .then(resolve, reject);
543
+ return promise;
544
+ }
480
545
 
546
+ async #shutdown(options?: KernelShutdownOptions): Promise<KernelShutdownResult> {
481
547
  this.#alive = false;
482
548
  this.#abortPendingExecutions("Python kernel shutdown", { kernelKilled: true });
483
549
 
@@ -72,6 +72,10 @@ async function startServer(): Promise<BridgeServer> {
72
72
  if (!sessionId || !name) {
73
73
  return Response.json({ ok: false, error: "Missing session/name" }, { status: 400 });
74
74
  }
75
+ const live = registrations.get(capability);
76
+ if (live !== registration || registration.signal?.aborted) {
77
+ return new Response("Forbidden", { status: 403 });
78
+ }
75
79
  if (sessionId !== registration.sessionId) {
76
80
  return new Response("Forbidden", { status: 403 });
77
81
  }
@@ -0,0 +1,9 @@
1
+ const SAFE_CSS_COLOR =
2
+ /^(?:#[0-9a-fA-F]{3,8}|rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)|rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(?:0|1|0?\.\d+)\s*\))$/;
3
+
4
+ /** Keep a theme color that is safe to place in a `<style>` block. */
5
+ export function cssColorOrFallback(value: string | undefined, fallback: string): string {
6
+ const trimmed = value?.trim();
7
+ if (trimmed && SAFE_CSS_COLOR.test(trimmed)) return trimmed;
8
+ return fallback;
9
+ }
@@ -0,0 +1,16 @@
1
+ import * as fs from "node:fs";
2
+
3
+ const HTML_EXPORT_DESTINATION_OPEN_FLAGS =
4
+ fs.constants.O_WRONLY | fs.constants.O_CREAT | (process.platform === "win32" ? 0 : (fs.constants.O_NOFOLLOW ?? 0));
5
+
6
+ /** Owner-only HTML export. A replaced file is locked down on the opened descriptor. */
7
+ export function openHtmlExportDestination(outputPath: string): number {
8
+ const fd = fs.openSync(outputPath, HTML_EXPORT_DESTINATION_OPEN_FLAGS, 0o600);
9
+ try {
10
+ fs.fchmodSync(fd, 0o600);
11
+ return fd;
12
+ } catch (error) {
13
+ fs.closeSync(fd);
14
+ throw error;
15
+ }
16
+ }
@@ -6,6 +6,8 @@ import { APP_NAME, isEnoent } from "@gajae-code/utils";
6
6
  import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/theme";
7
7
  import { SessionManager } from "../../session/session-manager";
8
8
  // Pre-generated template (created by scripts/generate-template.ts at publish time)
9
+ import { cssColorOrFallback } from "./css-color";
10
+ import { openHtmlExportDestination } from "./html-export-destination";
9
11
  import { TEMPLATE } from "./template.generated";
10
12
 
11
13
  export interface ExportOptions {
@@ -78,16 +80,16 @@ async function generateThemeVars(themeName?: string): Promise<string> {
78
80
  const colors = await getResolvedThemeColors(themeName);
79
81
  const lines: string[] = [];
80
82
  for (const [key, value] of Object.entries(colors)) {
81
- lines.push(`--${key}: ${value};`);
83
+ lines.push(`--${key}: ${cssColorOrFallback(value, "transparent")};`);
82
84
  }
83
85
 
84
86
  const themeExport = await getThemeExportColors(themeName);
85
87
  const userMessageBg = colors.userMessageBg || "#343541";
86
88
  const derived = deriveExportColors(userMessageBg);
87
89
 
88
- lines.push(`--body-bg: ${themeExport.pageBg ?? derived.pageBg};`);
89
- lines.push(`--container-bg: ${themeExport.cardBg ?? derived.cardBg};`);
90
- lines.push(`--info-bg: ${themeExport.infoBg ?? derived.infoBg};`);
90
+ lines.push(`--body-bg: ${cssColorOrFallback(themeExport.pageBg, derived.pageBg)};`);
91
+ lines.push(`--container-bg: ${cssColorOrFallback(themeExport.cardBg, derived.cardBg)};`);
92
+ lines.push(`--info-bg: ${cssColorOrFallback(themeExport.infoBg, derived.infoBg)};`);
91
93
 
92
94
  return lines.join(" ");
93
95
  }
@@ -168,11 +170,6 @@ const HTML_EXPORT_SOURCE_OPEN_FLAGS =
168
170
  syncFs.constants.O_NONBLOCK |
169
171
  (process.platform === "win32" ? 0 : (syncFs.constants.O_NOFOLLOW ?? 0));
170
172
 
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
173
  function sameFileIdentity(left: syncFs.BigIntStats, right: syncFs.BigIntStats): boolean {
177
174
  return left.dev === right.dev && left.ino === right.ino;
178
175
  }
@@ -192,7 +189,7 @@ async function openHtmlExportDescriptors(
192
189
  }
193
190
  await assertExportDoesNotAliasSource(sourcePath, outputPath);
194
191
  }
195
- destination = syncFs.openSync(outputPath, HTML_EXPORT_DESTINATION_OPEN_FLAGS, 0o666);
192
+ destination = openHtmlExportDestination(outputPath);
196
193
  if (source !== undefined) {
197
194
  const sourceIdentity = syncFs.fstatSync(source, { bigint: true });
198
195
  const destinationIdentity = syncFs.fstatSync(destination, { bigint: true });