@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
@@ -4,9 +4,26 @@ import type { ParsedIrcMessage } from "./utils/irc-message";
4
4
 
5
5
  type InlineMode = "persistent" | "ephemeral";
6
6
 
7
- const MAX_RECORDS = 10_000;
8
- const MAX_RETAINED_UTF8_BYTES = 16 * 1024 * 1024;
9
- const MAX_SEEN_IDENTITIES = 100_000;
7
+ export const IRC_OBSERVATION_LEDGER_MAX_RECORDS = 10_000;
8
+ export const IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES = 16 * 1024 * 1024;
9
+ export const IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES = 100_000;
10
+
11
+ export const __ircLedgerPerfCounters = {
12
+ enabled: false,
13
+ epochAdvances: 0,
14
+ snapshot() {
15
+ return { epochAdvances: this.epochAdvances };
16
+ },
17
+ enable(): void {
18
+ this.enabled = true;
19
+ },
20
+ disable(): void {
21
+ this.enabled = false;
22
+ },
23
+ reset(): void {
24
+ this.epochAdvances = 0;
25
+ },
26
+ };
10
27
 
11
28
  export type IrcObservationRecord = Readonly<
12
29
  ParsedIrcMessage & {
@@ -22,7 +39,7 @@ function measureRetainedUtf8Bytes(message: ParsedIrcMessage): number {
22
39
  let bytes = 4;
23
40
  for (const value of [message.observationId, message.from, message.to, message.text, message.kind]) {
24
41
  bytes += Buffer.byteLength(value, "utf8");
25
- if (bytes > MAX_RETAINED_UTF8_BYTES) return bytes;
42
+ if (bytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES) return bytes;
26
43
  }
27
44
  return bytes;
28
45
  }
@@ -43,12 +60,28 @@ export class IrcObservationLedger {
43
60
  #nextSequence = 0;
44
61
  #seenObservationIdentities = new Set<string>();
45
62
  #identityCapacityExhausted = false;
63
+ #retiredSessionIdentities = new Set<string>();
46
64
  #evictedObservationIds = new Set<string>();
65
+ #mutationEpoch = 0;
66
+
67
+ get mutationEpoch(): number {
68
+ return this.#mutationEpoch;
69
+ }
70
+
71
+ #advanceMutationEpoch(): void {
72
+ this.#mutationEpoch++;
73
+ if (__ircLedgerPerfCounters.enabled) __ircLedgerPerfCounters.epochAdvances++;
74
+ }
47
75
 
48
76
  #rememberObservationIdentity(observationId: string): boolean {
49
77
  const identity = tombstoneIdentity(observationId);
50
- if (this.#seenObservationIdentities.has(identity) || this.#identityCapacityExhausted) return false;
51
- if (this.#seenObservationIdentities.size >= MAX_SEEN_IDENTITIES) {
78
+ if (
79
+ this.#seenObservationIdentities.has(identity) ||
80
+ this.#retiredSessionIdentities.has(identity) ||
81
+ this.#identityCapacityExhausted
82
+ )
83
+ return false;
84
+ if (this.#seenObservationIdentities.size >= IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES) {
52
85
  this.#identityCapacityExhausted = true;
53
86
  return false;
54
87
  }
@@ -56,6 +89,18 @@ export class IrcObservationLedger {
56
89
  return true;
57
90
  }
58
91
 
92
+ #retireCurrentSessionIdentities(): void {
93
+ for (const identity of this.#seenObservationIdentities) {
94
+ this.#retiredSessionIdentities.delete(identity);
95
+ this.#retiredSessionIdentities.add(identity);
96
+ while (this.#retiredSessionIdentities.size > IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES) {
97
+ const oldest = this.#retiredSessionIdentities.values().next().value;
98
+ if (oldest === undefined) break;
99
+ this.#retiredSessionIdentities.delete(oldest);
100
+ }
101
+ }
102
+ }
103
+
59
104
  #evict(observationId: string): void {
60
105
  const record = this.#records.get(observationId);
61
106
  if (!record) return;
@@ -65,7 +110,10 @@ export class IrcObservationLedger {
65
110
  }
66
111
 
67
112
  #enforceBounds(): void {
68
- while (this.#records.size > MAX_RECORDS || this.#retainedUtf8Bytes > MAX_RETAINED_UTF8_BYTES) {
113
+ while (
114
+ this.#records.size > IRC_OBSERVATION_LEDGER_MAX_RECORDS ||
115
+ this.#retainedUtf8Bytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES
116
+ ) {
69
117
  const oldestObservationId = this.#records.keys().next().value;
70
118
  if (oldestObservationId === undefined) return;
71
119
  this.#evict(oldestObservationId);
@@ -76,10 +124,9 @@ export class IrcObservationLedger {
76
124
  observe(message: ParsedIrcMessage, panelVisibleAtObservation: boolean): IrcObservationRecord | undefined {
77
125
  const existing = this.#records.get(message.observationId);
78
126
  if (existing) return existing;
79
- if (!this.#rememberObservationIdentity(message.observationId)) return undefined;
80
-
81
127
  const estimatedBytes = measureRetainedUtf8Bytes(message);
82
- if (estimatedBytes > MAX_RETAINED_UTF8_BYTES) return undefined;
128
+ if (estimatedBytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES) return undefined;
129
+ if (!this.#rememberObservationIdentity(message.observationId)) return undefined;
83
130
 
84
131
  const observedAt = Date.now();
85
132
  const mode: InlineMode = panelVisibleAtObservation ? "ephemeral" : "persistent";
@@ -94,6 +141,7 @@ export class IrcObservationLedger {
94
141
  this.#records.set(record.observationId, record);
95
142
  this.#retainedUtf8Bytes += estimatedBytes;
96
143
  this.#enforceBounds();
144
+ this.#advanceMutationEpoch();
97
145
  return this.#records.get(record.observationId);
98
146
  }
99
147
 
@@ -116,12 +164,20 @@ export class IrcObservationLedger {
116
164
  return observationIds;
117
165
  }
118
166
 
119
- reset(): void {
120
- for (const observationId of this.#records.keys()) {
121
- this.#evictedObservationIds.add(observationId);
167
+ reset(options?: { retireCurrentSessionIdentities?: boolean }): void {
168
+ const retainedRecordsChanged = this.#records.size > 0;
169
+ if (options?.retireCurrentSessionIdentities) this.#retireCurrentSessionIdentities();
170
+ else this.#retiredSessionIdentities.clear();
171
+ if (retainedRecordsChanged) {
172
+ for (const observationId of this.#records.keys()) {
173
+ this.#evictedObservationIds.add(observationId);
174
+ }
175
+ this.#records.clear();
176
+ this.#retainedUtf8Bytes = 0;
122
177
  }
123
- this.#records.clear();
124
- this.#retainedUtf8Bytes = 0;
125
178
  this.#nextSequence = 0;
179
+ this.#seenObservationIdentities.clear();
180
+ this.#identityCapacityExhausted = false;
181
+ if (retainedRecordsChanged) this.#advanceMutationEpoch();
126
182
  }
127
183
  }
@@ -3,12 +3,10 @@ import {
3
3
  type AutocompleteProvider,
4
4
  CombinedAutocompleteProvider,
5
5
  extractSlashCommandTokenPrefix,
6
- getKeybindings,
7
6
  getSlashCommandMatchRank,
8
7
  type SlashCommand,
9
8
  } from "@sayknow-cli/tui";
10
9
  import type { KeybindingsManager } from "../config/keybindings";
11
- import { formatKeyHints } from "../config/keybindings";
12
10
  import { isSettingsInitialized, settings } from "../config/settings";
13
11
  import { applyEmojiCompletion, getEmojiSuggestions, isEmojiPrefix, tryEmojiInlineReplace } from "./emoji-autocomplete";
14
12
 
@@ -44,6 +42,11 @@ interface PromptActionAutocompleteOptions {
44
42
  moveCursorToMessageStart: () => void;
45
43
  moveCursorToLineStart: () => void;
46
44
  moveCursorToLineEnd: () => void;
45
+ /**
46
+ * Ghost-text next-prompt prediction shown in the empty composer as a dim
47
+ * inline hint (Tab accepts it via the editor's onTab handler).
48
+ */
49
+ getPromptSuggestion?: () => string | null;
47
50
  }
48
51
 
49
52
  function fuzzyMatch(query: string, target: string): boolean {
@@ -186,11 +189,18 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
186
189
  #baseProvider: CombinedAutocompleteProvider;
187
190
  #actions: PromptActionDefinition[];
188
191
  #commands: SlashCommand[];
189
-
190
- constructor(commands: SlashCommand[], basePath: string, actions: PromptActionDefinition[]) {
192
+ #getPromptSuggestion: (() => string | null) | undefined;
193
+
194
+ constructor(
195
+ commands: SlashCommand[],
196
+ basePath: string,
197
+ actions: PromptActionDefinition[],
198
+ getPromptSuggestion?: () => string | null,
199
+ ) {
191
200
  this.#baseProvider = new CombinedAutocompleteProvider(commands, basePath);
192
201
  this.#actions = actions;
193
202
  this.#commands = commands;
203
+ this.#getPromptSuggestion = getPromptSuggestion;
194
204
  }
195
205
 
196
206
  async getSuggestions(
@@ -309,6 +319,11 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
309
319
  }
310
320
 
311
321
  getInlineHint(lines: string[], cursorLine: number, cursorCol: number): string | null {
322
+ // An empty composer renders the pending prompt suggestion as ghost text.
323
+ if (lines.every(line => line === "")) {
324
+ const suggestion = this.#getPromptSuggestion?.();
325
+ if (suggestion) return suggestion;
326
+ }
312
327
  return this.#baseProvider.getInlineHint?.(lines, cursorLine, cursorCol) ?? null;
313
328
  }
314
329
  trySyncSlashCompletion(textBeforeCursor: string): { items: AutocompleteItem[]; prefix: string } | null {
@@ -382,12 +397,11 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
382
397
  export function createPromptActionAutocompleteProvider(
383
398
  options: PromptActionAutocompleteOptions,
384
399
  ): PromptActionAutocompleteProvider {
385
- const editorKeybindings = getKeybindings();
386
400
  const actions: PromptActionDefinition[] = [
387
401
  {
388
402
  id: "new-session",
389
403
  label: "Start new session",
390
- description: formatKeyHints(options.keybindings.getKeys("app.session.new")) || "/new",
404
+ description: options.keybindings.getDisplayString("app.session.new"),
391
405
  keywords: ["new", "session", "fresh", "clear", "start", "conversation"],
392
406
  execute: options.newSession,
393
407
  },
@@ -401,21 +415,21 @@ export function createPromptActionAutocompleteProvider(
401
415
  {
402
416
  id: "copy-line",
403
417
  label: "Copy current line",
404
- description: formatKeyHints(options.keybindings.getKeys("app.clipboard.copyLine")),
418
+ description: options.keybindings.getDisplayString("app.clipboard.copyLine"),
405
419
  keywords: ["copy", "line", "clipboard", "current"],
406
420
  execute: options.copyCurrentLine,
407
421
  },
408
422
  {
409
423
  id: "copy-prompt",
410
424
  label: "Copy whole prompt",
411
- description: formatKeyHints(options.keybindings.getKeys("app.clipboard.copyPrompt")),
425
+ description: options.keybindings.getDisplayString("app.clipboard.copyPrompt"),
412
426
  keywords: ["copy", "prompt", "clipboard", "message"],
413
427
  execute: options.copyPrompt,
414
428
  },
415
429
  {
416
430
  id: "paste-image",
417
431
  label: "Paste image from clipboard",
418
- description: formatKeyHints(options.keybindings.getKeys("app.clipboard.pasteImage")),
432
+ description: options.keybindings.getDisplayString("app.clipboard.pasteImage"),
419
433
  keywords: ["paste", "image", "clipboard", "screenshot", "attach", "vision"],
420
434
  execute: options.pasteImage,
421
435
  },
@@ -429,7 +443,7 @@ export function createPromptActionAutocompleteProvider(
429
443
  {
430
444
  id: "undo",
431
445
  label: "Undo",
432
- description: formatKeyHints(editorKeybindings.getKeys("tui.editor.undo")),
446
+ description: options.keybindings.getDisplayString("tui.editor.undo"),
433
447
  keywords: ["undo", "revert", "edit", "history"],
434
448
  execute: options.undo,
435
449
  },
@@ -450,18 +464,23 @@ export function createPromptActionAutocompleteProvider(
450
464
  {
451
465
  id: "cursor-line-start",
452
466
  label: "Move cursor to beginning of line",
453
- description: formatKeyHints(editorKeybindings.getKeys("tui.editor.cursorLineStart")),
467
+ description: options.keybindings.getDisplayString("tui.editor.cursorLineStart"),
454
468
  keywords: ["move", "cursor", "line", "start", "beginning", "home"],
455
469
  execute: options.moveCursorToLineStart,
456
470
  },
457
471
  {
458
472
  id: "cursor-line-end",
459
473
  label: "Move cursor to end of line",
460
- description: formatKeyHints(editorKeybindings.getKeys("tui.editor.cursorLineEnd")),
474
+ description: options.keybindings.getDisplayString("tui.editor.cursorLineEnd"),
461
475
  keywords: ["move", "cursor", "line", "end"],
462
476
  execute: options.moveCursorToLineEnd,
463
477
  },
464
478
  ];
465
479
 
466
- return new PromptActionAutocompleteProvider(options.commands, options.basePath, actions);
480
+ return new PromptActionAutocompleteProvider(
481
+ options.commands,
482
+ options.basePath,
483
+ actions,
484
+ options.getPromptSuggestion,
485
+ );
467
486
  }
@@ -0,0 +1,96 @@
1
+ import { logger } from "@sayknow-cli/utils";
2
+ import { generatePromptSuggestion } from "../utils/prompt-suggestion";
3
+ import type { InteractiveModeContext } from "./types";
4
+
5
+ /**
6
+ * Owns the ghost-text next-prompt prediction lifecycle for interactive mode.
7
+ *
8
+ * After each agent turn ends (and only when `promptSuggestions` is enabled
9
+ * and the composer is empty), a smol-model prediction of the user's next
10
+ * prompt is generated in the background. The result renders as dim ghost
11
+ * text in the empty composer via the autocomplete provider's inline hint;
12
+ * Tab accepts it, typing dismisses it, and a new turn clears it.
13
+ */
14
+ export class PromptSuggestionController {
15
+ #ctx: InteractiveModeContext;
16
+ #current: string | null = null;
17
+ /** Monotonic token; bumping it invalidates any in-flight generation. */
18
+ #generation = 0;
19
+
20
+ constructor(ctx: InteractiveModeContext) {
21
+ this.#ctx = ctx;
22
+ }
23
+
24
+ /** Current suggestion to render as ghost text, or null. */
25
+ get current(): string | null {
26
+ return this.#current;
27
+ }
28
+
29
+ /** Drop the current suggestion and invalidate any in-flight generation. */
30
+ clear(): void {
31
+ this.#generation++;
32
+ if (this.#current === null) return;
33
+ this.#current = null;
34
+ this.#ctx.ui.requestRender();
35
+ }
36
+
37
+ /** A new agent turn started; any pending suggestion is stale. */
38
+ onAgentStart(): void {
39
+ this.clear();
40
+ }
41
+
42
+ /**
43
+ * Editor content changed. A non-empty composer means the user is writing
44
+ * their own prompt: dismiss the suggestion and cancel pending generation.
45
+ */
46
+ notifyEditorChanged(text: string): void {
47
+ if (text.length > 0) this.clear();
48
+ }
49
+
50
+ /**
51
+ * Agent turn ended; kick off background generation when the feature is
52
+ * enabled and the composer is idle and empty.
53
+ */
54
+ onAgentEnd(): void {
55
+ if (!this.#ctx.settings.get("promptSuggestions")) return;
56
+ if (this.#ctx.shutdownRequested) return;
57
+ if (this.#ctx.session.isStreaming) return;
58
+ if (this.#ctx.editor.getText().trim() !== "") return;
59
+
60
+ const generation = ++this.#generation;
61
+ void generatePromptSuggestion(
62
+ this.#ctx.session.messages,
63
+ this.#ctx.session.modelRegistry,
64
+ this.#ctx.settings,
65
+ this.#ctx.session.sessionId,
66
+ this.#ctx.session.model,
67
+ provider => this.#ctx.session.agent.metadataForProvider(provider),
68
+ )
69
+ .then(suggestion => {
70
+ if (generation !== this.#generation) return;
71
+ if (!suggestion) return;
72
+ // The world may have moved on while the model was thinking.
73
+ if (this.#ctx.session.isStreaming) return;
74
+ if (this.#ctx.editor.getText().trim() !== "") return;
75
+ this.#current = suggestion;
76
+ this.#ctx.ui.requestRender();
77
+ })
78
+ .catch(error => {
79
+ logger.debug("prompt-suggestion: generation failed", { error: String(error) });
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Tab handler for the composer: accept the suggestion when the composer
85
+ * is empty. Returns true when Tab was consumed.
86
+ */
87
+ tryAcceptOnTab(text: string): boolean {
88
+ const suggestion = this.#current;
89
+ if (!suggestion || text.trim() !== "") return false;
90
+ // setText fires onChange with non-empty text, which clears #current.
91
+ this.#ctx.editor.setText(suggestion);
92
+ this.clear();
93
+ this.#ctx.ui.requestRender();
94
+ return true;
95
+ }
96
+ }
@@ -115,6 +115,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
115
115
  compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
116
116
  clearContext: () => session.clearContext(),
117
117
  cycleModel: () => session.cycleModel(),
118
+ setModelProfile: name => session.activateModelProfileForControl(name),
118
119
  cycleThinkingLevel: () => session.cycleThinkingLevel(),
119
120
  setQueueMode: (kind, mode) => {
120
121
  if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
@@ -131,7 +132,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
131
132
  }
132
133
  return false;
133
134
  },
134
- invokeSkill: (name, args) => session.invokeSkill(name, args),
135
+ invokeSkill: (name, args, options) => session.invokeSkill(name, args, options),
135
136
  setPlanMode: on => session.setSdkPlanMode(on),
136
137
  operateGoal: (op, objective) => session.operateGoal(op, objective),
137
138
  getSkillState: () => session.skills.map(skill => ({ name: skill.name, description: skill.description })),
@@ -379,7 +380,24 @@ export async function initializeExtensions(session: AgentSession, options: Initi
379
380
  const disabled = [...(session.settings.get("disabledExtensions") ?? [])];
380
381
  const on = input.on === true;
381
382
  const next = on ? disabled.filter(value => value !== id) : [...new Set([...disabled, id])];
382
- session.settings.set("disabledExtensions", next);
383
+ if (!session.settings.canWriteDurableConfig()) {
384
+ throw Object.assign(
385
+ new Error(
386
+ "Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
387
+ ),
388
+ { code: "invalid_request" },
389
+ );
390
+ }
391
+ try {
392
+ session.settings.set("disabledExtensions", next);
393
+ } catch (error) {
394
+ if (!session.settings.canWriteDurableConfig()) {
395
+ throw Object.assign(new Error(error instanceof Error ? error.message : String(error)), {
396
+ code: "invalid_request",
397
+ });
398
+ }
399
+ throw error;
400
+ }
383
401
  return { changed: true, enabled: on };
384
402
  }
385
403
  case "session.delete":
@@ -219,9 +219,9 @@ export async function dispatchRpcCommand(
219
219
  case "set_host_tools": {
220
220
  const tools = normalizeHostToolDefinitions(command.tools);
221
221
  const rpcTools = hostToolRegistry.setTools(tools);
222
- // TODO(upstream-v0.11): AgentSession.refreshRpcHostTools was removed; the host-tool
223
- // registry already holds the updated set via hostToolRegistry.setTools above.
224
- void rpcTools;
222
+ // TODO(upstream-v0.11): AgentSession.refreshRpcHostTools was removed; the host-tool
223
+ // registry already holds the updated set via hostToolRegistry.setTools above.
224
+ void rpcTools;
225
225
  return rpcSuccess(id, "set_host_tools", { toolNames: tools.map(tool => tool.name) });
226
226
  }
227
227
 
@@ -95,7 +95,7 @@ export interface AskSelectedAckRecoveryParticipant {
95
95
 
96
96
  /** SDK-native surface for emitting a workflow gate and awaiting its answer. */
97
97
  export interface WorkflowGateEmitter {
98
- isUnattended(): boolean;
98
+ supportsRemoteGateAnswers(): boolean;
99
99
  emitGate(input: OpenGateInput): Promise<unknown>;
100
100
  onGateEmitted?(listener: (gate: WorkflowGate) => void): () => void;
101
101
  resolveGate?(response: WorkflowGateResponse): Promise<WorkflowGateResolution>;
@@ -167,7 +167,7 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
167
167
  });
168
168
  }
169
169
 
170
- isUnattended(): boolean {
170
+ supportsRemoteGateAnswers(): boolean {
171
171
  return true;
172
172
  }
173
173
  setRuntimeTurnProvider(provider: (() => string | undefined) | null): void {
@@ -32,6 +32,7 @@ import type { ToolExecutionHandle } from "./components/tool-execution";
32
32
  import type { StatusLineComponent } from "./components/tool-status-header";
33
33
  import type { IrcObservationLedger } from "./irc-observation-ledger";
34
34
  import type { OAuthManualInputManager } from "./oauth-manual-input";
35
+ import type { PromptSuggestionController } from "./prompt-suggestion-controller";
35
36
  import type { Theme } from "./theme/theme";
36
37
  import type { ParsedIrcMessage } from "./utils/irc-message";
37
38
 
@@ -156,6 +157,8 @@ export interface InteractiveModeContext {
156
157
  skillCommands: Map<string, Skill>;
157
158
  oauthManualInput: OAuthManualInputManager;
158
159
  todoPhases: TodoPhase[];
160
+ /** Ghost-text next-prompt prediction; absent in ACP/lightweight test contexts. */
161
+ promptSuggestion?: PromptSuggestionController;
159
162
 
160
163
  // Lifecycle
161
164
  init(): Promise<void>;
@@ -245,6 +248,8 @@ export interface InteractiveModeContext {
245
248
  getAssistantViewportAnchorId?(message: AssistantMessage): string;
246
249
  findLastAssistantMessage(): AssistantMessage | undefined;
247
250
  extractAssistantText(message: AssistantMessage): string;
251
+ /** Records one semantic visible-transcript mutation for the sticky viewport. */
252
+ recordVisibleTranscriptMutation?(): void;
248
253
  updateEditorTopBorder(): void;
249
254
  updateEditorBorderColor(): void;
250
255
  rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void;
@@ -338,6 +343,7 @@ export interface InteractiveModeContext {
338
343
  handleBackgroundCommand(): void;
339
344
  handleImagePaste(): Promise<boolean>;
340
345
  handleBtwCommand(question: string): Promise<void>;
346
+ handleBtwFollowUp(question: string): Promise<"accepted" | "busy" | "closed" | "rejected">;
341
347
  hasActiveBtw(): boolean;
342
348
  handleBtwEscape(): boolean;
343
349
  cycleThinkingLevel(): void;
@@ -1,60 +1,88 @@
1
+ import type { Keybinding } from "@sayknow-cli/tui";
1
2
  import type { AppKeybinding, KeybindingsManager } from "../../config/keybindings";
2
3
 
3
4
  export interface HotkeysMarkdownBindings {
4
- keybindings: Pick<KeybindingsManager, "getDisplayString">;
5
+ keybindings: Pick<KeybindingsManager, "formatAccessibleKeyHint" | "getAccessibleDisplayString">;
6
+ }
7
+
8
+ function key(bindings: HotkeysMarkdownBindings, action: Keybinding): string {
9
+ return bindings.keybindings.getAccessibleDisplayString(action) || "Disabled";
5
10
  }
6
11
 
7
12
  function appKey(bindings: HotkeysMarkdownBindings, action: AppKeybinding): string {
8
- return bindings.keybindings.getDisplayString(action) || "Disabled";
13
+ return key(bindings, action);
14
+ }
15
+
16
+ export function formatHotkeyMarkdownCode(label: string, escapeTablePipes = true): string {
17
+ const escaped = escapeTablePipes ? label.replace(/\|/g, "\\|") : label;
18
+ let longestBacktickRun = 0;
19
+ for (const match of escaped.matchAll(/`+/g)) {
20
+ longestBacktickRun = Math.max(longestBacktickRun, match[0].length);
21
+ }
22
+ const fence = "`".repeat(longestBacktickRun + 1);
23
+ const padding = escaped.startsWith("`") || escaped.endsWith("`") ? " " : "";
24
+ return `${fence}${padding}${escaped}${padding}${fence}`;
9
25
  }
10
26
 
11
27
  export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string {
28
+ const displayKey = (action: Keybinding): string => formatHotkeyMarkdownCode(key(bindings, action));
29
+ const displayAppKey = (action: AppKeybinding): string => formatHotkeyMarkdownCode(appKey(bindings, action));
30
+ const displayKeys = (...labels: string[]): string => formatHotkeyMarkdownCode(labels.join("/"));
31
+ const formatFixedKey = (key: string): string => bindings.keybindings.formatAccessibleKeyHint(key);
32
+ const displayKeyWithFixed = (action: Keybinding, fixedKey: string): string => {
33
+ const configured = key(bindings, action);
34
+ const fixed = formatFixedKey(fixedKey);
35
+ const labels = configured.split("/");
36
+ if (!labels.includes(fixed)) labels.push(fixed);
37
+ return displayKeys(...labels);
38
+ };
39
+
12
40
  return [
13
41
  "**Navigation**",
14
42
  "| Key | Action |",
15
43
  "|-----|--------|",
16
- "| `Arrow keys` | Move cursor / browse history (Up when empty) |",
17
- "| `Option+Left/Right` | Move by word |",
18
- "| `Ctrl+A` / `Home` / `Cmd+Left` | Start of line |",
19
- "| `Ctrl+E` / `End` / `Cmd+Right` | End of line |",
44
+ `| ${displayKeys(key(bindings, "tui.editor.cursorUp"), key(bindings, "tui.editor.cursorDown"))} | Move cursor / browse history (Up when empty) |`,
45
+ `| ${displayKeys(key(bindings, "tui.editor.cursorWordLeft"), key(bindings, "tui.editor.cursorWordRight"))} | Move by word |`,
46
+ `| ${displayKeyWithFixed("tui.editor.cursorLineStart", "ctrl+a")} | Start of line |`,
47
+ `| ${displayKeyWithFixed("tui.editor.cursorLineEnd", "ctrl+e")} | End of line |`,
20
48
  "",
21
49
  "**Editing**",
22
50
  "| Key | Action |",
23
51
  "|-----|--------|",
24
- "| `Enter` | Send / queue while busy |",
25
- `| \`${appKey(bindings, "app.message.queue")}\` | Queue message for next turn |`,
26
- `| \`${appKey(bindings, "app.message.dequeue")}\` | Select queued message to edit |`,
27
- "| `Shift+Enter` / `Ctrl+J` | New line |",
28
- "| `Ctrl+W` / `Option+Backspace` | Delete word backwards |",
29
- "| `Ctrl+U` | Delete to start of line |",
30
- "| `Ctrl+K` | Delete to end of line |",
31
- `| \`${appKey(bindings, "app.clipboard.copyLine")}\` | Copy current line |`,
32
- `| \`${appKey(bindings, "app.clipboard.copyPrompt")}\` | Copy whole prompt |`,
52
+ `| ${displayKey("tui.input.submit")} | Send / queue while busy |`,
53
+ `| ${displayAppKey("app.message.queue")} | Queue message for next turn |`,
54
+ `| ${displayAppKey("app.message.dequeue")} | Select queued message to edit |`,
55
+ `| ${displayKeys(key(bindings, "tui.input.newLine"), formatFixedKey("ctrl+j"))} | New line |`,
56
+ `| ${displayKeys(formatFixedKey("ctrl+w"), formatFixedKey("alt+backspace"))} | Delete word backwards |`,
57
+ `| ${displayKeys(formatFixedKey("ctrl+u"))} | Delete to start of line |`,
58
+ `| ${displayKeys(formatFixedKey("ctrl+k"))} | Delete to end of line |`,
59
+ `| ${displayAppKey("app.clipboard.copyLine")} | Copy current line |`,
60
+ `| ${displayAppKey("app.clipboard.copyPrompt")} | Copy whole prompt |`,
33
61
  "",
34
62
  "**Other**",
35
63
  "| Key | Action |",
36
64
  "|-----|--------|",
37
- "| `Tab` | Path completion / accept autocomplete |",
38
- `| \`${appKey(bindings, "app.interrupt")}\` | Cancel autocomplete / interrupt active work |`,
39
- `| \`${appKey(bindings, "app.clear")}\` | Clear editor (first) / exit (second) |`,
40
- `| \`${appKey(bindings, "app.exit")}\` | Exit (when editor is empty) |`,
41
- `| \`${appKey(bindings, "app.suspend")}\` | Suspend to background |`,
42
- `| \`${appKey(bindings, "app.commandPalette.open")}\` | Open command palette |`,
43
- `| \`${appKey(bindings, "app.session.new")}\` | Start a fresh session |`,
44
- `| \`${appKey(bindings, "app.thinking.cycle")}\` | Cycle thinking level |`,
45
- `| \`${appKey(bindings, "app.model.cycleForward")}\` | Cycle configured model roles |`,
46
- `| \`${appKey(bindings, "app.model.cycleBackward")}\` | Cycle configured model roles temporarily |`,
47
- `| \`${appKey(bindings, "app.model.selectTemporary")}\` | Select model (temporary) |`,
48
- `| \`${appKey(bindings, "app.model.select")}\` | Select default model |`,
49
- `| \`${appKey(bindings, "app.plan.toggle")}\` | Toggle plan mode |`,
50
- `| \`${appKey(bindings, "app.history.search")}\` | Search prompt history |`,
51
- `| \`${appKey(bindings, "app.tools.expand")}\` | Toggle tool output expansion |`,
52
- `| \`${appKey(bindings, "app.tool.backgroundFold")}\` twice | Fold supported foreground bash into a background job |`,
53
- `| \`${appKey(bindings, "app.thinking.toggle")}\` | Toggle thinking block visibility |`,
54
- `| \`${appKey(bindings, "app.editor.external")}\` | Edit message in external editor |`,
55
- `| \`${appKey(bindings, "app.clipboard.pasteImage")}\` | Paste image from clipboard |`,
56
- `| \`${appKey(bindings, "app.stt.toggle")}\` | Toggle speech-to-text recording |`,
57
- `| \`${appKey(bindings, "app.irc.sidebar.toggle")}\` | Toggle IRC sidebar |`,
65
+ `| ${displayKey("tui.input.tab")} | Path completion / accept autocomplete |`,
66
+ `| ${displayAppKey("app.interrupt")} | Cancel autocomplete / interrupt active work |`,
67
+ `| ${displayAppKey("app.clear")} | Clear editor (first) / exit (second) |`,
68
+ `| ${displayAppKey("app.exit")} | Exit (when editor is empty) |`,
69
+ `| ${displayAppKey("app.suspend")} | Suspend to background |`,
70
+ `| ${displayAppKey("app.commandPalette.open")} | Open command palette |`,
71
+ `| ${displayAppKey("app.session.new")} | Start a fresh session |`,
72
+ `| ${displayAppKey("app.thinking.cycle")} | Cycle thinking level |`,
73
+ `| ${displayAppKey("app.model.cycleForward")} | Cycle configured model roles |`,
74
+ `| ${displayAppKey("app.model.cycleBackward")} | Cycle configured model roles temporarily |`,
75
+ `| ${displayAppKey("app.model.selectTemporary")} | Select model (temporary) |`,
76
+ `| ${displayAppKey("app.model.select")} | Select default model |`,
77
+ `| ${displayAppKey("app.plan.toggle")} | Toggle plan mode |`,
78
+ `| ${displayAppKey("app.history.search")} | Search prompt history |`,
79
+ `| ${displayAppKey("app.tools.expand")} | Toggle tool output expansion |`,
80
+ `| ${displayAppKey("app.tool.backgroundFold")} twice | Fold supported foreground bash into a background job |`,
81
+ `| ${displayAppKey("app.thinking.toggle")} | Toggle thinking block visibility |`,
82
+ `| ${displayAppKey("app.editor.external")} | Edit message in external editor |`,
83
+ `| ${displayAppKey("app.clipboard.pasteImage")} | Paste image from clipboard |`,
84
+ `| ${displayAppKey("app.stt.toggle")} | Toggle speech-to-text recording |`,
85
+ `| ${displayAppKey("app.irc.sidebar.toggle")} | Toggle IRC sidebar |`,
58
86
  "| `?` | Show help / shortcuts |",
59
87
  "| `#` | Prompt actions (command-palette style actions) |",
60
88
  "| `/` | Slash commands (try `/help` or `/new`) |",