@sayknow-cli/coding-agent 0.4.7 → 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 +97 -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 +20 -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
@@ -5,7 +5,15 @@ import * as fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import type { NativeExactUnlinkResult } from "@sayknow-cli/natives";
7
7
  import * as native from "@sayknow-cli/natives";
8
- import { resolveEquivalentPath } from "@sayknow-cli/utils";
8
+ import { $credentialEnv, resolveEquivalentPath } from "@sayknow-cli/utils";
9
+
10
+ import {
11
+ isModelProfileError,
12
+ type ModelProfileErrorDetails,
13
+ validateModelProfileName,
14
+ } from "../../config/model-profile-contract";
15
+ import { mergeModelProfiles } from "../../config/model-profiles";
16
+ import { ModelsConfigFile } from "../../config/model-registry";
9
17
  import { validateManagedArtifactTree } from "../../session/internal/managed-session-storage";
10
18
  import {
11
19
  FileSessionStorage,
@@ -21,6 +29,7 @@ import {
21
29
  planLaunchWorktree,
22
30
  type SkcLaunchWorktreePlan,
23
31
  } from "../../skc-runtime/launch-worktree";
32
+ import type { SessionLifecycleMcpServer } from "../acp/mcp";
24
33
  import { SdkClient, SdkClientError } from "../client/client";
25
34
  import {
26
35
  type LogicalSessionCandidate,
@@ -207,6 +216,7 @@ export interface SessionLifecycleLaunchRequest {
207
216
  /** Broker-issued effect marker which the child echoes only after host readiness. */
208
217
  effectMarker?: string;
209
218
  modelPreset?: string;
219
+ mcpServers?: SessionLifecycleMcpServer[];
210
220
  worktree?: SessionLifecycleWorktreeTarget;
211
221
  receivedAt: number;
212
222
  requestedReadinessTimeoutMs: number;
@@ -240,6 +250,77 @@ function hasValidTranscriptAuthority(path: unknown, identity: unknown): path is
240
250
  return typeof path === "string" && path.length > 0 && isSessionLifecycleTranscriptIdentity(identity);
241
251
  }
242
252
 
253
+ function isSessionLifecycleMcpServer(value: unknown): value is SessionLifecycleMcpServer {
254
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
255
+ const server = value as Record<string, unknown>;
256
+ if (server.type === "http" || server.type === "sse") {
257
+ if (
258
+ !Object.keys(server).every(key => key === "type" || key === "name" || key === "url" || key === "headers") ||
259
+ typeof server.name !== "string" ||
260
+ !/^[A-Za-z0-9_.-]{1,100}$/.test(server.name) ||
261
+ typeof server.url !== "string" ||
262
+ server.url.length > 8_192
263
+ )
264
+ return false;
265
+ try {
266
+ const url = new URL(server.url);
267
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
268
+ } catch {
269
+ return false;
270
+ }
271
+ if (server.headers === undefined) return true;
272
+ if (typeof server.headers !== "object" || server.headers === null || Array.isArray(server.headers)) return false;
273
+ const headers = server.headers as Record<string, unknown>;
274
+ return (
275
+ Object.keys(headers).length <= 100 &&
276
+ Object.entries(headers).every(
277
+ ([name, headerValue]) =>
278
+ name.length > 0 &&
279
+ name.length <= 256 &&
280
+ !name.includes("\r") &&
281
+ !name.includes("\n") &&
282
+ typeof headerValue === "string" &&
283
+ headerValue.length <= 8_192 &&
284
+ !headerValue.includes("\r") &&
285
+ !headerValue.includes("\n"),
286
+ )
287
+ );
288
+ }
289
+ const env = server.env;
290
+ return (
291
+ Object.keys(server).every(
292
+ key => key === "type" || key === "name" || key === "command" || key === "args" || key === "env",
293
+ ) &&
294
+ (server.type === undefined || server.type === "stdio") &&
295
+ typeof server.name === "string" &&
296
+ /^[A-Za-z0-9_.-]{1,100}$/.test(server.name) &&
297
+ typeof server.command === "string" &&
298
+ server.command.length <= 4_096 &&
299
+ path.isAbsolute(server.command) &&
300
+ Array.isArray(server.args) &&
301
+ server.args.length <= 100 &&
302
+ server.args.every(argument => typeof argument === "string" && argument.length <= 8_192) &&
303
+ (env === undefined ||
304
+ (typeof env === "object" &&
305
+ env !== null &&
306
+ !Array.isArray(env) &&
307
+ Object.keys(env).length <= 100 &&
308
+ Object.entries(env).every(
309
+ ([name, envValue]) =>
310
+ /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && typeof envValue === "string" && envValue.length <= 32_768,
311
+ )))
312
+ );
313
+ }
314
+
315
+ function isSessionLifecycleMcpServers(value: unknown): value is SessionLifecycleMcpServer[] {
316
+ return (
317
+ Array.isArray(value) &&
318
+ value.length <= 64 &&
319
+ value.every(isSessionLifecycleMcpServer) &&
320
+ new Set(value.map(server => server.name)).size === value.length
321
+ );
322
+ }
323
+
243
324
  export function readSessionLifecycleLaunchRequest(
244
325
  value: string | undefined,
245
326
  now = Date.now(),
@@ -270,6 +351,7 @@ export function readSessionLifecycleLaunchRequest(
270
351
  (request.effectMarker !== undefined &&
271
352
  (typeof request.effectMarker !== "string" || !/^[A-Za-z0-9._-]{1,128}$/.test(request.effectMarker))) ||
272
353
  (request.modelPreset !== undefined && (typeof request.modelPreset !== "string" || !request.modelPreset)) ||
354
+ (request.mcpServers !== undefined && !isSessionLifecycleMcpServers(request.mcpServers)) ||
273
355
  !hasValidLifecycleDeadlines(
274
356
  {
275
357
  receivedAt: request.receivedAt as number,
@@ -302,6 +384,7 @@ type SessionLaunch = {
302
384
  sessionPath?: string;
303
385
  sessionIdentity?: SessionLifecycleTranscriptIdentity;
304
386
  modelPreset?: string;
387
+ mcpServers?: SessionLifecycleMcpServer[];
305
388
  worktree?: SessionLifecycleWorktreeTarget;
306
389
  worktreePlan?: SkcLaunchWorktreePlan;
307
390
  };
@@ -325,14 +408,36 @@ function serializeCleanupIdentity(identity: CleanupIdentity): BrokerCleanupIdent
325
408
  };
326
409
  }
327
410
 
328
- const fail = (code: string, message: string, cleanup?: CleanupEvidence): BrokerResponse => ({
411
+ const fail = (
412
+ code: string,
413
+ message: string,
414
+ cleanup?: CleanupEvidence,
415
+ details?: ModelProfileErrorDetails,
416
+ ): BrokerResponse => ({
329
417
  ok: false,
330
- error: { code: code as never, message, ...(cleanup ? { cleanup } : {}) },
418
+ error: { code: code as never, message, ...(details ? { details } : {}), ...(cleanup ? { cleanup } : {}) },
331
419
  });
332
420
  function text(value: unknown): string | undefined {
333
421
  return typeof value === "string" && value ? value : undefined;
334
422
  }
335
423
 
424
+ function validateBrokerModelPreset(agentDir: string, requestedProfile: string): string | BrokerResponse {
425
+ const modelsConfigFile = ModelsConfigFile.relocate(path.join(agentDir, "models.yml"));
426
+ modelsConfigFile.invalidate();
427
+ const loaded = modelsConfigFile.tryLoad();
428
+ const profiles = mergeModelProfiles(loaded.status === "ok" ? loaded.value.profiles : undefined);
429
+ try {
430
+ return validateModelProfileName(requestedProfile, profiles, loaded.status === "error" ? loaded.error : undefined);
431
+ } catch (error) {
432
+ if (isModelProfileError(error)) return fail(error.code, error.message, undefined, error.details);
433
+ throw error;
434
+ }
435
+ }
436
+
437
+ export function validateBrokerModelPresetForTest(agentDir: string, requestedProfile: string): string | BrokerResponse {
438
+ return validateBrokerModelPreset(agentDir, requestedProfile);
439
+ }
440
+
336
441
  function readinessTimeout(input: Input): number | BrokerResponse {
337
442
  const value = input.readinessTimeoutMs;
338
443
  if (value === undefined) return READY_TIMEOUT_MS;
@@ -601,12 +706,31 @@ async function reconcileReadyScope(broker: Broker, id: string, scope: string | u
601
706
  });
602
707
  }
603
708
 
709
+ /**
710
+ * Operator override for the session-host command, resolved from trusted
711
+ * environment sources only.
712
+ *
713
+ * The result is spawned directly, so whatever can set it chooses which binary
714
+ * the broker runs. `$env` merges the caller's `cwd/.env` into `process.env`, so
715
+ * reading it there would let repository content replace the session host;
716
+ * resolve it the same way provider credentials are (launching shell plus
717
+ * SKC/user-owned `.env` files, never the project `.env`).
718
+ */
719
+ function sdkSessionCommandOverride(): { file: string; args: string[] } | undefined {
720
+ const configured = $credentialEnv("SKC_SDK_SESSION_COMMAND");
721
+ if (!configured) return undefined;
722
+ const [file, ...args] = configured.trim().split(/\s+/);
723
+ return file ? { file, args } : undefined;
724
+ }
725
+
726
+ /** Test seam: the session-host command override as resolved from trusted env. */
727
+ export function sdkSessionCommandOverrideForTest(): { file: string; args: string[] } | undefined {
728
+ return sdkSessionCommandOverride();
729
+ }
730
+
604
731
  function command(broker: Broker): LifecycleCommand {
605
- const configured = process.env.SKC_SDK_SESSION_COMMAND;
606
- if (configured) {
607
- const [file, ...args] = configured.trim().split(/\s+/);
608
- if (file) return { file, args };
609
- }
732
+ const configured = sdkSessionCommandOverride();
733
+ if (configured) return configured;
610
734
  return lifecycleCommandResolversForTest.get(broker)?.() ?? resolveSdkInternalSpawnCommand("session-host-internal");
611
735
  }
612
736
 
@@ -623,7 +747,7 @@ type ReadyAuthority = {
623
747
  };
624
748
  type ReadinessResult =
625
749
  | { kind: "ready"; authority: ReadyAuthority }
626
- | { kind: "startup_failed"; message: string }
750
+ | { kind: "startup_failed"; failure: SdkStartupFailure }
627
751
  | { kind: "child_exited" }
628
752
  | { kind: "timeout" };
629
753
  const processIncarnationReadersForTest = new WeakMap<Broker, (pid: number) => string | undefined>();
@@ -796,19 +920,25 @@ function isLifecycleTranscriptEvidence(value: unknown): value is LifecycleTransc
796
920
  function isSdkStartupFailure(value: unknown): value is SdkStartupFailure {
797
921
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
798
922
  const failure = value as Record<string, unknown>;
799
- return (
800
- Object.keys(failure).length === 3 &&
801
- (failure.phase === "registration" || failure.phase === "startup") &&
802
- (failure.reason === "disabled" ||
803
- failure.reason === "ineligible" ||
804
- failure.reason === "factory_absent" ||
805
- failure.reason === "runner_absent" ||
806
- failure.reason === "pending" ||
807
- failure.reason === "failed") &&
808
- typeof failure.message === "string" &&
809
- Buffer.byteLength(failure.message) > 0 &&
810
- Buffer.byteLength(failure.message) <= 512
811
- );
923
+ const keys = Object.keys(failure);
924
+ if (
925
+ !keys.every(
926
+ key => key === "phase" || key === "reason" || key === "message" || key === "code" || key === "details",
927
+ ) ||
928
+ (failure.phase !== "registration" && failure.phase !== "startup") ||
929
+ (failure.reason !== "disabled" &&
930
+ failure.reason !== "ineligible" &&
931
+ failure.reason !== "factory_absent" &&
932
+ failure.reason !== "runner_absent" &&
933
+ failure.reason !== "pending" &&
934
+ failure.reason !== "failed") ||
935
+ typeof failure.message !== "string" ||
936
+ Buffer.byteLength(failure.message) === 0 ||
937
+ Buffer.byteLength(failure.message) > 512
938
+ )
939
+ return false;
940
+ if (failure.code === undefined && failure.details === undefined) return keys.length === 3;
941
+ return keys.length === 5 && isModelProfileError(failure);
812
942
  }
813
943
 
814
944
  function isLifecycleFailureArtifact(value: unknown): value is LifecycleFailureArtifact {
@@ -817,8 +947,14 @@ function isLifecycleFailureArtifact(value: unknown): value is LifecycleFailureAr
817
947
  if (!isEffectMarker(record)) return false;
818
948
  const artifact = value as LifecycleFailureArtifact;
819
949
  return (
820
- Object.keys(record).length === (artifact.transcript === undefined ? 7 : 8) &&
821
- isSdkStartupFailure({ phase: artifact.phase, reason: artifact.reason, message: artifact.message }) &&
950
+ Object.keys(record).length ===
951
+ 7 + (artifact.code === undefined ? 0 : 2) + (artifact.transcript === undefined ? 0 : 1) &&
952
+ isSdkStartupFailure({
953
+ phase: artifact.phase,
954
+ reason: artifact.reason,
955
+ message: artifact.message,
956
+ ...(artifact.code === undefined ? {} : { code: artifact.code, details: artifact.details }),
957
+ }) &&
822
958
  isRollbackResult(artifact.rollback) &&
823
959
  (artifact.transcript === undefined || isLifecycleTranscriptEvidence(artifact.transcript))
824
960
  );
@@ -864,6 +1000,8 @@ export async function writeSessionLifecycleFailure(
864
1000
  ...(transcript ? { transcript } : {}),
865
1001
  };
866
1002
  const bytes = Buffer.from(canonicalJson(artifact), "utf8");
1003
+ if (bytes.length > MAX_LIFECYCLE_METADATA_BYTES)
1004
+ throw new Error("Lifecycle startup failure exceeds the metadata size ceiling.");
867
1005
  const target = lifecycleFailurePath(root, id, effectMarker);
868
1006
  const temporary = path.join(directory, `.${id}.lifecycle.failure.${effectMarker}.${randomUUID()}.tmp`);
869
1007
  let published = false;
@@ -1240,6 +1378,10 @@ function validateLifecycleMetadataReplay(cleanup: CleanupEvidence): BrokerRespon
1240
1378
  if (!current) continue;
1241
1379
  if (!sameLifecycleCleanupIdentity(current.identity, file.identity))
1242
1380
  return fail("terminal_uncertain", "Lifecycle metadata candidate lacks exact replay authority.");
1381
+ // A completed file's recorded retained quarantine is receipt-bound durable
1382
+ // evidence; anything else that remains is an active survivor.
1383
+ if (file.completed && file.detachedPath && path.resolve(candidate) === path.resolve(file.detachedPath))
1384
+ continue;
1243
1385
  activeCandidates++;
1244
1386
  }
1245
1387
  if (file.completed && activeCandidates > 0)
@@ -1288,7 +1430,21 @@ function lifecycleMetadataReplayAbsent(cleanup: CleanupEvidence): boolean {
1288
1430
  const id = cleanup.sessionId!;
1289
1431
  const required = new Set<string>([lifecycleMarkerPath(root, id), lifecycleReadyPath(root, id)]);
1290
1432
  for (const file of files) for (const candidate of lifecycleCleanupCandidates(file)) required.add(candidate);
1291
- return [...required].every(absentLifecyclePath);
1433
+ for (const candidate of required) {
1434
+ if (absentLifecyclePath(candidate)) continue;
1435
+ // A completed file's recorded retained quarantine is durable evidence, not a
1436
+ // survivor — accept it only at its receipt-bound path and identity.
1437
+ const owner = files.find(
1438
+ file =>
1439
+ file.completed === true &&
1440
+ file.detachedPath !== undefined &&
1441
+ path.resolve(file.detachedPath) === path.resolve(candidate),
1442
+ );
1443
+ if (!owner) return false;
1444
+ const current = captureLifecycleFile(candidate, true, true);
1445
+ if (!current || !sameLifecycleCleanupIdentity(current.identity, owner.identity)) return false;
1446
+ }
1447
+ return true;
1292
1448
  }
1293
1449
 
1294
1450
  /**
@@ -1551,16 +1707,28 @@ async function reconcileLifecycleCleanup(
1551
1707
  const candidates = lifecycleCleanupCandidates(file);
1552
1708
  if (file.completed) {
1553
1709
  for (const candidate of candidates) {
1710
+ let stat: ReturnType<typeof fsSync.lstatSync>;
1554
1711
  try {
1555
- fsSync.lstatSync(candidate);
1556
- return fail(
1557
- "terminal_uncertain",
1558
- "Lifecycle cleanup receipt marks a target complete while an authorized candidate remains.",
1559
- );
1712
+ stat = fsSync.lstatSync(candidate);
1560
1713
  } catch (error) {
1561
- if ((error as NodeJS.ErrnoException).code !== "ENOENT")
1562
- return fail("terminal_uncertain", "Lifecycle cleanup completion could not be safely inspected.");
1714
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
1715
+ return fail("terminal_uncertain", "Lifecycle cleanup completion could not be safely inspected.");
1716
+ }
1717
+ // A completed file's recorded retained quarantine is durable evidence,
1718
+ // not a survivor — accept it only at its receipt-bound path and identity.
1719
+ if (
1720
+ file.detachedPath &&
1721
+ path.resolve(candidate) === path.resolve(file.detachedPath) &&
1722
+ stat.isFile() &&
1723
+ !stat.isSymbolicLink()
1724
+ ) {
1725
+ const current = captureLifecycleFile(candidate, true, true);
1726
+ if (current && sameLifecycleCleanupIdentity(current.identity, file.identity)) continue;
1563
1727
  }
1728
+ return fail(
1729
+ "terminal_uncertain",
1730
+ "Lifecycle cleanup receipt marks a target complete while an authorized candidate remains.",
1731
+ );
1564
1732
  }
1565
1733
  continue;
1566
1734
  }
@@ -1630,6 +1798,22 @@ async function reconcileLifecycleCleanup(
1630
1798
  quarantineName: path.basename(currentFile.plannedPath),
1631
1799
  });
1632
1800
  if (!result.ok) {
1801
+ if (result.code === "cleanup_pending" && result.detachedPath === currentFile.plannedPath) {
1802
+ // Typed retained authority: the native verified the exact identity-bound
1803
+ // detach and retained the quarantine as durable evidence. Record the
1804
+ // evidence and advance — never claim a terminal byte deletion.
1805
+ const lifecycleFiles = activeCleanup.lifecycleFiles!.map((candidate, candidateIndex) =>
1806
+ candidateIndex === index
1807
+ ? { ...candidate, detachedPath: result.detachedPath, completed: true as const }
1808
+ : candidate,
1809
+ );
1810
+ activeCleanup = { ...activeCleanup, lifecycleFiles };
1811
+ await broker.ledger.transition(identity, "effect_started", {
1812
+ response: fail("cleanup_pending", "Lifecycle cleanup completion was durably reconciled.", activeCleanup),
1813
+ });
1814
+ lifecycleCleanupHooksForTest.get(broker)?.();
1815
+ continue;
1816
+ }
1633
1817
  const lifecycleFiles = activeCleanup.lifecycleFiles!.map((candidate, candidateIndex) =>
1634
1818
  candidateIndex === index && result.detachedPath
1635
1819
  ? { ...candidate, detachedPath: result.detachedPath }
@@ -1664,6 +1848,22 @@ async function readSessionLifecycleFailure(
1664
1848
  ?.artifact;
1665
1849
  }
1666
1850
 
1851
+ export async function readSessionLifecycleFailureForTest(
1852
+ root: string,
1853
+ id: string,
1854
+ expected: { pid: number; effectMarker: string; incarnation: string },
1855
+ ): Promise<SdkStartupFailure | undefined> {
1856
+ const artifact = await readSessionLifecycleFailure(root, id, expected);
1857
+ return artifact
1858
+ ? {
1859
+ phase: artifact.phase,
1860
+ reason: artifact.reason,
1861
+ message: artifact.message,
1862
+ ...(artifact.code === undefined ? {} : { code: artifact.code, details: artifact.details }),
1863
+ }
1864
+ : undefined;
1865
+ }
1866
+
1667
1867
  async function hasDurableProcessIdentity(
1668
1868
  root: string,
1669
1869
  id: string,
@@ -2055,14 +2255,14 @@ async function waitForReady(
2055
2255
  ): Promise<ReadinessResult> {
2056
2256
  while (timing.now() < deadline) {
2057
2257
  const startupFailure = await readSessionLifecycleFailure(root, id, expected);
2058
- if (startupFailure) return { kind: "startup_failed", message: startupFailure.message };
2258
+ if (startupFailure) return { kind: "startup_failed", failure: startupFailure };
2059
2259
  if (
2060
2260
  observeProcess(expected.pid, expected.incarnation, value => processIncarnationForBroker(broker, value)) ===
2061
2261
  "exited"
2062
2262
  ) {
2063
2263
  const finalStartupFailure = await readSessionLifecycleFailure(root, id, expected);
2064
2264
  return finalStartupFailure
2065
- ? { kind: "startup_failed", message: finalStartupFailure.message }
2265
+ ? { kind: "startup_failed", failure: finalStartupFailure }
2066
2266
  : { kind: "child_exited" };
2067
2267
  }
2068
2268
  try {
@@ -2190,10 +2390,15 @@ async function launchInput(
2190
2390
  const requested = sessionId(input);
2191
2391
  if (requested !== undefined && !isCanonicalSessionId(requested))
2192
2392
  return fail("invalid_input", "sessionId must be a canonical safe identifier.");
2393
+ if (input.modelPreset !== undefined && (typeof input.modelPreset !== "string" || input.modelPreset.length === 0))
2394
+ return fail("invalid_input", "modelPreset must be a non-empty exact profile ID.");
2193
2395
  const modelPreset = text(input.modelPreset);
2396
+ if (input.mcpServers !== undefined && !isSessionLifecycleMcpServers(input.mcpServers))
2397
+ return fail("invalid_input", "mcpServers must contain unique valid stdio, HTTP, or SSE server definitions.");
2398
+ const mcpServers = input.mcpServers as SessionLifecycleMcpServer[] | undefined;
2194
2399
 
2195
2400
  if (operation === "session.create")
2196
- return { id: randomUUID(), cwd, root: resolvedRoot, modelPreset, worktree, worktreePlan };
2401
+ return { id: randomUUID(), cwd, root: resolvedRoot, modelPreset, mcpServers, worktree, worktreePlan };
2197
2402
  if (operation === "session.resume") {
2198
2403
  if (!requested) return fail("invalid_input", "sessionId is required to resume a saved session.");
2199
2404
  const savedPath = text(input.sessionPath);
@@ -2207,6 +2412,7 @@ async function launchInput(
2207
2412
  sessionPath: saved.path,
2208
2413
  sessionIdentity: saved.identity,
2209
2414
  modelPreset,
2415
+ mcpServers,
2210
2416
  worktree,
2211
2417
  worktreePlan,
2212
2418
  };
@@ -2228,6 +2434,7 @@ async function launchInput(
2228
2434
  sourceSessionIdentity: source.identity,
2229
2435
  sourceCwd,
2230
2436
  modelPreset,
2437
+ mcpServers,
2231
2438
  worktree,
2232
2439
  worktreePlan,
2233
2440
  };
@@ -2614,6 +2821,11 @@ async function executeLifecycleResponse(
2614
2821
 
2615
2822
  const launch = await launchInput(broker, operation, input);
2616
2823
  if ("ok" in launch) return launch;
2824
+ if (launch.modelPreset) {
2825
+ const validatedModelPreset = validateBrokerModelPreset(broker.settings.agentDir, launch.modelPreset);
2826
+ if (typeof validatedModelPreset !== "string") return validatedModelPreset;
2827
+ launch.modelPreset = validatedModelPreset;
2828
+ }
2617
2829
  if (!hasProcessIncarnationAuthority())
2618
2830
  return fail(
2619
2831
  "incarnation_unavailable",
@@ -2692,6 +2904,7 @@ async function executeLifecycleResponse(
2692
2904
  ...(launch.sessionPath ? { sessionPath: launch.sessionPath } : {}),
2693
2905
  ...(launch.sessionIdentity ? { sessionIdentity: launch.sessionIdentity } : {}),
2694
2906
  ...(launch.modelPreset ? { modelPreset: launch.modelPreset } : {}),
2907
+ ...(launch.mcpServers ? { mcpServers: launch.mcpServers } : {}),
2695
2908
  ...(launch.worktree ? { worktree: launch.worktree } : {}),
2696
2909
  };
2697
2910
  let child: ChildProcess | undefined;
@@ -2767,7 +2980,12 @@ async function executeLifecycleResponse(
2767
2980
  `Session ${launch.id} did not become ready and its spawned process could not be verified dead.`,
2768
2981
  );
2769
2982
  return readiness.kind === "startup_failed"
2770
- ? fail("spawn_failed", readiness.message)
2983
+ ? fail(
2984
+ readiness.failure.code ?? "spawn_failed",
2985
+ readiness.failure.message,
2986
+ undefined,
2987
+ readiness.failure.details,
2988
+ )
2771
2989
  : readiness.kind === "child_exited"
2772
2990
  ? fail("spawn_failed", `Session ${launch.id} exited before registering readiness.`)
2773
2991
  : fail(
@@ -3042,11 +3260,26 @@ async function executeLifecycleResponse(
3042
3260
  `Unable to delete saved session artifacts: ${error instanceof Error ? error.message : String(error)}`,
3043
3261
  );
3044
3262
  }
3045
- if (deleted.kind === "artifacts_removed") {
3263
+ const retainedArtifactsCleanup =
3264
+ deleted.kind === "cleanup_pending" &&
3265
+ deleted.phase === "artifacts" &&
3266
+ deleted.detachedArtifactsPath !== undefined &&
3267
+ cleanupTarget.plannedArtifactsPath !== undefined &&
3268
+ (deleted.detachedArtifactsPath === cleanupTarget.plannedArtifactsPath ||
3269
+ deleted.detachedArtifactsPath === `${cleanupTarget.plannedArtifactsPath}.removing`);
3270
+ if (deleted.kind === "artifacts_removed" || retainedArtifactsCleanup) {
3046
3271
  const transcriptPhaseCleanup = {
3047
3272
  ...preauthorizedCleanup,
3048
3273
  phase: "transcript" as const,
3049
3274
  artifactsRemoved: true,
3275
+ ...(deleted.kind === "cleanup_pending" && deleted.phase === "artifacts"
3276
+ ? {
3277
+ detachedArtifactsPath: deleted.detachedArtifactsPath,
3278
+ ...(deleted.artifactsIdentity
3279
+ ? { artifactsIdentity: serializeCleanupIdentity(deleted.artifactsIdentity) }
3280
+ : {}),
3281
+ }
3282
+ : {}),
3050
3283
  ...(preauthorizedCleanup.artifactTree
3051
3284
  ? { artifactTree: { ...preauthorizedCleanup.artifactTree, completed: true as const } }
3052
3285
  : {}),
@@ -3067,7 +3300,14 @@ async function executeLifecycleResponse(
3067
3300
  artifactsRemoved: true,
3068
3301
  });
3069
3302
  }
3070
- if (deleted.kind === "cleanup_pending")
3303
+ if (
3304
+ deleted.kind === "cleanup_pending" &&
3305
+ !(
3306
+ deleted.phase === "transcript" &&
3307
+ deleted.detachedTranscriptPath !== undefined &&
3308
+ deleted.detachedTranscriptPath === cleanupTarget.plannedTranscriptPath
3309
+ )
3310
+ )
3071
3311
  return fail(
3072
3312
  "cleanup_pending",
3073
3313
  `Saved session cleanup is pending in ${deleted.phase}: ${deleted.error.message}`,
@@ -3322,6 +3562,9 @@ export async function executeLifecycle(
3322
3562
  phase: evidence.artifact.phase,
3323
3563
  reason: evidence.artifact.reason,
3324
3564
  message: evidence.artifact.message,
3565
+ ...(evidence.artifact.code === undefined
3566
+ ? {}
3567
+ : { code: evidence.artifact.code, details: evidence.artifact.details }),
3325
3568
  rollback: {
3326
3569
  endpointGeneration: evidence.artifact.rollback.endpointGeneration,
3327
3570
  fenced: evidence.artifact.rollback.fenced,
@@ -3386,11 +3629,18 @@ export async function executeLifecycle(
3386
3629
  : startupFailure && cleanupProof
3387
3630
  ? {
3388
3631
  ok: false,
3389
- error: {
3390
- code: "spawn_failed",
3391
- message: "No ready SDK endpoint remains available.",
3392
- endpoint: "unavailable",
3393
- },
3632
+ error: startupFailure.code
3633
+ ? {
3634
+ code: startupFailure.code,
3635
+ message: startupFailure.message,
3636
+ details: startupFailure.details!,
3637
+ endpoint: "unavailable" as const,
3638
+ }
3639
+ : {
3640
+ code: "spawn_failed",
3641
+ message: "No ready SDK endpoint remains available.",
3642
+ endpoint: "unavailable" as const,
3643
+ },
3394
3644
  ...(durableEffects ? { durableEffects } : {}),
3395
3645
  startupFailure,
3396
3646
  }
@@ -131,6 +131,7 @@ function compactEvents(events: SessionIndexEvent[]): SessionIndexEvent[] {
131
131
 
132
132
  async function appendSync(file: string, value: string): Promise<void> {
133
133
  const h = await fs.open(file, "a", 0o600);
134
+ let failure: { error: unknown } | undefined;
134
135
  try {
135
136
  const data = Buffer.from(`${value}\n`);
136
137
  for (let offset = 0; offset < data.length; ) {
@@ -139,9 +140,18 @@ async function appendSync(file: string, value: string): Promise<void> {
139
140
  offset += bytesWritten;
140
141
  }
141
142
  await h.sync();
142
- } finally {
143
+ } catch (error) {
144
+ failure = { error };
145
+ }
146
+ try {
143
147
  await h.close();
148
+ } catch (error) {
149
+ // Bun may report EBADF when concurrent child-pipe teardown has already
150
+ // released a fully written and fsynced append handle. The descriptor is
151
+ // closed in that case; every other close failure remains fatal.
152
+ if ((error as NodeJS.ErrnoException).code !== "EBADF" && failure === undefined) failure = { error };
144
153
  }
154
+ if (failure !== undefined) throw failure.error;
145
155
  }
146
156
 
147
157
  async function syncDirectory(file: string): Promise<void> {
@@ -67,7 +67,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
67
67
  idleTimeoutMs: 60_000,
68
68
  rich: { enabled: true },
69
69
  richDraft: { enabled: false },
70
- toolActivity: { enabled: true },
70
+ toolActivity: { enabled: false },
71
71
  topics: { nameTemplate: undefined },
72
72
  btw: { enabled: true },
73
73
  streaming: { enabled: true },
@@ -102,7 +102,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
102
102
  idleTimeoutMs: 60_000,
103
103
  rich: { enabled: true },
104
104
  richDraft: { enabled: false },
105
- toolActivity: { enabled: true },
105
+ toolActivity: { enabled: false },
106
106
  topics: { nameTemplate: undefined },
107
107
  btw: { enabled: true },
108
108
  streaming: { enabled: true },
@@ -27,11 +27,19 @@ export type ChatDaemonAction = "stop" | "reload";
27
27
  * Generation 6 carries the retained managed filesystem authority boundary.
28
28
  * Generation 7 restores macOS daemon signaling (kill(2) with a start-time
29
29
  * incarnation recheck) so a live/hung owner can be replaced without an external
30
- * `kill -9`.
30
+ * `kill -9`. Generation 8 adopts Windows expected-identity ACL verification and
31
+ * repair for shared native authority. Generation 9 refreshes the shared native
32
+ * authority declaration contract with bounded frame-delivery acknowledgement.
33
+ * Generation 11 accepts typed retained exact-unlink cleanup authority — a
34
+ * concrete detached quarantine plus a proven-absent canonical lock pathname —
35
+ * when deleting an observed owner-lock lease. Generation 12 refreshes retained
36
+ * native path and process authority semantics. Generation 13 rejects special
37
+ * files before retained native authority opens. Generation 14 reloads shared
38
+ * chat daemons after notification configuration parsing changes.
31
39
  */
32
40
  export const CHAT_DAEMON_GENERATIONS: Readonly<Record<ChatDaemonKind, number>> = {
33
- discord: 7,
34
- slack: 7,
41
+ discord: 14,
42
+ slack: 14,
35
43
  };
36
44
 
37
45
  export function chatDaemonGeneration(kind: ChatDaemonKind): number {
@@ -937,10 +945,19 @@ async function ownsChatDaemonOwnerLock(lock: string, lease: ChatDaemonOwnerLockL
937
945
  /** Deletes only the exact lease observed by this contender; a successor is retained. */
938
946
  function unlinkExactChatDaemonOwnerLock(lock: string, lease: ChatDaemonOwnerLockLease): boolean {
939
947
  try {
940
- return native.exactUnlink(lock, {
948
+ const removed = native.exactUnlink(lock, {
941
949
  ...lease,
942
950
  quarantineName: `.skc-delete-chat-daemon-lock-${crypto.randomUUID()}`,
943
- }).ok;
951
+ });
952
+ if (removed.ok) return true;
953
+ // Accept only typed retained authority: a concrete detached quarantine plus
954
+ // a proven-absent canonical lock pathname. Anything else stays fail-closed.
955
+ return (
956
+ removed.code === "cleanup_pending" &&
957
+ typeof removed.detachedPath === "string" &&
958
+ removed.detachedPath.length > 0 &&
959
+ !fs.existsSync(lock)
960
+ );
944
961
  } catch {
945
962
  return false;
946
963
  }
@@ -5,8 +5,8 @@
5
5
  * removed), but the user can still adjust per-surface behaviour from inside a
6
6
  * session thread with small slash commands:
7
7
  *
8
- * - `/verbose` switch the mirror to bounded tool-owned summaries + provider-displayable reasoning summaries
9
- * - `/lean` switch back to lean (assistant text + tool names)
8
+ * - `/verbose` switch the mirror to per-turn assistant text + tool summaries + reasoning summaries
9
+ * - `/lean` settled assistant answer at idle + immediate ask lead-ins (no intermediate tool-turn flood)
10
10
  * - `/verbosity lean|verbose`
11
11
  * - `/redact on|off` toggle redaction of streamed content
12
12
  *