@sayknow-cli/coding-agent 0.4.6 → 0.5.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 (428) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +27 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Path-scoped mutation coordinator for edit tools.
3
+ *
4
+ * Serializes concurrent read→compute→write windows against the same absolute
5
+ * path so independent sessions cannot silently overwrite each other's successful
6
+ * disjoint edits (https://github.com/jaybeyond/Sayknow_CLI/issues/2900).
7
+ *
8
+ * - Always: in-process async mutex keyed by resolved absolute path.
9
+ * - Optionally: durable cross-process `<path>.lock` via `withFileLock` for real
10
+ * filesystem mutations (subagents / separate processes).
11
+ *
12
+ * Nested acquisition of the same path is not supported; callers that already
13
+ * hold the lock must not re-enter.
14
+ */
15
+ import * as path from "node:path";
16
+ import { type FileLockOptions, withFileLock } from "../config/file-lock";
17
+
18
+ type AsyncMutex = {
19
+ acquire(): Promise<() => void>;
20
+ };
21
+
22
+ const pathMutexes = new Map<string, AsyncMutex>();
23
+
24
+ function createAsyncMutex(): AsyncMutex {
25
+ let locked = false;
26
+ const waiters: Array<() => void> = [];
27
+ return {
28
+ async acquire(): Promise<() => void> {
29
+ if (!locked) {
30
+ locked = true;
31
+ return () => release();
32
+ }
33
+ const { promise, resolve } = Promise.withResolvers<void>();
34
+ waiters.push(resolve);
35
+ await promise;
36
+ return () => release();
37
+ },
38
+ };
39
+
40
+ function release(): void {
41
+ const next = waiters.shift();
42
+ if (next) {
43
+ next();
44
+ return;
45
+ }
46
+ locked = false;
47
+ }
48
+ }
49
+
50
+ function mutexFor(absolutePath: string): AsyncMutex {
51
+ const key = path.resolve(absolutePath);
52
+ let mutex = pathMutexes.get(key);
53
+ if (!mutex) {
54
+ mutex = createAsyncMutex();
55
+ pathMutexes.set(key, mutex);
56
+ }
57
+ return mutex;
58
+ }
59
+
60
+ async function withInProcessPathLock<T>(absolutePath: string, fn: () => Promise<T>): Promise<T> {
61
+ const release = await mutexFor(absolutePath).acquire();
62
+ try {
63
+ return await fn();
64
+ } finally {
65
+ release();
66
+ }
67
+ }
68
+
69
+ /** Default lock budget: long enough for large writes + formatter writeback. */
70
+ const DEFAULT_CROSS_PROCESS_LOCK: FileLockOptions = {
71
+ staleMs: 60_000,
72
+ retries: 600,
73
+ retryDelayMs: 50,
74
+ };
75
+
76
+ export type EditPathMutationOptions = {
77
+ /**
78
+ * When true (default), also acquire a durable cross-process file lock.
79
+ * Disable for injectible/in-memory filesystem tests that do not touch disk.
80
+ */
81
+ crossProcess?: boolean;
82
+ fileLock?: FileLockOptions;
83
+ };
84
+
85
+ /**
86
+ * Run `fn` while holding exclusive mutation rights for every absolute path.
87
+ * Paths are locked in lexicographic order to avoid deadlocks on multi-path ops
88
+ * (e.g. rename source + destination).
89
+ */
90
+ export async function withEditPathMutation<T>(
91
+ absolutePaths: readonly string[],
92
+ fn: () => Promise<T>,
93
+ options: EditPathMutationOptions = {},
94
+ ): Promise<T> {
95
+ const unique = [...new Set(absolutePaths.map(entry => path.resolve(entry)))].sort();
96
+ if (unique.length === 0) return fn();
97
+
98
+ const crossProcess = options.crossProcess !== false;
99
+ const fileLock = { ...DEFAULT_CROSS_PROCESS_LOCK, ...options.fileLock };
100
+
101
+ const runAt = async (index: number): Promise<T> => {
102
+ if (index >= unique.length) return fn();
103
+ const target = unique[index];
104
+ const next = () => runAt(index + 1);
105
+ return withInProcessPathLock(target, async () => {
106
+ if (!crossProcess) return next();
107
+ return withFileLock(target, next, fileLock);
108
+ });
109
+ };
110
+
111
+ return runAt(0);
112
+ }
@@ -30,7 +30,7 @@ function toolResultHasError(result: AgentToolResult): boolean {
30
30
  }
31
31
 
32
32
  function getTool(session: ToolSession, name: string): AgentTool {
33
- const tool = session.getToolByName?.(name);
33
+ const tool = session.getToolForExecution ? session.getToolForExecution(name) : session.getToolByName?.(name);
34
34
  if (!tool) {
35
35
  throw new ToolError(`Unknown tool from js runtime: ${name}`);
36
36
  }
@@ -61,7 +61,7 @@ export interface PythonExecutorOptions {
61
61
  /** @internal Bridge session id, set by `executePython` before delegating. */
62
62
  bridgeSessionId?: string;
63
63
  /** @internal Bridge endpoint info, set by `executePython` before delegating. */
64
- bridge?: { url: string; token: string };
64
+ bridge?: { url: string; capability: string };
65
65
  }
66
66
 
67
67
  export interface PythonKernelExecutor {
@@ -106,6 +106,7 @@ export interface PythonResult {
106
106
  interface PythonSession {
107
107
  sessionId: string;
108
108
  kernel: PythonKernel;
109
+ bridgeCapability?: string;
109
110
  ownerIds: Set<string>;
110
111
  hasFallbackOwner: boolean;
111
112
  queue: Promise<void>;
@@ -261,14 +262,14 @@ function buildKernelEnv(options: {
261
262
  sessionFile?: string;
262
263
  artifactsDir?: string;
263
264
  bridgeSessionId?: string;
264
- bridge?: { url: string; token: string };
265
+ bridge?: { url: string; capability: string };
265
266
  }): Record<string, string> | undefined {
266
267
  const env: Record<string, string> = {};
267
268
  if (options.sessionFile) env.PI_SESSION_FILE = options.sessionFile;
268
269
  if (options.artifactsDir) env.PI_ARTIFACTS_DIR = options.artifactsDir;
269
270
  if (options.bridge && options.bridgeSessionId) {
270
271
  env.PI_TOOL_BRIDGE_URL = options.bridge.url;
271
- env.PI_TOOL_BRIDGE_TOKEN = options.bridge.token;
272
+ env.PI_TOOL_BRIDGE_CAPABILITY = options.bridge.capability;
272
273
  env.PI_TOOL_BRIDGE_SESSION = options.bridgeSessionId;
273
274
  }
274
275
  return Object.keys(env).length > 0 ? env : undefined;
@@ -328,6 +329,7 @@ async function acquireSession(sessionId: string, cwd: string, options: PythonExe
328
329
  const session: PythonSession = {
329
330
  sessionId,
330
331
  kernel,
332
+ bridgeCapability: options.bridge?.capability,
331
333
  ownerIds: new Set(),
332
334
  hasFallbackOwner: false,
333
335
  queue: Promise.resolve(),
@@ -361,12 +363,25 @@ async function replaceSessionKernel(
361
363
  throw new PythonExecutionCancelledError(false);
362
364
  }
363
365
  requireRemainingTimeoutMs(options.deadlineMs);
364
- const next = await startKernel(cwd, options);
365
- if (sessions.get(session.sessionId) !== session) {
366
- await next.shutdown().catch(() => undefined);
367
- throw new PythonExecutionCancelledError(false);
366
+ const bridge = options.bridge;
367
+ const previousCapability = bridge?.capability;
368
+ const nextCapability = bridge ? crypto.randomUUID() : undefined;
369
+ if (bridge && nextCapability) bridge.capability = nextCapability;
370
+ let next: PythonKernel | undefined;
371
+ try {
372
+ next = await startKernel(cwd, options);
373
+ if (sessions.get(session.sessionId) !== session) {
374
+ throw new PythonExecutionCancelledError(false);
375
+ }
376
+ session.kernel = next;
377
+ session.bridgeCapability = nextCapability;
378
+ } catch (err) {
379
+ await next?.shutdown().catch(() => undefined);
380
+ if (bridge && previousCapability && bridge.capability === nextCapability) {
381
+ bridge.capability = previousCapability;
382
+ }
383
+ throw err;
368
384
  }
369
- session.kernel = next;
370
385
  }
371
386
 
372
387
  async function resetSession(sessionId: string): Promise<void> {
@@ -481,8 +496,8 @@ async function executeWithKernel(
481
496
  displayOutputs.push({ type: "status", event });
482
497
  });
483
498
  const unregisterBridge =
484
- options?.toolSession && options?.bridgeSessionId
485
- ? registerPyToolBridge(options.bridgeSessionId, {
499
+ options?.toolSession && options?.bridgeSessionId && options.bridge
500
+ ? registerPyToolBridge(options.bridgeSessionId, options.bridge.capability, {
486
501
  toolSession: options.toolSession,
487
502
  signal: options.signal,
488
503
  emitStatus,
@@ -499,8 +514,11 @@ async function executeWithKernel(
499
514
  });
500
515
 
501
516
  if (result.cancelled) {
517
+ // Prefer the caller-configured timeout for the user-facing annotation.
518
+ // Remaining wall-clock budget can shrink after async setup (Settings.init,
519
+ // kernel start) and would otherwise flake Math.round() second formatting.
502
520
  const annotation = result.timedOut
503
- ? formatKernelTimeoutAnnotation(executionTimeoutMs, result.kernelKilled ?? false)
521
+ ? formatKernelTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs, result.kernelKilled ?? false)
504
522
  : undefined;
505
523
  let crashNotice: string | null = null;
506
524
  if (result.kernelKilled) {
@@ -554,7 +572,9 @@ async function executeWithKernel(
554
572
  cancelled: true,
555
573
  displayOutputs,
556
574
  stdinRequested: false,
557
- ...(await sink.dump(timedOut ? formatTimeoutAnnotation(executionTimeoutMs) : undefined)),
575
+ ...(await sink.dump(
576
+ timedOut ? formatTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs) : undefined,
577
+ )),
558
578
  };
559
579
  }
560
580
  const error = err instanceof Error ? err : new Error(String(err));
@@ -578,7 +598,8 @@ async function ensureKernelAvailable(cwd: string, options: PythonExecutorOptions
578
598
  async function ensureToolBridge(options: PythonExecutorOptions): Promise<void> {
579
599
  if (!options.toolSession || options.bridge) return;
580
600
  try {
581
- options.bridge = await ensurePyToolBridge();
601
+ const bridge = await ensurePyToolBridge();
602
+ options.bridge = { ...bridge, capability: crypto.randomUUID() };
582
603
  } catch (err) {
583
604
  logger.warn("Failed to start Python tool bridge", {
584
605
  error: err instanceof Error ? err.message : String(err),
@@ -607,6 +628,9 @@ async function executeOnSession(code: string, cwd: string, options: PythonExecut
607
628
  await resetSession(sessionId);
608
629
  }
609
630
  const session = await acquireSession(sessionId, cwd, options);
631
+ if (options.bridge && session.bridgeCapability) {
632
+ options.bridge.capability = session.bridgeCapability;
633
+ }
610
634
  return await runQueued(session, options, async () => {
611
635
  if (options.signal?.aborted) {
612
636
  throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
@@ -8,9 +8,6 @@
8
8
  * Shutdown writes `{"type":"exit"}` and escalates to SIGTERM/SIGKILL on
9
9
  * timeout.
10
10
  */
11
- import * as fs from "node:fs";
12
- import * as os from "node:os";
13
- import * as path from "node:path";
14
11
  import { $env, isBunTestRuntime, logger, Snowflake } from "@sayknow-cli/utils";
15
12
  import type { Subprocess } from "bun";
16
13
  import { $ } from "bun";
@@ -18,7 +15,7 @@ import { Settings } from "../../config/settings";
18
15
  import { type KernelDisplayOutput, renderKernelDisplay } from "./display";
19
16
  import { resolvePythonIpcTrace, resolvePythonSkipCheck } from "./env";
20
17
  import { PYTHON_PRELUDE } from "./prelude";
21
- import RUNNER_SCRIPT from "./runner.py" with { type: "text" };
18
+ import { ensureRunnerScript } from "./runner-artifact";
22
19
  import { ensurePythonRuntime, filterEnv, type PythonRuntimeOptions } from "./runtime";
23
20
 
24
21
  export type { KernelDisplayOutput, PythonStatusEvent } from "./display";
@@ -27,23 +24,6 @@ export { renderKernelDisplay } from "./display";
27
24
  // Dual-read: `SKC_PYTHON_IPC_TRACE` is preferred, then legacy `PI_PYTHON_IPC_TRACE`.
28
25
  const TRACE_IPC = resolvePythonIpcTrace($env);
29
26
 
30
- // Cache the runner script on disk so the subprocess loads it normally. Cached
31
- // per script hash so installs don't race across versions.
32
- const RUNNER_CACHE_DIR = path.join(os.tmpdir(), "skc-python-runner");
33
- let RUNNER_SCRIPT_PATH: string | null = null;
34
-
35
- async function ensureRunnerScript(): Promise<string> {
36
- if (RUNNER_SCRIPT_PATH) return RUNNER_SCRIPT_PATH;
37
- await fs.promises.mkdir(RUNNER_CACHE_DIR, { recursive: true });
38
- const hash = Bun.hash(RUNNER_SCRIPT).toString(36);
39
- const target = path.join(RUNNER_CACHE_DIR, `runner-${hash}.py`);
40
- if (!fs.existsSync(target)) {
41
- await Bun.write(target, RUNNER_SCRIPT);
42
- }
43
- RUNNER_SCRIPT_PATH = target;
44
- return target;
45
- }
46
-
47
27
  const SHUTDOWN_GRACE_MS = 1_000;
48
28
  const STARTUP_TIMEOUT_MS = 10_000;
49
29
  // How long to wait after SIGINT for the runner to emit `done`. If the cell is
@@ -453,10 +453,10 @@ if "__skc_prelude_loaded__" not in globals():
453
453
 
454
454
  if all(
455
455
  _k in os.environ
456
- for _k in ("PI_TOOL_BRIDGE_URL", "PI_TOOL_BRIDGE_TOKEN", "PI_TOOL_BRIDGE_SESSION")
456
+ for _k in ("PI_TOOL_BRIDGE_URL", "PI_TOOL_BRIDGE_CAPABILITY", "PI_TOOL_BRIDGE_SESSION")
457
457
  ):
458
458
  tool = _ToolProxy(
459
459
  os.environ["PI_TOOL_BRIDGE_URL"],
460
- os.environ["PI_TOOL_BRIDGE_TOKEN"],
460
+ os.environ["PI_TOOL_BRIDGE_CAPABILITY"],
461
461
  os.environ["PI_TOOL_BRIDGE_SESSION"],
462
462
  )
@@ -0,0 +1,84 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { postmortem } from "@sayknow-cli/utils";
5
+ import RUNNER_SCRIPT from "./runner.py" with { type: "text" };
6
+
7
+ const DIRECTORY_MODE = 0o700;
8
+ const FILE_MODE = 0o600;
9
+ const DIRECTORY_PREFIX = "skc-python-runner-";
10
+
11
+ interface RunnerScriptArtifact {
12
+ directory: string;
13
+ scriptPath: string;
14
+ }
15
+
16
+ async function initializeRunnerScript(tempRoot: string): Promise<RunnerScriptArtifact> {
17
+ const directory = await fs.mkdtemp(path.join(tempRoot, DIRECTORY_PREFIX));
18
+ try {
19
+ if (process.platform !== "win32") await fs.chmod(directory, DIRECTORY_MODE);
20
+ const scriptPath = path.join(directory, "runner.py");
21
+ const handle = await fs.open(scriptPath, "wx", FILE_MODE);
22
+ try {
23
+ await handle.writeFile(RUNNER_SCRIPT, { encoding: "utf8" });
24
+ if (process.platform !== "win32") await handle.chmod(FILE_MODE);
25
+ } finally {
26
+ await handle.close();
27
+ }
28
+ return { directory, scriptPath };
29
+ } catch (error) {
30
+ await fs.rm(directory, { recursive: true, force: true }).catch(() => {});
31
+ throw error;
32
+ }
33
+ }
34
+
35
+ export interface RunnerScriptCache {
36
+ ensureRunnerScript(): Promise<string>;
37
+ cleanup(): Promise<void>;
38
+ }
39
+
40
+ export function createRunnerScriptCache(tempRoot: string): RunnerScriptCache {
41
+ let initialization: Promise<RunnerScriptArtifact> | null = null;
42
+ let artifact: RunnerScriptArtifact | null = null;
43
+ let cleanupAttempt: Promise<void> | null = null;
44
+
45
+ return {
46
+ async ensureRunnerScript() {
47
+ if (artifact) return artifact.scriptPath;
48
+ if (!initialization) initialization = initializeRunnerScript(tempRoot);
49
+ try {
50
+ artifact = await initialization;
51
+ return artifact.scriptPath;
52
+ } catch (error) {
53
+ if (initialization) initialization = null;
54
+ throw error;
55
+ }
56
+ },
57
+ async cleanup() {
58
+ if (cleanupAttempt) return await cleanupAttempt;
59
+ cleanupAttempt = (async () => {
60
+ const initialized = await initialization?.catch(() => null);
61
+ const directory = artifact?.directory ?? initialized?.directory;
62
+ artifact = null;
63
+ initialization = null;
64
+ if (directory) await fs.rm(directory, { recursive: true, force: true });
65
+ })();
66
+ try {
67
+ await cleanupAttempt;
68
+ } finally {
69
+ cleanupAttempt = null;
70
+ }
71
+ },
72
+ };
73
+ }
74
+
75
+ /** @internal Test-only initializer for isolated artifact assertions. */
76
+ export function createRunnerScriptInitializer(tempRoot: string): () => Promise<string> {
77
+ return createRunnerScriptCache(tempRoot).ensureRunnerScript;
78
+ }
79
+
80
+ const processRunnerScriptCache = createRunnerScriptCache(os.tmpdir());
81
+
82
+ export const ensureRunnerScript = () => processRunnerScriptCache.ensureRunnerScript();
83
+
84
+ postmortem.register("python-runner-artifact", () => processRunnerScriptCache.cleanup());
@@ -19,7 +19,10 @@ export interface PyToolBridgeEntry {
19
19
 
20
20
  export interface PyToolBridgeInfo {
21
21
  url: string;
22
- token: string;
22
+ }
23
+
24
+ interface PyToolBridgeRegistration extends PyToolBridgeEntry {
25
+ sessionId: string;
23
26
  }
24
27
 
25
28
  interface BridgeServer {
@@ -27,11 +30,20 @@ interface BridgeServer {
27
30
  stop: () => Promise<void>;
28
31
  }
29
32
 
30
- const registrations = new Map<string, PyToolBridgeEntry>();
33
+ const registrations = new Map<string, PyToolBridgeRegistration>();
31
34
  let serverPromise: Promise<BridgeServer> | null = null;
32
35
 
36
+ function isCanonicalCapability(capability: string): boolean {
37
+ return capability.length > 0 && !/\s/.test(capability);
38
+ }
39
+
40
+ function parseBearerCapability(authorization: string | null): string | null {
41
+ if (!authorization?.startsWith("Bearer ")) return null;
42
+ const capability = authorization.slice("Bearer ".length);
43
+ return isCanonicalCapability(capability) ? capability : null;
44
+ }
45
+
33
46
  async function startServer(): Promise<BridgeServer> {
34
- const token = crypto.randomUUID();
35
47
  const server = Bun.serve({
36
48
  hostname: "127.0.0.1",
37
49
  port: 0,
@@ -40,7 +52,12 @@ async function startServer(): Promise<BridgeServer> {
40
52
  if (req.method !== "POST" || url.pathname !== "/v1/tool") {
41
53
  return new Response("Not Found", { status: 404 });
42
54
  }
43
- if (req.headers.get("authorization") !== `Bearer ${token}`) {
55
+ const capability = parseBearerCapability(req.headers.get("authorization"));
56
+ if (!capability) {
57
+ return new Response("Forbidden", { status: 403 });
58
+ }
59
+ const registration = registrations.get(capability);
60
+ if (!registration) {
44
61
  return new Response("Forbidden", { status: 403 });
45
62
  }
46
63
 
@@ -55,19 +72,15 @@ async function startServer(): Promise<BridgeServer> {
55
72
  if (!sessionId || !name) {
56
73
  return Response.json({ ok: false, error: "Missing session/name" }, { status: 400 });
57
74
  }
58
- const entry = registrations.get(sessionId);
59
- if (!entry) {
60
- return Response.json(
61
- { ok: false, error: `No active Python tool bridge session: ${sessionId}` },
62
- { status: 200 },
63
- );
75
+ if (sessionId !== registration.sessionId) {
76
+ return new Response("Forbidden", { status: 403 });
64
77
  }
65
78
 
66
79
  try {
67
80
  const value = await callSessionTool(name, body.args, {
68
- session: entry.toolSession,
69
- signal: entry.signal,
70
- emitStatus: entry.emitStatus,
81
+ session: registration.toolSession,
82
+ signal: registration.signal,
83
+ emitStatus: registration.emitStatus,
71
84
  });
72
85
  return Response.json({ ok: true, value });
73
86
  } catch (err) {
@@ -81,7 +94,6 @@ async function startServer(): Promise<BridgeServer> {
81
94
 
82
95
  const info: PyToolBridgeInfo = {
83
96
  url: `http://${server.hostname}:${server.port}`,
84
- token,
85
97
  };
86
98
  logger.debug("Python tool bridge listening", { url: info.url });
87
99
 
@@ -111,11 +123,15 @@ export async function ensurePyToolBridge(): Promise<PyToolBridgeInfo> {
111
123
  * Register a tool session for the duration of one execution. The returned
112
124
  * function MUST be called to remove the entry once execution finishes.
113
125
  */
114
- export function registerPyToolBridge(sessionId: string, entry: PyToolBridgeEntry): () => void {
115
- registrations.set(sessionId, entry);
126
+ export function registerPyToolBridge(sessionId: string, capability: string, entry: PyToolBridgeEntry): () => void {
127
+ if (!isCanonicalCapability(capability)) {
128
+ throw new Error("Python tool bridge capability must be a non-empty canonical bearer token");
129
+ }
130
+ const registration = { ...entry, sessionId };
131
+ registrations.set(capability, registration);
116
132
  return () => {
117
- if (registrations.get(sessionId) === entry) {
118
- registrations.delete(sessionId);
133
+ if (registrations.get(capability) === registration) {
134
+ registrations.delete(capability);
119
135
  }
120
136
  };
121
137
  }
@@ -201,6 +201,7 @@ export class ExtensionRunner {
201
201
  #getAllToolsFn: ExtensionContext["getAllTools"] = () => [];
202
202
  #getResolveToolFn: ExtensionContext["resolveTool"] = () => undefined;
203
203
  #cycleModelFn: ExtensionContextActions["cycleModel"] = undefined;
204
+ #setModelProfileFn: ExtensionContextActions["setModelProfile"] = undefined;
204
205
  #cycleThinkingLevelFn: ExtensionContextActions["cycleThinkingLevel"] = undefined;
205
206
  #setQueueModeFn: ExtensionContextActions["setQueueMode"] = undefined;
206
207
  #getSkillStateFn: ExtensionContextActions["getSkillState"] = undefined;
@@ -313,6 +314,7 @@ export class ExtensionRunner {
313
314
  this.#getAllToolsFn = contextActions.getAllTools ?? (() => []);
314
315
  this.#getResolveToolFn = contextActions.resolveTool ?? (() => undefined);
315
316
  this.#cycleModelFn = contextActions.cycleModel;
317
+ this.#setModelProfileFn = contextActions.setModelProfile;
316
318
  this.#cycleThinkingLevelFn = contextActions.cycleThinkingLevel;
317
319
  this.#setQueueModeFn = contextActions.setQueueMode;
318
320
  this.#getSkillStateFn = contextActions.getSkillState;
@@ -562,6 +564,7 @@ export class ExtensionRunner {
562
564
  getAllTools: () => this.#getAllToolsFn(),
563
565
  resolveTool: name => this.#getResolveToolFn(name),
564
566
  cycleModel: async () => await this.#cycleModelFn?.(),
567
+ setModelProfile: async name => (await this.#setModelProfileFn?.(name)) ?? false,
565
568
  cycleThinkingLevel: () => this.#cycleThinkingLevelFn?.(),
566
569
  setQueueMode: (kind, mode) => this.#setQueueModeFn?.(kind, mode) ?? false,
567
570
  invokeSkill: async (name, args) => await this.#invokeSkillFn?.(name, args),
@@ -581,6 +584,7 @@ export class ExtensionRunner {
581
584
  setSdkPermissionProvider: provider => this.#setSdkPermissionProviderFn?.(provider),
582
585
  sdkBindings: () => [
583
586
  ...(this.#cycleModelFn ? ["cycleModel"] : []),
587
+ ...(this.#setModelProfileFn ? ["setModelProfile"] : []),
584
588
  ...(this.#cycleThinkingLevelFn ? ["cycleThinkingLevel"] : []),
585
589
  ...(this.#setQueueModeFn ? ["setQueueMode"] : []),
586
590
  ...(this.#getSkillStateFn ? ["getSkillState"] : []),
@@ -290,6 +290,18 @@ export interface ExtensionTranscriptEntry {
290
290
  textSummary: string;
291
291
  ts: string;
292
292
  body?: string;
293
+ /**
294
+ * Durable, image-free message blocks used by rich transcript consumers such
295
+ * as ACP replay. Binary image payloads intentionally remain unavailable.
296
+ */
297
+ content?: Array<
298
+ | { type: "text"; text: string }
299
+ | { type: "thinking"; thinking: string }
300
+ | { type: "toolCall"; id: string; name: string; arguments: unknown }
301
+ >;
302
+ toolCallId?: string;
303
+ toolName?: string;
304
+ isError?: boolean;
293
305
  }
294
306
 
295
307
  export interface ContextUsage {
@@ -361,6 +373,7 @@ export interface ExtensionContext {
361
373
  resolveTool(name: string): Pick<Tool, "safeSummary" | "safeSummaryFields"> | undefined;
362
374
  /** Session control seams used by the SDK host. */
363
375
  cycleModel(): Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
376
+ setModelProfile?(name: string): Promise<boolean>;
364
377
  cycleThinkingLevel(): ThinkingLevel | undefined;
365
378
  setQueueMode(kind: "steering" | "follow_up" | "interrupt", mode: unknown): boolean;
366
379
  getSkillState(): unknown;
@@ -379,7 +392,15 @@ export interface ExtensionContext {
379
392
 
380
393
  getJobs(): unknown;
381
394
  /** Typed skill and mode controls exposed to the SDK host. */
382
- invokeSkill?(name: string, args?: string): Promise<unknown>;
395
+ invokeSkill?(
396
+ name: string,
397
+ args?: string,
398
+ options?: {
399
+ onPreflightAccepted?: () => void;
400
+ onPreflightAcceptCommit?: () => void | Promise<void>;
401
+ onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
402
+ },
403
+ ): Promise<unknown>;
383
404
  setPlanMode?(on: boolean): unknown;
384
405
  operateGoal?(op: "create" | "get" | "resume" | "pause" | "complete" | "drop", objective?: string): Promise<unknown>;
385
406
 
@@ -1122,7 +1143,11 @@ export interface ExtensionAPI {
1122
1143
  /** Send a user message to the agent, or queue it when deliverAs is set. */
1123
1144
  sendUserMessage(
1124
1145
  content: string | (TextContent | ImageContent)[],
1125
- options?: { deliverAs?: "steer" | "followUp"; onPreflightAccepted?: () => void },
1146
+ options?: {
1147
+ deliverAs?: "steer" | "followUp";
1148
+ onPreflightAccepted?: () => void;
1149
+ onPreflightAcceptCommit?: () => void | Promise<void>;
1150
+ },
1126
1151
  ): Promise<void>;
1127
1152
 
1128
1153
  /** Append a custom entry to the session for state persistence (not sent to LLM). */
@@ -1331,7 +1356,11 @@ export type SendMessageHandler = <T = unknown>(
1331
1356
 
1332
1357
  export type SendUserMessageHandler = (
1333
1358
  content: string | (TextContent | ImageContent)[],
1334
- options?: { deliverAs?: "steer" | "followUp"; onPreflightAccepted?: () => void },
1359
+ options?: {
1360
+ deliverAs?: "steer" | "followUp";
1361
+ onPreflightAccepted?: () => void;
1362
+ onPreflightAcceptCommit?: () => void | Promise<void>;
1363
+ },
1335
1364
  ) => void | Promise<void>;
1336
1365
 
1337
1366
  export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
@@ -1429,6 +1458,7 @@ export interface ExtensionContextActions {
1429
1458
  clearContext?: () => Promise<boolean>;
1430
1459
  /** Session control and query seams exposed to the per-session SDK host. */
1431
1460
  cycleModel?: () => Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
1461
+ setModelProfile?: (name: string) => Promise<boolean>;
1432
1462
  cycleThinkingLevel?: () => ThinkingLevel | undefined;
1433
1463
  setQueueMode?: (kind: "steering" | "follow_up" | "interrupt", mode: unknown) => boolean;
1434
1464
  getSkillState?: () => unknown;
@@ -1455,7 +1485,15 @@ export interface ExtensionContextActions {
1455
1485
  | undefined,
1456
1486
  ) => void;
1457
1487
  sdkControl?: (operation: string, input: Record<string, unknown>) => unknown | Promise<unknown>;
1458
- invokeSkill?: (name: string, args?: string) => Promise<unknown>;
1488
+ invokeSkill?: (
1489
+ name: string,
1490
+ args?: string,
1491
+ options?: {
1492
+ onPreflightAccepted?: () => void;
1493
+ onPreflightAcceptCommit?: () => void | Promise<void>;
1494
+ onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
1495
+ },
1496
+ ) => Promise<unknown>;
1459
1497
  setPlanMode?: (on: boolean) => unknown;
1460
1498
  operateGoal?: (
1461
1499
  op: "create" | "get" | "resume" | "pause" | "complete" | "drop",