@sayknow-cli/coding-agent 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +20 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -21,6 +21,13 @@ export type SkillDiscoveryToolInput = z.infer<typeof skillDiscoverySchema>;
21
21
  export interface SkillDiscoveryToolDetails {
22
22
  candidates: RuntimeSkillDiscoveryCandidate[];
23
23
  count: number;
24
+ /**
25
+ * Present only when zero candidates were returned AND discovery config gates
26
+ * (`skills.enabled` / `skills.enablePiProject` / `skills.enablePiUser`)
27
+ * prevented some or all of the requested scope from being searched. Without
28
+ * this, a disabled config is indistinguishable from "no skills exist".
29
+ */
30
+ notice?: string;
24
31
  }
25
32
 
26
33
  export class SkillDiscoveryTool implements AgentTool<typeof skillDiscoverySchema, SkillDiscoveryToolDetails> {
@@ -57,17 +64,46 @@ export class SkillDiscoveryTool implements AgentTool<typeof skillDiscoverySchema
57
64
  signal?: AbortSignal,
58
65
  ): Promise<AgentToolResult<SkillDiscoveryToolDetails>> {
59
66
  return untilAborted(signal, async () => {
67
+ const source = input.source ?? "all";
60
68
  const candidates = await discoverRuntimeSkills({
61
69
  cwd: this.#session.cwd,
62
70
  query: input.query,
63
- source: input.source ?? "all",
71
+ source,
64
72
  limit: input.limit,
65
73
  policy: this.#getRuntimeSkillPolicy(),
66
74
  });
75
+ const details: SkillDiscoveryToolDetails = { candidates, count: candidates.length };
76
+ if (candidates.length === 0) {
77
+ const notice = this.#disabledPolicyNotice(source);
78
+ if (notice) details.notice = notice;
79
+ }
67
80
  return {
68
- content: [{ type: "text", text: JSON.stringify({ candidates, count: candidates.length }, null, 2) }],
69
- details: { candidates, count: candidates.length },
81
+ content: [{ type: "text", text: JSON.stringify(details, null, 2) }],
82
+ details,
70
83
  };
71
84
  });
72
85
  }
86
+
87
+ /**
88
+ * Explain an empty result that is caused by disabled discovery config rather
89
+ * than by an actually empty skill catalog.
90
+ */
91
+ #disabledPolicyNotice(source: "all" | "project" | "user"): string | undefined {
92
+ const policy = this.#getRuntimeSkillPolicy();
93
+ if (policy.enabled !== true) {
94
+ return "Runtime skill discovery is disabled: `skills.enabled` is false, so no skill directories were searched. Enable it with `skc config set skills.enabled true` (project and user scopes additionally require `skills.enablePiProject` / `skills.enablePiUser`).";
95
+ }
96
+ const skipped: string[] = [];
97
+ const commands: string[] = [];
98
+ if ((source === "all" || source === "project") && policy.enablePiProject !== true) {
99
+ skipped.push("project (`skills.enablePiProject` is false)");
100
+ commands.push("`skc config set skills.enablePiProject true`");
101
+ }
102
+ if ((source === "all" || source === "user") && policy.enablePiUser !== true) {
103
+ skipped.push("user (`skills.enablePiUser` is false)");
104
+ commands.push("`skc config set skills.enablePiUser true`");
105
+ }
106
+ if (skipped.length === 0) return undefined;
107
+ return `Skill discovery skipped disabled scope(s): ${skipped.join(", ")}. Enable them with ${commands.join(" and ")}.`;
108
+ }
73
109
  }
@@ -272,6 +272,7 @@ const COMMENT_OR_TERMINATOR_ERROR =
272
272
  "SQLite 'where' clause must not contain comments or statement terminators; use '?q=SELECT ...' for raw SQL";
273
273
  const FORBIDDEN_KEYWORD_ERROR =
274
274
  "SQLite 'where' clause must not contain LIMIT/OFFSET/UNION/INTERSECT/EXCEPT/ATTACH/DETACH/PRAGMA; use '?q=SELECT ...' for raw SQL";
275
+ const RAW_QUERY_ERROR = "SQLite raw queries must contain exactly one explicit SELECT statement";
275
276
 
