hoomanjs 1.46.0 → 1.55.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 (340) hide show
  1. package/README.md +44 -18
  2. package/dist/acp/acp-agent.d.ts +42 -0
  3. package/dist/acp/acp-agent.js +336 -47
  4. package/dist/acp/acp-agent.js.map +1 -1
  5. package/dist/acp/approvals.js +11 -10
  6. package/dist/acp/approvals.js.map +1 -1
  7. package/dist/acp/browser.d.ts +9 -0
  8. package/dist/acp/browser.js +23 -0
  9. package/dist/acp/browser.js.map +1 -0
  10. package/dist/acp/meta/vscode.d.ts +8 -7
  11. package/dist/acp/meta/vscode.js +10 -11
  12. package/dist/acp/meta/vscode.js.map +1 -1
  13. package/dist/acp/session-config.d.ts +2 -2
  14. package/dist/acp/session-config.js +24 -20
  15. package/dist/acp/session-config.js.map +1 -1
  16. package/dist/acp/sessions/store.d.ts +3 -3
  17. package/dist/acp/utils/tool-kind.js +8 -6
  18. package/dist/acp/utils/tool-kind.js.map +1 -1
  19. package/dist/chat/app.js +91 -11
  20. package/dist/chat/app.js.map +1 -1
  21. package/dist/chat/approvals.js +3 -0
  22. package/dist/chat/approvals.js.map +1 -1
  23. package/dist/chat/components/ApprovalPrompt.js +15 -25
  24. package/dist/chat/components/ApprovalPrompt.js.map +1 -1
  25. package/dist/chat/components/BackgroundJobsPanel.d.ts +10 -0
  26. package/dist/chat/components/BackgroundJobsPanel.js +15 -0
  27. package/dist/chat/components/BackgroundJobsPanel.js.map +1 -0
  28. package/dist/chat/components/BottomChrome.d.ts +6 -1
  29. package/dist/chat/components/BottomChrome.js +3 -2
  30. package/dist/chat/components/BottomChrome.js.map +1 -1
  31. package/dist/chat/components/ChromePicker.d.ts +7 -2
  32. package/dist/chat/components/ChromePicker.js +18 -2
  33. package/dist/chat/components/ChromePicker.js.map +1 -1
  34. package/dist/chat/components/Composer.js +3 -2
  35. package/dist/chat/components/Composer.js.map +1 -1
  36. package/dist/chat/components/DownloadPanel.js +2 -1
  37. package/dist/chat/components/DownloadPanel.js.map +1 -1
  38. package/dist/chat/components/EmptyChatBanner.js +2 -1
  39. package/dist/chat/components/EmptyChatBanner.js.map +1 -1
  40. package/dist/chat/components/PromptInput.js +2 -1
  41. package/dist/chat/components/PromptInput.js.map +1 -1
  42. package/dist/chat/components/QuestionPrompt.js +5 -4
  43. package/dist/chat/components/QuestionPrompt.js.map +1 -1
  44. package/dist/chat/components/QueuedPrompts.js +3 -2
  45. package/dist/chat/components/QueuedPrompts.js.map +1 -1
  46. package/dist/chat/components/ReasoningStrip.js +2 -1
  47. package/dist/chat/components/ReasoningStrip.js.map +1 -1
  48. package/dist/chat/components/RetryEvent.js +2 -1
  49. package/dist/chat/components/RetryEvent.js.map +1 -1
  50. package/dist/chat/components/SelectPicker.js +2 -1
  51. package/dist/chat/components/SelectPicker.js.map +1 -1
  52. package/dist/chat/components/ShellToolEvent.d.ts +12 -0
  53. package/dist/chat/components/ShellToolEvent.js +94 -0
  54. package/dist/chat/components/ShellToolEvent.js.map +1 -0
  55. package/dist/chat/components/SlashCommands.js +2 -1
  56. package/dist/chat/components/SlashCommands.js.map +1 -1
  57. package/dist/chat/components/Spinner.js +2 -1
  58. package/dist/chat/components/Spinner.js.map +1 -1
  59. package/dist/chat/components/StatusBar.js +19 -15
  60. package/dist/chat/components/StatusBar.js.map +1 -1
  61. package/dist/chat/components/ThinkingStatus.js +2 -1
  62. package/dist/chat/components/ThinkingStatus.js.map +1 -1
  63. package/dist/chat/components/ThoughtEvent.js +2 -1
  64. package/dist/chat/components/ThoughtEvent.js.map +1 -1
  65. package/dist/chat/components/TodoPanel.js +2 -1
  66. package/dist/chat/components/TodoPanel.js.map +1 -1
  67. package/dist/chat/components/ToolEvent.d.ts +2 -1
  68. package/dist/chat/components/ToolEvent.js +9 -2
  69. package/dist/chat/components/ToolEvent.js.map +1 -1
  70. package/dist/chat/components/ToolEventFileResult.js +8 -7
  71. package/dist/chat/components/ToolEventFileResult.js.map +1 -1
  72. package/dist/chat/components/Transcript.d.ts +4 -2
  73. package/dist/chat/components/Transcript.js +4 -4
  74. package/dist/chat/components/Transcript.js.map +1 -1
  75. package/dist/chat/components/markdown/BlockRenderer.js +3 -2
  76. package/dist/chat/components/markdown/BlockRenderer.js.map +1 -1
  77. package/dist/chat/components/markdown/CodeBlock.js +3 -2
  78. package/dist/chat/components/markdown/CodeBlock.js.map +1 -1
  79. package/dist/chat/components/markdown/InlineRenderer.js +6 -5
  80. package/dist/chat/components/markdown/InlineRenderer.js.map +1 -1
  81. package/dist/chat/components/shared.js +7 -6
  82. package/dist/chat/components/shared.js.map +1 -1
  83. package/dist/chat/types.d.ts +7 -0
  84. package/dist/cli.js +162 -44
  85. package/dist/cli.js.map +1 -1
  86. package/dist/configure/app.js +46 -29
  87. package/dist/configure/app.js.map +1 -1
  88. package/dist/configure/components/BusyScreen.js +2 -1
  89. package/dist/configure/components/BusyScreen.js.map +1 -1
  90. package/dist/configure/components/MenuScreen.js +3 -2
  91. package/dist/configure/components/MenuScreen.js.map +1 -1
  92. package/dist/configure/components/PromptForm.js +2 -1
  93. package/dist/configure/components/PromptForm.js.map +1 -1
  94. package/dist/configure/components/SelectMenuItem.js +4 -3
  95. package/dist/configure/components/SelectMenuItem.js.map +1 -1
  96. package/dist/configure/utils.js +4 -3
  97. package/dist/configure/utils.js.map +1 -1
  98. package/dist/core/agent/default-retry-strategy.js +46 -12
  99. package/dist/core/agent/default-retry-strategy.js.map +1 -1
  100. package/dist/core/agent/gitignore-guard-plugin.js +17 -2
  101. package/dist/core/agent/gitignore-guard-plugin.js.map +1 -1
  102. package/dist/core/agent/index.js +38 -7
  103. package/dist/core/agent/index.js.map +1 -1
  104. package/dist/core/approvals/allowlist.js +1 -0
  105. package/dist/core/approvals/allowlist.js.map +1 -1
  106. package/dist/core/approvals/intervention.d.ts +10 -2
  107. package/dist/core/approvals/intervention.js +46 -8
  108. package/dist/core/approvals/intervention.js.map +1 -1
  109. package/dist/core/config.d.ts +9 -2
  110. package/dist/core/config.js +8 -4
  111. package/dist/core/config.js.map +1 -1
  112. package/dist/core/index.d.ts +2 -2
  113. package/dist/core/mcp/oauth/service.js +2 -2
  114. package/dist/core/mcp/oauth/service.js.map +1 -1
  115. package/dist/core/models/bedrock.js +6 -0
  116. package/dist/core/models/bedrock.js.map +1 -1
  117. package/dist/core/models/groq.js +34 -14
  118. package/dist/core/models/groq.js.map +1 -1
  119. package/dist/core/models/minimax.js +1 -1
  120. package/dist/core/models/minimax.js.map +1 -1
  121. package/dist/core/models/types.d.ts +10 -2
  122. package/dist/core/models/types.js +1 -0
  123. package/dist/core/models/types.js.map +1 -1
  124. package/dist/core/modes/definitions.d.ts +4 -2
  125. package/dist/core/modes/definitions.js +77 -9
  126. package/dist/core/modes/definitions.js.map +1 -1
  127. package/dist/core/modes/index.d.ts +1 -1
  128. package/dist/core/modes/index.js +1 -1
  129. package/dist/core/modes/index.js.map +1 -1
  130. package/dist/core/modes/registry.js +4 -9
  131. package/dist/core/modes/registry.js.map +1 -1
  132. package/dist/core/modes/schema.d.ts +1 -1
  133. package/dist/core/modes/schema.js +1 -1
  134. package/dist/core/modes/schema.js.map +1 -1
  135. package/dist/core/prompts/agents/{review.md → code-review.md} +2 -2
  136. package/dist/core/prompts/agents/design-review.md +35 -0
  137. package/dist/core/prompts/agents/{test-investigator.md → quality-analyst.md} +2 -2
  138. package/dist/core/prompts/modes/agent.md +2 -0
  139. package/dist/core/prompts/modes/ask.md +2 -2
  140. package/dist/core/prompts/modes/design.md +37 -0
  141. package/dist/core/prompts/modes/plan.md +5 -5
  142. package/dist/core/prompts/{runtime.js → runtime/agents.js} +2 -2
  143. package/dist/core/prompts/runtime/agents.js.map +1 -0
  144. package/dist/core/prompts/runtime/design.d.ts +11 -0
  145. package/dist/core/prompts/runtime/design.js +78 -0
  146. package/dist/core/prompts/runtime/design.js.map +1 -0
  147. package/dist/core/prompts/{session-mode-appendix.js → session-mode.js} +1 -1
  148. package/dist/core/prompts/session-mode.js.map +1 -0
  149. package/dist/core/prompts/static/ask-user.md +3 -2
  150. package/dist/core/prompts/static/fetch.md +2 -1
  151. package/dist/core/prompts/static/filesystem.md +2 -1
  152. package/dist/core/prompts/static/shell.md +21 -1
  153. package/dist/core/prompts/static/subagents.md +2 -1
  154. package/dist/core/prompts/static/switch-mode.md +10 -0
  155. package/dist/core/prompts/system.d.ts +1 -0
  156. package/dist/core/prompts/system.js +11 -3
  157. package/dist/core/prompts/system.js.map +1 -1
  158. package/dist/core/shell/index.d.ts +7 -0
  159. package/dist/core/shell/index.js +6 -0
  160. package/dist/core/shell/index.js.map +1 -0
  161. package/dist/core/shell/manager.d.ts +23 -0
  162. package/dist/core/shell/manager.js +604 -0
  163. package/dist/core/shell/manager.js.map +1 -0
  164. package/dist/core/shell/output-buffer.d.ts +30 -0
  165. package/dist/core/shell/output-buffer.js +116 -0
  166. package/dist/core/shell/output-buffer.js.map +1 -0
  167. package/dist/core/shell/registry.d.ts +5 -0
  168. package/dist/core/shell/registry.js +24 -0
  169. package/dist/core/shell/registry.js.map +1 -0
  170. package/dist/core/{tools/shell.d.ts → shell/terminal-backend.d.ts} +20 -18
  171. package/dist/core/shell/terminal-backend.js +18 -0
  172. package/dist/core/shell/terminal-backend.js.map +1 -0
  173. package/dist/core/shell/tools.d.ts +41 -0
  174. package/dist/core/{tools/shell.js → shell/tools.js} +186 -11
  175. package/dist/core/shell/tools.js.map +1 -0
  176. package/dist/core/shell/types.d.ts +95 -0
  177. package/dist/core/shell/types.js +2 -0
  178. package/dist/core/shell/types.js.map +1 -0
  179. package/dist/core/skills/built-in/hooman-config/SKILL.md +30 -35
  180. package/dist/core/skills/built-in/hooman-config/search.md +5 -3
  181. package/dist/core/skills/built-in/hooman-design/SKILL.md +97 -0
  182. package/dist/core/skills/built-in/hooman-design/assets/article-shell.html +87 -0
  183. package/dist/core/skills/built-in/hooman-design/assets/auth-shell.html +143 -0
  184. package/dist/core/skills/built-in/hooman-design/assets/business-card-shell.html +112 -0
  185. package/dist/core/skills/built-in/hooman-design/assets/contact-shell.html +181 -0
  186. package/dist/core/skills/built-in/hooman-design/assets/dashboard-shell.html +90 -0
  187. package/dist/core/skills/built-in/hooman-design/assets/data-table-shell.html +114 -0
  188. package/dist/core/skills/built-in/hooman-design/assets/deck-shell.html +187 -0
  189. package/dist/core/skills/built-in/hooman-design/assets/dialog-shell.html +98 -0
  190. package/dist/core/skills/built-in/hooman-design/assets/email-shell.html +101 -0
  191. package/dist/core/skills/built-in/hooman-design/assets/empty-state-shell.html +92 -0
  192. package/dist/core/skills/built-in/hooman-design/assets/flyer-shell.html +168 -0
  193. package/dist/core/skills/built-in/hooman-design/assets/invite-shell.html +167 -0
  194. package/dist/core/skills/built-in/hooman-design/assets/landing-shell.html +184 -0
  195. package/dist/core/skills/built-in/hooman-design/assets/mobile-frame-shell.html +109 -0
  196. package/dist/core/skills/built-in/hooman-design/assets/newsletter-shell.html +141 -0
  197. package/dist/core/skills/built-in/hooman-design/assets/onboarding-shell.html +131 -0
  198. package/dist/core/skills/built-in/hooman-design/assets/pricing-shell.html +160 -0
  199. package/dist/core/skills/built-in/hooman-design/assets/prototype-shell.html +83 -0
  200. package/dist/core/skills/built-in/hooman-design/assets/resume-shell.html +228 -0
  201. package/dist/core/skills/built-in/hooman-design/assets/settings-shell.html +196 -0
  202. package/dist/core/skills/built-in/hooman-design/assets/tokens/botanical-calm.css +34 -0
  203. package/dist/core/skills/built-in/hooman-design/assets/tokens/copper-ledger.css +34 -0
  204. package/dist/core/skills/built-in/hooman-design/assets/tokens/editorial-ink.css +34 -0
  205. package/dist/core/skills/built-in/hooman-design/assets/tokens/fintech-clear.css +34 -0
  206. package/dist/core/skills/built-in/hooman-design/assets/tokens/harbor-navy.css +35 -0
  207. package/dist/core/skills/built-in/hooman-design/assets/tokens/midnight-editorial.css +34 -0
  208. package/dist/core/skills/built-in/hooman-design/assets/tokens/night-console.css +34 -0
  209. package/dist/core/skills/built-in/hooman-design/assets/tokens/paper-mono.css +35 -0
  210. package/dist/core/skills/built-in/hooman-design/assets/tokens/signal-mono.css +34 -0
  211. package/dist/core/skills/built-in/hooman-design/assets/tokens/soft-product.css +34 -0
  212. package/dist/core/skills/built-in/hooman-design/assets/tokens/swiss-grid.css +34 -0
  213. package/dist/core/skills/built-in/hooman-design/assets/tokens/warm-atelier.css +34 -0
  214. package/dist/core/skills/built-in/hooman-design/assets/waitlist-shell.html +139 -0
  215. package/dist/core/skills/built-in/hooman-design/craft/accessibility-baseline.md +32 -0
  216. package/dist/core/skills/built-in/hooman-design/craft/anti-ai-slop.md +27 -0
  217. package/dist/core/skills/built-in/hooman-design/craft/color-surfaces.md +45 -0
  218. package/dist/core/skills/built-in/hooman-design/craft/data-viz.md +24 -0
  219. package/dist/core/skills/built-in/hooman-design/craft/deck-slides.md +46 -0
  220. package/dist/core/skills/built-in/hooman-design/craft/forms-validation.md +35 -0
  221. package/dist/core/skills/built-in/hooman-design/craft/iconography.md +19 -0
  222. package/dist/core/skills/built-in/hooman-design/craft/layout-overflow.md +31 -0
  223. package/dist/core/skills/built-in/hooman-design/craft/motion.md +41 -0
  224. package/dist/core/skills/built-in/hooman-design/craft/print.md +37 -0
  225. package/dist/core/skills/built-in/hooman-design/craft/responsive.md +30 -0
  226. package/dist/core/skills/built-in/hooman-design/craft/spacing-density.md +46 -0
  227. package/dist/core/skills/built-in/hooman-design/craft/state-coverage.md +21 -0
  228. package/dist/core/skills/built-in/hooman-design/craft/typography.md +73 -0
  229. package/dist/core/skills/built-in/hooman-design/directions.md +115 -0
  230. package/dist/core/skills/built-in/hooman-design/references/artifact-layout.md +15 -0
  231. package/dist/core/skills/built-in/hooman-design/references/export.md +47 -0
  232. package/dist/core/skills/built-in/hooman-design/references/workflow.md +80 -0
  233. package/dist/core/skills/built-in/hooman-mcp/SKILL.md +2 -2
  234. package/dist/core/state/llm-modality.d.ts +13 -0
  235. package/dist/core/state/llm-modality.js +19 -0
  236. package/dist/core/state/llm-modality.js.map +1 -0
  237. package/dist/core/state/plan.d.ts +1 -1
  238. package/dist/core/state/plan.js +1 -1
  239. package/dist/core/state/shell-jobs.d.ts +9 -0
  240. package/dist/core/state/shell-jobs.js +20 -0
  241. package/dist/core/state/shell-jobs.js.map +1 -0
  242. package/dist/core/state/tool-approvals.d.ts +12 -9
  243. package/dist/core/state/tool-approvals.js +94 -52
  244. package/dist/core/state/tool-approvals.js.map +1 -1
  245. package/dist/core/state/turn-boundaries.d.ts +33 -0
  246. package/dist/core/state/turn-boundaries.js +49 -0
  247. package/dist/core/state/turn-boundaries.js.map +1 -0
  248. package/dist/core/subagents/index.d.ts +1 -1
  249. package/dist/core/subagents/index.js +1 -1
  250. package/dist/core/subagents/registry.js +15 -6
  251. package/dist/core/subagents/registry.js.map +1 -1
  252. package/dist/core/subagents/tool.d.ts +8 -2
  253. package/dist/core/subagents/tool.js +93 -44
  254. package/dist/core/subagents/tool.js.map +1 -1
  255. package/dist/core/theme.d.ts +14 -0
  256. package/dist/core/theme.js +14 -0
  257. package/dist/core/theme.js.map +1 -0
  258. package/dist/core/tools/ask-user.d.ts +1 -1
  259. package/dist/core/tools/ask-user.js +1 -1
  260. package/dist/core/tools/export.d.ts +8 -0
  261. package/dist/core/tools/export.js +202 -0
  262. package/dist/core/tools/export.js.map +1 -0
  263. package/dist/core/tools/fetch.d.ts +2 -0
  264. package/dist/core/tools/fetch.js +92 -55
  265. package/dist/core/tools/fetch.js.map +1 -1
  266. package/dist/core/tools/filesystem.d.ts +2 -1
  267. package/dist/core/tools/filesystem.js +38 -4
  268. package/dist/core/tools/filesystem.js.map +1 -1
  269. package/dist/core/tools/index.d.ts +3 -2
  270. package/dist/core/tools/index.js +3 -2
  271. package/dist/core/tools/index.js.map +1 -1
  272. package/dist/core/tools/preview.d.ts +10 -0
  273. package/dist/core/tools/preview.js +147 -0
  274. package/dist/core/tools/preview.js.map +1 -0
  275. package/dist/core/tools/switch-mode.d.ts +6 -0
  276. package/dist/core/tools/switch-mode.js +188 -0
  277. package/dist/core/tools/switch-mode.js.map +1 -0
  278. package/dist/core/tools/web-search.js +176 -0
  279. package/dist/core/tools/web-search.js.map +1 -1
  280. package/dist/core/utils/browser.d.ts +8 -1
  281. package/dist/core/utils/browser.js +17 -1
  282. package/dist/core/utils/browser.js.map +1 -1
  283. package/dist/core/utils/export-design.d.ts +40 -0
  284. package/dist/core/utils/export-design.js +188 -0
  285. package/dist/core/utils/export-design.js.map +1 -0
  286. package/dist/core/utils/export-figma.d.ts +17 -0
  287. package/dist/core/utils/export-figma.js +189 -0
  288. package/dist/core/utils/export-figma.js.map +1 -0
  289. package/dist/core/utils/export-sketch.d.ts +11 -0
  290. package/dist/core/utils/export-sketch.js +456 -0
  291. package/dist/core/utils/export-sketch.js.map +1 -0
  292. package/dist/core/utils/fig-archive.d.ts +47 -0
  293. package/dist/core/utils/fig-archive.js +181 -0
  294. package/dist/core/utils/fig-archive.js.map +1 -0
  295. package/dist/core/utils/metadata.d.ts +30 -0
  296. package/dist/core/utils/metadata.js +122 -0
  297. package/dist/core/utils/metadata.js.map +1 -1
  298. package/dist/core/utils/models-prefetch.d.ts +53 -0
  299. package/dist/core/utils/models-prefetch.js +469 -0
  300. package/dist/core/utils/models-prefetch.js.map +1 -0
  301. package/dist/core/utils/onboarding-config.d.ts +48 -0
  302. package/dist/core/utils/onboarding-config.js +373 -0
  303. package/dist/core/utils/onboarding-config.js.map +1 -0
  304. package/dist/core/utils/paths.d.ts +9 -7
  305. package/dist/core/utils/paths.js +12 -10
  306. package/dist/core/utils/paths.js.map +1 -1
  307. package/dist/core/utils/preview-server.d.ts +20 -0
  308. package/dist/core/utils/preview-server.js +212 -0
  309. package/dist/core/utils/preview-server.js.map +1 -0
  310. package/dist/core/utils/search-probe.d.ts +19 -0
  311. package/dist/core/utils/search-probe.js +165 -0
  312. package/dist/core/utils/search-probe.js.map +1 -0
  313. package/dist/core/utils/seeds/empty-slides.deck +0 -0
  314. package/dist/core/utils/url-safety.d.ts +2 -0
  315. package/dist/core/utils/url-safety.js +47 -0
  316. package/dist/core/utils/url-safety.js.map +1 -0
  317. package/dist/core/utils/zstd.d.ts +6 -0
  318. package/dist/core/utils/zstd.js +39 -0
  319. package/dist/core/utils/zstd.js.map +1 -0
  320. package/dist/exec/approvals.js +5 -5
  321. package/dist/exec/approvals.js.map +1 -1
  322. package/dist/index.d.ts +11 -5
  323. package/dist/index.js +8 -5
  324. package/dist/index.js.map +1 -1
  325. package/dist/onboarding/app.d.ts +5 -0
  326. package/dist/onboarding/app.js +255 -0
  327. package/dist/onboarding/app.js.map +1 -0
  328. package/dist/onboarding/index.d.ts +5 -0
  329. package/dist/onboarding/index.js +27 -0
  330. package/dist/onboarding/index.js.map +1 -0
  331. package/package.json +16 -3
  332. package/dist/core/prompts/runtime.js.map +0 -1
  333. package/dist/core/prompts/session-mode-appendix.js.map +0 -1
  334. package/dist/core/prompts/static/planning.md +0 -3
  335. package/dist/core/tools/plan.d.ts +0 -5
  336. package/dist/core/tools/plan.js +0 -150
  337. package/dist/core/tools/plan.js.map +0 -1
  338. package/dist/core/tools/shell.js.map +0 -1
  339. /package/dist/core/prompts/{runtime.d.ts → runtime/agents.d.ts} +0 -0
  340. /package/dist/core/prompts/{session-mode-appendix.d.ts → session-mode.d.ts} +0 -0
