@sayknow-cli/coding-agent 0.4.7 → 0.5.1

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 +122 -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,6 +4,7 @@
4
4
  * Handles /mcp subcommands for managing MCP servers.
5
5
  */
6
6
  import * as path from "node:path";
7
+ import { resolveMCPOAuthResourceOrigin, resolveMCPOAuthTokenEndpoint } from "@sayknow-cli/ai";
7
8
  import { Spacer, Text } from "@sayknow-cli/tui";
8
9
  import { getMCPConfigPath, getProjectDir } from "@sayknow-cli/utils";
9
10
  import type { SourceMeta } from "../../capability/types";
@@ -61,6 +62,7 @@ function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string)
61
62
  */
62
63
  interface OAuthFlowResult {
63
64
  credentialId: string;
65
+ tokenEndpoint: string;
64
66
  clientId?: string;
65
67
  clientSecret?: string;
66
68
  }
@@ -423,6 +425,7 @@ export class MCPCommandController {
423
425
  try {
424
426
  const oauthClientSecret = finalConfig.oauth?.clientSecret ?? "";
425
427
  const oauthResult = await this.#handleOAuthFlow(
428
+ finalConfig.url,
426
429
  oauth.authorizationUrl,
427
430
  oauth.tokenUrl,
428
431
  oauth.clientId ?? finalConfig.oauth?.clientId ?? "",
@@ -439,7 +442,7 @@ export class MCPCommandController {
439
442
  auth: {
440
443
  type: "oauth",
441
444
  credentialId: oauthResult.credentialId,
442
- tokenUrl: oauth.tokenUrl,
445
+ tokenUrl: oauthResult.tokenEndpoint,
443
446
  clientId: persistedClientId,
444
447
  clientSecret: persistedClientSecret,
445
448
  },
@@ -480,8 +483,15 @@ export class MCPCommandController {
480
483
  done();
481
484
  this.#handleWizardCancel();
482
485
  },
483
- async (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string) => {
484
- return await this.#handleOAuthFlow(authUrl, tokenUrl, clientId, clientSecret, scopes);
486
+ async (
487
+ endpointUrl: string,
488
+ authUrl: string,
489
+ tokenUrl: string,
490
+ clientId: string,
491
+ clientSecret: string,
492
+ scopes: string,
493
+ ) => {
494
+ return await this.#handleOAuthFlow(endpointUrl, authUrl, tokenUrl, clientId, clientSecret, scopes);
485
495
  },
486
496
  async (config: MCPServerConfig) => {
487
497
  return await this.#handleTestConnection(config);
@@ -503,6 +513,7 @@ export class MCPCommandController {
503
513
  * Handle OAuth authentication flow for MCP server
504
514
  */
505
515
  async #handleOAuthFlow(
516
+ endpointUrl: string,
506
517
  authUrl: string,
507
518
  tokenUrl: string,
508
519
  clientId: string,
@@ -514,11 +525,13 @@ export class MCPCommandController {
514
525
  ): Promise<OAuthFlowResult> {
515
526
  const authStorage = this.ctx.session.modelRegistry.authStorage;
516
527
  let parsedAuthUrl: URL;
528
+ const resourceOrigin = resolveMCPOAuthResourceOrigin(endpointUrl);
529
+ const tokenEndpoint = resolveMCPOAuthTokenEndpoint(tokenUrl);
530
+ if (!resourceOrigin || !tokenEndpoint) throw new Error("Invalid MCP OAuth configuration.");
517
531
 
518
532
  // Validate OAuth URLs
519
533
  try {
520
534
  parsedAuthUrl = new URL(authUrl);
521
- new URL(tokenUrl);
522
535
  } catch (_error) {
523
536
  throw new Error(
524
537
  `Invalid OAuth URLs. Please check:\n Authorization URL: ${authUrl}\n Token URL: ${tokenUrl}`,
@@ -533,7 +546,7 @@ export class MCPCommandController {
533
546
  const flow = new MCPOAuthFlow(
534
547
  {
535
548
  authorizationUrl: authUrl,
536
- tokenUrl: tokenUrl,
549
+ tokenUrl: tokenEndpoint,
537
550
  clientId: resolvedClientId,
538
551
  clientSecret: resolvedClientSecret,
539
552
  scopes: scopes || undefined,
@@ -618,6 +631,10 @@ export class MCPCommandController {
618
631
  const oauthCredential: OAuthCredential = {
619
632
  type: "oauth",
620
633
  ...credentials,
634
+ mcpBinding: {
635
+ resourceOrigin,
636
+ tokenEndpoint,
637
+ },
621
638
  };
622
639
 
623
640
  // Store under a synthetic provider name
@@ -625,6 +642,7 @@ export class MCPCommandController {
625
642
 
626
643
  return {
627
644
  credentialId,
645
+ tokenEndpoint,
628
646
  clientId: flow.resolvedClientId,
629
647
  clientSecret: flow.registeredClientSecret,
630
648
  };
@@ -1328,6 +1346,10 @@ export class MCPCommandController {
1328
1346
  this.ctx.showError(`Server "${name}" is disabled. Run /mcp enable ${name} first.`);
1329
1347
  return;
1330
1348
  }
1349
+ if (found.config.type !== "http" && found.config.type !== "sse") {
1350
+ this.ctx.showError(`Server "${name}" does not use a remote MCP transport.`);
1351
+ return;
1352
+ }
1331
1353
 
1332
1354
  const currentAuth = (found.config as MCPServerConfig & { auth?: MCPAuthConfig }).auth;
1333
1355
  if (currentAuth?.type === "oauth") {
@@ -1341,6 +1363,7 @@ export class MCPCommandController {
1341
1363
  this.#showMessage(["", theme.fg("muted", `Reauthorizing "${name}"...`), ""].join("\n"));
1342
1364
 
1343
1365
  const oauthResult = await this.#handleOAuthFlow(
1366
+ found.config.url,
1344
1367
  oauth.authorizationUrl,
1345
1368
  oauth.tokenUrl,
1346
1369
  oauth.clientId ?? found.config.oauth?.clientId ?? "",
@@ -1359,7 +1382,7 @@ export class MCPCommandController {
1359
1382
  auth: {
1360
1383
  type: "oauth",
1361
1384
  credentialId: oauthResult.credentialId,
1362
- tokenUrl: oauth.tokenUrl,
1385
+ tokenUrl: oauthResult.tokenEndpoint,
1363
1386
  clientId: persistedClientId,
1364
1387
  clientSecret: persistedClientSecret,
1365
1388
  },
@@ -2057,7 +2057,10 @@ export class SelectorController {
2057
2057
  this.#clearTransientSessionUi();
2058
2058
  const migrationPolicy =
2059
2059
  this.ctx.settings?.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
2060
- const writableSessionPath = await SessionManager.prepareManagedCandidateForWrite(sessionPath, migrationPolicy);
2060
+ const writableSessionPath = await this.ctx.sessionManager.prepareManagedCandidateForWrite(
2061
+ sessionPath,
2062
+ migrationPolicy,
2063
+ );
2061
2064
 
2062
2065
  // Switch session via AgentSession (emits hook and tool session events)
2063
2066
  if (!(await this.ctx.session.switchSession(writableSessionPath))) return;
@@ -16,7 +16,14 @@ import {
16
16
  import { APP_NAME, adjustHsv, getProjectDir, logger, postmortem } from "@sayknow-cli/utils";
17
17
  import chalk from "chalk";
18
18
  import { AsyncJobManager } from "../async";
19
- import { type AppKeybinding, KeybindingsManager } from "../config/keybindings";
19
+ import {
20
+ type AppKeybinding,
21
+ defaultMessageQueueKeysForPlatform,
22
+ formatKeyHint,
23
+ formatKeyHints,
24
+ KeybindingsManager,
25
+ type KeyDisplayContext,
26
+ } from "../config/keybindings";
20
27
  import { isSettingsInitialized, type Settings, settings } from "../config/settings";
21
28
  import { DEFAULT_SKC_DEFINITION_NAMES } from "../defaults/skc-defaults";
22
29
  import type {
@@ -41,6 +48,7 @@ import type { SessionContext, SessionManager } from "../session/session-manager"
41
48
  import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
42
49
  import type { LspStartupServerInfo } from "../tools";
43
50
  import { formatPhaseDisplayName } from "../tools/todo-write";
51
+ import { copyToClipboard } from "../utils/clipboard";
44
52
  import type { EventBus } from "../utils/event-bus";
45
53
  import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
46
54
  import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
@@ -52,7 +60,7 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
52
60
  import type { HookEditorComponent } from "./components/hook-editor";
53
61
  import type { HookInputComponent } from "./components/hook-input";
54
62
  import type { HookSelectorComponent } from "./components/hook-selector";
55
- import { IrcSplitViewComponent } from "./components/irc-sidebar";
63
+ import { computeIrcSplitWidths, getIrcSidebarSemanticToken, IrcSplitViewComponent } from "./components/irc-sidebar";
56
64
  import {
57
65
  getPetUnavailableWarning,
58
66
  isPetAvailable,
@@ -64,6 +72,7 @@ import type { ToolExecutionHandle } from "./components/tool-execution";
64
72
  import { StatusLineComponent } from "./components/tool-status-header";
65
73
  import { composeToolText } from "./components/tool-transcript-format";
66
74
  import {
75
+ type RecentSession,
67
76
  WelcomeComponent,
68
77
  type WelcomeLogoMode,
69
78
  type LspServerInfo as WelcomeLspServerInfo,
@@ -83,6 +92,7 @@ import { TodoCommandController } from "./controllers/todo-command-controller";
83
92
  import { IrcObservationLedger } from "./irc-observation-ledger";
84
93
  import { JobsObserver } from "./jobs-observer";
85
94
  import { OAuthManualInputManager } from "./oauth-manual-input";
95
+ import { PromptSuggestionController } from "./prompt-suggestion-controller";
86
96
  import { SessionObserverRegistry } from "./session-observer-registry";
87
97
  import { interruptHint } from "./shared";
88
98
  import { shouldShowExtensionCommand } from "./slash-command-visibility";
@@ -105,9 +115,60 @@ import {
105
115
  import type { ParsedIrcMessage } from "./utils/irc-message";
106
116
  import { addChatChild, prepareTranscriptRebuild, UiHelpers } from "./utils/ui-helpers";
107
117
 
108
- const COMPOSER_NEWLINE_HINT = process.platform === "win32" ? "Alt+Enter/Ctrl+J" : "Shift+Enter/Ctrl+J";
109
- export const DEFAULT_COMPOSER_PLACEHOLDER = `Type your message... ${COMPOSER_NEWLINE_HINT}: New line · Ctrl+C: Clear · Ctrl+R: Search history · Shift+Tab: Reasoning`;
118
+ function buildComposerPlaceholder(
119
+ keybindings: Pick<KeybindingsManager, "getDisplayString">,
120
+ context: KeyDisplayContext,
121
+ options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
122
+ ): string {
123
+ const parts: string[] = [];
124
+ const submitKey = options.busy ? keybindings.getDisplayString("tui.input.submit", context) : "";
125
+ if (submitKey) {
126
+ const submitAction = options.busyPromptMode === "steer" ? "Steer" : "Queue";
127
+ parts.push(`${submitKey}: ${submitAction}`);
128
+ }
129
+
130
+ const queueKey = keybindings.getDisplayString("app.message.queue", context);
131
+ const submitQueues = options.busy && options.busyPromptMode === "queue" && submitKey;
132
+ if (queueKey && !submitQueues) parts.push(`${queueKey}: ${options.busy ? "Queue" : "Queue (busy)"}`);
133
+
134
+ const actionHints = [
135
+ ["app.thinking.cycle", "Thinking"],
136
+ ["app.model.select", "Model"],
137
+ ["app.history.search", "History"],
138
+ ] as const;
139
+ for (const [id, label] of actionHints) {
140
+ const key = keybindings.getDisplayString(id, context);
141
+ if (key) parts.push(`${key}: ${label}`);
142
+ }
143
+
144
+ const newlineKeys = context.platform === "win32" ? ["alt+enter", "ctrl+j"] : ["shift+enter", "ctrl+j"];
145
+ parts.push(`${formatKeyHints(newlineKeys, context)}: New line`, `${formatKeyHint("ctrl+c", context)}: Clear`);
146
+ return `Type your message... ${parts.join(" · ")}`;
147
+ }
148
+
149
+ export function getDefaultComposerPlaceholder(
150
+ context: KeyDisplayContext = { platform: process.platform },
151
+ keybindings?: Pick<KeybindingsManager, "getDisplayString">,
152
+ ): string {
153
+ const effectiveKeybindings =
154
+ keybindings ??
155
+ KeybindingsManager.inMemory({
156
+ "app.message.queue": defaultMessageQueueKeysForPlatform(context.platform),
157
+ });
158
+ return buildComposerPlaceholder(effectiveKeybindings, context, { busy: false, busyPromptMode: "steer" });
159
+ }
160
+
161
+ export const DEFAULT_COMPOSER_PLACEHOLDER = getDefaultComposerPlaceholder();
162
+
163
+ export function getComposerPlaceholder(
164
+ keybindings: Pick<KeybindingsManager, "getDisplayString">,
165
+ context: KeyDisplayContext,
166
+ options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
167
+ ): string {
168
+ return buildComposerPlaceholder(keybindings, context, options);
169
+ }
110
170
  const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
171
+ const COMPOSER_RIGHT_GUTTER_WIDTH = 1;
111
172
 
112
173
  const IRC_SIDEBAR_TOGGLE_SHADOWING_ACTIONS: readonly AppKeybinding[] = [
113
174
  "app.plan.toggle",
@@ -128,23 +189,6 @@ export function getWelcomeTranscriptReservedRows(chatContainer: Container, width
128
189
  ? 0
129
190
  : chatContainer.render(width).length;
130
191
  }
131
- const FRIENDLY_KEY_PARTS: Record<string, string> = {
132
- alt: "Alt",
133
- cmd: "Cmd",
134
- command: "Cmd",
135
- ctrl: "Ctrl",
136
- enter: "Enter",
137
- meta: process.platform === "darwin" ? "Command" : "Meta",
138
- option: "Option",
139
- shift: "Shift",
140
- };
141
-
142
- function formatShortcutForPlaceholder(key: string): string {
143
- return key
144
- .split("+")
145
- .map(part => FRIENDLY_KEY_PARTS[part.toLowerCase()] ?? (part.length === 1 ? part.toUpperCase() : part))
146
- .join("+");
147
- }
148
192
 
149
193
  const HINT_SHIMMER_PALETTE: ShimmerPalette = {
150
194
  low: "dim",
@@ -169,9 +213,9 @@ function configureDefaultComposerChrome(editor: CustomEditor): void {
169
213
  editor.setClosedBorderBox(true);
170
214
  editor.setPromptGutter(undefined);
171
215
  editor.setInputPrefix(getDefaultInputPrefix());
172
- editor.setPlaceholder(DEFAULT_COMPOSER_PLACEHOLDER);
216
+ editor.setPlaceholder(getDefaultComposerPlaceholder());
173
217
  editor.setPaddingX(1);
174
- editor.setRightGutterWidth(1);
218
+ editor.setRightGutterWidth(COMPOSER_RIGHT_GUTTER_WIDTH);
175
219
  editor.setTopBorder(undefined);
176
220
  }
177
221
 
@@ -264,6 +308,10 @@ export interface InteractiveModeOptions {
264
308
  initialMessages?: string[];
265
309
  }
266
310
 
311
+ export function selectShutdownDraft(editorText: string, hasActiveBtw: boolean): string {
312
+ return hasActiveBtw ? "" : editorText;
313
+ }
314
+
267
315
  export class InteractiveMode implements InteractiveModeContext {
268
316
  session: AgentSession;
269
317
  sessionManager: SessionManager;
@@ -336,6 +384,7 @@ export class InteractiveMode implements InteractiveModeContext {
336
384
  fileSlashCommands: Set<string> = new Set();
337
385
  skillCommands: Map<string, Skill> = new Map();
338
386
  oauthManualInput: OAuthManualInputManager = new OAuthManualInputManager();
387
+ promptSuggestion: PromptSuggestionController;
339
388
 
340
389
  #baseSlashCommands: SlashCommand[] = [];
341
390
  #resolvedSlashCommands: SlashCommand[] = [];
@@ -347,6 +396,7 @@ export class InteractiveMode implements InteractiveModeContext {
347
396
  #petUnavailableWarningDisposer?: () => void;
348
397
  readonly #version: string;
349
398
  readonly #changelogMarkdown: string | undefined;
399
+ readonly #keyDisplayContext: KeyDisplayContext;
350
400
 
351
401
  readonly lspServers: LspStartupServerInfo[] | undefined = undefined;
352
402
  mcpManager?: import("../runtime-mcp").MCPManager;
@@ -366,6 +416,9 @@ export class InteractiveMode implements InteractiveModeContext {
366
416
  #sttController: SttModeController | undefined;
367
417
  #resizeHandler?: () => void;
368
418
  #observerRegistry: SessionObserverRegistry;
419
+ #viewportOutputRevision = 0n;
420
+ #viewportOutputIdentity: string;
421
+
369
422
  #transcriptRegistry = new TranscriptItemRegistry();
370
423
 
371
424
  /** Direct controller capabilities for consumers that coordinate mode transitions. */
@@ -394,6 +447,7 @@ export class InteractiveMode implements InteractiveModeContext {
394
447
  lspServers: LspStartupServerInfo[] | undefined = undefined,
395
448
  mcpManager?: import("../runtime-mcp").MCPManager,
396
449
  eventBus?: EventBus,
450
+ keyDisplayContext: KeyDisplayContext = { platform: process.platform },
397
451
  ) {
398
452
  this.session = session;
399
453
  this.sessionManager = session.sessionManager;
@@ -432,6 +486,10 @@ export class InteractiveMode implements InteractiveModeContext {
432
486
  this.lspServers = lspServers;
433
487
  this.mcpManager = mcpManager;
434
488
  this.#eventBus = eventBus;
489
+ this.#keyDisplayContext = keyDisplayContext;
490
+ this.#viewportOutputIdentity = `session:${this.sessionManager.getSessionId()}`;
491
+
492
+ this.keybindings.setDisplayContext(this.#keyDisplayContext);
435
493
  const thisMode = this;
436
494
  this.#goalModeController = new GoalModeController({
437
495
  session: this.session,
@@ -503,6 +561,14 @@ export class InteractiveMode implements InteractiveModeContext {
503
561
 
504
562
  this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"), {
505
563
  enableMouse: settings.get("mouse.enabled"),
564
+ copySelection: async text => {
565
+ try {
566
+ await copyToClipboard(text);
567
+ this.showStatus("Selection copied to clipboard");
568
+ } catch (error) {
569
+ this.showError(`Failed to copy selection: ${error instanceof Error ? error.message : String(error)}`);
570
+ }
571
+ },
506
572
  });
507
573
  this.ui.setClearOnShrink(settings.get("clearOnShrink"));
508
574
  this.chatContainer = new Container();
@@ -513,6 +579,7 @@ export class InteractiveMode implements InteractiveModeContext {
513
579
  this.btwContainer = new Container();
514
580
  this.editor = new CustomEditor(getEditorTheme());
515
581
  configureDefaultComposerChrome(this.editor);
582
+ this.editor.setPlaceholder(this.#getComposerPlaceholder());
516
583
  this.editor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
517
584
  this.editor.setAutocompleteMaxVisible(settings.get("autocompleteMaxVisible"));
518
585
  this.editor.onAutocompleteCancel = () => {
@@ -540,7 +607,11 @@ export class InteractiveMode implements InteractiveModeContext {
540
607
  this.hookWidgetContainerBelow = new Container();
541
608
  this.editorContainer = new Container();
542
609
  this.editorContainer.addChild(this.editor);
543
- this.statusLine = new StatusLineComponent(session, { version: this.#version, focusDomain: "composer" });
610
+ this.statusLine = new StatusLineComponent(session, {
611
+ version: this.#version,
612
+ focusDomain: "composer",
613
+ keyDisplayContext: this.#keyDisplayContext,
614
+ });
544
615
  this.statusLine.setAutoCompactEnabled(session.autoCompactionEnabled);
545
616
 
546
617
  this.hideThinkingBlock = settings.get("hideThinkingBlock");
@@ -576,6 +647,7 @@ export class InteractiveMode implements InteractiveModeContext {
576
647
  this.#selectorController = new SelectorController(this);
577
648
  this.#inputController = new InputController(this);
578
649
  this.statusLine.setActionRegistry(this.#inputController.actionRegistry, () => this.keybindings);
650
+ this.promptSuggestion = new PromptSuggestionController(this);
579
651
  this.#observerRegistry = new SessionObserverRegistry();
580
652
  }
581
653
 
@@ -596,6 +668,7 @@ export class InteractiveMode implements InteractiveModeContext {
596
668
  if (this.isInitialized) return;
597
669
 
598
670
  this.keybindings = logger.time("InteractiveMode.init:keybindings", () => KeybindingsManager.create());
671
+ this.keybindings.setDisplayContext(this.#keyDisplayContext);
599
672
 
600
673
  // Register session manager flush for signal handlers (SIGINT, SIGTERM, SIGHUP)
601
674
  this.#cleanupUnsubscribe = postmortem.register("session-manager-flush", () => this.sessionManager.flush());
@@ -618,15 +691,9 @@ export class InteractiveMode implements InteractiveModeContext {
618
691
  const modelName = this.session.model?.name ?? "Unknown";
619
692
  const providerName = this.session.model?.provider ?? "Unknown";
620
693
 
621
- // Get recent sessions
622
- const recentSessions = await logger.time("InteractiveMode.init:recentSessions", () =>
623
- getRecentSessions(this.sessionManager.getSessionDir()).then(sessions =>
624
- sessions.map(s => ({
625
- name: s.name,
626
- timeAgo: s.timeAgo,
627
- })),
628
- ),
629
- );
694
+ // Session history is display-only. Never hold the editor and keyboard behind
695
+ // transcript I/O; populate the welcome trail after the interactive surface exists.
696
+ const recentSessions: RecentSession[] = [];
630
697
 
631
698
  const startupQuiet = settings.get("startup.quiet");
632
699
  const welcomeLogoMode = resolveWelcomeLogoMode(settings.get("startup.welcomeBannerMode"));
@@ -652,7 +719,9 @@ export class InteractiveMode implements InteractiveModeContext {
652
719
  getViewportRows: () => this.ui.terminal.rows,
653
720
  getReservedBottomRows: getWelcomeReservedBottomRows,
654
721
  changelogMarkdown: this.#changelogMarkdown,
722
+ rightGutterWidth: COMPOSER_RIGHT_GUTTER_WIDTH,
655
723
  collapseChangelog: settings.get("collapseChangelog"),
724
+ keyDisplayContext: this.#keyDisplayContext,
656
725
  },
657
726
  );
658
727
 
@@ -674,6 +743,11 @@ export class InteractiveMode implements InteractiveModeContext {
674
743
  this.ui.addChild(this.hookWidgetContainerBelow);
675
744
  this.ui.setBottomPinnedComponent(this.statusLine);
676
745
  this.ui.setFocus(this.editor);
746
+ this.ui.setViewportOutputSource({
747
+ identity: this.#viewportOutputIdentity,
748
+ revision: this.#viewportOutputRevision,
749
+ });
750
+
677
751
  this.petWidget?.dispose();
678
752
  this.petWidget = this.#createPetWidget(this.editor);
679
753
  const configuredPetMode = settings.get("pet.mode");
@@ -706,6 +780,12 @@ export class InteractiveMode implements InteractiveModeContext {
706
780
 
707
781
  this.#inputController.setupKeyHandlers();
708
782
  this.#inputController.setupEditorSubmitHandler();
783
+ this.editor.onViewportPageScroll = direction => {
784
+ this.ui.scrollViewportPages(direction);
785
+ };
786
+ this.editor.onViewportFollowLive = () => {
787
+ this.ui.followLiveViewport();
788
+ };
709
789
 
710
790
  // Wire observer registry to EventBus
711
791
  if (this.#eventBus) {
@@ -749,6 +829,31 @@ export class InteractiveMode implements InteractiveModeContext {
749
829
  if (this.settings.get("tasksPane.defaultVisible")) this.showTasksPane();
750
830
  this.#syncIrcSidebarAvailabilityFromSettings();
751
831
  this.ui.requestRender(true);
832
+ if (this.#welcomeComponent) {
833
+ const welcomeComponent = this.#welcomeComponent;
834
+ const recentSessionsTimer = setTimeout(() => {
835
+ void logger
836
+ .time("InteractiveMode.init:recentSessions", () =>
837
+ getRecentSessions(this.sessionManager.getSessionDir()),
838
+ )
839
+ .then(sessions => {
840
+ if (this.#welcomeComponent !== welcomeComponent) return;
841
+ welcomeComponent.setRecentSessions(
842
+ sessions.map(session => ({
843
+ name: session.name,
844
+ timeAgo: session.timeAgo,
845
+ })),
846
+ );
847
+ this.ui.requestRender();
848
+ })
849
+ .catch(error => {
850
+ logger.debug("Failed to load recent sessions for welcome screen", {
851
+ error: error instanceof Error ? error.message : String(error),
852
+ });
853
+ });
854
+ }, 0);
855
+ recentSessionsTimer.unref?.();
856
+ }
752
857
 
753
858
  // GitHub star reminder (interactive-only). Register the decline-driven
754
859
  // injection contributor and schedule the launch nudge after the first
@@ -802,7 +907,9 @@ export class InteractiveMode implements InteractiveModeContext {
802
907
  // Set up theme file watcher
803
908
  onThemeChange(() => {
804
909
  clearRenderCache();
910
+ this.#ircSplitView.invalidateTheme();
805
911
  configureDefaultComposerChrome(this.editor);
912
+ this.editor.setPlaceholder(this.#getComposerPlaceholder());
806
913
  this.ui.invalidate();
807
914
  this.updateEditorChrome();
808
915
  this.ui.requestRender();
@@ -1021,25 +1128,11 @@ export class InteractiveMode implements InteractiveModeContext {
1021
1128
  return this.session.isStreaming || this.session.isCompacting;
1022
1129
  }
1023
1130
 
1024
- #getFirstKeyForAction(action: AppKeybinding): string | undefined {
1025
- return this.keybindings.getKeys(action)[0];
1026
- }
1027
-
1028
- #getMessageQueueShortcut(): string | undefined {
1029
- const preferredAction: AppKeybinding =
1030
- process.platform === "darwin" ? "app.message.followUp" : "app.message.queue";
1031
- const fallbackAction: AppKeybinding =
1032
- process.platform === "darwin" ? "app.message.queue" : "app.message.followUp";
1033
- return this.#getFirstKeyForAction(preferredAction) ?? this.#getFirstKeyForAction(fallbackAction);
1034
- }
1035
-
1036
1131
  #getComposerPlaceholder(): string {
1037
- if (!this.#isPromptDeliveryBusy()) return DEFAULT_COMPOSER_PLACEHOLDER;
1038
- const enterAction = this.settings.get("busyPromptMode") === "steer" ? "Steer" : "Queue";
1039
- const parts = [`Enter: ${enterAction}`];
1040
- const queueKey = this.#getMessageQueueShortcut();
1041
- if (queueKey) parts.push(`${formatShortcutForPlaceholder(queueKey)}: Queue`);
1042
- return `${DEFAULT_COMPOSER_PLACEHOLDER} · ${parts.join(" · ")}`;
1132
+ return getComposerPlaceholder(this.keybindings, this.#keyDisplayContext, {
1133
+ busy: this.#isPromptDeliveryBusy(),
1134
+ busyPromptMode: this.settings.get("busyPromptMode"),
1135
+ });
1043
1136
  }
1044
1137
 
1045
1138
  #getWelcomeReservedRows(width: number): number {
@@ -1056,6 +1149,7 @@ export class InteractiveMode implements InteractiveModeContext {
1056
1149
  this.statusLine,
1057
1150
  this.hookWidgetContainerAbove,
1058
1151
  this.editorContainer,
1152
+ this.petFloorContainer,
1059
1153
  this.hookWidgetContainerBelow,
1060
1154
  ].reduce((rows, component) => rows + component.render(width).length, 0);
1061
1155
 
@@ -1123,8 +1217,22 @@ export class InteractiveMode implements InteractiveModeContext {
1123
1217
  this.ui.requestRender();
1124
1218
  return false;
1125
1219
  }
1220
+ if (!settings.canWriteDurableConfig()) {
1221
+ this.showError(
1222
+ "Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
1223
+ );
1224
+ return false;
1225
+ }
1226
+ try {
1227
+ settings.set("pet.mode", mode);
1228
+ } catch (error) {
1229
+ if (!settings.canWriteDurableConfig()) {
1230
+ this.showError(error instanceof Error ? error.message : String(error));
1231
+ return false;
1232
+ }
1233
+ throw error;
1234
+ }
1126
1235
  apply(mode);
1127
- settings.set("pet.mode", mode);
1128
1236
  this.ui.requestRender();
1129
1237
  return true;
1130
1238
  }
@@ -1322,19 +1430,20 @@ export class InteractiveMode implements InteractiveModeContext {
1322
1430
  if (this.#isShuttingDown) return;
1323
1431
  this.#isShuttingDown = true;
1324
1432
 
1325
- // Snapshot the editor before any teardown empties it. Persisting the draft
1326
- // here covers Ctrl+D shutdown with non-empty text; for /exit the editor is
1327
- // already cleared so saveDraft("") just removes any stale sidecar.
1328
- const draftText = this.editor.getText();
1433
+ // `/btw` owns the shared composer while its panel is open. Never persist a
1434
+ // side-chat draft or pending side-chat images into the main-session draft.
1435
+ const hadActiveBtw = this.#btwController.hasOpenPanel();
1436
+ const draftText = selectShutdownDraft(this.editor.getText(), hadActiveBtw);
1437
+ if (hadActiveBtw) this.pendingImages = [];
1438
+ this.#btwController.dispose();
1329
1439
 
1330
- // Flush pending session writes before shutdown
1440
+ // Flush pending session writes before shutdown.
1331
1441
  await this.sessionManager.flush();
1332
1442
  try {
1333
1443
  await this.sessionManager.saveDraft(draftText);
1334
1444
  } catch (err) {
1335
1445
  logger.warn("Failed to save session draft", { error: String(err) });
1336
1446
  }
1337
- this.#btwController.dispose();
1338
1447
 
1339
1448
  // Emit shutdown event to hooks
1340
1449
  this.session.setSdkPlanModeHandler(null);
@@ -1393,6 +1502,7 @@ export class InteractiveMode implements InteractiveModeContext {
1393
1502
  : new CustomEditor(getEditorTheme());
1394
1503
 
1395
1504
  configureDefaultComposerChrome(nextEditor);
1505
+ nextEditor.setPlaceholder(this.#getComposerPlaceholder());
1396
1506
  nextEditor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
1397
1507
  nextEditor.setAutocompleteMaxVisible(this.settings.get("autocompleteMaxVisible"));
1398
1508
  nextEditor.onAutocompleteCancel = () => {
@@ -1421,6 +1531,12 @@ export class InteractiveMode implements InteractiveModeContext {
1421
1531
 
1422
1532
  this.#inputController.setupKeyHandlers();
1423
1533
  this.#inputController.setupEditorSubmitHandler();
1534
+ this.editor.onViewportPageScroll = direction => {
1535
+ this.ui.scrollViewportPages(direction);
1536
+ };
1537
+ this.editor.onViewportFollowLive = () => {
1538
+ this.ui.followLiveViewport();
1539
+ };
1424
1540
 
1425
1541
  void this.refreshSlashCommandState().catch(error => {
1426
1542
  logger.warn("Failed to refresh slash command state for custom editor", { error: String(error) });
@@ -1571,6 +1687,21 @@ export class InteractiveMode implements InteractiveModeContext {
1571
1687
  return this.#uiHelpers.isKnownSlashCommand(text);
1572
1688
  }
1573
1689
 
1690
+ /** Advances the sticky-viewport source only for semantic transcript output. */
1691
+ recordVisibleTranscriptMutation(): void {
1692
+ const identity = `session:${this.sessionManager.getSessionId()}`;
1693
+ if (identity !== this.#viewportOutputIdentity) {
1694
+ this.#viewportOutputIdentity = identity;
1695
+ this.#viewportOutputRevision = 0n;
1696
+ } else {
1697
+ this.#viewportOutputRevision += 1n;
1698
+ }
1699
+ this.ui.setViewportOutputSource({
1700
+ identity: this.#viewportOutputIdentity,
1701
+ revision: this.#viewportOutputRevision,
1702
+ });
1703
+ }
1704
+
1574
1705
  addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[] {
1575
1706
  return this.#uiHelpers.addMessageToChat(message, options);
1576
1707
  }
@@ -2025,7 +2156,11 @@ export class InteractiveMode implements InteractiveModeContext {
2025
2156
  }
2026
2157
 
2027
2158
  hasActiveBtw(): boolean {
2028
- return this.#btwController.hasActiveRequest();
2159
+ return this.#btwController.hasOpenPanel();
2160
+ }
2161
+
2162
+ handleBtwFollowUp(question: string): Promise<"accepted" | "busy" | "closed" | "rejected"> {
2163
+ return this.#btwController.submitFollowUp(question);
2029
2164
  }
2030
2165
 
2031
2166
  handleBtwEscape(): boolean {
@@ -2094,12 +2229,18 @@ export class InteractiveMode implements InteractiveModeContext {
2094
2229
  }
2095
2230
 
2096
2231
  resetIrcSidebarSession(): void {
2097
- this.ircLedger.reset();
2232
+ const sidebarVisible = this.#ircSplitView.effectiveSidebarVisible(this.ui.terminal.columns);
2233
+ const rightWidth = computeIrcSplitWidths(this.ui.terminal.columns).rightWidth;
2234
+ const beforeToken = sidebarVisible ? getIrcSidebarSemanticToken(this.ircLedger, rightWidth) : "";
2235
+ this.ircLedger.reset({ retireCurrentSessionIdentities: true });
2236
+ this.#ircSplitView.resetSource();
2237
+ const afterToken = sidebarVisible ? getIrcSidebarSemanticToken(this.ircLedger, rightWidth) : "";
2098
2238
  this.#eventController.resetIrcObservations();
2099
2239
  this.#ircSidebarRequestedVisible = false;
2100
2240
  this.#ircSplitView.setVisible(false);
2101
2241
  this.#uiHelpers.resetIrcSidebarHint();
2102
2242
  this.#syncIrcSidebarAvailabilityFromSettings();
2243
+ if (sidebarVisible && beforeToken !== afterToken) this.recordVisibleTranscriptMutation();
2103
2244
  }
2104
2245
 
2105
2246
  #invalidateIrcSidebarRender(): void {