276
277
  /**
277
278
  * Scans a `where=` clause character-by-character, tracking single- and double-quoted
@@ -363,6 +364,66 @@ function validateWhereClause(where: string | undefined): string | undefined {
363
364
  return trimmed;
364
365
  }
365
366
 
367
+ /**
368
+ * Keep raw reads deliberately narrower than SQLite's full grammar. Comments are
369
+ * rejected, and a statement terminator is accepted only as the final
370
+ * non-whitespace character. Quoted semicolons remain ordinary query data.
371
+ */
372
+ export function validateSqliteReadQuery(sql: string): string {
373
+ const trimmed = sql.trim();
374
+ if (!/^SELECT\b/i.test(trimmed)) {
375
+ throw new ToolError(RAW_QUERY_ERROR);
376
+ }
377
+
378
+ let quoteEnd: "'" | '"' | "`" | "]" | null = null;
379
+ for (let index = 0; index < trimmed.length; index++) {
380
+ const char = trimmed[index];
381
+ const next = trimmed[index + 1];
382
+ if (char === "\0") {
383
+ throw new ToolError(RAW_QUERY_ERROR);
384
+ }
385
+
386
+ if (quoteEnd) {
387
+ if (char === quoteEnd) {
388
+ if (quoteEnd !== "]" && next === quoteEnd) {
389
+ index += 1;
390
+ } else {
391
+ quoteEnd = null;
392
+ }
393
+ }
394
+ continue;
395
+ }
396
+
397
+ if (char === "'" || char === '"' || char === "`") {
398
+ quoteEnd = char;
399
+ continue;
400
+ }
401
+ if (char === "[") {
402
+ quoteEnd = "]";
403
+ continue;
404
+ }
405
+ if ((char === "-" && next === "-") || (char === "/" && next === "*") || (char === "*" && next === "/")) {
406
+ throw new ToolError(RAW_QUERY_ERROR);
407
+ }
408
+ if (char === ";" && trimmed.slice(index + 1).trim().length > 0) {
409
+ throw new ToolError(RAW_QUERY_ERROR);
410
+ }
411
+ }
412
+
413
+ if (quoteEnd) {
414
+ throw new ToolError(RAW_QUERY_ERROR);
415
+ }
416
+ return trimmed;
417
+ }
418
+
419
+ export function enforceSqliteQueryOnly(db: Database): void {
420
+ db.run("PRAGMA query_only = ON");
421
+ const enabled = db.prepare<{ query_only: number }, []>("PRAGMA query_only").get()?.query_only;
422
+ if (enabled !== 1) {
423
+ throw new ToolError("SQLite read connection could not enable query-only mode");
424
+ }
425
+ }
426
+
366
427
  function normalizeWriteValue(value: unknown, column: string): SqliteBinding {
367
428
  if (value === null) return null;
368
429
  if (
@@ -444,10 +505,7 @@ export function parseSqliteSelector(subPath: string, queryString: string): Sqlit
444
505
  if (normalizedSubPath || otherKeys.length > 0) {
445
506
  throw new ToolError("SQLite raw queries cannot be combined with table selectors or pagination");
446
507
  }
447
- if (!rawQuery.trim()) {
448
- throw new ToolError("SQLite query parameter 'q' cannot be empty");
449
- }
450
- return { kind: "raw", sql: rawQuery };
508
+ return { kind: "raw", sql: validateSqliteReadQuery(rawQuery) };
451
509
  }
452
510
 
453
511
  if (!normalizedSubPath) {
@@ -602,7 +660,7 @@ export function executeReadQuery(
602
660
  sql: string,
603
661
  maxRows: number = MAX_RAW_QUERY_ROWS,
604
662
  ): { columns: string[]; rows: Record<string, unknown>[]; truncated: boolean } {
605
- const statement = db.prepare<SqliteRow, []>(sql);
663
+ const statement = db.prepare<SqliteRow, []>(validateSqliteReadQuery(sql));
606
664
  if (statement.paramsCount > 0) {
607
665
  throw new ToolError("SQLite raw queries do not support bound parameters");
608
666
  }
@@ -11,6 +11,12 @@ import type { Component } from "@sayknow-cli/tui";
11
11
  import { Text } from "@sayknow-cli/tui";
12
12
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
13
13
  import type { Theme } from "../modes/theme/theme";
14
+ import {
15
+ collectProviderDegradationGroups,
16
+ hasActiveProviderRetryInProgress,
17
+ providerProgressAgeLabel,
18
+ providerRetryPhaseLabel,
19
+ } from "../task/provider-retry-status";
14
20
  import { renderSubagentLiveProgress } from "../task/render";
15
21
  import { Ellipsis, Hasher, renderStatusLine } from "../tui";
16
22
  import {
@@ -42,7 +48,8 @@ const PREVIEW_LINE_WIDTH = 80;
42
48
  * wall-clock displays are deliberately kept OUT of the cached body — the animated
43
49
  * spinner and the fresh duration live in the cheap per-subagent status line, and
44
50
  * `renderSubagentLiveProgress` is invoked with `staticTime` so current-tool elapsed
45
- * and retry countdowns are never baked into cached lines.
51
+ * and retry countdowns are never baked into cached lines. Snapshots with an active
52
+ * retry anywhere in their nested task tree bypass this cache and render dynamically.
46
53
  */
47
54
  const SUBAGENT_BODY_CACHE_MAX = 128;
48
55
  const subagentBodyCache = new Map<bigint, string[]>();
@@ -77,6 +84,15 @@ export const subagentBodyCacheTestHooks = {
77
84
  },
78
85
  };
79
86
 
87
+ function snapshotHasActiveRetry(snapshot: SubagentSnapshot): boolean {
88
+ if (snapshot.liveProgressAvailable === false || !snapshot.progress) return false;
89
+ return hasActiveProviderRetryInProgress(snapshot.progress);
90
+ }
91
+
92
+ function boundSubagentBodyLines(lines: string[], width: number): string[] {
93
+ return lines.map(line => (line.length > 0 ? truncateToWidth(replaceTabs(line), width, Ellipsis.Omit) : ""));
94
+ }
95
+
80
96
  function renderCachedSubagentBody(
81
97
  snapshot: SubagentSnapshot,
82
98
  signature: string,
@@ -92,9 +108,7 @@ function renderCachedSubagentBody(
92
108
  subagentBodyCache.set(key, hit);
93
109
  return hit;
94
110
  }
95
- const lines = renderSubagentSnapshotBody(snapshot, expanded, theme).map(line =>
96
- line.length > 0 ? truncateToWidth(line, width, Ellipsis.Omit) : "",
97
- );
111
+ const lines = boundSubagentBodyLines(renderSubagentSnapshotBody(snapshot, expanded, theme), width);
98
112
  subagentBodyRenderCount += 1;
99
113
  subagentBodyCache.set(key, lines);
100
114
  if (subagentBodyCache.size > SUBAGENT_BODY_CACHE_MAX) {
@@ -104,6 +118,16 @@ function renderCachedSubagentBody(
104
118
  return lines;
105
119
  }
106
120
 
121
+ function renderDynamicSubagentBody(
122
+ snapshot: SubagentSnapshot,
123
+ expanded: boolean,
124
+ width: number,
125
+ theme: Theme,
126
+ ): string[] {
127
+ subagentBodyRenderCount += 1;
128
+ return boundSubagentBodyLines(renderSubagentSnapshotBody(snapshot, expanded, theme, false), width);
129
+ }
130
+
107
131
  function statusIconKind(status: SubagentSnapshot["status"]): ToolUIStatus {
108
132
  switch (status) {
109
133
  case "completed":
@@ -131,15 +155,25 @@ function renderSubagentStatusLine(snapshot: SubagentSnapshot, theme: Theme, spin
131
155
  snapshot.status === "running" ? spinnerFrame : undefined,
132
156
  );
133
157
  const id = theme.fg("muted", truncateToWidth(replaceTabs(snapshot.id), PREVIEW_LINE_WIDTH, Ellipsis.Unicode));
134
- const status = theme.fg("dim", snapshot.status);
158
+ const retryState = snapshot.liveProgressAvailable !== false ? snapshot.progress?.retryState : undefined;
159
+ const status = retryState
160
+ ? theme.fg(
161
+ "warning",
162
+ `provider degraded · ${providerRetryPhaseLabel(retryState.kind)} · ${providerProgressAgeLabel(retryState, Date.now())}`,
163
+ )
164
+ : theme.fg("dim", snapshot.status);
135
165
  const duration = theme.fg("dim", formatDuration(snapshot.durationMs));
136
166
  return `${icon} ${id} ${status} ${duration}`;
137
167
  }
138
168
 
139
- // Heavy, cacheable per-subagent body: a pure function of (snapshot content, expanded,
140
- // theme). No spinner frame and no wall-clock displays leak in (live progress uses
141
- // `staticTime`), so the module body cache can never serve stale or frozen-ticking lines.
142
- function renderSubagentSnapshotBody(snapshot: SubagentSnapshot, expanded: boolean, theme: Theme): string[] {
169
+ // Heavy per-subagent body. The cache path uses staticTime=true; the bounded dynamic
170
+ // path opts into wall-clock displays when an active retry exists in the nested tree.
171
+ function renderSubagentSnapshotBody(
172
+ snapshot: SubagentSnapshot,
173
+ expanded: boolean,
174
+ theme: Theme,
175
+ staticTime = true,
176
+ ): string[] {
143
177
  const lines: string[] = [];
144
178
 
145
179
  // Static receipt fields (parity with the markdown content for non-await actions).
@@ -174,9 +208,9 @@ function renderSubagentSnapshotBody(snapshot: SubagentSnapshot, expanded: boolea
174
208
  // exists (subagent.ts #liveProgressFields), but the renderer also honors an
175
209
  // explicit `liveProgressAvailable: false` so stale retained progress can never
176
210
  // resurrect a live panel (AC5). `staticTime` keeps wall-clock displays out of
177
- // these cached lines.
211
+ // cached lines when the body is served by the cache.
178
212
  if (snapshot.progress && snapshot.liveProgressAvailable !== false) {
179
- for (const pl of renderSubagentLiveProgress(snapshot.progress, expanded, theme, undefined, true)) {
213
+ for (const pl of renderSubagentLiveProgress(snapshot.progress, expanded, theme, undefined, staticTime)) {
180
214
  lines.push(` ${pl}`);
181
215
  }
182
216
  } else if (snapshot.liveProgressAvailable && (snapshot.status === "running" || snapshot.status === "queued")) {
@@ -251,26 +285,50 @@ export const subagentToolRenderer = {
251
285
  },
252
286
  theme,
253
287
  );
254
- const out: string[] = [truncateToWidth(header, width, Ellipsis.Omit)];
288
+ const out: string[] = [truncateToWidth(replaceTabs(header), width, Ellipsis.Omit)];
255
289
  // Discoverability: the inline panel is a bounded preview; the session
256
290
  // observer (ctrl+s) streams the full per-subagent message history.
257
291
  if (runningCount > 0) {
258
- out.push(truncateToWidth(` ${theme.fg("dim", "(ctrl+s to observe sessions)")}`, width, Ellipsis.Omit));
292
+ out.push(
293
+ truncateToWidth(
294
+ replaceTabs(` ${theme.fg("dim", "(ctrl+s to observe sessions)")}`),
295
+ width,
296
+ Ellipsis.Omit,
297
+ ),
298
+ );
299
+ }
300
+ const liveProgress = subagents.flatMap(snapshot =>
301
+ snapshot.progress && snapshot.liveProgressAvailable !== false ? [snapshot.progress] : [],
302
+ );
303
+ for (const group of collectProviderDegradationGroups(liveProgress)) {
304
+ out.push(
305
+ truncateToWidth(
306
+ replaceTabs(
307
+ ` ${theme.fg("warning", `provider degraded: ${group.count} subagents retrying on ${group.provider}`)}`,
308
+ ),
309
+ width,
310
+ Ellipsis.Omit,
311
+ ),
312
+ );
259
313
  }
260
314
 
261
315
  snapshotSignatures ??= subagents.map(snapshot =>
262
316
  subagentAwaitRenderedStateSignature([snapshot], result.details),
263
317
  );
264
318
  subagents.forEach((snapshot, index) => {
265
- // Fresh per-subagent status line (cheap), then the cached heavy body.
319
+ // Fresh per-subagent status line (cheap), then a cached or dynamic body.
266
320
  out.push(
267
321
  truncateToWidth(
268
- renderSubagentStatusLine(snapshot, theme, options.spinnerFrame),
322
+ replaceTabs(renderSubagentStatusLine(snapshot, theme, options.spinnerFrame)),
269
323
  width,
270
324
  Ellipsis.Omit,
271
325
  ),
272
326
  );
273
- out.push(...renderCachedSubagentBody(snapshot, snapshotSignatures![index]!, expanded, width, theme));
327
+ out.push(
328
+ ...(snapshotHasActiveRetry(snapshot)
329
+ ? renderDynamicSubagentBody(snapshot, expanded, width, theme)
330
+ : renderCachedSubagentBody(snapshot, snapshotSignatures![index]!, expanded, width, theme)),
331
+ );
274
332
  });
275
333
  return out;
276
334
  },
@@ -936,6 +936,8 @@ function canonicalizeProgressForSignature(progress: AgentProgress): unknown {
936
936
  attempt: progress.retryState.attempt,
937
937
  maxAttempts: progress.retryState.maxAttempts,
938
938
  unbounded: progress.retryState.unbounded ?? false,
939
+ kind: progress.retryState.kind,
940
+ provider: progress.retryState.provider ?? null,
939
941
  delayMs: progress.retryState.delayMs,
940
942
  errorMessage: progress.retryState.errorMessage,
941
943
  // startedAtMs intentionally excluded (drives countdown only).
@@ -1,6 +1,6 @@
1
1
  import type { AgentToolResult } from "@sayknow-cli/agent-core";
2
2
  import type { ImageContent, TextContent } from "@sayknow-cli/ai";
3
- import type { OutputSummary, TruncationResult } from "../session/streaming-output";
3
+ import type { OutputSummary, ReadWindow, TruncationResult } from "../session/streaming-output";
4
4
  import type { OutputMeta, TruncationOptions, TruncationSummaryOptions, TruncationTextOptions } from "./output-meta";
5
5
  import { outputMeta } from "./output-meta";
6
6
 
@@ -32,6 +32,19 @@ export class ToolResultBuilder<TDetails extends DetailsWithMeta> {
32
32
  return this;
33
33
  }
34
34
 
35
+ truncationWindows(
36
+ windows: ReadWindow,
37
+ options?: {
38
+ artifactId?: string;
39
+ noticeOwner?: "body";
40
+ maxBytes?: number;
41
+ rangeBase?: "file" | "window";
42
+ },
43
+ ): this {
44
+ this.#meta.truncationWindows(windows, options);
45
+ return this;
46
+ }
47
+
35
48
  truncationFromSummary(summary: OutputSummary, options: TruncationSummaryOptions): this {
36
49
  this.#meta.truncationFromSummary(summary, options);
37
50
  return this;
@@ -1,4 +1,4 @@
1
- import { $env } from "@sayknow-cli/utils/env";
1
+ import { $pickenv } from "@sayknow-cli/utils/env";
2
2
 
3
3
  export type EditMode = "replace" | "patch" | "hashline" | "vim" | "apply_patch";
4
4
 
@@ -34,7 +34,7 @@ export function resolveEditMode(session: EditModeSessionLike): EditMode {
34
34
  const modelVariant = session.settings.getEditVariantForModel?.(activeModel);
35
35
  if (modelVariant) return modelVariant;
36
36
 
37
- const envMode = normalizeEditMode($env.PI_EDIT_VARIANT);
37
+ const envMode = normalizeEditMode($pickenv("SKC_EDIT_VARIANT", "PI_EDIT_VARIANT"));
38
38
  if (envMode) return envMode;
39
39
 
40
40
  const settingsMode = normalizeEditMode(String(session.settings.get("edit.mode") ?? ""));
@@ -143,6 +143,7 @@ function tokenizePastedPathCandidates(
143
143
  text: string,
144
144
  options: DecodePastedPathOptions,
145
145
  maxCandidates: number,
146
+ requireCompletePayload = false,
146
147
  ): TokenizePastedPathsResult {
147
148
  if (text.length > MAX_PASTED_IMAGE_PASTE_CHARACTERS) return { kind: "invalid" };
148
149
  const platform = options.platform ?? process.platform;
@@ -154,7 +155,9 @@ function tokenizePastedPathCandidates(
154
155
 
155
156
  const startCandidate = (): TokenizePastedPathsResult | undefined => {
156
157
  if (candidateStarted) return undefined;
157
- if (candidates.length >= maxCandidates) return { kind: "too-many", maxCandidates, candidates: [...candidates] };
158
+ if (!requireCompletePayload && candidates.length >= maxCandidates) {
159
+ return { kind: "too-many", maxCandidates, candidates: [...candidates] };
160
+ }
158
161
  candidateStarted = true;
159
162
  return undefined;
160
163
  };
@@ -167,7 +170,9 @@ function tokenizePastedPathCandidates(
167
170
  const finishCandidate = (): TokenizePastedPathsResult | undefined => {
168
171
  if (!candidateStarted) return undefined;
169
172
  if (!candidate) return { kind: "invalid" };
170
- if (candidates.length >= maxCandidates) return { kind: "too-many", maxCandidates, candidates: [...candidates] };
173
+ if (!requireCompletePayload && candidates.length >= maxCandidates) {
174
+ return { kind: "too-many", maxCandidates, candidates: [...candidates] };
175
+ }
171
176
  const decoded = decodeListToken(candidate, options, platform);
172
177
  if (decoded === undefined) return { kind: "invalid" };
173
178
  candidates.push(decoded);
@@ -225,6 +230,7 @@ function tokenizePastedPathCandidates(
225
230
  if (state !== "normal") return { kind: "invalid" };
226
231
  const finalResult = finishCandidate();
227
232
  if (finalResult) return finalResult;
233
+ if (candidates.length > maxCandidates) return { kind: "too-many", maxCandidates, candidates };
228
234
  return candidates.length === 0 ? { kind: "invalid" } : { kind: "candidates", candidates };
229
235
  }
230
236
 
@@ -257,8 +263,12 @@ function isRecognizedClipboardTempPath(filePath: string, platform: NodeJS.Platfo
257
263
  /** Resolve one recognized clipboard-temp image path lexically, before any filesystem access. */
258
264
  export function resolvePastedImagePath(text: string, options: ResolvePastedImagePathOptions = {}): string | undefined {
259
265
  const platform = options.platform ?? process.platform;
260
- const candidate = decodePastedPathCandidate(text, options);
261
- if (!candidate || !IMAGE_FILE_EXTENSION_PATTERN.test(candidate)) return undefined;
266
+ const terminalNewlinesTrimmed = text.replace(/(?:\r\n|\r|\n)+$/, "");
267
+ if (/\r|\n/.test(terminalNewlinesTrimmed)) return undefined;
268
+ const candidates = decodePastedPathCandidates(terminalNewlinesTrimmed, options, 1);
269
+ if (candidates?.length !== 1) return undefined;
270
+ const candidate = candidates[0];
271
+ if (!IMAGE_FILE_EXTENSION_PATTERN.test(candidate)) return undefined;
262
272
  if (platform === "win32" && isRemoteWindowsPath(candidate)) return undefined;
263
273
  const resolved = resolveCandidate(candidate, options, platform);
264
274
  return isRecognizedClipboardTempPath(resolved, platform) ? resolved : undefined;
@@ -274,10 +284,9 @@ export function parsePastedImagePaths(
274
284
  options: ResolvePastedImagePathOptions = {},
275
285
  ): PastedImagePathParseResult | undefined {
276
286
  const platform = options.platform ?? process.platform;
277
- const tokenized = tokenizePastedPathCandidates(text, options, MAX_PASTED_IMAGE_COUNT);
287
+ const tokenized = tokenizePastedPathCandidates(text, options, MAX_PASTED_IMAGE_COUNT, true);
278
288
  if (tokenized.kind === "too-many") {
279
289
  if (
280
- tokenized.candidates.length !== MAX_PASTED_IMAGE_COUNT ||
281
290
  tokenized.candidates.some(
282
291
  candidate =>
283
292
  !IMAGE_FILE_EXTENSION_PATTERN.test(candidate) ||