@@ -9,6 +9,8 @@ import { MODE_DEFINITIONS } from "../core/modes/definitions.js";
9
9
  import { DEFAULT_SESSION_MODE, isKnownSessionMode, } from "../core/modes/schema.js";
10
10
  import { getModeState, setSessionMode } from "../core/state/session-mode.js";
11
11
  import { isYoloEnabled, setYoloEnabled } from "../core/state/yolo.js";
12
+ import { setLlmModality } from "../core/state/llm-modality.js";
13
+ import { dropTurnBoundariesFrom, getTurnBoundary, recordTurnBoundary, } from "../core/state/turn-boundaries.js";
12
14
  import { ChatTurnSteeringController, createChatTurnSteeringIntervention, } from "../core/agent/turn-steering.js";
13
15
  import { getAgentConversationManager, getAgentSessionManager, } from "../core/agent/index.js";
14
16
  import { readBundledPrompt } from "../core/prompts/bundled.js";
@@ -17,10 +19,10 @@ import { subscribeModelDownloadProgress, } from "../core/utils/download-progress
17
19
  import { subscribeModelRetryProgress, } from "../core/agent/retry-progress.js";
18
20
  import { toAdditiveUsage } from "../core/utils/usage.js";
19
21
  import { computeUsageCostUsd, configuredLlmContext, contextTokensFromUsage, resolveLlmMetadata, } from "../core/utils/metadata.js";
