@sayknow-cli/coding-agent 0.4.6 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +27 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -12,7 +12,7 @@ import {
12
12
  readArgsTargetInternalUrl,
13
13
  } from "../../modes/components/read-tool-group";
14
14
  import { TodoReminderComponent } from "../../modes/components/todo-reminder";
15
- import { ToolExecutionComponent } from "../../modes/components/tool-execution";
15
+ import { ToolExecutionComponent, type ToolExecutionHandle } from "../../modes/components/tool-execution";
16
16
  import { TtsrNotificationComponent } from "../../modes/components/ttsr-notification";
17
17
  import { getSymbolTheme, theme } from "../../modes/theme/theme";
18
18
  import type { InteractiveModeContext, TodoPhase } from "../../modes/types";
@@ -23,6 +23,7 @@ import type { AgentSessionEvent } from "../../session/agent-session";
23
23
  import { type CustomMessage, isSilentAbort, readPendingDisplayTag } from "../../session/messages";
24
24
  import { transferSessionMessageIdentity } from "../../session/session-manager";
25
25
  import type { ResolveToolDetails } from "../../tools/resolve";
26
+ import { computeIrcSplitWidths, getIrcSidebarSemanticToken } from "../components/irc-sidebar";
26
27
  import type { IrcObservationRecord } from "../irc-observation-ledger";
27
28
  import { interruptHint } from "../shared";
28
29
  import { buildAbortDisplayMessage } from "../utils/abort-message";
@@ -36,7 +37,14 @@ type AgentSessionEventKind = AgentSessionEvent["type"];
36
37
 
37
38
  /** Test-only performance counters for advisory baseline tests. */
38
39
  export const __eventControllerPerfCounters = {
40
+ enabled: false,
39
41
  messageUpdateContentVisits: 0,
42
+ enable(): void {
43
+ this.enabled = true;
44
+ },
45
+ disable(): void {
46
+ this.enabled = false;
47
+ },
40
48
  reset(): void {
41
49
  this.messageUpdateContentVisits = 0;
42
50
  },
@@ -118,6 +126,9 @@ export class EventController {
118
126
  #toolIntentCache = new Map<string, { args: unknown; intent: string | undefined }>();
119
127
  #thinkingContentIndices = new Set<number>();
120
128
  #handlers: AgentSessionEventHandlers;
129
+ #visibleTranscriptChanged = false;
130
+ #handlingEvent = false;
131
+ #eventQueue: Promise<void> = Promise.resolve();
121
132
 
122
133
  constructor(private ctx: InteractiveModeContext) {
123
134
  this.#handlers = {
@@ -162,6 +173,26 @@ export class EventController {
162
173
  this.clearIrcExpiryTimers();
163
174
  }
164
175
 
176
+ #recordVisibleTranscriptMutation(): void {
177
+ this.#visibleTranscriptChanged = true;
178
+ }
179
+
180
+ #observeVisibleTranscriptMutation(): void {
181
+ if (this.#handlingEvent) {
182
+ this.#recordVisibleTranscriptMutation();
183
+ return;
184
+ }
185
+ this.ctx.recordVisibleTranscriptMutation?.();
186
+ }
187
+
188
+ #consumeToolVisibleChange(handle: ToolExecutionHandle): void {
189
+ // Structural third-party renderers may not implement the optional exact-consume
190
+ // capability; preserve revision correctness conservatively until they opt in.
191
+ if (typeof handle.consumeVisibleTranscriptChange !== "function" || handle.consumeVisibleTranscriptChange()) {
192
+ this.#recordVisibleTranscriptMutation();
193
+ }
194
+ }
195
+
165
196
  #resetReadGroup(): void {
166
197
  this.#lastReadGroup = undefined;
167
198
  }
@@ -228,20 +259,30 @@ export class EventController {
228
259
  });
229
260
  }
230
261
 
