@sayknow-cli/coding-agent 0.4.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +20 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -4,21 +4,19 @@ import type { AgentToolResult } from "@sayknow-cli/agent-core";
4
4
  import type { ImageContent, TextContent } from "@sayknow-cli/ai";
5
5
  import { htmlToMarkdown } from "@sayknow-cli/natives";
6
6
  import { type Component, Text } from "@sayknow-cli/tui";
7
- import { $which, ptree, truncate } from "@sayknow-cli/utils";
7
+ import { ptree, truncate } from "@sayknow-cli/utils";
8
8
  import type { Settings } from "../config/settings";
9
9
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
10
10
  import { type Theme, theme } from "../modes/theme/theme";
11
11
  import type { ToolSession } from "../sdk";
12
12
  import type { AgentStorage } from "../session/agent-storage";
13
- import { DEFAULT_MAX_BYTES, truncateHead } from "../session/streaming-output";
13
+ import { DEFAULT_MAX_BYTES, type TruncationDirection, truncateContent } from "../session/streaming-output";
14
14
  import { renderStatusLine } from "../tui";
15
15
  import { CachedOutputBlock } from "../tui/output-block";
16
16
  import { formatDimensionNote, resizeImage } from "../utils/image-resize";
17
17
  import { parseHtmlLazy } from "../utils/linkedom";
18
- import { ensureTool } from "../utils/tools-manager";
19
- import { INSANE_NOTES, tryInsaneFetch } from "../web/insane/bridge";
20
- import { validatePublicHttpUrl, validatePublicHttpUrlForInsane } from "../web/insane/url-guard";
21
- import { extractWithParallel, findParallelApiKey, getParallelExtractContent } from "../web/parallel";
18
+ import { INSANE_NOTES } from "../web/insane/bridge";
19
+ import { validatePublicHttpUrl } from "../web/insane/url-guard";
22
20
  import { specialHandlers } from "../web/scrapers";
23
21
  import type { RenderResult } from "../web/scrapers/types";
24
22
  import { finalizeOutput, loadPage, looksLikeHtml, MAX_OUTPUT_CHARS } from "../web/scrapers/types";
@@ -93,13 +91,6 @@ const MAX_INLINE_IMAGE_OUTPUT_BYTES = 300 * 1024;
93
91
  // Utilities
94
92
  // =============================================================================
95
93
 
96
- /**
97
- * Check if a command exists (cross-platform)
98
- */
99
- function hasCommand(cmd: string): boolean {
100
- return Boolean($which(cmd));
101
- }
102
-
103
94
  /**
104
95
  * Build llms.txt candidates scoped to the requested URL
105
96
  */
@@ -537,96 +528,18 @@ async function parseFeedToMarkdown(content: string, maxItems = 10): Promise<stri
537
528
  return content; // Fall back to raw content
538
529
  }
539
530
 