20
- import { buildSessionConfigOptions, currentModelName, CONFIG_ID_EFFORT, CONFIG_ID_MODE, CONFIG_ID_MODEL, MODE_VALUE_YOLO, } from "./session-config.js";
22
+ import { buildSessionConfigOptions, currentModelName, CONFIG_ID_EFFORT, CONFIG_ID_MODE, CONFIG_ID_MODEL, CONFIG_ID_YOLO, } from "./session-config.js";
21
23
  import { activeProviderName, parseReasoningEffortArg, withReasoningEffort, } from "../core/utils/reasoning-effort.js";
22
24
  import { ACP_SLASH_COMMANDS, parseAcpSlashCommand, } from "./commands.js";
23
- import { ENTER_PLAN_MODE_TOOL, EXIT_PLAN_MODE_TOOL, } from "../core/state/tool-approvals.js";
25
+ import { SWITCH_MODE_TOOL } from "../core/state/tool-approvals.js";
24
26
  import { runWithCwd } from "../core/utils/cwd-context.js";
25
27
  import { runWithAgentMemoryScope } from "../core/memory/index.js";
26
28
  import { createSessionConfig, } from "../core/session-config.js";
@@ -32,12 +34,14 @@ import { takeFileToolDisplay } from "../core/state/file-tool-display.js";
32
34
  import { getTodoViewState } from "../core/state/todos.js";