231
- async handleEvent(event: AgentSessionEvent): Promise<void> {
232
- if (!this.ctx.isInitialized) {
233
- await this.ctx.init();
234
- }
235
-
236
- this.ctx.statusLine.invalidate();
237
- this.ctx.updateEditorTopBorder();
262
+ handleEvent(event: AgentSessionEvent): Promise<void> {
263
+ const dispatch = this.#eventQueue.then(() => this.#dispatchEvent(event));
264
+ this.#eventQueue = dispatch.catch(() => {});
265
+ return dispatch;
266
+ }
238
267
 
239
- const run = this.#handlers[event.type] as (e: AgentSessionEvent) => Promise<void>;
240
- await run(event);
241
- if (this.ctx.isTranscriptViewerOpen?.()) this.ctx.refreshTranscriptViewer?.();
268
+ async #dispatchEvent(event: AgentSessionEvent): Promise<void> {
269
+ if (!this.ctx.isInitialized) await this.ctx.init();
270
+ this.#visibleTranscriptChanged = false;
271
+ this.#handlingEvent = true;
272
+ try {
273
+ this.ctx.statusLine.invalidate();
274
+ this.ctx.updateEditorTopBorder();
275
+ const run = this.#handlers[event.type] as (e: AgentSessionEvent) => Promise<void>;
276
+ await run(event);
277
+ if (this.#visibleTranscriptChanged) this.ctx.recordVisibleTranscriptMutation?.();
278
+ if (this.ctx.isTranscriptViewerOpen?.()) this.ctx.refreshTranscriptViewer?.();
279
+ } finally {
280
+ this.#handlingEvent = false;
281
+ }
242
282
  }
243
283
 
244
284
  async #handleAgentStart(_event: Extract<AgentSessionEvent, { type: "agent_start" }>): Promise<void> {
285
+ this.ctx.promptSuggestion?.onAgentStart();
245
286
  this.#lastIntent = undefined;
246
287
  this.#readToolCallArgs.clear();
247
288
  this.#readToolCallAssistantComponents.clear();
@@ -267,8 +308,12 @@ export class EventController {
267
308
  const parsed = parseIrcMessage(message);
268
309
  if (!parsed) return;
269
310
  const arrival = this.ctx.captureIrcArrivalSnapshot();
311
+ const rightWidth = arrival.panelVisible
312
+ ? computeIrcSplitWidths(this.ctx.ui.terminal?.columns ?? 0).rightWidth
313
+ : 0;
314
+ const beforeToken = rightWidth > 0 ? getIrcSidebarSemanticToken(this.ctx.ircLedger, rightWidth) : "";
270
315
  const record = this.ctx.ircLedger.observe(parsed, arrival.panelVisible);
271
- this.#cleanupEvictedIrcObservations();
316
+ const evictedInlineRemoved = this.#cleanupIrcObservationIds(this.ctx.ircLedger.drainEvictedObservationIds());
272
317
  if (!record) return;
273
318
  const signature = `irc:${record.observationId}`;
274
319
  if (this.#renderedCustomMessages.has(signature)) return;
@@ -278,9 +323,13 @@ export class EventController {
278
323
  this.#renderedIrcComponents.set(record.observationId, components);
279
324
  this.#scheduleIrcExpiry(record, components);
280
325
  this.ctx.ui.requestRender();
326
+ const afterToken = rightWidth > 0 ? getIrcSidebarSemanticToken(this.ctx.ircLedger, rightWidth) : "";
327
+ if (components.length > 0 || evictedInlineRemoved || (rightWidth > 0 && beforeToken !== afterToken)) {
328
+ this.#recordVisibleTranscriptMutation();
329
+ }
281
330
  }
282
331
 
283
- #cleanupIrcObservationIds(observationIds: readonly string[]): void {
332
+ #cleanupIrcObservationIds(observationIds: readonly string[]): boolean {
284
333
  const removedComponents = new Set<Component>();
285
334
  for (const observationId of observationIds) {
286
335
  const timer = this.#ircExpiryTimers.get(observationId);
@@ -293,23 +342,26 @@ export class EventController {
293
342
  for (const component of this.ctx.removeRenderedIrcInlineComponents(observationId) ?? []) {
294
343
  removedComponents.add(component);
295
344
  }
296
-
297
345
  this.#renderedCustomMessages.delete(`irc:${observationId}`);
298
346
  }
299
347
  for (const component of removedComponents) this.ctx.chatContainer.removeChild(component);
348
+ return removedComponents.size > 0;
300
349
  }
301
350
 
302
351
  #cleanupEvictedIrcObservations(): void {
303
- this.#cleanupIrcObservationIds(this.ctx.ircLedger.drainEvictedObservationIds());
352
+ if (this.#cleanupIrcObservationIds(this.ctx.ircLedger.drainEvictedObservationIds())) {
353
+ this.#recordVisibleTranscriptMutation();
354
+ }
304
355
  }
305
356
 
306
- #cleanupExpiredIrcInlineComponents(observationId: string): void {
357
+ #cleanupExpiredIrcInlineComponents(observationId: string): boolean {
307
358
  const removedComponents = new Set<Component>(this.#renderedIrcComponents.get(observationId));
308
359
  this.#renderedIrcComponents.delete(observationId);
309
360
  for (const component of this.ctx.removeRenderedIrcInlineComponents(observationId) ?? []) {
310
361
  removedComponents.add(component);
311
362
  }
312
363
  for (const component of removedComponents) this.ctx.chatContainer.removeChild(component);
364
+ return removedComponents.size > 0;
313
365
  }
314
366
 
315
367
  async #handleMessageStart(event: Extract<AgentSessionEvent, { type: "message_start" }>): Promise<void> {
@@ -325,6 +377,7 @@ export class EventController {
325
377
  this.#renderedCustomMessages.add(signature);
326
378
  this.#resetReadGroup();
327
379
  this.ctx.addMessageToChat(event.message);
380
+ if (event.message.display) this.#recordVisibleTranscriptMutation();
328
381
  // Tag-keyed pending-bar refresh: when AgentSession.#handleAgentEvent
329
382
  // spliced this dequeued custom message out of #steeringMessages /
330
383
  // #followUpMessages (it ran before this emit), the array state is
@@ -372,10 +425,12 @@ export class EventController {
372
425
  this.ctx.updatePendingMessagesDisplay();
373
426
  }
374
427
  this.ctx.ui.requestRender();
428
+ if (!wasOptimistic) this.#recordVisibleTranscriptMutation();
375
429
  } else if (event.message.role === "fileMention") {
376
430
  this.#resetReadGroup();
377
431
  this.ctx.addMessageToChat(event.message);
378
432
  this.ctx.ui.requestRender();
433
+ this.#recordVisibleTranscriptMutation();
379
434
  } else if (event.message.role === "assistant") {
380
435
  this.#lastThinkingCount = 0;
381
436
  this.#resetReadGroup();
@@ -386,6 +441,7 @@ export class EventController {
386
441
  this.ctx.hideThinkingBlock,
387
442
  () => this.ctx.ui.requestRender(),
388
443
  this.ctx.getAssistantViewportAnchorId?.(event.message),
444
+ () => this.#observeVisibleTranscriptMutation(),
389
445
  );
390
446
  this.ctx.streamingMessage = event.message;
391
447
  addChatChild(this.ctx, this.ctx.streamingComponent);
@@ -428,15 +484,10 @@ export class EventController {
428
484
  this.#cleanupEvictedIrcObservations();
429
485
  const projectedObservationIds = new Set(inlineProjection.map(record => record.observationId));
430
486
  for (const [observationId, components] of componentsByObservationId) {
431
- const record = this.ctx.ircLedger.getRecord(observationId);
432
- if (
433
- !projectedObservationIds.has(observationId) ||
434
- (record?.mode === "ephemeral" && record.expiresAt! - now <= 0)
435
- ) {
487
+ if (!projectedObservationIds.has(observationId)) {
436
488
  for (const component of components) {
437
489
  this.ctx.chatContainer.removeChild(component);
438
490
  }
439
- this.#renderedIrcComponents.delete(observationId);
440
491
  componentsByObservationId.delete(observationId);
441
492
  }
442
493
  }
@@ -467,12 +518,12 @@ export class EventController {
467
518
  }
468
519
  const remainingMs = record.expiresAt! - Date.now();
469
520
  if (remainingMs <= 0) {
470
- this.#cleanupExpiredIrcInlineComponents(record.observationId);
521
+ if (this.#cleanupExpiredIrcInlineComponents(record.observationId)) this.#observeVisibleTranscriptMutation();
471
522
  return false;
472
523
  }
473
524
  const timer = setTimeout(() => {
474
525
  this.#ircExpiryTimers.delete(record.observationId);
475
- this.#cleanupExpiredIrcInlineComponents(record.observationId);
526
+ if (this.#cleanupExpiredIrcInlineComponents(record.observationId)) this.#observeVisibleTranscriptMutation();
476
527
  this.ctx.ui.requestRender();
477
528
  }, remainingMs);
478
529
  timer.unref?.();
@@ -495,6 +546,7 @@ export class EventController {
495
546
  this.#resetReadGroup();
496
547
  this.ctx.addMessageToChat(event.message);
497
548
  this.ctx.ui.requestRender();
549
+ this.#recordVisibleTranscriptMutation();
498
550
  }
499
551
 
500
552
  async #handleModelFallbackSwitched(
@@ -548,19 +600,24 @@ export class EventController {
548
600
  }
549
601
 
550
602
  for (const content of contentsToProcess) {
551
- __eventControllerPerfCounters.messageUpdateContentVisits += 1;
603
+ if (__eventControllerPerfCounters.enabled) __eventControllerPerfCounters.messageUpdateContentVisits += 1;
552
604
  if (content.type !== "toolCall") continue;
553
605
  if (content.name === "read") {
554
606
  if (!readArgsHaveTarget(content.arguments)) continue;
555
607
  if (!readArgsTargetInternalUrl(content.arguments)) {
556
608
  this.#trackReadToolCall(content.id, content.arguments);
557
609
  const component = this.ctx.pendingTools.get(content.id);
558
- if (component) component.updateArgs(content.arguments, content.id);
559
- else {
610
+ if (component) {
611
+ component.updateArgs(content.arguments, content.id);
612
+ this.#consumeToolVisibleChange(component);
613
+ } else {
560
614
  const group = this.#getReadGroup();
561
615
  group.updateArgs(content.arguments, content.id);
562
616
  this.ctx.pendingTools.set(content.id, group);
617
+ this.#recordVisibleTranscriptMutation();
618
+ group.consumeVisibleTranscriptChange();
563
619
  }
620
+
564
621
  continue;
565
622
  }
566
623
  }
@@ -581,6 +638,7 @@ export class EventController {
581
638
  editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
582
639
  editAllowFuzzy: settings.get("edit.fuzzyMatch"),
583
640
  hashlineAutoDropPureInsertDuplicates: settings.get("edit.hashlineAutoDropPureInsertDuplicates"),
641
+ onVisibleTranscriptMutation: () => this.#observeVisibleTranscriptMutation(),
584
642
  },
585
643
  tool,
586
644
  this.ctx.ui,
@@ -590,8 +648,10 @@ export class EventController {
590
648
  component.setExpanded(this.ctx.toolOutputExpanded);
591
649
  this.ctx.pendingTools.set(content.id, component);
592
650
  addChatChild(this.ctx, component);
651
+ this.#recordVisibleTranscriptMutation();
593
652
  } else {
594
653
  this.ctx.pendingTools.get(content.id)?.updateArgs(renderArgs, content.id);
654
+ this.#consumeToolVisibleChange(this.ctx.pendingTools.get(content.id)!);
595
655
  }
596
656
 
597
657
  const args = content.arguments;
@@ -658,6 +718,7 @@ export class EventController {
658
718
  if (this.ctx.streamingMessage.stopReason !== "aborted" && this.ctx.streamingMessage.stopReason !== "error") {
659
719
  for (const [toolCallId, component] of this.ctx.pendingTools.entries()) {
660
720
  component.setArgsComplete(toolCallId);
721
+ this.#consumeToolVisibleChange(component);
661
722
  }
662
723
  }
663
724
  this.#lastAssistantComponent = this.ctx.streamingComponent;
@@ -684,6 +745,8 @@ export class EventController {
684
745
  this.ctx.pendingTools.set(event.toolCallId, group);
685
746
  }
686
747
  this.ctx.ui.requestRender();
748
+ this.#consumeToolVisibleChange(this.ctx.pendingTools.get(event.toolCallId)!);
749
+
687
750
  return;
688
751
  }
689
752
 
@@ -697,6 +760,7 @@ export class EventController {
697
760
  editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
698
761
  editAllowFuzzy: settings.get("edit.fuzzyMatch"),
699
762
  hashlineAutoDropPureInsertDuplicates: settings.get("edit.hashlineAutoDropPureInsertDuplicates"),
763
+ onVisibleTranscriptMutation: () => this.#observeVisibleTranscriptMutation(),
700
764
  },
701
765
  tool,
702
766
  this.ctx.ui,
@@ -706,6 +770,8 @@ export class EventController {
706
770
  component.setExpanded(this.ctx.toolOutputExpanded);
707
771
  this.ctx.pendingTools.set(event.toolCallId, component);
708
772
  addChatChild(this.ctx, component);
773
+ this.#recordVisibleTranscriptMutation();
774
+
709
775
  this.ctx.ui.requestRender();
710
776
  }
711
777
  }
@@ -722,6 +788,8 @@ export class EventController {
722
788
  !isFinalAsyncState,
723
789
  event.toolCallId,
724
790
  );
791
+ this.#consumeToolVisibleChange(component);
792
+
725
793
  if (isFinalAsyncState) {
726
794
  this.ctx.pendingTools.delete(event.toolCallId);
727
795
  this.#backgroundToolCallIds.delete(event.toolCallId);
@@ -736,6 +804,8 @@ export class EventController {
736
804
  const component = this.ctx.pendingTools.get(event.toolCallId);
737
805
  if (component) {
738
806
  component.updateResult({ ...event.result, isError: event.isError }, false, event.toolCallId);
807
+ this.#consumeToolVisibleChange(component);
808
+
739
809
  this.ctx.pendingTools.delete(event.toolCallId);
740
810
  }
741
811
  const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
@@ -760,6 +830,8 @@ export class EventController {
760
830
  const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
761
831
  const isBackgroundRunning = asyncState === "running";
762
832
  component.updateResult({ ...event.result, isError: event.isError }, isBackgroundRunning, event.toolCallId);
833
+ this.#consumeToolVisibleChange(component);
834
+
763
835
  if (isBackgroundRunning) {
764
836
  this.#backgroundToolCallIds.add(event.toolCallId);
765
837
  } else {
@@ -775,6 +847,8 @@ export class EventController {
775
847
  const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
776
848
  const isBackgroundRunning = asyncState === "running";
777
849
  component.updateResult({ ...event.result, isError: event.isError }, isBackgroundRunning, event.toolCallId);
850
+ this.#consumeToolVisibleChange(component);
851
+
778
852
  if (isBackgroundRunning) {
779
853
  this.#backgroundToolCallIds.add(event.toolCallId);
780
854
  } else {
@@ -836,6 +910,7 @@ export class EventController {
836
910
  this.ctx.ui.requestRender();
837
911
  this.#scheduleIdleCompaction();
838
912
  this.sendCompletionNotification();
913
+ this.ctx.promptSuggestion?.onAgentEnd();
839
914
  }
840
915
 
841
916
  async #handleAutoCompactionStart(
@@ -847,6 +922,10 @@ export class EventController {
847
922
  this.ctx.editor.onEscape = () => {
848
923
  this.ctx.session.abortCompaction();
849
924
  };
925
+ if (this.ctx.loadingAnimation) {
926
+ this.ctx.loadingAnimation.stop();
927
+ this.ctx.loadingAnimation = undefined;
928
+ }
850
929
  this.ctx.statusContainer.clear();
851
930
  const reasonText =
852
931
  event.reason === "overflow" ? "Context overflow detected, " : event.reason === "idle" ? "Idle " : "";
@@ -887,6 +966,12 @@ export class EventController {
887
966
  } else if (event.willRetry && !isHandoffAction) {
888
967
  this.ctx.showStatus("Context overflow maintenance completed");
889
968
  }
969
+ } else if (
970
+ event.skipped &&
971
+ event.errorMessage?.startsWith("Context overflow recovery skipped: nothing eligible to compact.") &&
972
+ !isHandoffAction
973
+ ) {
974
+ this.ctx.showStatus(event.errorMessage);
890
975
  } else if (event.errorMessage) {
891
976
  this.ctx.showWarning(event.errorMessage);
892
977
  } else if (isHandoffAction) {
@@ -939,6 +1024,10 @@ export class EventController {
939
1024
  this.ctx.session.abortRetry();
940
1025
  }
941
1026
  };
1027
+ if (this.ctx.loadingAnimation) {
1028
+ this.ctx.loadingAnimation.stop();
1029
+ this.ctx.loadingAnimation = undefined;
1030
+ }
942
1031
  this.ctx.statusContainer.clear();
943
1032
  // Stop any prior retry loader/timer before installing a new one.
944
1033
  this.ctx.retryLoader?.stop();
@@ -989,12 +1078,15 @@ export class EventController {
989
1078
  const component = new TtsrNotificationComponent(event.rules);
990
1079
  component.setExpanded(this.ctx.toolOutputExpanded);
991
1080
  addChatChild(this.ctx, component);
1081
+ this.#recordVisibleTranscriptMutation();
1082
+
992
1083
  this.ctx.ui.requestRender();
993
1084
  }
994
1085
 
995
1086
  async #handleTodoReminder(event: Extract<AgentSessionEvent, { type: "todo_reminder" }>): Promise<void> {
996
1087
  const component = new TodoReminderComponent(event.todos, event.attempt, event.maxAttempts);
997
1088
  addChatChild(this.ctx, component);
1089
+ this.#recordVisibleTranscriptMutation();
998
1090
  this.ctx.ui.requestRender();
999
1091
  }
1000
1092
 
@@ -282,7 +282,24 @@ export class ExtensionUiController {
282
282
  const disabled = [...(session.settings.get("disabledExtensions") ?? [])];
283
283
  const on = input.on === true;
284
284
  const next = on ? disabled.filter(value => value !== id) : [...new Set([...disabled, id])];
285
- session.settings.set("disabledExtensions", next);
285
+ if (!session.settings.canWriteDurableConfig()) {
286
+ throw Object.assign(
287
+ new Error(
288
+ "Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
289
+ ),
290
+ { code: "invalid_request" },
291
+ );
292
+ }
293
+ try {
294
+ session.settings.set("disabledExtensions", next);
295
+ } catch (error) {
296
+ if (!session.settings.canWriteDurableConfig()) {
297
+ throw Object.assign(new Error(error instanceof Error ? error.message : String(error)), {
298
+ code: "invalid_request",
299
+ });
300
+ }
301
+ throw error;
302
+ }
286
303
  return { changed: true, enabled: on };
287
304
  }
288
305
  case "session.delete":
@@ -426,6 +443,7 @@ export class ExtensionUiController {
426
443
  getSystemPrompt: () => this.ctx.session.systemPrompt,
427
444
  clearContext: () => this.ctx.session.clearContext(),
428
445
  cycleModel: () => this.ctx.session.cycleModel(),
446
+ setModelProfile: name => this.ctx.session.activateModelProfileForControl(name),
429
447
  cycleThinkingLevel: () => this.ctx.session.cycleThinkingLevel(),
430
448
  setQueueMode: (kind, mode) => {
431
449
  if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
@@ -442,16 +460,12 @@ export class ExtensionUiController {
442
460
  }
443
461
  return false;
444
462
  },
445
- invokeSkill: (name, args) => this.ctx.session.invokeSkill(name, args),
463
+ invokeSkill: (name, args, options) => this.ctx.session.invokeSkill(name, args, options),
446
464
  setPlanMode: on => this.ctx.session.setSdkPlanMode(on),
447
465
  operateGoal: (op, objective) => this.ctx.session.operateGoal(op, objective),
448
466
  getSkillState: () =>
449
467
  this.ctx.session.skills.map(skill => ({ name: skill.name, description: skill.description })),
450
- getConfigItems: () => ({
451
- steeringMode: this.ctx.session.steeringMode,
452
- followUpMode: this.ctx.session.followUpMode,
453
- interruptMode: this.ctx.session.interruptMode,
454
- }),
468
+ getConfigItems: () => this.ctx.session.getSdkConfigItems(),
455
469
  getBranchCandidates: () => this.ctx.sessionManager.getTree(),
456
470
  getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
457
471
  setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
@@ -728,6 +742,7 @@ export class ExtensionUiController {
728
742
  getSystemPrompt: () => this.ctx.session.systemPrompt,
729
743
  clearContext: () => this.ctx.session.clearContext(),
730
744
  cycleModel: () => this.ctx.session.cycleModel(),
745
+ setModelProfile: name => this.ctx.session.activateModelProfileForControl(name),
731
746
  cycleThinkingLevel: () => this.ctx.session.cycleThinkingLevel(),
732
747
  setQueueMode: (kind, mode) => {
733
748
  if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
@@ -746,11 +761,7 @@ export class ExtensionUiController {
746
761
  },
747
762
  getSkillState: () =>
748
763
  this.ctx.session.skills.map(skill => ({ name: skill.name, description: skill.description })),
749
- getConfigItems: () => ({
750
- steeringMode: this.ctx.session.steeringMode,
751
- followUpMode: this.ctx.session.followUpMode,
752
- interruptMode: this.ctx.session.interruptMode,
753
- }),
764
+ getConfigItems: () => this.ctx.session.getSdkConfigItems(),
754
765
  getBranchCandidates: () => this.ctx.sessionManager.getTree(),
755
766
  getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
756
767
  setSdkPermissionProvider: provider => this.ctx.session.setSdkPermissionProvider(provider),
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { type AgentMessage, ThinkingLevel } from "@sayknow-cli/agent-core";
4
4
  import { type AutocompleteProvider, matchesKey, type SlashCommand } from "@sayknow-cli/tui";
5
- import { $env, logger, sanitizeText } from "@sayknow-cli/utils";
5
+ import { $pickenv, logger, sanitizeText } from "@sayknow-cli/utils";
6
6
  import { type AppKeybinding, KEYBINDINGS } from "../../config/keybindings";
7
7
  import { isSettingsInitialized, settings } from "../../config/settings";
8
8
  import { resolveSubskillActivationForSkillInvocation } from "../../extensibility/skc-plugins";
@@ -33,6 +33,13 @@ import type { PasteTextContext } from "../components/custom-editor";
33
33
  import { QueuePaneComponent } from "../components/queue-pane";
34
34
  import { type QueuedMessageMoveDirection, QueuedMessageSelectorComponent } from "../components/queued-message-selector";
35
35
 
36
+ const QUEUE_SELECTOR_NAVIGATION_ACTIONS = [
37
+ "tui.select.up",
38
+ "tui.select.down",
39
+ "tui.select.pageUp",
40
+ "tui.select.pageDown",
41
+ ] as const;
42
+
36
43
  interface Expandable {
37
44
  setExpanded(expanded: boolean): void;
38
45
  setManuallyExpanded?(expanded: boolean): void;
@@ -151,15 +158,24 @@ export class InputController {
151
158
  case "app.editor.external":
152
159
  return Boolean(getEditorCommand());
153
160
  case "app.message.followUp":
154
- case "app.message.queue":
155
161
  return (
156
162
  this.ctx.session.isStreaming ||
157
163
  this.ctx.session.isCompacting ||
158
164
  this.ctx.session.isBashRunning ||
159
165
  this.ctx.session.isEvalRunning
160
166
  );
167
+ case "app.message.queue":
168
+ return (
169
+ this.ctx.editor.getText().trim().length > 0 &&
170
+ (this.ctx.session.isStreaming ||
171
+ this.ctx.session.isCompacting ||
172
+ this.ctx.session.isBashRunning ||
173
+ this.ctx.session.isEvalRunning)
174
+ );
161
175
  case "app.message.dequeue":
162
- return this.ctx.session.queuedMessageCount > 0;
176
+ return (
177
+ this.ctx.session.getQueuedMessageEntries().length > 0 || this.ctx.compactionQueuedMessages.length > 0
178
+ );
163
179
  case "app.clipboard.copyPrompt":
164
180
  return this.ctx.editor.getText().length > 0;
165
181
  case "app.session.tree":
@@ -725,8 +741,12 @@ export class InputController {
725
741
  });
726
742
  }
727
743
 
744
+ // Tab on an empty composer accepts the pending ghost-text prompt suggestion.
745
+ this.ctx.editor.onTab = (text: string) => this.ctx.promptSuggestion?.tryAcceptOnTab(text) === true;
746
+
728
747
  this.ctx.editor.onChange = (text: string) => {
729
748
  this.#resetEscapeGestures();
749
+ this.ctx.promptSuggestion?.notifyEditorChanged(text);
730
750
  const wasBashMode = this.ctx.isBashMode;
731
751
  const wasBashNoContext = this.ctx.isBashNoContext;
732
752
  const wasPythonMode = this.ctx.isPythonMode;
@@ -752,6 +772,29 @@ export class InputController {
752
772
  async submitText(text: string, composer: ComposerSubmissionOptions): Promise<void> {
753
773
  text = text.trim();
754
774
  if ((!isSettingsInitialized() || settings.get("emojiAutocomplete")) && text) text = expandEmoticons(text);
775
+ if (this.ctx.hasActiveBtw()) {
776
+ if (!text) return;
777
+ if (!text.startsWith("/")) {
778
+ const result = await this.ctx.handleBtwFollowUp(text);
779
+ if (result === "accepted" && this.#canModifyComposer(composer)) {
780
+ this.ctx.editor.setText("");
781
+ this.ctx.pendingImages = [];
782
+ }
783
+ return;
784
+ }
785
+ }
786
+
787
+ if (/^\/btw(?:\s|$)/.test(text)) {
788
+ const slashResult = await executeBuiltinSlashCommand(text, {
789
+ ctx: this.ctx,
790
+ handleBackgroundCommand: () => this.handleBackgroundCommand(),
791
+ composer,
792
+ });
793
+ if (slashResult === true) {
794
+ this.ctx.pendingImages = [];
795
+ return;
796
+ }
797
+ }
755
798
 
756
799
  // Empty submit while streaming with queued messages: flush queues immediately
757
800
  if (!text && this.ctx.session.isStreaming && this.ctx.session.queuedMessageCount > 0) {
@@ -907,7 +950,7 @@ export class InputController {
907
950
 
908
951
  // Generate session title on first message
909
952
  const hasUserMessages = this.ctx.session.messages.some((m: AgentMessage) => m.role === "user");
910
- if (!hasUserMessages && !this.ctx.sessionManager.getSessionName() && !$env.PI_NO_TITLE) {
953
+ if (!hasUserMessages && !this.ctx.sessionManager.getSessionName() && !$pickenv("SKC_NO_TITLE", "PI_NO_TITLE")) {
911
954
  const registry = this.ctx.session.modelRegistry;
912
955
  generateSessionTitle(
913
956
  text,
@@ -1010,6 +1053,21 @@ export class InputController {
1010
1053
  };
1011
1054
  const pane = new QueuePaneComponent(entries, {
1012
1055
  selectedIndex,
1056
+ formatKeyHint: key => this.ctx.keybindings.formatKeyHint(key),
1057
+ formatSelectAction: action => this.ctx.keybindings.getDisplayString(action),
1058
+ matchesSelectAction: (keyData, action) =>
1059
+ this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
1060
+ resolveSelectNavigation: keyData =>
1061
+ QUEUE_SELECTOR_NAVIGATION_ACTIONS.find(action =>
1062
+ this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
1063
+ ),
1064
+ onSelect: entry => {
1065
+ const restored = this.#restoreQueuedMessageToEditor(entry);
1066
+ close();
1067
+ this.ctx.showStatus(
1068
+ restored === 0 ? "Queued message is no longer available" : "Restored queued message to editor",
1069
+ );
1070
+ },
1013
1071
  onDelete: (entry, index) => {
1014
1072
  const deleted = this.ctx.session.removeQueuedMessageForEditing(entry.id) !== undefined;
1015
1073
  const remaining = this.ctx.session.getQueuedMessageEntries();
@@ -1182,7 +1240,17 @@ export class InputController {
1182
1240
  this.#restoreEditorFocus();
1183
1241
  this.ctx.ui.requestRender();
1184
1242
  },
1185
- { selectedIndex },
1243
+ {
1244
+ selectedIndex,
1245
+ formatKeyHint: key => this.ctx.keybindings.formatKeyHint(key),
1246
+ formatSelectAction: action => this.ctx.keybindings.getDisplayString(action),
1247
+ matchesSelectAction: (keyData, action) =>
1248
+ this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
1249
+ resolveSelectNavigation: keyData =>
1250
+ QUEUE_SELECTOR_NAVIGATION_ACTIONS.find(action =>
1251
+ this.ctx.keybindings.getKeys(action).some(key => matchesKey(keyData, key)),
1252
+ ),
1253
+ },
1186
1254
  );
1187
1255
  this.ctx.editorContainer.clear();
1188
1256
  this.ctx.editorContainer.addChild(selector);
@@ -1357,6 +1425,14 @@ export class InputController {
1357
1425
  async handleFollowUp(): Promise<void> {
1358
1426
  const text = this.ctx.editor.getText().trim();
1359
1427
  if (!text) return;
1428
+ // While /btw is open, plain text stays in the side chat. Slash-origin
1429
+ // input keeps normal dispatch so commands remain available.
1430
+ if (this.ctx.hasActiveBtw() && !text.startsWith("/")) {
1431
+ if ((await this.ctx.handleBtwFollowUp(text)) === "accepted") {
1432
+ this.ctx.editor.setText("");
1433
+ }
1434
+ return;
1435
+ }
1360
1436
 
1361
1437
  // Compaction first: while compacting, queue free text and `/skill:*`
1362
1438
  // commands in the compaction-local queue. `flushCompactionQueue`
@@ -1758,6 +1834,7 @@ export class InputController {
1758
1834
  moveCursorToMessageStart: () => this.ctx.editor.moveToMessageStart(),
1759
1835
  moveCursorToLineStart: () => this.ctx.editor.moveToLineStart(),
1760
1836
  moveCursorToLineEnd: () => this.ctx.editor.moveToLineEnd(),
1837
+ getPromptSuggestion: () => this.ctx.promptSuggestion?.current ?? null,
1761
1838
  });
1762
1839
  }
1763
1840
 
@@ -1967,9 +2044,24 @@ export class InputController {
1967
2044
  }
1968
2045
 
1969
2046
  toggleThinkingBlockVisibility(): void {
1970
- this.ctx.hideThinkingBlock = !this.ctx.hideThinkingBlock;
1971
- settings.set("hideThinkingBlock", this.ctx.hideThinkingBlock);
1972
- this.ctx.session.agent.hideThinkingSummary = this.ctx.hideThinkingBlock;
2047
+ if (!settings.canWriteDurableConfig()) {
2048
+ this.ctx.showError(
2049
+ "Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
2050
+ );
2051
+ return;
2052
+ }
2053
+ const hideThinkingBlock = !this.ctx.hideThinkingBlock;
2054
+ try {
2055
+ settings.set("hideThinkingBlock", hideThinkingBlock);
2056
+ } catch (error) {
2057
+ if (!settings.canWriteDurableConfig()) {
2058
+ this.ctx.showError(error instanceof Error ? error.message : String(error));
2059
+ return;
2060
+ }
2061
+ throw error;
2062
+ }
2063
+ this.ctx.hideThinkingBlock = hideThinkingBlock;
2064
+ this.ctx.session.setThinkingVisibility(hideThinkingBlock ? "hidden" : "visible");
1973
2065
 
1974
2066
  // Rebuild chat from session messages
1975
2067
  // Detach the live streaming component before the disposing clear() so the