540
- /**
541
- * Render HTML to markdown using Parallel, jina, trafilatura, lynx (in order of preference)
542
- */
543
- async function renderHtmlToText(
544
- url: string,
531
+ export async function renderHtmlToText(
545
532
  html: string,
546
- timeout: number,
547
- settings: Settings,
548
- userSignal: AbortSignal | undefined,
549
- storage: AgentStorage | null,
533
+ signal?: AbortSignal,
550
534
  ): Promise<{ content: string; ok: boolean; method: string }> {
551
- const signal = ptree.combineSignals(userSignal, timeout * 1000);
552
- const execOptions = {
553
- mode: "group" as const,
554
- allowNonZero: true,
555
- allowAbort: true,
556
- stderr: "full" as const,
557
- signal,
558
- };
559
-
560
- // Try Parallel extract first when credentials are configured
561
- if (settings.get("providers.parallelFetch") && findParallelApiKey(storage)) {
562
- try {
563
- const parallelResult = await extractWithParallel(
564
- [url],
565
- {
566
- objective: "Extract the main content",
567
- excerpts: true,
568
- fullContent: false,
569
- signal,
570
- },
571
- storage,
572
- );
573
- const firstDocument = parallelResult.results[0];
574
- if (firstDocument) {
575
- const content = getParallelExtractContent(firstDocument);
576
- if (content.trim().length > 100 && !isLowQualityOutput(content)) {
577
- return { content, ok: true, method: "parallel" };
578
- }
579
- }
580
- } catch {
581
- // Parallel extract failed, continue to next method
582
- signal?.throwIfAborted();
583
- }
584
- }
585
-
586
- // Try jina first (reader API)
587
535
  try {
588
- const jinaUrl = `https://r.jina.ai/${url}`;
589
- const response = await fetch(jinaUrl, {
590
- headers: { Accept: "text/markdown" },
591
- signal,
592
- });
593
- if (response.ok) {
594
- const content = await response.text();
595
- if (content.trim().length > 100 && !isLowQualityOutput(content)) {
596
- return { content, ok: true, method: "jina" };
597
- }
598
- }
599
- } catch {
600
- // Jina failed, continue to next method
601
536
  signal?.throwIfAborted();
602
- }
603
-
604
- // Try trafilatura (auto-install via uv/pip)
605
- const trafilatura = await ensureTool("trafilatura", { signal, silent: true });
606
- if (trafilatura) {
607
- const result = await ptree.exec([trafilatura, "-u", url, "--output-format", "markdown"], execOptions);
608
- if (result.ok && result.stdout.trim().length > 100) {
609
- return { content: result.stdout, ok: true, method: "trafilatura" };
610
- }
611
- }
612
-
613
- // Try lynx (can't auto-install, system package)
614
- const lynx = hasCommand("lynx");
615
- if (lynx) {
616
- const result = await ptree.exec(["lynx", "-dump", "-nolist", "-width", "250", url], execOptions);
617
- if (result.ok) {
618
- return { content: result.stdout, ok: true, method: "lynx" };
619
- }
620
- }
621
-
622
- // Fall back to native converter (fastest, no network/subprocess)
623
- try {
624
537
  const content = await htmlToMarkdown(html, { cleanContent: true });
625
538
  if (content.trim().length > 100 && !isLowQualityOutput(content)) {
626
539
  return { content, ok: true, method: "native" };
627
540
  }
628
541
  } catch {
629
- // Native converter failed, continue to next method
542
+ // Native conversion failed; the caller returns its bounded raw HTML.
630
543
  signal?.throwIfAborted();
631
544
  }
632
545
  return { content: "", ok: false, method: "none" };
@@ -707,14 +620,6 @@ async function handleSpecialUrls(
707
620
  // Main Render Function
708
621
  // =============================================================================
709
622
 
710
- /**
711
- * Opt-in insane-search fallback for blocked / degraded public URL reads.
712
- *
713
- * Returns a finalized `method: "insane"` result on success, or null (so the
714
- * caller continues with its normal degraded behavior). Fail-closed: no note,
715
- * guard DNS, dependency probe, or subprocess when raw mode or the opt-in
716
- * setting is off. The public-URL guard runs BEFORE any probe/spawn.
717
- */
718
623
  export async function tryInsaneFallback(args: {
719
624
  url: string;
720
625
  finalUrl: string;
@@ -727,32 +632,7 @@ export async function tryInsaneFallback(args: {
727
632
  }): Promise<FetchRenderResult | null> {
728
633
  if (args.raw) return null;
729
634
  if (args.settings.get("web.insaneFallback") !== true) return null;
730
-
731
- const target = args.finalUrl || args.url;
732
- const guard = await validatePublicHttpUrlForInsane(target);
733
- if (!guard.ok) {
734
- args.notes.push(INSANE_NOTES.guardBlocked(guard.reason));
735
- return null;
736
- }
737
-
738
- const result = await tryInsaneFetch(guard.url.toString(), {
739
- timeoutMs: args.timeout * 1000,
740
- signal: args.signal,
741
- });
742
- if (result.ok) {
743
- const output = finalizeOutput(result.content);
744
- return {
745
- url: args.url,
746
- finalUrl: target,
747
- contentType: "text/markdown",
748
- method: "insane",
749
- content: output.content,
750
- fetchedAt: args.fetchedAt,
751
- truncated: output.truncated,
752
- notes: [...args.notes, ...result.notes],
753
- };
754
- }
755
- for (const note of result.notes) args.notes.push(note);
635
+ args.notes.push(INSANE_NOTES.securityDisabled);
756
636
  return null;
757
637
  }
758
638
 
@@ -772,6 +652,7 @@ async function renderUrl(
772
652
  if (signal?.aborted) {
773
653
  throw new ToolAbortError();
774
654
  }
655
+ const preflightSignal = ptree.combineSignals(signal, timeout * 1000);
775
656
 
776
657
  // Handle internal protocol URLs (e.g., pi-internal://) - return empty
777
658
  if (url.startsWith("pi-internal://")) {
@@ -789,7 +670,8 @@ async function renderUrl(
789
670
 
790
671
  // Step 0: Normalize URL (ensure scheme for special handlers)
791
672
  url = normalizeUrl(url);
792
- const publicUrl = await validatePublicHttpUrl(url);
673
+ const publicUrl = await validatePublicHttpUrl(url, { signal: preflightSignal });
674
+ if (signal?.aborted) throw new ToolAbortError();
793
675
  if (!publicUrl.ok) {
794
676
  notes.push(`Blocked URL fetch: target URL is not public HTTP(S): ${publicUrl.reason}`);
795
677
  return {
@@ -1138,10 +1020,10 @@ async function renderUrl(
1138
1020
  throw new ToolAbortError();
1139
1021
  }
1140
1022
 
1141
- // 5E: Render HTML with lynx or html2text
1142
- const htmlResult = await renderHtmlToText(finalUrl, rawContent, timeout, settings, signal, storage);
1023
+ // 5E: Render only the bytes already fetched above.
1024
+ const htmlResult = await renderHtmlToText(rawContent, signal);
1143
1025
  if (!htmlResult.ok) {
1144
- notes.push("html rendering failed (lynx/html2text unavailable)");
1026
+ notes.push("native HTML rendering failed");
1145
1027
  const insane = await tryInsaneFallback({ url, finalUrl, timeout, raw, settings, signal, fetchedAt, notes });
1146
1028
  if (insane) return insane;
1147
1029
  const output = finalizeOutput(rawContent);
@@ -1264,7 +1146,12 @@ interface ReadUrlCacheEntry {
1264
1146
  artifactId?: string;
1265
1147
  details: ReadUrlToolDetails;
1266
1148
  image?: FetchImagePayload;
1149
+ /** Complete URL output, including its preamble and body. */
1267
1150
  output: string;
1151
+ /** UTF-16 code-unit offset of the preamble in `output`. */
1152
+ preambleChars: number;
1153
+ /** Offset of the preamble after `wrapUntrustedContent(output)` is persisted. */
1154
+ wrappedPreambleChars?: number;
1268
1155
  }
1269
1156
 
1270
1157
  const readUrlCache = new Map<string, ReadUrlCacheEntry>();
@@ -1296,7 +1183,13 @@ async function materializeReadUrlCacheEntry(
1296
1183
  if (entry.artifactId) {
1297
1184
  const artifactOutput = await readArtifactOutput(session, entry.artifactId);
1298
1185
  if (artifactOutput !== null) {
1299
- return { ...entry, output: artifactOutput };
1186
+ // Artifacts retain their trust-boundary wrapper. Keep the raw preamble
1187
+ // coordinate alongside the escaped coordinate for live/rehydrated parity.
1188
+ return {
1189
+ ...entry,
1190
+ output: artifactOutput,
1191
+ ...(entry.wrappedPreambleChars !== undefined ? { wrappedPreambleChars: entry.wrappedPreambleChars } : {}),
1192
+ };
1300
1193
  }
1301
1194
  }
1302
1195
  return null;
@@ -1336,8 +1229,8 @@ async function buildReadUrlCacheEntry(
1336
1229
 
1337
1230
  const storage = session.settings.getStorage();
1338
1231
  const result = await renderUrl(url, effectiveTimeout, raw, session.settings, signal, storage);
1339
- const output = buildUrlReadOutput(result, result.content);
1340
- const artifactId = options?.ensureArtifact ? await persistReadUrlArtifact(session, output) : undefined;
1232
+ const built = buildUrlReadOutput(result, result.content);
1233
+ const artifactId = options?.ensureArtifact ? await persistReadUrlArtifact(session, built.output) : undefined;
1341
1234
 
1342
1235
  return {
1343
1236
  artifactId,
@@ -1351,7 +1244,9 @@ async function buildReadUrlCacheEntry(
1351
1244
  notes: result.notes,
1352
1245
  },
1353
1246
  image: result.image,
1354
- output,
1247
+ output: built.output,
1248
+ preambleChars: built.preambleChars,
1249
+ wrappedPreambleChars: built.wrappedPreambleChars,
1355
1250
  };
1356
1251
  }
1357
1252
 
@@ -1381,60 +1276,138 @@ export async function loadReadUrlCacheEntry(
1381
1276
 
1382
1277
  const UNTRUSTED_CONTENT_OPEN = "<untrusted-content>";
1383
1278
  const UNTRUSTED_CONTENT_CLOSE = "</untrusted-content>";
1279
+ export const WRAP_PREFIX_CHARS = `${UNTRUSTED_CONTENT_OPEN}\n`.length;
1280
+ const WRAP_SUFFIX = `\n${UNTRUSTED_CONTENT_CLOSE}`;
1281
+
1282
+ export function escapeUntrustedContent(content: string): string {
1283
+ return content.replace(/<\/untrusted-content>/gi, "&lt;/untrusted-content>");
1284
+ }
1384
1285
 
1385
1286
  export function wrapUntrustedContent(content: string): string {
1386
- return `${UNTRUSTED_CONTENT_OPEN}\n${content.replace(/<\/untrusted-content>/gi, "&lt;/untrusted-content>")}\n${UNTRUSTED_CONTENT_CLOSE}`;
1287
+ return `${UNTRUSTED_CONTENT_OPEN}\n${escapeUntrustedContent(content)}${WRAP_SUFFIX}`;
1387
1288
  }
1388
1289
 
1389
- function buildUrlReadOutput(result: FetchRenderResult, content: string): string {
1390
- let output = "";
1391
- output += `URL: ${result.finalUrl}\n`;
1392
- output += `Content-Type: ${result.contentType}\n`;
1393
- output += `Method: ${result.method}\n`;
1290
+ function isWrappedReadUrlOutput(output: string): boolean {
1291
+ return output.startsWith(`${UNTRUSTED_CONTENT_OPEN}\n`) && output.endsWith(WRAP_SUFFIX);
1292
+ }
1293
+
1294
+ /**
1295
+ * Remove only the outer URL-output wrapper before line pagination. The content
1296
+ * remains escaped exactly as persisted; it is deliberately never unescaped.
1297
+ */
1298
+ export function prepareReadUrlSelectorInput(
1299
+ output: string,
1300
+ preambleChars: number,
1301
+ wrappedPreambleChars?: number,
1302
+ ): { text: string; preambleChars: number } {
1303
+ if (!isWrappedReadUrlOutput(output)) return { text: output, preambleChars };
1304
+ const bodyEnd = output.length - WRAP_SUFFIX.length;
1305
+ return {
1306
+ text: output.slice(WRAP_PREFIX_CHARS, Math.max(WRAP_PREFIX_CHARS, bodyEnd)),
1307
+ preambleChars: Math.max(0, (wrappedPreambleChars ?? preambleChars) - WRAP_PREFIX_CHARS),
1308
+ };
1309
+ }
1310
+ interface BuiltUrlReadOutput {
1311
+ output: string;
1312
+ preambleChars: number;
1313
+ wrappedPreambleChars: number;
1314
+ }
1315
+
1316
+ function buildReadUrlPreamble(
1317
+ result: Pick<FetchRenderResult, "finalUrl" | "contentType" | "method" | "notes">,
1318
+ ): string {
1319
+ let preamble = "";
1320
+ preamble += `URL: ${result.finalUrl}\n`;
1321
+ preamble += `Content-Type: ${result.contentType}\n`;
1322
+ preamble += `Method: ${result.method}\n`;
1394
1323
  if (result.notes.length > 0) {
1395
- output += `Notes: ${result.notes.join("; ")}\n`;
1324
+ preamble += `Notes: ${result.notes.join("; ")}\n`;
1396
1325
  }
1397
- output += `\n---\n\n`;
1398
- output += content;
1399
- return output;
1326
+ preamble += "\n---\n\n";
1327
+ return preamble;
1328
+ }
1329
+
1330
+ function buildUrlReadOutput(result: FetchRenderResult, content: string): BuiltUrlReadOutput {
1331
+ const preamble = buildReadUrlPreamble(result);
1332
+ const preambleChars = preamble.length;
1333
+ const output = `${preamble}${content}`;
1334
+ return {
1335
+ output,
1336
+ preambleChars,
1337
+ wrappedPreambleChars: WRAP_PREFIX_CHARS + escapeUntrustedContent(preamble).length,
1338
+ };
1400
1339
  }
1401
1340
 
1402
1341
  export async function executeReadUrl(
1403
1342
  session: ToolSession,
1404
1343
  params: { path: string; raw?: boolean },
1405
1344
  signal?: AbortSignal,
1345
+ direction: TruncationDirection = "head",
1406
1346
  ): Promise<AgentToolResult<ReadUrlToolDetails>> {
1407
1347
  let cacheEntry = await loadReadUrlCacheEntry(session, params, signal, { preferCached: true });
1408
- const truncation = truncateHead(cacheEntry.output, {
1348
+ // `head` intentionally retains the historical whole-output accounting so the
1349
+ // existing URL golden remains byte-identical. Non-head directions account for
1350
+ // and truncate only the body; preamble bytes/chars are never part of that cap.
1351
+ const effectiveDirection =
1352
+ direction !== "head" && cacheEntry.wrappedPreambleChars === undefined ? "head" : direction;
1353
+ const isWrappedOutput = isWrappedReadUrlOutput(cacheEntry.output);
1354
+ const preambleChars = isWrappedOutput
1355
+ ? (cacheEntry.wrappedPreambleChars ??
1356
+ WRAP_PREFIX_CHARS + escapeUntrustedContent(buildReadUrlPreamble(cacheEntry.details)).length)
1357
+ : cacheEntry.preambleChars;
1358
+ const wrappedPrefix = isWrappedOutput ? WRAP_PREFIX_CHARS : 0;
1359
+ const wrappedSuffix = isWrappedOutput ? `\n${UNTRUSTED_CONTENT_CLOSE}` : "";
1360
+ const bodyEnd = cacheEntry.output.length - wrappedSuffix.length;
1361
+ const unwrappedOutput = isWrappedOutput
1362
+ ? cacheEntry.output.slice(wrappedPrefix, Math.max(wrappedPrefix, bodyEnd))
1363
+ : cacheEntry.output;
1364
+ const truncationInput =
1365
+ effectiveDirection === "head"
1366
+ ? unwrappedOutput
1367
+ : cacheEntry.output.slice(preambleChars, Math.max(preambleChars, bodyEnd));
1368
+ const truncation = truncateContent(truncationInput, {
1409
1369
  maxBytes: DEFAULT_MAX_BYTES,
1410
1370
  maxLines: FETCH_DEFAULT_MAX_LINES,
1371
+ direction: effectiveDirection,
1411
1372
  });
1412
1373
  const needsArtifact = truncation.truncated;
1413
1374
  if (needsArtifact && !cacheEntry.artifactId) {
1414
1375
  cacheEntry = await ensureReadUrlCacheArtifact(session, cacheEntry);
1415
1376
  cacheReadUrlEntry(session, params.path, params.raw ?? false, cacheEntry);
1416
1377
  }
1417
- const output = needsArtifact ? truncation.content : cacheEntry.output;
1378
+ const output =
1379
+ effectiveDirection !== "head"
1380
+ ? needsArtifact
1381
+ ? `${cacheEntry.output.slice(0, preambleChars)}${truncation.content}${wrappedSuffix}`
1382
+ : cacheEntry.output
1383
+ : truncation.content;
1418
1384
  const details: ReadUrlToolDetails = {
1419
1385
  ...cacheEntry.details,
1420
1386
  truncated: Boolean(cacheEntry.details.truncated || needsArtifact),
1421
1387
  };
1422
1388
 
1423
- const contentBlocks: Array<TextContent | ImageContent> = [{ type: "text", text: wrapUntrustedContent(output) }];
1389
+ const contentBlocks: Array<TextContent | ImageContent> = [
1390
+ { type: "text", text: isWrappedOutput && effectiveDirection !== "head" ? output : wrapUntrustedContent(output) },
1391
+ ];
1424
1392
  if (cacheEntry.image) {
1425
1393
  contentBlocks.push({ type: "image", data: cacheEntry.image.data, mimeType: cacheEntry.image.mimeType });
1426
1394
  }
1427
1395
 
1428
1396
  const resultBuilder = toolResult(details).content(contentBlocks).sourceUrl(details.finalUrl);
1429
1397
  if (needsArtifact) {
1430
- resultBuilder.truncation(truncation, { direction: "head", artifactId: cacheEntry.artifactId });
1398
+ resultBuilder.truncation(truncation, {
1399
+ direction: effectiveDirection === "both" ? "middle" : effectiveDirection === "last" ? "tail" : "head",
1400
+ artifactId: cacheEntry.artifactId,
1401
+ ...(effectiveDirection !== "head" ? { maxBytes: DEFAULT_MAX_BYTES } : {}),
1402
+ });
1431
1403
  } else if (cacheEntry.details.truncated) {
1432
- const outputLines = cacheEntry.output.split("\n").length;
1433
- const outputBytes = Buffer.byteLength(cacheEntry.output, "utf-8");
1404
+ const reportedText = effectiveDirection === "head" ? cacheEntry.output : truncationInput;
1405
+ const outputLines = reportedText.split("\n").length;
1406
+ const outputBytes = Buffer.byteLength(reportedText, "utf-8");
1434
1407
  const totalBytes = Math.max(outputBytes + 1, MAX_OUTPUT_CHARS + 1);
1435
1408
  const totalLines = outputLines + 1;
1436
- resultBuilder.truncationFromText(cacheEntry.output, {
1437
- direction: "tail",
1409
+ resultBuilder.truncationFromText(reportedText, {
1410
+ direction: effectiveDirection === "both" ? "middle" : effectiveDirection === "last" ? "tail" : "head",
1438
1411
  totalLines,
1439
1412
  totalBytes,
1440
1413
  maxBytes: MAX_OUTPUT_CHARS,