33
35
  import { UPDATE_TODOS_TOOL_NAME } from "../core/tools/todo.js";
34
36
  import { setAskUserBackend } from "../core/tools/ask-user.js";
37
+ import { setBrowserPreviewBackend } from "../core/utils/browser.js";
35
38
  import { setTextFsBackend } from "../core/tools/filesystem.js";
36
- import { setTerminalBackend, } from "../core/tools/shell.js";
39
+ import { setTerminalBackend, clearShellJobManager, getShellJobManager, } from "../core/shell/index.js";
37
40
  import { createAcpToolApprovalIntervention } from "./approvals.js";
38
41
  import { createAcpAskUserBackend } from "./questions.js";
42
+ import { createAcpBrowserPreviewBackend } from "./browser.js";
39
43
  import { extractAcpClientUserId } from "./meta/user-id.js";
40
- import { extractAcpVscodeFlag } from "./meta/vscode.js";
44
+ import { isAcpVscodeHost } from "./meta/vscode.js";
41
45
  import { deriveSessionTitleFromEcho } from "./sessions/title.js";
42
46
  import { acpPromptEchoText, acpPromptToInvokeArgs } from "./prompt-invoke.js";
43
47
  import { normalizeAcpSessionMcpServers } from "./mcp-servers.js";
@@ -338,6 +342,7 @@ export class HoomanAcpAgent {
338
342
  if (record) {
339
343
  this.#sessions.delete(params.sessionId);
340
344
  record.turnAbort?.abort();
345
+ await this.#teardownShellJobs(params.sessionId, record);
341
346
  await record.mcpDisconnect();
342
347
  }
343
348
  await deleteSessionEntry(this.#acpRoot, params.sessionId);
@@ -352,7 +357,7 @@ export class HoomanAcpAgent {
352
357
  const sessionId = crypto.randomUUID();
353
358
  const clientUserId = extractAcpClientUserId(params._meta) ?? null;
354
359
  const mcpServers = normalizeAcpSessionMcpServers(params.mcpServers);
355
- const vscode = extractAcpVscodeFlag(params._meta);
360
+ const vscode = isAcpVscodeHost();
356
361
  const mode = DEFAULT_SESSION_MODE;
357
362
  const now = new Date().toISOString();
358
363
  const entry = {
@@ -369,6 +374,7 @@ export class HoomanAcpAgent {
369
374
  await writeSessionEntry(this.#acpRoot, entry);
370
375
  const record = await this.#bootstrapSession(sessionId, params.cwd, clientUserId ?? sessionId, mcpServers, mode, undefined, vscode);
371
376
  this.#sessions.set(sessionId, record);
377
+ this.#subscribeShellJobs(record.agent, this.#requireClient(), sessionId);
372
378
  await this.#advertiseCommands(this.#requireClient(), sessionId);
373
379
  return {
374
380
  sessionId,
@@ -402,7 +408,7 @@ export class HoomanAcpAgent {
402
408
  const mcpServers = params.mcpServers && params.mcpServers.length > 0
403
409
  ? normalizeAcpSessionMcpServers(params.mcpServers)
404
410
  : (sourceEntry.mcpServers ?? []);
405
- const vscode = extractAcpVscodeFlag(params._meta) || sourceEntry.vscode === true;
411
+ const vscode = isAcpVscodeHost();
406
412
  const mode = resolveSessionMode(sourceEntry.sessionMode);
407
413
  const now = new Date().toISOString();
408
414
  const title = sourceEntry.title
@@ -449,6 +455,58 @@ export class HoomanAcpAgent {
449
455
  configOptions: buildSessionConfigOptions(forkConfig, mode, sourceEntry.yolo === true),
450
456
  };
451
457
  }
458
+ /**
459
+ * Turn revert (custom `_hoomanjs/rewind_session` method, not part
460
+ * of the ACP spec): splice `agent.messages` back to the message index
461
+ * bookmarked for `messageId` (see {@link recordTurnBoundary}), dropping
462
+ * that turn and every turn after it, then persist the trimmed history.
463
+ *
464
+ * Returns `reverted: false` instead of erroring when `messageId` has no
465
+ * recorded boundary — e.g. the session was reloaded since that turn ran,
466
+ * so its in-memory bookmark (and the client's matching file-edit
467
+ * baselines) no longer exist.
468
+ */
469
+ async rewindSession(params) {
470
+ const rec = this.#sessions.get(params.sessionId);
471
+ if (!rec) {
472
+ throw RequestError.invalidParams({ sessionId: params.sessionId });
473
+ }
474
+ if (this.#isTurnActive(rec)) {
475
+ throw RequestError.invalidParams({
476
+ sessionId: params.sessionId,
477
+ message: "Cannot rewind a session while a turn is still running.",
478
+ });
479
+ }
480
+ const boundary = getTurnBoundary(rec.agent, params.messageId);
481
+ if (boundary === undefined) {
482
+ return { reverted: false };
483
+ }
484
+ rec.agent.messages.length = boundary;
485
+ dropTurnBoundariesFrom(rec.agent, params.messageId);
486
+ await getAgentSessionManager(rec.agent)?.saveSnapshot({
487
+ target: rec.agent,
488
+ isLatest: true,
489
+ });
490
+ return { reverted: true };
491
+ }
492
+ /**
493
+ * Custom `_hoomanjs/stop_shell_job`: stop a background shell job by id.
494
+ * Routes through {@link ShellJobManager.stop}, which kills the host terminal
495
+ * (process group) and emits the completion notification the UI listens for.
496
+ */
497
+ async stopShellJob(params) {
498
+ const rec = this.#sessions.get(params.sessionId);
499
+ if (!rec) {
500
+ throw RequestError.invalidParams({ sessionId: params.sessionId });
501
+ }
502
+ const manager = getShellJobManager(rec.agent);
503
+ const existing = manager.get(params.jobId);
504
+ if (!existing) {
505
+ return { stopped: false };
506
+ }
507
+ await manager.stop(params.jobId);
508
+ return { stopped: true };
509
+ }
452
510
  async loadSession(params, client) {
453
511
  const { record, mode } = await this.#reactivateSession({
454
512
  sessionId: params.sessionId,
@@ -489,6 +547,7 @@ export class HoomanAcpAgent {
489
547
  if (record) {
490
548
  this.#sessions.delete(params.sessionId);
491
549
  record.turnAbort?.abort();
550
+ await this.#teardownShellJobs(params.sessionId, record);
492
551
  await record.mcpDisconnect();
493
552
  }
494
553
  return {};
@@ -512,7 +571,7 @@ export class HoomanAcpAgent {
512
571
  const mcpServers = params.mcpServers.length > 0
513
572
  ? normalizeAcpSessionMcpServers(params.mcpServers)
514
573
  : (existing.mcpServers ?? []);
515
- const vscode = extractAcpVscodeFlag(params.meta) || existing.vscode === true;
574
+ const vscode = isAcpVscodeHost();
516
575
  const mode = resolveSessionMode(existing.sessionMode);
517
576
  // Bootstrapping restores the Strands snapshot (messages + appState) during
518
577
  // `agent.initialize()` — the snapshot is the conversation source of truth.
@@ -529,6 +588,7 @@ export class HoomanAcpAgent {
529
588
  await replayConversationHistory(client, params.sessionId, record.agent.messages);
530
589
  }
531
590
  this.#sessions.set(params.sessionId, record);
591
+ this.#subscribeShellJobs(record.agent, client, params.sessionId);
532
592
  await patchSessionEntry(this.#acpRoot, params.sessionId, {
533
593
  cwd: params.cwd,
534
594
  ...(fromRequest !== undefined ? { userId: fromRequest || null } : {}),
@@ -581,6 +641,27 @@ export class HoomanAcpAgent {
581
641
  if (!rec) {
582
642
  throw RequestError.invalidParams({ sessionId: params.sessionId });
583
643
  }
644
+ // Yolo is a boolean toggle (auto-approve all tool calls), so it carries a
645
+ // boolean value rather than a string one; handle it before the string
646
+ // guard below that the select-style options rely on.
647
+ if (params.configId === CONFIG_ID_YOLO) {
648
+ if (typeof params.value !== "boolean") {
649
+ throw RequestError.invalidParams({
650
+ configId: params.configId,
651
+ message: `Config option "${params.configId}" expects a boolean value`,
652
+ });
653
+ }
654
+ const enabled = params.value;
655
+ if (isYoloEnabled(rec.agent) !== enabled) {
656
+ setYoloEnabled(rec.agent, enabled);
657
+ await patchSessionEntry(this.#acpRoot, params.sessionId, {
658
+ yolo: enabled,
659
+ });
660
+ }
661
+ return {
662
+ configOptions: buildSessionConfigOptions(rec.config, resolveSessionMode(getModeState(rec.agent).mode), isYoloEnabled(rec.agent)),
663
+ };
664
+ }
584
665
  if (typeof params.value !== "string") {
585
666
  throw RequestError.invalidParams({
586
667
  configId: params.configId,
@@ -589,39 +670,16 @@ export class HoomanAcpAgent {
589
670
  }
590
671
  const value = params.value;
591
672
  if (params.configId === CONFIG_ID_MODE) {
592
- if (value === MODE_VALUE_YOLO) {
593
- // Yolo is agent mode with auto-approval on, not a distinct SessionMode.
594
- if (this.#transitionMode(rec, "agent")) {
595
- // Keep the legacy `modes` surface in sync too (see Session Config
596
- // Options spec: "Agents SHOULD keep both in sync").
597
- await this.#syncCurrentMode(client, params.sessionId, rec);
598
- }
599
- if (!isYoloEnabled(rec.agent)) {
600
- setYoloEnabled(rec.agent, true);
601
- await patchSessionEntry(this.#acpRoot, params.sessionId, {
602
- yolo: true,
603
- });
604
- }
673
+ if (!isKnownSessionMode(value)) {
674
+ throw RequestError.invalidParams({
675
+ value,
676
+ message: `Unknown mode "${value}"`,
677
+ });
605
678
  }
606
- else {
607
- if (!isKnownSessionMode(value)) {
608
- throw RequestError.invalidParams({
609
- value,
610
- message: `Unknown mode "${value}"`,
611
- });
612
- }
613
- if (this.#transitionMode(rec, value)) {
614
- // Keep the legacy `modes` surface in sync too (see Session Config
615
- // Options spec: "Agents SHOULD keep both in sync").
616
- await this.#syncCurrentMode(client, params.sessionId, rec);
617
- }
618
- // Selecting a plain mode always turns yolo back off.
619
- if (isYoloEnabled(rec.agent)) {
620
- setYoloEnabled(rec.agent, false);
621
- await patchSessionEntry(this.#acpRoot, params.sessionId, {
622
- yolo: false,
623
- });
624
- }
679
+ if (this.#transitionMode(rec, value)) {
680
+ // Keep the legacy `modes` surface in sync too (see Session Config
681
+ // Options spec: "Agents SHOULD keep both in sync").
682
+ await this.#syncCurrentMode(client, params.sessionId, rec);
625
683
  }
626
684
  }
627
685
  else if (params.configId === CONFIG_ID_MODEL) {
@@ -788,6 +846,7 @@ export class HoomanAcpAgent {
788
846
  const provider = await modelProviders[resolved.provider]();
789
847
  rec.agent.model = provider.create(resolved.providerOptions, resolved.llmOptions);
790
848
  rec.metadata = await resolveLlmMetadata(resolved.metadata, resolved.llmOptions.model, resolved.provider, configuredLlmContext(resolved)).catch(() => null);
849
+ setLlmModality(rec.agent, rec.metadata?.modality);
791
850
  }
792
851
  catch (error) {
793
852
  if (previous) {
@@ -937,11 +996,13 @@ export class HoomanAcpAgent {
937
996
  let stopReason = "end_turn";
938
997
  try {
939
998
  const echo = acpPromptEchoText(params.prompt);
999
+ let turnMessageId;
940
1000
  if (echo.length > 0) {
1001
+ turnMessageId = crypto.randomUUID();
941
1002
  await this.#sendUpdate(client, params.sessionId, {
942
1003
  sessionUpdate: "user_message_chunk",
943
1004
  content: { type: "text", text: echo },
944
- messageId: crypto.randomUUID(),
1005
+ messageId: turnMessageId,
945
1006
  });
946
1007
  await this.#maybeDeriveTitle(client, params.sessionId, echo);
947
1008
  }
@@ -969,6 +1030,14 @@ export class HoomanAcpAgent {
969
1030
  },
970
1031
  ], rec.metadata)
971
1032
  : acpPromptToInvokeArgs(params.prompt, rec.metadata);
1033
+ // The `messageId` we just generated for this turn's user_message_chunk
1034
+ // echo (see the ACP MessageId RFD) doubles as a durable handle for
1035
+ // Turn revert: bookmark where this turn's messages start,
1036
+ // before `agent.stream(...)` appends them, so a later `rewindSession`
1037
+ // can splice history back to exactly this point.
1038
+ if (turnMessageId) {
1039
+ recordTurnBoundary(rec.agent, turnMessageId, rec.agent.messages.length);
1040
+ }
972
1041
  const signals = [turnAbort.signal];
973
1042
  if (this.#connectionSignal) {
974
1043
  signals.push(this.#connectionSignal);
@@ -1001,7 +1070,18 @@ export class HoomanAcpAgent {
1001
1070
  // live output with a JSON result blob. The structured result
1002
1071
  // still travels to the model via `rawOutput`.
1003
1072
  const terminalId = rec.terminalByToolCall.get(ev.toolUse.toolUseId);
1004
- rec.terminalByToolCall.delete(ev.toolUse.toolUseId);
1073
+ // Keep terminalByToolCall for background jobs so live polling
1074
+ // can continue; only drop the mapping for foreground shells.
1075
+ const bgJob = extractBackgroundShellFromResult(ev.result);
1076
+ if (!bgJob) {
1077
+ rec.terminalByToolCall.delete(ev.toolUse.toolUseId);
1078
+ }
1079
+ else if (terminalId) {
1080
+ rec.shellJobs.set(bgJob.jobId, {
1081
+ terminalId,
1082
+ toolCallId: ev.toolUse.toolUseId,
1083
+ });
1084
+ }
1005
1085
  const content = terminalId !== undefined
1006
1086
  ? [{ type: "terminal", terminalId }]
1007
1087
  : (diff ?? toolResultToAcpContent(ev.result));
@@ -1012,14 +1092,27 @@ export class HoomanAcpAgent {
1012
1092
  rawOutput: ev.result.toJSON(),
1013
1093
  content,
1014
1094
  ...(locations ? { locations } : {}),
1095
+ ...(bgJob
1096
+ ? {
1097
+ _meta: {
1098
+ "hoomanjs/shell_job": {
1099
+ event: "started",
1100
+ job_id: bgJob.jobId,
1101
+ description: bgJob.description,
1102
+ status: bgJob.jobStatus ?? "running",
1103
+ terminal_id: terminalId,
1104
+ tool_call_id: ev.toolUse.toolUseId,
1105
+ },
1106
+ },
1107
+ }
1108
+ : {}),
1015
1109
  });
1016
1110
  if (ev.toolUse.name === UPDATE_TODOS_TOOL_NAME &&
1017
1111
  ev.result.status === "success") {
1018
1112
  await this.#sendPlanUpdate(client, params.sessionId, rec);
1019
1113
  }
1020
1114
  if (ev.result.status === "success" &&
1021
- (ev.toolUse.name === ENTER_PLAN_MODE_TOOL ||
1022
- ev.toolUse.name === EXIT_PLAN_MODE_TOOL)) {
1115
+ ev.toolUse.name === SWITCH_MODE_TOOL) {
1023
1116
  await this.#syncCurrentMode(client, params.sessionId, rec);
1024
1117
  }
1025
1118
  }
@@ -1135,8 +1228,8 @@ export class HoomanAcpAgent {
1135
1228
  });
1136
1229
  }
1137
1230
  /**
1138
- * Reflect an agent-driven mode change (e.g. `enter_plan_mode` /
1139
- * `exit_plan_mode`) to the client via `current_mode_update` and persist it.
1231
+ * Reflect an agent-driven mode change (`switch_mode`) to the client via
1232
+ * `current_mode_update` and persist it.
1140
1233
  */
1141
1234
  async #syncCurrentMode(client, sessionId, rec) {
1142
1235
  const modeId = resolveSessionMode(getModeState(rec.agent).mode);
@@ -1379,8 +1472,10 @@ export class HoomanAcpAgent {
1379
1472
  this.#registerTextFsBackend(agent, client, sessionId);
1380
1473
  this.#registerTerminalBackend(agent, client, sessionId);
1381
1474
  setAskUserBackend(agent, createAcpAskUserBackend(client, sessionId));
1475
+ setBrowserPreviewBackend(agent, createAcpBrowserPreviewBackend(client, sessionId));
1382
1476
  const activeLlm = config.llm;
1383
1477
  const metadata = await resolveLlmMetadata(activeLlm.metadata, activeLlm.llmOptions.model, activeLlm.provider, configuredLlmContext(activeLlm)).catch(() => null);
1478
+ setLlmModality(agent, metadata?.modality);
1384
1479
  return {
1385
1480
  cwd,
1386
1481
  agent,
@@ -1393,6 +1488,8 @@ export class HoomanAcpAgent {
1393
1488
  streamingToolInputJson: new Map(),
1394
1489
  lastStreamToolUseId: null,
1395
1490
  terminalByToolCall: new Map(),
1491
+ shellJobs: new Map(),
1492
+ shellJobUnsub: null,
1396
1493
  currentAssistantMessageId: null,
1397
1494
  pendingModeReapply: false,
1398
1495
  pendingModelRebuild: false,
@@ -1447,7 +1544,134 @@ export class HoomanAcpAgent {
1447
1544
  }
1448
1545
  setTerminalBackend(agent, {
1449
1546
  run: (request) => this.#runClientTerminal(client, sessionId, request),
1547
+ spawn: (request) => this.#spawnClientTerminal(client, sessionId, request),
1548
+ readOutput: (terminalId) => this.#readClientTerminal(client, sessionId, terminalId),
1549
+ kill: (terminalId) => this.#killClientTerminal(client, sessionId, terminalId),
1550
+ });
1551
+ }
1552
+ /** Call after the SessionRecord is stored in `#sessions`. */
1553
+ #subscribeShellJobs(agent, client, sessionId) {
1554
+ const record = this.#sessions.get(sessionId);
1555
+ if (!record) {
1556
+ return;
1557
+ }
1558
+ record.shellJobUnsub?.();
1559
+ const manager = getShellJobManager(agent);
1560
+ record.shellJobUnsub = manager.on((event) => {
1561
+ void this.#onShellJobEvent(client, sessionId, event);
1562
+ });
1563
+ }
1564
+ async #onShellJobEvent(client, sessionId, event) {
1565
+ if (event.type !== "completed" &&
1566
+ event.type !== "stopped" &&
1567
+ event.type !== "failed") {
1568
+ return;
1569
+ }
1570
+ const job = event.job;
1571
+ const meta = {
1572
+ "hoomanjs/shell_job": {
1573
+ event: event.type,
1574
+ job_id: job.id,
1575
+ description: job.description,
1576
+ status: job.status,
1577
+ ready: job.ready,
1578
+ exit_code: job.exitCode,
1579
+ signal: job.signal,
1580
+ terminal_id: job.terminalId,
1581
+ tool_call_id: job.toolUseId,
1582
+ },
1583
+ };
1584
+ // Meta-only update for the jobs bar — empty text so nothing lands in the
1585
+ // transcript. Fire-and-forget release: awaiting a nested terminal/* RPC
1586
+ // from inside stop_shell_job deadlocks the ACP connection.
1587
+ void this.#sendUpdate(client, sessionId, {
1588
+ sessionUpdate: "agent_message_chunk",
1589
+ content: {
1590
+ type: "text",
1591
+ text: "",
1592
+ },
1593
+ _meta: meta,
1594
+ }).catch(() => undefined);
1595
+ const record = this.#sessions.get(sessionId);
1596
+ const hosted = record?.shellJobs.get(job.id);
1597
+ if (hosted) {
1598
+ record?.shellJobs.delete(job.id);
1599
+ void client
1600
+ .request(methods.client.terminal.release, {
1601
+ sessionId,
1602
+ terminalId: hosted.terminalId,
1603
+ })
1604
+ .catch(() => undefined);
1605
+ }
1606
+ }
1607
+ async #spawnClientTerminal(client, sessionId, request) {
1608
+ const created = await client.request(methods.client.terminal.create, {
1609
+ sessionId,
1610
+ command: request.command,
1611
+ args: request.args,
1612
+ cwd: request.cwd,
1613
+ ...(request.outputByteLimit !== undefined
1614
+ ? { outputByteLimit: request.outputByteLimit }
1615
+ : {}),
1616
+ }, { cancellationSignal: request.cancelSignal });
1617
+ const terminalId = created.terminalId;
1618
+ const record = this.#sessions.get(sessionId);
1619
+ if (request.jobId && record) {
1620
+ record.shellJobs.set(request.jobId, {
1621
+ terminalId,
1622
+ toolCallId: request.toolUseId,
1623
+ });
1624
+ }
1625
+ if (request.toolUseId) {
1626
+ record?.terminalByToolCall.set(request.toolUseId, terminalId);
1627
+ await this.#sendUpdate(client, sessionId, {
1628
+ sessionUpdate: "tool_call_update",
1629
+ toolCallId: request.toolUseId,
1630
+ status: "in_progress",
1631
+ content: [{ type: "terminal", terminalId }],
1632
+ });
1633
+ }
1634
+ return { terminalId };
1635
+ }
1636
+ async #readClientTerminal(client, sessionId, terminalId) {
1637
+ const output = await client.request(methods.client.terminal.output, {
1638
+ sessionId,
1639
+ terminalId,
1450
1640
  });
1641
+ return {
1642
+ output: output.output,
1643
+ truncated: output.truncated,
1644
+ exitCode: output.exitStatus?.exitCode ?? null,
1645
+ signal: output.exitStatus?.signal ?? null,
1646
+ };
1647
+ }
1648
+ async #killClientTerminal(client, sessionId, terminalId) {
1649
+ await client
1650
+ .request(methods.client.terminal.kill, { sessionId, terminalId })
1651
+ .catch(() => undefined);
1652
+ }
1653
+ async #teardownShellJobs(sessionId, record) {
1654
+ record.shellJobUnsub?.();
1655
+ record.shellJobUnsub = null;
1656
+ const client = this.#client;
1657
+ for (const [jobId, hosted] of record.shellJobs) {
1658
+ if (client) {
1659
+ await client
1660
+ .request(methods.client.terminal.kill, {
1661
+ sessionId,
1662
+ terminalId: hosted.terminalId,
1663
+ })
1664
+ .catch(() => undefined);
1665
+ await client
1666
+ .request(methods.client.terminal.release, {
1667
+ sessionId,
1668
+ terminalId: hosted.terminalId,
1669
+ })
1670
+ .catch(() => undefined);
1671
+ }
1672
+ record.shellJobs.delete(jobId);
1673
+ }
1674
+ await clearShellJobManager(record.agent).catch(() => undefined);
1451
1675
  }
1452
1676
  async #runClientTerminal(client, sessionId, request) {
1453
1677
  const created = await client.request(methods.client.terminal.create, {
@@ -1545,10 +1769,11 @@ export class HoomanAcpAgent {
1545
1769
  }
1546
1770
  }
1547
1771
  async #disposeAll() {
1548
- const records = [...this.#sessions.values()];
1772
+ const entries = [...this.#sessions.entries()];
1549
1773
  this.#sessions.clear();
1550
- for (const record of records) {
1774
+ for (const [sessionId, record] of entries) {
1551
1775
  record.turnAbort?.abort();
1776
+ await this.#teardownShellJobs(sessionId, record);
1552
1777
  await record.mcpDisconnect();
1553
1778
  }
1554
1779
  }
@@ -1565,6 +1790,8 @@ export function createAcpApp(agent) {
1565
1790
  .onRequest(methods.agent.session.list, (ctx) => agent.listSessions(ctx.params))
1566
1791
  .onRequest(methods.agent.session.delete, (ctx) => agent.deleteSession(ctx.params))
1567
1792
  .onRequest(methods.agent.session.fork, (ctx) => agent.forkSession(ctx.params, ctx.client))
1793
+ .onRequest("_hoomanjs/rewind_session", (params) => params, (ctx) => agent.rewindSession(ctx.params))
1794
+ .onRequest("_hoomanjs/stop_shell_job", (params) => params, (ctx) => agent.stopShellJob(ctx.params))
1568
1795
  .onRequest(methods.agent.session.setMode, (ctx) => agent.setSessionMode(ctx.params, ctx.client))
1569
1796
  .onRequest(methods.agent.session.setConfigOption, (ctx) => agent.setSessionConfigOption(ctx.params, ctx.client))
1570
1797
  .onRequest(methods.agent.session.prompt, (ctx) => agent.prompt(ctx.params))
@@ -1577,4 +1804,66 @@ export async function runAcpStdio() {
1577
1804
  const connection = createAcpApp(new HoomanAcpAgent(identity)).connect(stream);
1578
1805
  await connection.closed;
1579
1806
  }
1807
+ /**
1808
+ * Pull a background shell job payload out of a Strands tool result.
1809
+ * The tool returns `{ status: "background", job_id, ... }` which the SDK wraps
1810
+ * as a ToolResultBlock with a json content block.
1811
+ */
1812
+ function extractBackgroundShellFromResult(result) {
1813
+ const fromValue = (value) => {
1814
+ if (!value || typeof value !== "object") {
1815
+ return null;
1816
+ }
1817
+ const record = value;
1818
+ if (record.status === "background" && typeof record.job_id === "string") {
1819
+ return {
1820
+ jobId: record.job_id,
1821
+ description: typeof record.description === "string"
1822
+ ? record.description
1823
+ : undefined,
1824
+ jobStatus: typeof record.job_status === "string" ? record.job_status : undefined,
1825
+ };
1826
+ }
1827
+ return null;
1828
+ };
1829
+ if (Array.isArray(result.content)) {
1830
+ for (const block of result.content) {
1831
+ if (!block || typeof block !== "object") {
1832
+ continue;
1833
+ }
1834
+ const json = block.json;
1835
+ const nested = fromValue(json ?? block);
1836
+ if (nested) {
1837
+ return nested;
1838
+ }
1839
+ }
1840
+ }
1841
+ try {
1842
+ const serialized = result.toJSON?.();
1843
+ if (serialized && typeof serialized === "object") {
1844
+ const wrapped = serialized;
1845
+ const content = wrapped.toolResult?.content ?? wrapped.content;
1846
+ if (Array.isArray(content)) {
1847
+ for (const block of content) {
1848
+ if (!block || typeof block !== "object") {
1849
+ continue;
1850
+ }
1851
+ const json = block.json;
1852
+ const nested = fromValue(json ?? block);
1853
+ if (nested) {
1854
+ return nested;
1855
+ }
1856
+ }
1857
+ }
1858
+ const direct = fromValue(serialized);
1859
+ if (direct) {
1860
+ return direct;
1861
+ }
1862
+ }
1863
+ }
1864
+ catch {
1865
+ // ignore serialization errors
1866
+ }
1867
+ return null;
1868
+ }
1580
1869
  //# sourceMappingURL=acp-agent.js.map