@bastani/atomic 0.7.17 → 0.8.0-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 (1233) hide show
  1. package/CHANGELOG.md +4183 -0
  2. package/README.md +655 -0
  3. package/dist/builtin/intercom/CHANGELOG.md +195 -0
  4. package/dist/builtin/intercom/LICENSE +21 -0
  5. package/dist/builtin/intercom/README.md +484 -0
  6. package/dist/builtin/intercom/broker/broker.ts +346 -0
  7. package/dist/builtin/intercom/broker/client.ts +535 -0
  8. package/dist/builtin/intercom/broker/framing.ts +57 -0
  9. package/dist/builtin/intercom/broker/paths.ts +21 -0
  10. package/dist/builtin/intercom/broker/spawn.ts +308 -0
  11. package/dist/builtin/intercom/config.ts +109 -0
  12. package/dist/builtin/intercom/index.ts +1780 -0
  13. package/dist/builtin/intercom/package.json +59 -0
  14. package/dist/builtin/intercom/reply-tracker.ts +102 -0
  15. package/dist/builtin/intercom/skills/intercom/SKILL.md +513 -0
  16. package/dist/builtin/intercom/types.ts +46 -0
  17. package/dist/builtin/intercom/ui/compose.ts +139 -0
  18. package/dist/builtin/intercom/ui/inline-message.ts +76 -0
  19. package/dist/builtin/intercom/ui/session-list.ts +162 -0
  20. package/dist/builtin/mcp/CHANGELOG.md +346 -0
  21. package/dist/builtin/mcp/LICENSE +21 -0
  22. package/dist/builtin/mcp/OAUTH.md +324 -0
  23. package/dist/builtin/mcp/README.md +373 -0
  24. package/dist/builtin/mcp/agent-dir.ts +21 -0
  25. package/dist/builtin/mcp/app-bridge.bundle.js +67 -0
  26. package/dist/builtin/mcp/cli.js +186 -0
  27. package/dist/builtin/mcp/commands.ts +420 -0
  28. package/dist/builtin/mcp/config.ts +667 -0
  29. package/dist/builtin/mcp/consent-manager.ts +64 -0
  30. package/dist/builtin/mcp/direct-tools.ts +427 -0
  31. package/dist/builtin/mcp/errors.ts +219 -0
  32. package/dist/builtin/mcp/glimpse-ui.ts +80 -0
  33. package/dist/builtin/mcp/host-html-template.ts +427 -0
  34. package/dist/builtin/mcp/index.ts +334 -0
  35. package/dist/builtin/mcp/init.ts +336 -0
  36. package/dist/builtin/mcp/lifecycle.ts +93 -0
  37. package/dist/builtin/mcp/logger.ts +169 -0
  38. package/dist/builtin/mcp/mcp-auth-flow.ts +362 -0
  39. package/dist/builtin/mcp/mcp-auth.ts +297 -0
  40. package/dist/builtin/mcp/mcp-callback-server.ts +284 -0
  41. package/dist/builtin/mcp/mcp-oauth-provider.ts +302 -0
  42. package/dist/builtin/mcp/mcp-panel.ts +826 -0
  43. package/dist/builtin/mcp/mcp-setup-panel.ts +577 -0
  44. package/dist/builtin/mcp/metadata-cache.ts +201 -0
  45. package/dist/builtin/mcp/npx-resolver.ts +424 -0
  46. package/dist/builtin/mcp/oauth-handler.ts +60 -0
  47. package/dist/builtin/mcp/onboarding-state.ts +68 -0
  48. package/dist/builtin/mcp/package.json +61 -0
  49. package/dist/builtin/mcp/proxy-modes.ts +803 -0
  50. package/dist/builtin/mcp/resource-tools.ts +17 -0
  51. package/dist/builtin/mcp/sampling-handler.ts +268 -0
  52. package/dist/builtin/mcp/server-manager.ts +375 -0
  53. package/dist/builtin/mcp/state.ts +41 -0
  54. package/dist/builtin/mcp/tool-metadata.ts +152 -0
  55. package/dist/builtin/mcp/tool-registrar.ts +46 -0
  56. package/dist/builtin/mcp/tool-result-renderer.ts +65 -0
  57. package/dist/builtin/mcp/types.ts +441 -0
  58. package/dist/builtin/mcp/ui-resource-handler.ts +145 -0
  59. package/dist/builtin/mcp/ui-server.ts +623 -0
  60. package/dist/builtin/mcp/ui-session.ts +384 -0
  61. package/dist/builtin/mcp/ui-stream-types.ts +89 -0
  62. package/dist/builtin/mcp/utils.ts +129 -0
  63. package/dist/builtin/subagents/CHANGELOG.md +1019 -0
  64. package/dist/builtin/subagents/README.md +991 -0
  65. package/dist/builtin/subagents/agents/code-simplifier.md +84 -0
  66. package/dist/builtin/subagents/agents/codebase-analyzer.md +158 -0
  67. package/dist/builtin/subagents/agents/codebase-locator.md +113 -0
  68. package/dist/builtin/subagents/agents/codebase-online-researcher.md +317 -0
  69. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +236 -0
  70. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +181 -0
  71. package/dist/builtin/subagents/agents/codebase-research-locator.md +146 -0
  72. package/dist/builtin/subagents/agents/debugger.md +92 -0
  73. package/dist/builtin/subagents/package.json +67 -0
  74. package/dist/builtin/subagents/prompts/gather-context-and-clarify.md +20 -0
  75. package/dist/builtin/subagents/prompts/parallel-cleanup.md +60 -0
  76. package/dist/builtin/subagents/prompts/parallel-context-build.md +55 -0
  77. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +77 -0
  78. package/dist/builtin/subagents/prompts/parallel-research.md +58 -0
  79. package/dist/builtin/subagents/prompts/parallel-review.md +52 -0
  80. package/dist/builtin/subagents/prompts/review-loop.md +48 -0
  81. package/dist/builtin/subagents/skills/subagent/SKILL.md +734 -0
  82. package/dist/builtin/subagents/src/agents/agent-management.ts +644 -0
  83. package/dist/builtin/subagents/src/agents/agent-scope.ts +6 -0
  84. package/dist/builtin/subagents/src/agents/agent-selection.ts +23 -0
  85. package/dist/builtin/subagents/src/agents/agent-serializer.ts +84 -0
  86. package/dist/builtin/subagents/src/agents/agents.ts +809 -0
  87. package/dist/builtin/subagents/src/agents/chain-serializer.ts +137 -0
  88. package/dist/builtin/subagents/src/agents/frontmatter.ts +29 -0
  89. package/dist/builtin/subagents/src/agents/identity.ts +30 -0
  90. package/dist/builtin/subagents/src/agents/skills.ts +630 -0
  91. package/dist/builtin/subagents/src/extension/control-notices.ts +92 -0
  92. package/dist/builtin/subagents/src/extension/doctor.ts +199 -0
  93. package/dist/builtin/subagents/src/extension/index.ts +586 -0
  94. package/dist/builtin/subagents/src/extension/schemas.ts +168 -0
  95. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +378 -0
  96. package/dist/builtin/subagents/src/intercom/result-intercom.ts +269 -0
  97. package/dist/builtin/subagents/src/runs/background/async-execution.ts +612 -0
  98. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +267 -0
  99. package/dist/builtin/subagents/src/runs/background/async-resume.ts +332 -0
  100. package/dist/builtin/subagents/src/runs/background/async-status.ts +295 -0
  101. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +63 -0
  102. package/dist/builtin/subagents/src/runs/background/notify.ts +108 -0
  103. package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +45 -0
  104. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +250 -0
  105. package/dist/builtin/subagents/src/runs/background/run-status.ts +193 -0
  106. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +291 -0
  107. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +1760 -0
  108. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +13 -0
  109. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +1333 -0
  110. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +932 -0
  111. package/dist/builtin/subagents/src/runs/foreground/execution.ts +902 -0
  112. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +2231 -0
  113. package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +125 -0
  114. package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +175 -0
  115. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +103 -0
  116. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +108 -0
  117. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +163 -0
  118. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +115 -0
  119. package/dist/builtin/subagents/src/runs/shared/run-history.ts +56 -0
  120. package/dist/builtin/subagents/src/runs/shared/single-output.ts +154 -0
  121. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +226 -0
  122. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +152 -0
  123. package/dist/builtin/subagents/src/runs/shared/worktree.ts +577 -0
  124. package/dist/builtin/subagents/src/shared/artifacts.ts +99 -0
  125. package/dist/builtin/subagents/src/shared/atomic-json.ts +16 -0
  126. package/dist/builtin/subagents/src/shared/file-coalescer.ts +40 -0
  127. package/dist/builtin/subagents/src/shared/fork-context.ts +76 -0
  128. package/dist/builtin/subagents/src/shared/formatters.ts +133 -0
  129. package/dist/builtin/subagents/src/shared/jsonl-writer.ts +81 -0
  130. package/dist/builtin/subagents/src/shared/model-info.ts +78 -0
  131. package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +85 -0
  132. package/dist/builtin/subagents/src/shared/session-identity.ts +10 -0
  133. package/dist/builtin/subagents/src/shared/session-tokens.ts +44 -0
  134. package/dist/builtin/subagents/src/shared/settings.ts +397 -0
  135. package/dist/builtin/subagents/src/shared/status-format.ts +49 -0
  136. package/dist/builtin/subagents/src/shared/types.ts +732 -0
  137. package/dist/builtin/subagents/src/shared/utils.ts +440 -0
  138. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +397 -0
  139. package/dist/builtin/subagents/src/slash/slash-bridge.ts +174 -0
  140. package/dist/builtin/subagents/src/slash/slash-commands.ts +528 -0
  141. package/dist/builtin/subagents/src/slash/slash-live-state.ts +292 -0
  142. package/dist/builtin/subagents/src/tui/render-helpers.ts +80 -0
  143. package/dist/builtin/subagents/src/tui/render.ts +1257 -0
  144. package/dist/builtin/web-access/CHANGELOG.md +387 -0
  145. package/dist/builtin/web-access/LICENSE +21 -0
  146. package/dist/builtin/web-access/README.md +346 -0
  147. package/dist/builtin/web-access/activity.ts +101 -0
  148. package/dist/builtin/web-access/chrome-cookies.ts +322 -0
  149. package/dist/builtin/web-access/code-search.ts +107 -0
  150. package/dist/builtin/web-access/curator-page.ts +3359 -0
  151. package/dist/builtin/web-access/curator-server.ts +605 -0
  152. package/dist/builtin/web-access/exa.ts +521 -0
  153. package/dist/builtin/web-access/extract.ts +701 -0
  154. package/dist/builtin/web-access/gemini-api.ts +113 -0
  155. package/dist/builtin/web-access/gemini-search.ts +362 -0
  156. package/dist/builtin/web-access/gemini-url-context.ts +126 -0
  157. package/dist/builtin/web-access/gemini-web-config.ts +54 -0
  158. package/dist/builtin/web-access/gemini-web.ts +396 -0
  159. package/dist/builtin/web-access/github-api.ts +196 -0
  160. package/dist/builtin/web-access/github-extract.ts +635 -0
  161. package/dist/builtin/web-access/index.ts +2347 -0
  162. package/dist/builtin/web-access/package.json +54 -0
  163. package/dist/builtin/web-access/pdf-extract.ts +192 -0
  164. package/dist/builtin/web-access/perplexity.ts +196 -0
  165. package/dist/builtin/web-access/rsc-extract.ts +338 -0
  166. package/dist/builtin/web-access/storage.ts +72 -0
  167. package/dist/builtin/web-access/summary-review.ts +276 -0
  168. package/dist/builtin/web-access/utils.ts +44 -0
  169. package/dist/builtin/web-access/video-extract.ts +379 -0
  170. package/dist/builtin/web-access/youtube-extract.ts +311 -0
  171. package/dist/builtin/workflows/CHANGELOG.md +20 -0
  172. package/dist/builtin/workflows/README.md +323 -0
  173. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +567 -0
  174. package/dist/builtin/workflows/builtin/index.ts +10 -0
  175. package/dist/builtin/workflows/builtin/open-claude-design.ts +985 -0
  176. package/dist/builtin/workflows/builtin/ralph.ts +613 -0
  177. package/dist/builtin/workflows/package.json +89 -0
  178. package/dist/builtin/workflows/skills/create-spec/SKILL.md +247 -0
  179. package/dist/builtin/workflows/skills/impeccable/SKILL.md +173 -0
  180. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +190 -0
  181. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +175 -0
  182. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +133 -0
  183. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +113 -0
  184. package/dist/builtin/workflows/skills/impeccable/reference/brand.md +118 -0
  185. package/dist/builtin/workflows/skills/impeccable/reference/clarify.md +174 -0
  186. package/dist/builtin/workflows/skills/impeccable/reference/codex.md +105 -0
  187. package/dist/builtin/workflows/skills/impeccable/reference/cognitive-load.md +106 -0
  188. package/dist/builtin/workflows/skills/impeccable/reference/color-and-contrast.md +105 -0
  189. package/dist/builtin/workflows/skills/impeccable/reference/colorize.md +154 -0
  190. package/dist/builtin/workflows/skills/impeccable/reference/craft.md +123 -0
  191. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +261 -0
  192. package/dist/builtin/workflows/skills/impeccable/reference/delight.md +302 -0
  193. package/dist/builtin/workflows/skills/impeccable/reference/distill.md +111 -0
  194. package/dist/builtin/workflows/skills/impeccable/reference/document.md +427 -0
  195. package/dist/builtin/workflows/skills/impeccable/reference/extract.md +69 -0
  196. package/dist/builtin/workflows/skills/impeccable/reference/harden.md +347 -0
  197. package/dist/builtin/workflows/skills/impeccable/reference/heuristics-scoring.md +234 -0
  198. package/dist/builtin/workflows/skills/impeccable/reference/interaction-design.md +195 -0
  199. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +141 -0
  200. package/dist/builtin/workflows/skills/impeccable/reference/live.md +622 -0
  201. package/dist/builtin/workflows/skills/impeccable/reference/motion-design.md +109 -0
  202. package/dist/builtin/workflows/skills/impeccable/reference/onboard.md +234 -0
  203. package/dist/builtin/workflows/skills/impeccable/reference/optimize.md +258 -0
  204. package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +130 -0
  205. package/dist/builtin/workflows/skills/impeccable/reference/personas.md +179 -0
  206. package/dist/builtin/workflows/skills/impeccable/reference/polish.md +242 -0
  207. package/dist/builtin/workflows/skills/impeccable/reference/product.md +62 -0
  208. package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +99 -0
  209. package/dist/builtin/workflows/skills/impeccable/reference/responsive-design.md +114 -0
  210. package/dist/builtin/workflows/skills/impeccable/reference/shape.md +165 -0
  211. package/dist/builtin/workflows/skills/impeccable/reference/spatial-design.md +100 -0
  212. package/dist/builtin/workflows/skills/impeccable/reference/teach.md +156 -0
  213. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +124 -0
  214. package/dist/builtin/workflows/skills/impeccable/reference/typography.md +159 -0
  215. package/dist/builtin/workflows/skills/impeccable/reference/ux-writing.md +107 -0
  216. package/dist/builtin/workflows/skills/impeccable/scripts/cleanup-deprecated.mjs +284 -0
  217. package/dist/builtin/workflows/skills/impeccable/scripts/command-metadata.json +94 -0
  218. package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +226 -0
  219. package/dist/builtin/workflows/skills/impeccable/scripts/design-parser.mjs +820 -0
  220. package/dist/builtin/workflows/skills/impeccable/scripts/detect-csp.mjs +198 -0
  221. package/dist/builtin/workflows/skills/impeccable/scripts/impeccable-paths.mjs +110 -0
  222. package/dist/builtin/workflows/skills/impeccable/scripts/is-generated.mjs +69 -0
  223. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +646 -0
  224. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-session.js +123 -0
  225. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +4865 -0
  226. package/dist/builtin/workflows/skills/impeccable/scripts/live-complete.mjs +75 -0
  227. package/dist/builtin/workflows/skills/impeccable/scripts/live-completion.mjs +18 -0
  228. package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +446 -0
  229. package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +200 -0
  230. package/dist/builtin/workflows/skills/impeccable/scripts/live-resume.mjs +48 -0
  231. package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +847 -0
  232. package/dist/builtin/workflows/skills/impeccable/scripts/live-session-store.mjs +254 -0
  233. package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +47 -0
  234. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +632 -0
  235. package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +247 -0
  236. package/dist/builtin/workflows/skills/impeccable/scripts/load-context.mjs +141 -0
  237. package/dist/builtin/workflows/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  238. package/dist/builtin/workflows/skills/impeccable/scripts/pin.mjs +214 -0
  239. package/dist/builtin/workflows/skills/playwright-cli/SKILL.md +392 -0
  240. package/dist/builtin/workflows/skills/playwright-cli/references/element-attributes.md +23 -0
  241. package/dist/builtin/workflows/skills/playwright-cli/references/playwright-tests.md +39 -0
  242. package/dist/builtin/workflows/skills/playwright-cli/references/request-mocking.md +87 -0
  243. package/dist/builtin/workflows/skills/playwright-cli/references/running-code.md +241 -0
  244. package/dist/builtin/workflows/skills/playwright-cli/references/session-management.md +225 -0
  245. package/dist/builtin/workflows/skills/playwright-cli/references/spec-driven-testing.md +305 -0
  246. package/dist/builtin/workflows/skills/playwright-cli/references/storage-state.md +275 -0
  247. package/dist/builtin/workflows/skills/playwright-cli/references/test-generation.md +134 -0
  248. package/dist/builtin/workflows/skills/playwright-cli/references/tracing.md +139 -0
  249. package/dist/builtin/workflows/skills/playwright-cli/references/video-recording.md +143 -0
  250. package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +263 -0
  251. package/dist/builtin/workflows/skills/prompt-engineer/references/advanced_patterns.md +271 -0
  252. package/dist/builtin/workflows/skills/prompt-engineer/references/core_prompting.md +137 -0
  253. package/dist/builtin/workflows/skills/prompt-engineer/references/quality_improvement.md +193 -0
  254. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +226 -0
  255. package/dist/builtin/workflows/skills/tdd/SKILL.md +109 -0
  256. package/dist/builtin/workflows/skills/tdd/deep-modules.md +33 -0
  257. package/dist/builtin/workflows/skills/tdd/interface-design.md +31 -0
  258. package/dist/builtin/workflows/skills/tdd/mocking.md +59 -0
  259. package/dist/builtin/workflows/skills/tdd/refactoring.md +10 -0
  260. package/dist/builtin/workflows/skills/tdd/tests.md +61 -0
  261. package/dist/builtin/workflows/skills/workflow/SKILL.md +255 -0
  262. package/dist/builtin/workflows/skills/workflow/references/context-engineering/advanced-evaluation.md +404 -0
  263. package/dist/builtin/workflows/skills/workflow/references/context-engineering/bdi-mental-states.md +313 -0
  264. package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-compression.md +274 -0
  265. package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-degradation.md +208 -0
  266. package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-fundamentals.md +203 -0
  267. package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-optimization.md +197 -0
  268. package/dist/builtin/workflows/skills/workflow/references/context-engineering/evaluation.md +253 -0
  269. package/dist/builtin/workflows/skills/workflow/references/context-engineering/filesystem-context.md +289 -0
  270. package/dist/builtin/workflows/skills/workflow/references/context-engineering/hosted-agents.md +262 -0
  271. package/dist/builtin/workflows/skills/workflow/references/context-engineering/memory-systems.md +221 -0
  272. package/dist/builtin/workflows/skills/workflow/references/context-engineering/multi-agent-patterns.md +259 -0
  273. package/dist/builtin/workflows/skills/workflow/references/context-engineering/project-development.md +293 -0
  274. package/dist/builtin/workflows/skills/workflow/references/context-engineering/tool-design.md +273 -0
  275. package/dist/builtin/workflows/skills/workflow/references/context-engineering.md +23 -0
  276. package/dist/builtin/workflows/skills/workflow/references/design-checklist.md +79 -0
  277. package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +107 -0
  278. package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +140 -0
  279. package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +168 -0
  280. package/dist/builtin/workflows/src/extension/companions.ts +210 -0
  281. package/dist/builtin/workflows/src/extension/config-loader.ts +493 -0
  282. package/dist/builtin/workflows/src/extension/discovery.ts +501 -0
  283. package/dist/builtin/workflows/src/extension/dispatcher.ts +173 -0
  284. package/dist/builtin/workflows/src/extension/index.ts +2143 -0
  285. package/dist/builtin/workflows/src/extension/mcp.ts +110 -0
  286. package/dist/builtin/workflows/src/extension/render-call.ts +39 -0
  287. package/dist/builtin/workflows/src/extension/render-result.ts +214 -0
  288. package/dist/builtin/workflows/src/extension/renderers.ts +87 -0
  289. package/dist/builtin/workflows/src/extension/runtime.ts +360 -0
  290. package/dist/builtin/workflows/src/extension/status-writer.ts +167 -0
  291. package/dist/builtin/workflows/src/extension/wiring.ts +555 -0
  292. package/dist/builtin/workflows/src/extension/workflow-schema.ts +102 -0
  293. package/dist/builtin/workflows/src/index.ts +25 -0
  294. package/dist/builtin/workflows/src/intercom/intercom-bridge.ts +93 -0
  295. package/dist/builtin/workflows/src/intercom/intercom-routing.ts +125 -0
  296. package/dist/builtin/workflows/src/intercom/result-intercom.ts +240 -0
  297. package/dist/builtin/workflows/src/runs/background/cancellation-registry.ts +113 -0
  298. package/dist/builtin/workflows/src/runs/background/job-tracker.ts +81 -0
  299. package/dist/builtin/workflows/src/runs/background/runner.ts +152 -0
  300. package/dist/builtin/workflows/src/runs/background/status.ts +354 -0
  301. package/dist/builtin/workflows/src/runs/foreground/executor.ts +1522 -0
  302. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +233 -0
  303. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +712 -0
  304. package/dist/builtin/workflows/src/runs/shared/concurrency.ts +76 -0
  305. package/dist/builtin/workflows/src/runs/shared/graph-inference.ts +69 -0
  306. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +293 -0
  307. package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +83 -0
  308. package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +170 -0
  309. package/dist/builtin/workflows/src/runs/shared/worktree.ts +577 -0
  310. package/dist/builtin/workflows/src/shared/persistence-compaction-policy.ts +72 -0
  311. package/dist/builtin/workflows/src/shared/persistence-restore.ts +257 -0
  312. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +145 -0
  313. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +196 -0
  314. package/dist/builtin/workflows/src/shared/store-types.ts +160 -0
  315. package/dist/builtin/workflows/src/shared/store.ts +579 -0
  316. package/dist/builtin/workflows/src/shared/types.ts +566 -0
  317. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +224 -0
  318. package/dist/builtin/workflows/src/tui/chat-surface.ts +511 -0
  319. package/dist/builtin/workflows/src/tui/color-utils.ts +64 -0
  320. package/dist/builtin/workflows/src/tui/connectors.ts +88 -0
  321. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +307 -0
  322. package/dist/builtin/workflows/src/tui/edge.ts +24 -0
  323. package/dist/builtin/workflows/src/tui/graph-canvas.ts +108 -0
  324. package/dist/builtin/workflows/src/tui/graph-theme.ts +283 -0
  325. package/dist/builtin/workflows/src/tui/graph-view.ts +1217 -0
  326. package/dist/builtin/workflows/src/tui/header.ts +172 -0
  327. package/dist/builtin/workflows/src/tui/inline-form-card.ts +421 -0
  328. package/dist/builtin/workflows/src/tui/inline-form-editor.ts +638 -0
  329. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +326 -0
  330. package/dist/builtin/workflows/src/tui/inline-form-store.ts +78 -0
  331. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +163 -0
  332. package/dist/builtin/workflows/src/tui/inputs-picker.ts +888 -0
  333. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +154 -0
  334. package/dist/builtin/workflows/src/tui/layout.ts +153 -0
  335. package/dist/builtin/workflows/src/tui/node-card.ts +274 -0
  336. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +277 -0
  337. package/dist/builtin/workflows/src/tui/prompt-card.ts +501 -0
  338. package/dist/builtin/workflows/src/tui/renderers.ts +15 -0
  339. package/dist/builtin/workflows/src/tui/run-detail.ts +339 -0
  340. package/dist/builtin/workflows/src/tui/session-confirm.ts +202 -0
  341. package/dist/builtin/workflows/src/tui/session-list.ts +32 -0
  342. package/dist/builtin/workflows/src/tui/session-overlays.ts +239 -0
  343. package/dist/builtin/workflows/src/tui/session-picker.ts +399 -0
  344. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +1873 -0
  345. package/dist/builtin/workflows/src/tui/status-helpers.ts +73 -0
  346. package/dist/builtin/workflows/src/tui/status-list.ts +361 -0
  347. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +206 -0
  348. package/dist/builtin/workflows/src/tui/switcher.ts +121 -0
  349. package/dist/builtin/workflows/src/tui/text-helpers.ts +31 -0
  350. package/dist/builtin/workflows/src/tui/toast.ts +106 -0
  351. package/dist/builtin/workflows/src/tui/widget.ts +348 -0
  352. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +285 -0
  353. package/dist/builtin/workflows/src/tui/workflow-list.ts +224 -0
  354. package/dist/builtin/workflows/src/workflows/define-workflow.ts +150 -0
  355. package/dist/builtin/workflows/src/workflows/identity.ts +39 -0
  356. package/dist/builtin/workflows/src/workflows/registry.ts +113 -0
  357. package/dist/bun/cli.d.ts +3 -0
  358. package/dist/bun/cli.d.ts.map +1 -0
  359. package/dist/bun/cli.js +9 -0
  360. package/dist/bun/cli.js.map +1 -0
  361. package/dist/bun/register-bedrock.d.ts +2 -0
  362. package/dist/bun/register-bedrock.d.ts.map +1 -0
  363. package/dist/bun/register-bedrock.js +4 -0
  364. package/dist/bun/register-bedrock.js.map +1 -0
  365. package/dist/bun/restore-sandbox-env.d.ts +13 -0
  366. package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
  367. package/dist/bun/restore-sandbox-env.js +32 -0
  368. package/dist/bun/restore-sandbox-env.js.map +1 -0
  369. package/dist/cli/args.d.ts +53 -0
  370. package/dist/cli/args.d.ts.map +1 -0
  371. package/dist/cli/args.js +341 -0
  372. package/dist/cli/args.js.map +1 -0
  373. package/dist/cli/config-selector.d.ts +14 -0
  374. package/dist/cli/config-selector.d.ts.map +1 -0
  375. package/dist/cli/config-selector.js +31 -0
  376. package/dist/cli/config-selector.js.map +1 -0
  377. package/dist/cli/file-processor.d.ts +15 -0
  378. package/dist/cli/file-processor.d.ts.map +1 -0
  379. package/dist/cli/file-processor.js +83 -0
  380. package/dist/cli/file-processor.js.map +1 -0
  381. package/dist/cli/initial-message.d.ts +18 -0
  382. package/dist/cli/initial-message.d.ts.map +1 -0
  383. package/dist/cli/initial-message.js +22 -0
  384. package/dist/cli/initial-message.js.map +1 -0
  385. package/dist/cli/list-models.d.ts +9 -0
  386. package/dist/cli/list-models.d.ts.map +1 -0
  387. package/dist/cli/list-models.js +98 -0
  388. package/dist/cli/list-models.js.map +1 -0
  389. package/dist/cli/session-picker.d.ts +9 -0
  390. package/dist/cli/session-picker.d.ts.map +1 -0
  391. package/dist/cli/session-picker.js +35 -0
  392. package/dist/cli/session-picker.js.map +1 -0
  393. package/dist/cli.d.ts +3 -0
  394. package/dist/cli.d.ts.map +1 -0
  395. package/dist/cli.js +20 -0
  396. package/dist/cli.js.map +1 -0
  397. package/dist/config.d.ts +102 -0
  398. package/dist/config.d.ts.map +1 -0
  399. package/dist/config.js +411 -0
  400. package/dist/config.js.map +1 -0
  401. package/dist/core/agent-session-runtime.d.ts +117 -0
  402. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  403. package/dist/core/agent-session-runtime.js +292 -0
  404. package/dist/core/agent-session-runtime.js.map +1 -0
  405. package/dist/core/agent-session-services.d.ts +86 -0
  406. package/dist/core/agent-session-services.d.ts.map +1 -0
  407. package/dist/core/agent-session-services.js +117 -0
  408. package/dist/core/agent-session-services.js.map +1 -0
  409. package/dist/core/agent-session.d.ts +595 -0
  410. package/dist/core/agent-session.d.ts.map +1 -0
  411. package/dist/core/agent-session.js +2518 -0
  412. package/dist/core/agent-session.js.map +1 -0
  413. package/dist/core/auth-guidance.d.ts +5 -0
  414. package/dist/core/auth-guidance.d.ts.map +1 -0
  415. package/dist/core/auth-guidance.js +21 -0
  416. package/dist/core/auth-guidance.js.map +1 -0
  417. package/dist/core/auth-storage.d.ts +141 -0
  418. package/dist/core/auth-storage.d.ts.map +1 -0
  419. package/dist/core/auth-storage.js +437 -0
  420. package/dist/core/auth-storage.js.map +1 -0
  421. package/dist/core/bash-executor.d.ts +32 -0
  422. package/dist/core/bash-executor.d.ts.map +1 -0
  423. package/dist/core/bash-executor.js +111 -0
  424. package/dist/core/bash-executor.js.map +1 -0
  425. package/dist/core/builtin-packages.d.ts +14 -0
  426. package/dist/core/builtin-packages.d.ts.map +1 -0
  427. package/dist/core/builtin-packages.js +113 -0
  428. package/dist/core/builtin-packages.js.map +1 -0
  429. package/dist/core/compaction/branch-summarization.d.ts +88 -0
  430. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  431. package/dist/core/compaction/branch-summarization.js +243 -0
  432. package/dist/core/compaction/branch-summarization.js.map +1 -0
  433. package/dist/core/compaction/compaction.d.ts +121 -0
  434. package/dist/core/compaction/compaction.d.ts.map +1 -0
  435. package/dist/core/compaction/compaction.js +615 -0
  436. package/dist/core/compaction/compaction.js.map +1 -0
  437. package/dist/core/compaction/index.d.ts +7 -0
  438. package/dist/core/compaction/index.d.ts.map +1 -0
  439. package/dist/core/compaction/index.js +7 -0
  440. package/dist/core/compaction/index.js.map +1 -0
  441. package/dist/core/compaction/utils.d.ts +38 -0
  442. package/dist/core/compaction/utils.d.ts.map +1 -0
  443. package/dist/core/compaction/utils.js +153 -0
  444. package/dist/core/compaction/utils.js.map +1 -0
  445. package/dist/core/defaults.d.ts +3 -0
  446. package/dist/core/defaults.d.ts.map +1 -0
  447. package/dist/core/defaults.js +2 -0
  448. package/dist/core/defaults.js.map +1 -0
  449. package/dist/core/diagnostics.d.ts +15 -0
  450. package/dist/core/diagnostics.d.ts.map +1 -0
  451. package/dist/core/diagnostics.js +2 -0
  452. package/dist/core/diagnostics.js.map +1 -0
  453. package/dist/core/event-bus.d.ts +9 -0
  454. package/dist/core/event-bus.d.ts.map +1 -0
  455. package/dist/core/event-bus.js +25 -0
  456. package/dist/core/event-bus.js.map +1 -0
  457. package/dist/core/exec.d.ts +29 -0
  458. package/dist/core/exec.d.ts.map +1 -0
  459. package/dist/core/exec.js +75 -0
  460. package/dist/core/exec.js.map +1 -0
  461. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  462. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  463. package/dist/core/export-html/ansi-to-html.js +249 -0
  464. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  465. package/dist/core/export-html/index.d.ts +37 -0
  466. package/dist/core/export-html/index.d.ts.map +1 -0
  467. package/dist/core/export-html/index.js +224 -0
  468. package/dist/core/export-html/index.js.map +1 -0
  469. package/dist/core/export-html/template.css +1066 -0
  470. package/dist/core/export-html/template.html +55 -0
  471. package/dist/core/export-html/template.js +1834 -0
  472. package/dist/core/export-html/tool-renderer.d.ts +34 -0
  473. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  474. package/dist/core/export-html/tool-renderer.js +108 -0
  475. package/dist/core/export-html/tool-renderer.js.map +1 -0
  476. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  477. package/dist/core/export-html/vendor/marked.min.js +6 -0
  478. package/dist/core/extensions/index.d.ts +12 -0
  479. package/dist/core/extensions/index.d.ts.map +1 -0
  480. package/dist/core/extensions/index.js +9 -0
  481. package/dist/core/extensions/index.js.map +1 -0
  482. package/dist/core/extensions/loader.d.ts +24 -0
  483. package/dist/core/extensions/loader.d.ts.map +1 -0
  484. package/dist/core/extensions/loader.js +501 -0
  485. package/dist/core/extensions/loader.js.map +1 -0
  486. package/dist/core/extensions/runner.d.ts +159 -0
  487. package/dist/core/extensions/runner.d.ts.map +1 -0
  488. package/dist/core/extensions/runner.js +817 -0
  489. package/dist/core/extensions/runner.js.map +1 -0
  490. package/dist/core/extensions/types.d.ts +1173 -0
  491. package/dist/core/extensions/types.d.ts.map +1 -0
  492. package/dist/core/extensions/types.js +45 -0
  493. package/dist/core/extensions/types.js.map +1 -0
  494. package/dist/core/extensions/wrapper.d.ts +20 -0
  495. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  496. package/dist/core/extensions/wrapper.js +22 -0
  497. package/dist/core/extensions/wrapper.js.map +1 -0
  498. package/dist/core/footer-data-provider.d.ts +52 -0
  499. package/dist/core/footer-data-provider.d.ts.map +1 -0
  500. package/dist/core/footer-data-provider.js +309 -0
  501. package/dist/core/footer-data-provider.js.map +1 -0
  502. package/dist/core/index.d.ts +12 -0
  503. package/dist/core/index.d.ts.map +1 -0
  504. package/dist/core/index.js +12 -0
  505. package/dist/core/index.js.map +1 -0
  506. package/dist/core/keybindings.d.ts +353 -0
  507. package/dist/core/keybindings.d.ts.map +1 -0
  508. package/dist/core/keybindings.js +294 -0
  509. package/dist/core/keybindings.js.map +1 -0
  510. package/dist/core/messages.d.ts +77 -0
  511. package/dist/core/messages.d.ts.map +1 -0
  512. package/dist/core/messages.js +123 -0
  513. package/dist/core/messages.js.map +1 -0
  514. package/dist/core/model-registry.d.ts +150 -0
  515. package/dist/core/model-registry.d.ts.map +1 -0
  516. package/dist/core/model-registry.js +726 -0
  517. package/dist/core/model-registry.js.map +1 -0
  518. package/dist/core/model-resolver.d.ts +110 -0
  519. package/dist/core/model-resolver.d.ts.map +1 -0
  520. package/dist/core/model-resolver.js +493 -0
  521. package/dist/core/model-resolver.js.map +1 -0
  522. package/dist/core/output-guard.d.ts +6 -0
  523. package/dist/core/output-guard.d.ts.map +1 -0
  524. package/dist/core/output-guard.js +59 -0
  525. package/dist/core/output-guard.js.map +1 -0
  526. package/dist/core/package-manager.d.ts +198 -0
  527. package/dist/core/package-manager.d.ts.map +1 -0
  528. package/dist/core/package-manager.js +1970 -0
  529. package/dist/core/package-manager.js.map +1 -0
  530. package/dist/core/prompt-templates.d.ts +52 -0
  531. package/dist/core/prompt-templates.d.ts.map +1 -0
  532. package/dist/core/prompt-templates.js +250 -0
  533. package/dist/core/prompt-templates.js.map +1 -0
  534. package/dist/core/provider-display-names.d.ts +2 -0
  535. package/dist/core/provider-display-names.d.ts.map +1 -0
  536. package/dist/core/provider-display-names.js +33 -0
  537. package/dist/core/provider-display-names.js.map +1 -0
  538. package/dist/core/resolve-config-value.d.ts +23 -0
  539. package/dist/core/resolve-config-value.d.ts.map +1 -0
  540. package/dist/core/resolve-config-value.js +126 -0
  541. package/dist/core/resolve-config-value.js.map +1 -0
  542. package/dist/core/resource-loader.d.ts +196 -0
  543. package/dist/core/resource-loader.d.ts.map +1 -0
  544. package/dist/core/resource-loader.js +698 -0
  545. package/dist/core/resource-loader.js.map +1 -0
  546. package/dist/core/sdk.d.ts +107 -0
  547. package/dist/core/sdk.d.ts.map +1 -0
  548. package/dist/core/sdk.js +291 -0
  549. package/dist/core/sdk.js.map +1 -0
  550. package/dist/core/session-cwd.d.ts +19 -0
  551. package/dist/core/session-cwd.d.ts.map +1 -0
  552. package/dist/core/session-cwd.js +37 -0
  553. package/dist/core/session-cwd.js.map +1 -0
  554. package/dist/core/session-manager.d.ts +333 -0
  555. package/dist/core/session-manager.d.ts.map +1 -0
  556. package/dist/core/session-manager.js +1118 -0
  557. package/dist/core/session-manager.js.map +1 -0
  558. package/dist/core/settings-manager.d.ts +261 -0
  559. package/dist/core/settings-manager.d.ts.map +1 -0
  560. package/dist/core/settings-manager.js +773 -0
  561. package/dist/core/settings-manager.js.map +1 -0
  562. package/dist/core/skills.d.ts +60 -0
  563. package/dist/core/skills.d.ts.map +1 -0
  564. package/dist/core/skills.js +404 -0
  565. package/dist/core/skills.js.map +1 -0
  566. package/dist/core/slash-commands.d.ts +14 -0
  567. package/dist/core/slash-commands.d.ts.map +1 -0
  568. package/dist/core/slash-commands.js +25 -0
  569. package/dist/core/slash-commands.js.map +1 -0
  570. package/dist/core/source-info.d.ts +18 -0
  571. package/dist/core/source-info.d.ts.map +1 -0
  572. package/dist/core/source-info.js +19 -0
  573. package/dist/core/source-info.js.map +1 -0
  574. package/dist/core/system-prompt.d.ts +28 -0
  575. package/dist/core/system-prompt.d.ts.map +1 -0
  576. package/dist/core/system-prompt.js +120 -0
  577. package/dist/core/system-prompt.js.map +1 -0
  578. package/dist/core/telemetry.d.ts +3 -0
  579. package/dist/core/telemetry.d.ts.map +1 -0
  580. package/dist/core/telemetry.js +10 -0
  581. package/dist/core/telemetry.js.map +1 -0
  582. package/dist/core/timings.d.ts +8 -0
  583. package/dist/core/timings.d.ts.map +1 -0
  584. package/dist/core/timings.js +32 -0
  585. package/dist/core/timings.js.map +1 -0
  586. package/dist/core/tools/ask-user-question/ask-user-question.d.ts +10 -0
  587. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -0
  588. package/dist/core/tools/ask-user-question/ask-user-question.js +82 -0
  589. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -0
  590. package/dist/core/tools/ask-user-question/config.d.ts +11 -0
  591. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -0
  592. package/dist/core/tools/ask-user-question/config.js +34 -0
  593. package/dist/core/tools/ask-user-question/config.js.map +1 -0
  594. package/dist/core/tools/ask-user-question/index.d.ts +19 -0
  595. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -0
  596. package/dist/core/tools/ask-user-question/index.js +19 -0
  597. package/dist/core/tools/ask-user-question/index.js.map +1 -0
  598. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts +36 -0
  599. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -0
  600. package/dist/core/tools/ask-user-question/state/build-questionnaire.js +213 -0
  601. package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -0
  602. package/dist/core/tools/ask-user-question/state/key-router.d.ts +53 -0
  603. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -0
  604. package/dist/core/tools/ask-user-question/state/key-router.js +240 -0
  605. package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -0
  606. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +61 -0
  607. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -0
  608. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +147 -0
  609. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -0
  610. package/dist/core/tools/ask-user-question/state/row-intent.d.ts +91 -0
  611. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -0
  612. package/dist/core/tools/ask-user-question/state/row-intent.js +91 -0
  613. package/dist/core/tools/ask-user-question/state/row-intent.js.map +1 -0
  614. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +21 -0
  615. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -0
  616. package/dist/core/tools/ask-user-question/state/selectors/contract.js +2 -0
  617. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -0
  618. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts +44 -0
  619. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -0
  620. package/dist/core/tools/ask-user-question/state/selectors/derivations.js +76 -0
  621. package/dist/core/tools/ask-user-question/state/selectors/derivations.js.map +1 -0
  622. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts +15 -0
  623. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -0
  624. package/dist/core/tools/ask-user-question/state/selectors/focus.js +18 -0
  625. package/dist/core/tools/ask-user-question/state/selectors/focus.js.map +1 -0
  626. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts +16 -0
  627. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -0
  628. package/dist/core/tools/ask-user-question/state/selectors/projections.js +71 -0
  629. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -0
  630. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +44 -0
  631. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -0
  632. package/dist/core/tools/ask-user-question/state/state-reducer.js +239 -0
  633. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -0
  634. package/dist/core/tools/ask-user-question/state/state.d.ts +42 -0
  635. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -0
  636. package/dist/core/tools/ask-user-question/state/state.js +2 -0
  637. package/dist/core/tools/ask-user-question/state/state.js.map +1 -0
  638. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +30 -0
  639. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -0
  640. package/dist/core/tools/ask-user-question/tool/format-answer.js +39 -0
  641. package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -0
  642. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +29 -0
  643. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -0
  644. package/dist/core/tools/ask-user-question/tool/response-envelope.js +46 -0
  645. package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -0
  646. package/dist/core/tools/ask-user-question/tool/types.d.ts +113 -0
  647. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -0
  648. package/dist/core/tools/ask-user-question/tool/types.js +81 -0
  649. package/dist/core/tools/ask-user-question/tool/types.js.map +1 -0
  650. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts +21 -0
  651. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -0
  652. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.js +49 -0
  653. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.js.map +1 -0
  654. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts +18 -0
  655. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -0
  656. package/dist/core/tools/ask-user-question/view/body-residual-spacer.js +21 -0
  657. package/dist/core/tools/ask-user-question/view/body-residual-spacer.js.map +1 -0
  658. package/dist/core/tools/ask-user-question/view/component-binding.d.ts +23 -0
  659. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -0
  660. package/dist/core/tools/ask-user-question/view/component-binding.js +16 -0
  661. package/dist/core/tools/ask-user-question/view/component-binding.js.map +1 -0
  662. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +40 -0
  663. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -0
  664. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +31 -0
  665. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -0
  666. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts +35 -0
  667. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -0
  668. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js +91 -0
  669. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -0
  670. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +43 -0
  671. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -0
  672. package/dist/core/tools/ask-user-question/view/components/option-list-view.js +37 -0
  673. package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -0
  674. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts +36 -0
  675. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -0
  676. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js +66 -0
  677. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map +1 -0
  678. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts +46 -0
  679. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -0
  680. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +69 -0
  681. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -0
  682. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts +39 -0
  683. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -0
  684. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.js +76 -0
  685. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.js.map +1 -0
  686. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts +116 -0
  687. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -0
  688. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.js +173 -0
  689. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.js.map +1 -0
  690. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts +66 -0
  691. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -0
  692. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js +124 -0
  693. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js.map +1 -0
  694. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts +37 -0
  695. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -0
  696. package/dist/core/tools/ask-user-question/view/components/submit-picker.js +44 -0
  697. package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -0
  698. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts +32 -0
  699. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -0
  700. package/dist/core/tools/ask-user-question/view/components/tab-bar.js +33 -0
  701. package/dist/core/tools/ask-user-question/view/components/tab-bar.js.map +1 -0
  702. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +122 -0
  703. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -0
  704. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +161 -0
  705. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -0
  706. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts +66 -0
  707. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -0
  708. package/dist/core/tools/ask-user-question/view/dialog-builder.js +85 -0
  709. package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -0
  710. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts +58 -0
  711. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -0
  712. package/dist/core/tools/ask-user-question/view/props-adapter.js +67 -0
  713. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -0
  714. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts +24 -0
  715. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -0
  716. package/dist/core/tools/ask-user-question/view/stateful-view.js +2 -0
  717. package/dist/core/tools/ask-user-question/view/stateful-view.js.map +1 -0
  718. package/dist/core/tools/ask-user-question/view/tab-components.d.ts +15 -0
  719. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -0
  720. package/dist/core/tools/ask-user-question/view/tab-components.js +2 -0
  721. package/dist/core/tools/ask-user-question/view/tab-components.js.map +1 -0
  722. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts +71 -0
  723. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -0
  724. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js +129 -0
  725. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js.map +1 -0
  726. package/dist/core/tools/bash.d.ts +68 -0
  727. package/dist/core/tools/bash.d.ts.map +1 -0
  728. package/dist/core/tools/bash.js +338 -0
  729. package/dist/core/tools/bash.js.map +1 -0
  730. package/dist/core/tools/edit-diff.d.ts +85 -0
  731. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  732. package/dist/core/tools/edit-diff.js +338 -0
  733. package/dist/core/tools/edit-diff.js.map +1 -0
  734. package/dist/core/tools/edit.d.ts +49 -0
  735. package/dist/core/tools/edit.d.ts.map +1 -0
  736. package/dist/core/tools/edit.js +324 -0
  737. package/dist/core/tools/edit.js.map +1 -0
  738. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  739. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  740. package/dist/core/tools/file-mutation-queue.js +37 -0
  741. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  742. package/dist/core/tools/find.d.ts +35 -0
  743. package/dist/core/tools/find.d.ts.map +1 -0
  744. package/dist/core/tools/find.js +298 -0
  745. package/dist/core/tools/find.js.map +1 -0
  746. package/dist/core/tools/grep.d.ts +37 -0
  747. package/dist/core/tools/grep.d.ts.map +1 -0
  748. package/dist/core/tools/grep.js +304 -0
  749. package/dist/core/tools/grep.js.map +1 -0
  750. package/dist/core/tools/index.d.ts +42 -0
  751. package/dist/core/tools/index.d.ts.map +1 -0
  752. package/dist/core/tools/index.js +139 -0
  753. package/dist/core/tools/index.js.map +1 -0
  754. package/dist/core/tools/ls.d.ts +37 -0
  755. package/dist/core/tools/ls.d.ts.map +1 -0
  756. package/dist/core/tools/ls.js +169 -0
  757. package/dist/core/tools/ls.js.map +1 -0
  758. package/dist/core/tools/output-accumulator.d.ts +50 -0
  759. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  760. package/dist/core/tools/output-accumulator.js +172 -0
  761. package/dist/core/tools/output-accumulator.js.map +1 -0
  762. package/dist/core/tools/path-utils.d.ts +8 -0
  763. package/dist/core/tools/path-utils.d.ts.map +1 -0
  764. package/dist/core/tools/path-utils.js +81 -0
  765. package/dist/core/tools/path-utils.js.map +1 -0
  766. package/dist/core/tools/read.d.ts +35 -0
  767. package/dist/core/tools/read.d.ts.map +1 -0
  768. package/dist/core/tools/read.js +289 -0
  769. package/dist/core/tools/read.js.map +1 -0
  770. package/dist/core/tools/render-utils.d.ts +21 -0
  771. package/dist/core/tools/render-utils.d.ts.map +1 -0
  772. package/dist/core/tools/render-utils.js +49 -0
  773. package/dist/core/tools/render-utils.js.map +1 -0
  774. package/dist/core/tools/todos.d.ts +35 -0
  775. package/dist/core/tools/todos.d.ts.map +1 -0
  776. package/dist/core/tools/todos.js +906 -0
  777. package/dist/core/tools/todos.js.map +1 -0
  778. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  779. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  780. package/dist/core/tools/tool-definition-wrapper.js +34 -0
  781. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  782. package/dist/core/tools/truncate.d.ts +70 -0
  783. package/dist/core/tools/truncate.d.ts.map +1 -0
  784. package/dist/core/tools/truncate.js +205 -0
  785. package/dist/core/tools/truncate.js.map +1 -0
  786. package/dist/core/tools/write.d.ts +26 -0
  787. package/dist/core/tools/write.d.ts.map +1 -0
  788. package/dist/core/tools/write.js +212 -0
  789. package/dist/core/tools/write.js.map +1 -0
  790. package/dist/index.d.ts +28 -0
  791. package/dist/index.d.ts.map +1 -0
  792. package/dist/index.js +41 -0
  793. package/dist/index.js.map +1 -0
  794. package/dist/main.d.ts +13 -0
  795. package/dist/main.d.ts.map +1 -0
  796. package/dist/main.js +586 -0
  797. package/dist/main.js.map +1 -0
  798. package/dist/migrations.d.ts +33 -0
  799. package/dist/migrations.d.ts.map +1 -0
  800. package/dist/migrations.js +281 -0
  801. package/dist/migrations.js.map +1 -0
  802. package/dist/modes/index.d.ts +9 -0
  803. package/dist/modes/index.d.ts.map +1 -0
  804. package/dist/modes/index.js +8 -0
  805. package/dist/modes/index.js.map +1 -0
  806. package/dist/modes/interactive/assets/clankolas.png +3 -0
  807. package/dist/modes/interactive/components/armin.d.ts +34 -0
  808. package/dist/modes/interactive/components/armin.d.ts.map +1 -0
  809. package/dist/modes/interactive/components/armin.js +329 -0
  810. package/dist/modes/interactive/components/armin.js.map +1 -0
  811. package/dist/modes/interactive/components/assistant-message.d.ts +20 -0
  812. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  813. package/dist/modes/interactive/components/assistant-message.js +116 -0
  814. package/dist/modes/interactive/components/assistant-message.js.map +1 -0
  815. package/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  816. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  817. package/dist/modes/interactive/components/bash-execution.js +170 -0
  818. package/dist/modes/interactive/components/bash-execution.js.map +1 -0
  819. package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  820. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  821. package/dist/modes/interactive/components/bordered-loader.js +51 -0
  822. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  823. package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  824. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  825. package/dist/modes/interactive/components/branch-summary-message.js +42 -0
  826. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  827. package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  828. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  829. package/dist/modes/interactive/components/compaction-summary-message.js +43 -0
  830. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  831. package/dist/modes/interactive/components/config-selector.d.ts +71 -0
  832. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  833. package/dist/modes/interactive/components/config-selector.js +496 -0
  834. package/dist/modes/interactive/components/config-selector.js.map +1 -0
  835. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  836. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  837. package/dist/modes/interactive/components/countdown-timer.js +28 -0
  838. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  839. package/dist/modes/interactive/components/custom-editor.d.ts +29 -0
  840. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  841. package/dist/modes/interactive/components/custom-editor.js +113 -0
  842. package/dist/modes/interactive/components/custom-editor.js.map +1 -0
  843. package/dist/modes/interactive/components/custom-message.d.ts +20 -0
  844. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  845. package/dist/modes/interactive/components/custom-message.js +74 -0
  846. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  847. package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  848. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  849. package/dist/modes/interactive/components/daxnuts.js +138 -0
  850. package/dist/modes/interactive/components/daxnuts.js.map +1 -0
  851. package/dist/modes/interactive/components/diff.d.ts +12 -0
  852. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  853. package/dist/modes/interactive/components/diff.js +151 -0
  854. package/dist/modes/interactive/components/diff.js.map +1 -0
  855. package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  856. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  857. package/dist/modes/interactive/components/dynamic-border.js +20 -0
  858. package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  859. package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  860. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
  861. package/dist/modes/interactive/components/earendil-announcement.js +40 -0
  862. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
  863. package/dist/modes/interactive/components/extension-editor.d.ts +20 -0
  864. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  865. package/dist/modes/interactive/components/extension-editor.js +106 -0
  866. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  867. package/dist/modes/interactive/components/extension-input.d.ts +23 -0
  868. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  869. package/dist/modes/interactive/components/extension-input.js +55 -0
  870. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  871. package/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  872. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  873. package/dist/modes/interactive/components/extension-selector.js +75 -0
  874. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  875. package/dist/modes/interactive/components/footer.d.ts +27 -0
  876. package/dist/modes/interactive/components/footer.d.ts.map +1 -0
  877. package/dist/modes/interactive/components/footer.js +199 -0
  878. package/dist/modes/interactive/components/footer.js.map +1 -0
  879. package/dist/modes/interactive/components/index.d.ts +32 -0
  880. package/dist/modes/interactive/components/index.d.ts.map +1 -0
  881. package/dist/modes/interactive/components/index.js +33 -0
  882. package/dist/modes/interactive/components/index.js.map +1 -0
  883. package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  884. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  885. package/dist/modes/interactive/components/keybinding-hints.js +36 -0
  886. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  887. package/dist/modes/interactive/components/login-dialog.d.ts +46 -0
  888. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  889. package/dist/modes/interactive/components/login-dialog.js +158 -0
  890. package/dist/modes/interactive/components/login-dialog.js.map +1 -0
  891. package/dist/modes/interactive/components/model-selector.d.ts +47 -0
  892. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  893. package/dist/modes/interactive/components/model-selector.js +266 -0
  894. package/dist/modes/interactive/components/model-selector.js.map +1 -0
  895. package/dist/modes/interactive/components/oauth-selector.d.ts +31 -0
  896. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  897. package/dist/modes/interactive/components/oauth-selector.js +156 -0
  898. package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  899. package/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  900. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  901. package/dist/modes/interactive/components/scoped-models-selector.js +286 -0
  902. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  903. package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  904. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  905. package/dist/modes/interactive/components/session-selector-search.js +155 -0
  906. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  907. package/dist/modes/interactive/components/session-selector.d.ts +96 -0
  908. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  909. package/dist/modes/interactive/components/session-selector.js +836 -0
  910. package/dist/modes/interactive/components/session-selector.js.map +1 -0
  911. package/dist/modes/interactive/components/settings-selector.d.ts +67 -0
  912. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  913. package/dist/modes/interactive/components/settings-selector.js +371 -0
  914. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  915. package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  916. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  917. package/dist/modes/interactive/components/show-images-selector.js +38 -0
  918. package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  919. package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  920. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  921. package/dist/modes/interactive/components/skill-invocation-message.js +45 -0
  922. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  923. package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  924. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  925. package/dist/modes/interactive/components/theme-selector.js +48 -0
  926. package/dist/modes/interactive/components/theme-selector.js.map +1 -0
  927. package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  928. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  929. package/dist/modes/interactive/components/thinking-selector.js +50 -0
  930. package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  931. package/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  932. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  933. package/dist/modes/interactive/components/tool-execution.js +280 -0
  934. package/dist/modes/interactive/components/tool-execution.js.map +1 -0
  935. package/dist/modes/interactive/components/tree-selector.d.ts +89 -0
  936. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  937. package/dist/modes/interactive/components/tree-selector.js +1079 -0
  938. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  939. package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  940. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  941. package/dist/modes/interactive/components/user-message-selector.js +111 -0
  942. package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  943. package/dist/modes/interactive/components/user-message.d.ts +10 -0
  944. package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  945. package/dist/modes/interactive/components/user-message.js +28 -0
  946. package/dist/modes/interactive/components/user-message.js.map +1 -0
  947. package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  948. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  949. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  950. package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  951. package/dist/modes/interactive/interactive-mode.d.ts +369 -0
  952. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  953. package/dist/modes/interactive/interactive-mode.js +4709 -0
  954. package/dist/modes/interactive/interactive-mode.js.map +1 -0
  955. package/dist/modes/interactive/theme/catppuccin-frappe.json +90 -0
  956. package/dist/modes/interactive/theme/catppuccin-latte.json +90 -0
  957. package/dist/modes/interactive/theme/catppuccin-macchiato.json +90 -0
  958. package/dist/modes/interactive/theme/catppuccin-mocha.json +90 -0
  959. package/dist/modes/interactive/theme/dark.json +85 -0
  960. package/dist/modes/interactive/theme/light.json +84 -0
  961. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  962. package/dist/modes/interactive/theme/theme.d.ts +81 -0
  963. package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  964. package/dist/modes/interactive/theme/theme.js +970 -0
  965. package/dist/modes/interactive/theme/theme.js.map +1 -0
  966. package/dist/modes/interactive/whimsical-messages.d.ts +5 -0
  967. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -0
  968. package/dist/modes/interactive/whimsical-messages.js +464 -0
  969. package/dist/modes/interactive/whimsical-messages.js.map +1 -0
  970. package/dist/modes/print-mode.d.ts +28 -0
  971. package/dist/modes/print-mode.d.ts.map +1 -0
  972. package/dist/modes/print-mode.js +131 -0
  973. package/dist/modes/print-mode.js.map +1 -0
  974. package/dist/modes/rpc/jsonl.d.ts +17 -0
  975. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  976. package/dist/modes/rpc/jsonl.js +49 -0
  977. package/dist/modes/rpc/jsonl.js.map +1 -0
  978. package/dist/modes/rpc/rpc-client.d.ts +224 -0
  979. package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  980. package/dist/modes/rpc/rpc-client.js +409 -0
  981. package/dist/modes/rpc/rpc-client.js.map +1 -0
  982. package/dist/modes/rpc/rpc-mode.d.ts +20 -0
  983. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  984. package/dist/modes/rpc/rpc-mode.js +601 -0
  985. package/dist/modes/rpc/rpc-mode.js.map +1 -0
  986. package/dist/modes/rpc/rpc-types.d.ts +419 -0
  987. package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  988. package/dist/modes/rpc/rpc-types.js +8 -0
  989. package/dist/modes/rpc/rpc-types.js.map +1 -0
  990. package/dist/package-manager-cli.d.ts +4 -0
  991. package/dist/package-manager-cli.d.ts.map +1 -0
  992. package/dist/package-manager-cli.js +460 -0
  993. package/dist/package-manager-cli.js.map +1 -0
  994. package/dist/utils/ansi.d.ts +2 -0
  995. package/dist/utils/ansi.d.ts.map +1 -0
  996. package/dist/utils/ansi.js +52 -0
  997. package/dist/utils/ansi.js.map +1 -0
  998. package/dist/utils/changelog.d.ts +21 -0
  999. package/dist/utils/changelog.d.ts.map +1 -0
  1000. package/dist/utils/changelog.js +87 -0
  1001. package/dist/utils/changelog.js.map +1 -0
  1002. package/dist/utils/child-process.d.ts +12 -0
  1003. package/dist/utils/child-process.d.ts.map +1 -0
  1004. package/dist/utils/child-process.js +86 -0
  1005. package/dist/utils/child-process.js.map +1 -0
  1006. package/dist/utils/clipboard-image.d.ts +11 -0
  1007. package/dist/utils/clipboard-image.d.ts.map +1 -0
  1008. package/dist/utils/clipboard-image.js +245 -0
  1009. package/dist/utils/clipboard-image.js.map +1 -0
  1010. package/dist/utils/clipboard-native.d.ts +8 -0
  1011. package/dist/utils/clipboard-native.d.ts.map +1 -0
  1012. package/dist/utils/clipboard-native.js +14 -0
  1013. package/dist/utils/clipboard-native.js.map +1 -0
  1014. package/dist/utils/clipboard.d.ts +2 -0
  1015. package/dist/utils/clipboard.d.ts.map +1 -0
  1016. package/dist/utils/clipboard.js +117 -0
  1017. package/dist/utils/clipboard.js.map +1 -0
  1018. package/dist/utils/exif-orientation.d.ts +5 -0
  1019. package/dist/utils/exif-orientation.d.ts.map +1 -0
  1020. package/dist/utils/exif-orientation.js +158 -0
  1021. package/dist/utils/exif-orientation.js.map +1 -0
  1022. package/dist/utils/frontmatter.d.ts +8 -0
  1023. package/dist/utils/frontmatter.d.ts.map +1 -0
  1024. package/dist/utils/frontmatter.js +26 -0
  1025. package/dist/utils/frontmatter.js.map +1 -0
  1026. package/dist/utils/fs-watch.d.ts +5 -0
  1027. package/dist/utils/fs-watch.d.ts.map +1 -0
  1028. package/dist/utils/fs-watch.js +25 -0
  1029. package/dist/utils/fs-watch.js.map +1 -0
  1030. package/dist/utils/git.d.ts +26 -0
  1031. package/dist/utils/git.d.ts.map +1 -0
  1032. package/dist/utils/git.js +163 -0
  1033. package/dist/utils/git.js.map +1 -0
  1034. package/dist/utils/html.d.ts +7 -0
  1035. package/dist/utils/html.d.ts.map +1 -0
  1036. package/dist/utils/html.js +40 -0
  1037. package/dist/utils/html.js.map +1 -0
  1038. package/dist/utils/image-convert.d.ts +9 -0
  1039. package/dist/utils/image-convert.d.ts.map +1 -0
  1040. package/dist/utils/image-convert.js +39 -0
  1041. package/dist/utils/image-convert.js.map +1 -0
  1042. package/dist/utils/image-resize.d.ts +36 -0
  1043. package/dist/utils/image-resize.d.ts.map +1 -0
  1044. package/dist/utils/image-resize.js +137 -0
  1045. package/dist/utils/image-resize.js.map +1 -0
  1046. package/dist/utils/mime.d.ts +3 -0
  1047. package/dist/utils/mime.d.ts.map +1 -0
  1048. package/dist/utils/mime.js +69 -0
  1049. package/dist/utils/mime.js.map +1 -0
  1050. package/dist/utils/paths.d.ts +16 -0
  1051. package/dist/utils/paths.d.ts.map +1 -0
  1052. package/dist/utils/paths.js +50 -0
  1053. package/dist/utils/paths.js.map +1 -0
  1054. package/dist/utils/photon.d.ts +21 -0
  1055. package/dist/utils/photon.d.ts.map +1 -0
  1056. package/dist/utils/photon.js +121 -0
  1057. package/dist/utils/photon.js.map +1 -0
  1058. package/dist/utils/pi-user-agent.d.ts +2 -0
  1059. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  1060. package/dist/utils/pi-user-agent.js +5 -0
  1061. package/dist/utils/pi-user-agent.js.map +1 -0
  1062. package/dist/utils/shell.d.ts +30 -0
  1063. package/dist/utils/shell.d.ts.map +1 -0
  1064. package/dist/utils/shell.js +190 -0
  1065. package/dist/utils/shell.js.map +1 -0
  1066. package/dist/utils/sleep.d.ts +5 -0
  1067. package/dist/utils/sleep.d.ts.map +1 -0
  1068. package/dist/utils/sleep.js +17 -0
  1069. package/dist/utils/sleep.js.map +1 -0
  1070. package/dist/utils/syntax-highlight.d.ts +12 -0
  1071. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  1072. package/dist/utils/syntax-highlight.js +118 -0
  1073. package/dist/utils/syntax-highlight.js.map +1 -0
  1074. package/dist/utils/tools-manager.d.ts +3 -0
  1075. package/dist/utils/tools-manager.d.ts.map +1 -0
  1076. package/dist/utils/tools-manager.js +325 -0
  1077. package/dist/utils/tools-manager.js.map +1 -0
  1078. package/dist/utils/version-check.d.ts +14 -0
  1079. package/dist/utils/version-check.d.ts.map +1 -0
  1080. package/dist/utils/version-check.js +76 -0
  1081. package/dist/utils/version-check.js.map +1 -0
  1082. package/docs/compaction.md +394 -0
  1083. package/docs/custom-provider.md +646 -0
  1084. package/docs/development.md +71 -0
  1085. package/docs/docs.json +148 -0
  1086. package/docs/extensions.md +2596 -0
  1087. package/docs/images/doom-extension.png +0 -0
  1088. package/docs/images/exy.png +3 -0
  1089. package/docs/images/interactive-mode.png +0 -0
  1090. package/docs/images/tree-view.png +0 -0
  1091. package/docs/index.md +70 -0
  1092. package/docs/json.md +82 -0
  1093. package/docs/keybindings.md +197 -0
  1094. package/docs/models.md +474 -0
  1095. package/docs/packages.md +223 -0
  1096. package/docs/prompt-templates.md +88 -0
  1097. package/docs/providers.md +243 -0
  1098. package/docs/quickstart.md +142 -0
  1099. package/docs/rpc.md +1407 -0
  1100. package/docs/sdk.md +1129 -0
  1101. package/docs/session-format.md +412 -0
  1102. package/docs/sessions.md +137 -0
  1103. package/docs/settings.md +279 -0
  1104. package/docs/shell-aliases.md +13 -0
  1105. package/docs/skills.md +232 -0
  1106. package/docs/terminal-setup.md +106 -0
  1107. package/docs/termux.md +127 -0
  1108. package/docs/themes.md +299 -0
  1109. package/docs/tmux.md +61 -0
  1110. package/docs/tui.md +918 -0
  1111. package/docs/usage.md +277 -0
  1112. package/docs/windows.md +17 -0
  1113. package/examples/README.md +25 -0
  1114. package/examples/extensions/README.md +208 -0
  1115. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  1116. package/examples/extensions/bash-spawn-hook.ts +30 -0
  1117. package/examples/extensions/bookmark.ts +50 -0
  1118. package/examples/extensions/border-status-editor.ts +150 -0
  1119. package/examples/extensions/built-in-tool-renderer.ts +249 -0
  1120. package/examples/extensions/claude-rules.ts +86 -0
  1121. package/examples/extensions/commands.ts +72 -0
  1122. package/examples/extensions/confirm-destructive.ts +59 -0
  1123. package/examples/extensions/custom-compaction.ts +127 -0
  1124. package/examples/extensions/custom-footer.ts +64 -0
  1125. package/examples/extensions/custom-header.ts +73 -0
  1126. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  1127. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  1128. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  1129. package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
  1130. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  1131. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  1132. package/examples/extensions/dirty-repo-guard.ts +56 -0
  1133. package/examples/extensions/doom-overlay/README.md +46 -0
  1134. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  1135. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  1136. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  1137. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  1138. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  1139. package/examples/extensions/doom-overlay/index.ts +74 -0
  1140. package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  1141. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  1142. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  1143. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  1144. package/examples/extensions/dynamic-resources/index.ts +15 -0
  1145. package/examples/extensions/dynamic-tools.ts +74 -0
  1146. package/examples/extensions/event-bus.ts +43 -0
  1147. package/examples/extensions/file-trigger.ts +41 -0
  1148. package/examples/extensions/git-checkpoint.ts +53 -0
  1149. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  1150. package/examples/extensions/handoff.ts +191 -0
  1151. package/examples/extensions/hello.ts +26 -0
  1152. package/examples/extensions/hidden-thinking-label.ts +53 -0
  1153. package/examples/extensions/inline-bash.ts +94 -0
  1154. package/examples/extensions/input-transform.ts +43 -0
  1155. package/examples/extensions/interactive-shell.ts +196 -0
  1156. package/examples/extensions/mac-system-theme.ts +47 -0
  1157. package/examples/extensions/message-renderer.ts +59 -0
  1158. package/examples/extensions/minimal-mode.ts +426 -0
  1159. package/examples/extensions/modal-editor.ts +85 -0
  1160. package/examples/extensions/model-status.ts +31 -0
  1161. package/examples/extensions/notify.ts +55 -0
  1162. package/examples/extensions/overlay-qa-tests.ts +1348 -0
  1163. package/examples/extensions/overlay-test.ts +150 -0
  1164. package/examples/extensions/permission-gate.ts +34 -0
  1165. package/examples/extensions/pirate.ts +47 -0
  1166. package/examples/extensions/plan-mode/README.md +65 -0
  1167. package/examples/extensions/plan-mode/index.ts +340 -0
  1168. package/examples/extensions/plan-mode/utils.ts +168 -0
  1169. package/examples/extensions/preset.ts +430 -0
  1170. package/examples/extensions/prompt-customizer.ts +97 -0
  1171. package/examples/extensions/protected-paths.ts +30 -0
  1172. package/examples/extensions/provider-payload.ts +18 -0
  1173. package/examples/extensions/qna.ts +122 -0
  1174. package/examples/extensions/question.ts +264 -0
  1175. package/examples/extensions/questionnaire.ts +427 -0
  1176. package/examples/extensions/rainbow-editor.ts +88 -0
  1177. package/examples/extensions/reload-runtime.ts +37 -0
  1178. package/examples/extensions/rpc-demo.ts +118 -0
  1179. package/examples/extensions/sandbox/index.ts +321 -0
  1180. package/examples/extensions/sandbox/package-lock.json +92 -0
  1181. package/examples/extensions/sandbox/package.json +19 -0
  1182. package/examples/extensions/send-user-message.ts +97 -0
  1183. package/examples/extensions/session-name.ts +27 -0
  1184. package/examples/extensions/shutdown-command.ts +63 -0
  1185. package/examples/extensions/snake.ts +343 -0
  1186. package/examples/extensions/space-invaders.ts +560 -0
  1187. package/examples/extensions/ssh.ts +220 -0
  1188. package/examples/extensions/status-line.ts +32 -0
  1189. package/examples/extensions/structured-output.ts +65 -0
  1190. package/examples/extensions/subagent/README.md +172 -0
  1191. package/examples/extensions/subagent/agents/planner.md +37 -0
  1192. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  1193. package/examples/extensions/subagent/agents/scout.md +50 -0
  1194. package/examples/extensions/subagent/agents/worker.md +24 -0
  1195. package/examples/extensions/subagent/agents.ts +126 -0
  1196. package/examples/extensions/subagent/index.ts +987 -0
  1197. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  1198. package/examples/extensions/subagent/prompts/implement.md +10 -0
  1199. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  1200. package/examples/extensions/summarize.ts +206 -0
  1201. package/examples/extensions/system-prompt-header.ts +17 -0
  1202. package/examples/extensions/tic-tac-toe.ts +1008 -0
  1203. package/examples/extensions/timed-confirm.ts +70 -0
  1204. package/examples/extensions/titlebar-spinner.ts +58 -0
  1205. package/examples/extensions/todo.ts +297 -0
  1206. package/examples/extensions/tool-override.ts +144 -0
  1207. package/examples/extensions/tools.ts +141 -0
  1208. package/examples/extensions/trigger-compact.ts +50 -0
  1209. package/examples/extensions/truncated-tool.ts +195 -0
  1210. package/examples/extensions/widget-placement.ts +9 -0
  1211. package/examples/extensions/with-deps/index.ts +32 -0
  1212. package/examples/extensions/with-deps/package-lock.json +31 -0
  1213. package/examples/extensions/with-deps/package.json +22 -0
  1214. package/examples/extensions/working-indicator.ts +123 -0
  1215. package/examples/extensions/working-message-test.ts +25 -0
  1216. package/examples/rpc-extension-ui.ts +632 -0
  1217. package/examples/sdk/01-minimal.ts +26 -0
  1218. package/examples/sdk/02-custom-model.ts +53 -0
  1219. package/examples/sdk/03-custom-prompt.ts +75 -0
  1220. package/examples/sdk/04-skills.ts +55 -0
  1221. package/examples/sdk/05-tools.ts +48 -0
  1222. package/examples/sdk/06-extensions.ts +99 -0
  1223. package/examples/sdk/07-context-files.ts +47 -0
  1224. package/examples/sdk/08-prompt-templates.ts +51 -0
  1225. package/examples/sdk/09-api-keys-and-oauth.ts +52 -0
  1226. package/examples/sdk/10-settings.ts +53 -0
  1227. package/examples/sdk/11-sessions.ts +52 -0
  1228. package/examples/sdk/12-full-control.ts +77 -0
  1229. package/examples/sdk/13-session-runtime.ts +67 -0
  1230. package/examples/sdk/README.md +144 -0
  1231. package/package.json +94 -19
  1232. package/bin/atomic +0 -82
  1233. /package/{LICENSE → dist/builtin/workflows/LICENSE} +0 -0
@@ -0,0 +1,1873 @@
1
+ /**
2
+ * StageChatView — attached workflow-stage chat surface.
3
+ *
4
+ * The overlay keeps workflow-specific chrome for stage metadata / controls, but
5
+ * the chat body now delegates to packages/coding-agent's exported interactive
6
+ * components instead of maintaining parallel workflow message widgets:
7
+ * - user rows use `UserMessageComponent`
8
+ * - assistant/thinking rows use `AssistantMessageComponent`
9
+ * - tool rows use `ToolExecutionComponent` and built-in tool renderers
10
+ * - input reuses the host's custom editor factory when one is installed,
11
+ * otherwise `CustomEditor`; tests/headless fall back to the historical
12
+ * one-line editor
13
+ * - workflow notices remain lightweight workflow-specific rows because they
14
+ * are not coding-agent chat messages
15
+ *
16
+ * Behaviour:
17
+ * - **Idle** stage (empty transcript, not streaming, not settled): welcome
18
+ * panel describing the attached stage. Enter sends `handle.prompt(text)`.
19
+ * - **Running** stage with a live stream: Enter calls `handle.steer(text)`
20
+ * (interrupt mid-turn). Ctrl+F always queues a follow-up via
21
+ * `handle.followUp(text)`.
22
+ * - **Ctrl+P** calls `handle.pause()`; while paused, Enter calls
23
+ * `handle.resume(text)`.
24
+ * - **Ctrl+D** detaches (back to graph); **Escape** closes the popup.
25
+ * - **Blocked** stage: keystrokes absorbed; BLOCKED banner names the
26
+ * upstream awaiter.
27
+ * - **Settled** stage (no handle, completed/failed): editor renders in a
28
+ * disabled visual state and the hint strip collapses to back/close.
29
+ *
30
+ * cross-ref:
31
+ * - ui/stage-chat-mockup.html (canonical visual)
32
+ * - DESIGN.md §5 (Components — pill / box / banner vocabulary)
33
+ * - src/runs/foreground/stage-control-registry.ts (StageControlHandle)
34
+ * - src/shared/store-types.ts (StageSnapshot.notices, StageNotice)
35
+ * - https://pi.dev/docs/latest/tui (canonical Pi-tui component contract)
36
+ * - node_modules/@earendil-works/pi-tui/src/components/{box,text,spacer}.ts
37
+ */
38
+
39
+ import {
40
+ AssistantMessageComponent,
41
+ CustomEditor,
42
+ parseSkillBlock,
43
+ SessionManager,
44
+ SkillInvocationMessageComponent,
45
+ ToolExecutionComponent,
46
+ UserMessageComponent,
47
+ type AgentSession,
48
+ type AgentSessionEvent,
49
+ type SessionMessageEntry,
50
+ } from "@bastani/atomic";
51
+ import { Box, Container, Spacer, Text } from "@earendil-works/pi-tui";
52
+ import type { Component, EditorComponent, EditorTheme, TUI } from "@earendil-works/pi-tui";
53
+ import type { Store } from "../shared/store.js";
54
+ import type { StageNotice, StageSnapshot } from "../shared/store-types.js";
55
+ import type { GraphTheme } from "./graph-theme.js";
56
+ import type { StageControlHandle } from "../runs/foreground/stage-control-registry.js";
57
+ import { BOLD, RESET, hexBg, hexToAnsi, lerpColor } from "./color-utils.js";
58
+ import { truncateToWidth, visibleWidth } from "./text-helpers.js";
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Options & types
62
+ // ---------------------------------------------------------------------------
63
+
64
+ export interface StageChatViewOpts {
65
+ store: Store;
66
+ graphTheme: GraphTheme;
67
+ runId: string;
68
+ stageId: string;
69
+ /** The workflow display name, used in the title chrome `<workflow> / <stage>`. */
70
+ workflowName: string;
71
+ /**
72
+ * Live stage-control handle when available. When absent the chat is
73
+ * inspect-only (settled stage with no live handle).
74
+ */
75
+ handle?: StageControlHandle;
76
+ /** Called when the user presses Ctrl+D (back to graph). */
77
+ onDetach: () => void;
78
+ /** Called when the user presses Escape (close the whole popup). */
79
+ onClose: () => void;
80
+ /** Request a host TUI repaint after SDK events mutate local chat state. */
81
+ requestRender?: () => void;
82
+ /** Live pi-tui host objects. When present, stage input uses pi's editor UI. */
83
+ piTui?: TUI;
84
+ piKeybindings?: unknown;
85
+ /** Currently installed host editor factory, inherited from extension `ctx.ui.setEditorComponent()`. */
86
+ piEditorFactory?: (tui: TUI, theme: EditorTheme, keybindings: unknown) => EditorComponent;
87
+ /**
88
+ * Optional accessor returning the current terminal row count. The chat
89
+ * surface expands its body band to roughly `viewportRows` minus the fixed
90
+ * header / loader / editor / footer / hint rows so the popup fills the
91
+ * terminal under pi-tui's `width: "100%" / maxHeight: "100%"` geometry.
92
+ * Returning `undefined` falls back to the constant 32-row frame.
93
+ */
94
+ getViewportRows?: () => number | undefined;
95
+ }
96
+
97
+ /**
98
+ * Transcript model. Every variant carries a flat `.text` summary so consumers
99
+ * that read `_transcript` (tests, future serialisers) can recover the
100
+ * canonical user-visible string without knowing about the Pi-box payload.
101
+ */
102
+ interface BaseEntry {
103
+ readonly role: "user" | "assistant" | "thinking" | "tool" | "notice" | "system";
104
+ readonly text: string;
105
+ }
106
+ interface UserEntry extends BaseEntry {
107
+ readonly role: "user";
108
+ }
109
+ interface AssistantEntry extends BaseEntry {
110
+ readonly role: "assistant";
111
+ }
112
+ interface ThinkingEntry extends BaseEntry {
113
+ readonly role: "thinking";
114
+ }
115
+ interface SystemEntry extends BaseEntry {
116
+ readonly role: "system";
117
+ }
118
+ interface ToolEntry extends BaseEntry {
119
+ readonly role: "tool";
120
+ readonly name: string;
121
+ readonly toolCallId?: string;
122
+ readonly args?: string;
123
+ readonly output?: string;
124
+ readonly state: "pending" | "success" | "error";
125
+ }
126
+ interface NoticeEntry extends BaseEntry {
127
+ readonly role: "notice";
128
+ readonly noticeId: string;
129
+ readonly kind: StageNotice["kind"];
130
+ readonly value: string;
131
+ readonly from?: string;
132
+ readonly meta?: string;
133
+ }
134
+ type TranscriptEntry =
135
+ | UserEntry
136
+ | AssistantEntry
137
+ | ThinkingEntry
138
+ | SystemEntry
139
+ | ToolEntry
140
+ | NoticeEntry;
141
+ type AgentSnapshotMessage = AgentSession["messages"][number];
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Frame budget
145
+ // ---------------------------------------------------------------------------
146
+
147
+ /**
148
+ * Default line budget used when the host doesn't surface terminal dimensions
149
+ * (direct unit renders, lightweight test mocks). The mounted overlay
150
+ * overrides this by passing `getViewportRows()`.
151
+ */
152
+ const VIEW_LINE_COUNT = 32;
153
+
154
+ /** Header strip — `▎ STAGE wf / stage <meta> ● status` */
155
+ const HEADER_ROWS = 1;
156
+ /** Single dim rule between header and body. */
157
+ const SEP_ROWS = 1;
158
+ /** Loader: top rule + body + bottom rule when streaming. */
159
+ const LOADER_ROWS = 3;
160
+ /** Editor: top rule + ` ❯ … ` + bottom rule, always present. */
161
+ const EDITOR_ROWS = 3;
162
+ /** Footer: two dim lines. */
163
+ const FOOTER_ROWS = 2;
164
+ /** Hint strip: dashed rule + key bindings line. */
165
+ const HINTS_ROWS = 2;
166
+
167
+ /** Spinner glyphs — Braille spinner at 80ms per frame. */
168
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
169
+ /** Pi's Loader advances at 80ms; use the same cadence for embedded stage chats. */
170
+ const ANIMATION_FRAME_MS = 80;
171
+
172
+ const ITALIC = "\x1b[3m";
173
+ const FG_RESET = "\x1b[39m";
174
+ const WEIGHT_RESET = "\x1b[22m";
175
+ const ITALIC_RESET = "\x1b[23m";
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // Pi chat transcript adapter
179
+ // ---------------------------------------------------------------------------
180
+
181
+ /**
182
+ * Composes stage transcript rows with the same spacing rules as pi's
183
+ * InteractiveMode chat container. Workflow chrome (header, loader, footer,
184
+ * hints) remains owned by StageChatView; the base chat body is just the
185
+ * canonical coding-agent message components inside a pi-tui Container.
186
+ */
187
+ class PiChatTranscriptComponent implements Component {
188
+ constructor(
189
+ private readonly entries: readonly TranscriptEntry[],
190
+ private readonly renderEntry: (entry: TranscriptEntry) => Component,
191
+ ) {}
192
+
193
+ render(width: number): string[] {
194
+ const container = new Container();
195
+ for (const entry of this.entries) {
196
+ addTranscriptEntry(container, this.renderEntry(entry), entry.role);
197
+ }
198
+ return container.render(width);
199
+ }
200
+
201
+ invalidate(): void {}
202
+ }
203
+
204
+ function addTranscriptEntry(container: Container, component: Component, role: TranscriptEntry["role"]): void {
205
+ // Mirror InteractiveMode.addMessageToChat:
206
+ // - user/custom/system-like rows get a spacer only when something already
207
+ // exists above them;
208
+ // - assistant rows own their leading whitespace internally;
209
+ // - tool rows attach directly below the assistant turn that requested them.
210
+ if ((role === "user" || role === "notice" || role === "system") && container.children.length > 0) {
211
+ container.addChild(new Spacer(1));
212
+ }
213
+ container.addChild(component);
214
+ }
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // StageChatView
218
+ // ---------------------------------------------------------------------------
219
+
220
+ export class StageChatView implements Component {
221
+ private store: Store;
222
+ private theme: GraphTheme;
223
+ private runId: string;
224
+ private stageId: string;
225
+ private workflowName: string;
226
+ private handle: StageControlHandle | undefined;
227
+ private onDetach: () => void;
228
+ private onClose: () => void;
229
+ private requestRender: (() => void) | undefined;
230
+ private getViewportRows?: () => number | undefined;
231
+ private editor: EditorComponent | undefined;
232
+
233
+ private inputBuffer = "";
234
+ private transcript: TranscriptEntry[] = [];
235
+ private statusMessage = "";
236
+ /** True while a pending pause request is in flight (between ctrl+p and resolve). */
237
+ private localPaused = false;
238
+ /** De-dup set so the store subscription doesn't re-append known notices. */
239
+ private seenNoticeIds = new Set<string>();
240
+ /** Wall-clock at construction, used to colour the spinner frame stably. */
241
+ private attachedAt = Date.now();
242
+ /** True after SDK `agent_start` until `agent_end`; mirrors Pi's working-loader lifecycle. */
243
+ private sdkBusy = false;
244
+ /** Stable row pointers for the current streaming assistant message. */
245
+ private streamingAssistantIndex: number | undefined;
246
+ private streamingThinkingIndex: number | undefined;
247
+ /** Stable tool-call rows keyed exactly like Pi's `pendingTools` map. */
248
+ private toolEntryIndexes = new Map<string, number>();
249
+ /** User rows optimistically appended by this embedded editor, de-duped on SDK echo. */
250
+ private optimisticUserSignatures = new Set<string>();
251
+ /** Chat-mode repaint driver for Pi-style loaders/spinners. */
252
+ private animationTimer: ReturnType<typeof setInterval> | undefined;
253
+
254
+ private _unsubscribeStore: (() => void) | null = null;
255
+ private _unsubscribeHandle: (() => void) | null = null;
256
+
257
+ constructor(opts: StageChatViewOpts) {
258
+ this.store = opts.store;
259
+ this.theme = opts.graphTheme;
260
+ this.runId = opts.runId;
261
+ this.stageId = opts.stageId;
262
+ this.workflowName = opts.workflowName;
263
+ this.handle = opts.handle;
264
+ this.onDetach = opts.onDetach;
265
+ this.onClose = opts.onClose;
266
+ this.requestRender = opts.requestRender;
267
+ this.getViewportRows = opts.getViewportRows;
268
+ this.editor = this._createEditor(opts.piTui, opts.piKeybindings, opts.piEditorFactory);
269
+
270
+ // Seed transcript from the live SDK session at attach time, plus any
271
+ // stage notices the workflow body has already recorded.
272
+ this._snapshotMessagesFromHandle();
273
+ const initialStage = this._currentStage();
274
+ this._snapshotMessagesFromSessionFile(initialStage);
275
+ this._absorbStageNotices(initialStage);
276
+
277
+ this._unsubscribeStore = this.store.subscribe(() => {
278
+ const stage = this._currentStage();
279
+ let changed = false;
280
+ if (stage && stage.status === "paused" && !this.localPaused) {
281
+ this.localPaused = true;
282
+ changed = true;
283
+ } else if (stage && stage.status === "running" && this.localPaused) {
284
+ this.localPaused = false;
285
+ changed = true;
286
+ }
287
+ // Pick up notices recorded after attach (workflow body calling
288
+ // `stage.setModel`, `stage.compact`, …) so they thread through the
289
+ // transcript without a special render path.
290
+ changed = this._absorbStageNotices(stage) || changed;
291
+ this._syncAnimationTick();
292
+ if (changed) this.requestRender?.();
293
+ });
294
+
295
+ if (this.handle) {
296
+ this._unsubscribeHandle = this.handle.subscribe((event) => {
297
+ const changed = this._appendEvent(event);
298
+ this._syncAnimationTick();
299
+ if (changed) this.requestRender?.();
300
+ });
301
+ }
302
+ this._syncAnimationTick();
303
+ }
304
+
305
+ private _createEditor(
306
+ tui: TUI | undefined,
307
+ keybindings: unknown,
308
+ editorFactory: ((tui: TUI, theme: EditorTheme, keybindings: unknown) => EditorComponent) | undefined,
309
+ ): EditorComponent | undefined {
310
+ if (!tui || !keybindings) return undefined;
311
+ const editorTheme = editorThemeFromGraphTheme(this.theme);
312
+ const editor = this._createInheritedEditor(tui, editorTheme, keybindings, editorFactory) ??
313
+ new CustomEditor(
314
+ tui,
315
+ editorTheme,
316
+ keybindings as ConstructorParameters<typeof CustomEditor>[2],
317
+ { paddingX: 1, autocompleteMaxVisible: 5 },
318
+ );
319
+ editor.onChange = (text) => {
320
+ this.inputBuffer = text;
321
+ };
322
+ editor.onSubmit = (text) => {
323
+ void this._submit("auto", text);
324
+ };
325
+ return editor;
326
+ }
327
+
328
+ private _createInheritedEditor(
329
+ tui: TUI,
330
+ editorTheme: EditorTheme,
331
+ keybindings: unknown,
332
+ editorFactory: ((tui: TUI, theme: EditorTheme, keybindings: unknown) => EditorComponent) | undefined,
333
+ ): EditorComponent | undefined {
334
+ if (!editorFactory) return undefined;
335
+ try {
336
+ return editorFactory(tui, editorTheme, keybindings);
337
+ } catch {
338
+ return undefined;
339
+ }
340
+ }
341
+
342
+ // -------------------------------------------------------------------------
343
+ // Event ingestion
344
+ // -------------------------------------------------------------------------
345
+
346
+ private _snapshotMessagesFromHandle(): void {
347
+ if (!this.handle) return;
348
+ for (const message of this.handle.messages) {
349
+ const entry = transcriptEntryFromSnapshotMessage(message);
350
+ if (entry) this.transcript.push(entry);
351
+ }
352
+ }
353
+
354
+ private _snapshotMessagesFromSessionFile(stage: StageSnapshot | undefined): void {
355
+ if (this.transcript.length > 0) return;
356
+ const sessionFile = this.handle?.sessionFile ?? stage?.sessionFile;
357
+ if (sessionFile === undefined) return;
358
+
359
+ let entries: ReturnType<SessionManager["getEntries"]>;
360
+ try {
361
+ entries = SessionManager.open(sessionFile).getEntries();
362
+ } catch {
363
+ return;
364
+ }
365
+
366
+ for (const entry of entries) {
367
+ if (!isSessionMessageEntry(entry)) continue;
368
+ const transcriptEntry = transcriptEntryFromSnapshotMessage(entry.message as AgentSnapshotMessage);
369
+ if (transcriptEntry) this.transcript.push(transcriptEntry);
370
+ }
371
+ }
372
+
373
+ private _appendEvent(event: AgentSessionEvent): boolean {
374
+ // This mirrors pi-coding-agent InteractiveMode's event controller shape:
375
+ // session events mutate a long-lived chat model, then the host TUI is
376
+ // asked to render. Assistant rows are driven from full message snapshots
377
+ // when present; delta-only events remain supported for SDK/test shims.
378
+ const type = String((event as { type?: unknown }).type ?? "");
379
+ switch (type) {
380
+ case "agent_start":
381
+ this.sdkBusy = true;
382
+ this.toolEntryIndexes.clear();
383
+ this.statusMessage = "";
384
+ return true;
385
+
386
+ case "agent_end":
387
+ this.sdkBusy = false;
388
+ this.streamingAssistantIndex = undefined;
389
+ this.streamingThinkingIndex = undefined;
390
+ this.statusMessage = "";
391
+ return true;
392
+
393
+ case "message_start":
394
+ return this._handleMessageStart((event as { message?: unknown }).message);
395
+
396
+ case "message_update":
397
+ return this._handleMessageUpdate(event);
398
+
399
+ case "message_end":
400
+ return this._handleMessageEnd((event as { message?: unknown }).message);
401
+
402
+ case "tool_execution_start": {
403
+ const payload = event as { toolCallId?: unknown; toolName?: unknown; args?: unknown };
404
+ const name = typeof payload.toolName === "string" ? payload.toolName : "tool";
405
+ const toolCallId = typeof payload.toolCallId === "string" ? payload.toolCallId : undefined;
406
+ const args = summariseArgs(payload.args);
407
+ this._upsertToolEntry({ toolCallId, name, args, state: "pending" });
408
+ return true;
409
+ }
410
+
411
+ case "tool_execution_update": {
412
+ const payload = event as { toolCallId?: unknown; toolName?: unknown; partialResult?: unknown };
413
+ const partialOutput = extractToolResultText(payload.partialResult);
414
+ if (!partialOutput) return false;
415
+ this._upsertToolEntry({
416
+ toolCallId: typeof payload.toolCallId === "string" ? payload.toolCallId : undefined,
417
+ name: typeof payload.toolName === "string" ? payload.toolName : "tool",
418
+ output: partialOutput,
419
+ state: "pending",
420
+ });
421
+ return true;
422
+ }
423
+
424
+ case "tool_execution_end": {
425
+ const payload = event as { toolCallId?: unknown; toolName?: unknown; result?: unknown; isError?: unknown };
426
+ const toolCallId = typeof payload.toolCallId === "string" ? payload.toolCallId : undefined;
427
+ const output = extractToolResultText(payload.result);
428
+ this._upsertToolEntry({
429
+ toolCallId,
430
+ name: typeof payload.toolName === "string" ? payload.toolName : "tool",
431
+ output,
432
+ state: payload.isError === true ? "error" : "success",
433
+ });
434
+ if (toolCallId) this.toolEntryIndexes.delete(toolCallId);
435
+ return true;
436
+ }
437
+
438
+ case "tool_call":
439
+ case "tool_use": {
440
+ const name = String((event as { name?: unknown }).name ?? "tool");
441
+ const args = summariseArgs((event as { input?: unknown }).input);
442
+ this._upsertToolEntry({ name, args, state: "pending" });
443
+ return true;
444
+ }
445
+
446
+ case "tool_result": {
447
+ const name = String((event as { name?: unknown }).name ?? "tool");
448
+ const rawOutput = (event as { output?: unknown }).output;
449
+ const output = typeof rawOutput === "string" ? rawOutput : extractMessageText(rawOutput);
450
+ this._upsertToolEntry({
451
+ name,
452
+ output,
453
+ state: Boolean((event as { isError?: unknown }).isError) ? "error" : "success",
454
+ });
455
+ return true;
456
+ }
457
+
458
+ case "thinking_delta":
459
+ case "thinking": {
460
+ const delta = String(
461
+ (event as { delta?: unknown }).delta ?? (event as { text?: unknown }).text ?? "",
462
+ );
463
+ if (!delta) return false;
464
+ this._appendTextDelta("thinking", delta);
465
+ return true;
466
+ }
467
+
468
+ case "compaction_start":
469
+ this.sdkBusy = true;
470
+ this.statusMessage = "compacting context…";
471
+ return true;
472
+
473
+ case "compaction_end":
474
+ this.sdkBusy = false;
475
+ this.statusMessage = "";
476
+ return true;
477
+
478
+ case "auto_retry_start":
479
+ this.sdkBusy = true;
480
+ this.statusMessage = "retrying…";
481
+ return true;
482
+
483
+ case "auto_retry_end":
484
+ this.statusMessage = "";
485
+ return true;
486
+
487
+ default:
488
+ return false;
489
+ }
490
+ }
491
+
492
+ private _handleMessageStart(message: unknown): boolean {
493
+ if (!isMessageLike(message)) return false;
494
+ if (message.role === "assistant") {
495
+ this.streamingAssistantIndex = undefined;
496
+ this.streamingThinkingIndex = undefined;
497
+ return this._updateAssistantFromMessage(message);
498
+ }
499
+
500
+ const entry = transcriptEntryFromSnapshotMessage(message as AgentSnapshotMessage);
501
+ if (!entry) return false;
502
+ if (entry.role === "user") {
503
+ const signature = userMessageSignature(entry.text);
504
+ if (this.optimisticUserSignatures.delete(signature)) return false;
505
+ }
506
+ this.transcript.push(entry);
507
+ return true;
508
+ }
509
+
510
+ private _handleMessageUpdate(event: AgentSessionEvent): boolean {
511
+ const message = (event as { message?: unknown }).message;
512
+ const hasAssistantSnapshot = isMessageLike(message) && message.role === "assistant";
513
+ const snapshotHasPayload = hasAssistantSnapshot && assistantContentHasRenderablePayload(message.content);
514
+ let changed = false;
515
+ if (hasAssistantSnapshot) {
516
+ changed = this._updateAssistantFromMessage(message) || changed;
517
+ }
518
+
519
+ const assistantEvent = (event as { assistantMessageEvent?: { type?: unknown; delta?: unknown } }).assistantMessageEvent;
520
+ const streamType = String(assistantEvent?.type ?? "");
521
+ const delta = typeof assistantEvent?.delta === "string" ? assistantEvent.delta : "";
522
+ // Prefer Pi's full assistant message snapshot when it contains visible
523
+ // payload; use deltas only for delta-only SDK shims/events.
524
+ if (!changed && !snapshotHasPayload && streamType === "text_delta" && delta) {
525
+ this._appendTextDelta("assistant", delta);
526
+ changed = true;
527
+ } else if (!changed && !snapshotHasPayload && streamType === "thinking_delta" && delta) {
528
+ this._appendTextDelta("thinking", delta);
529
+ changed = true;
530
+ }
531
+
532
+ return changed;
533
+ }
534
+
535
+ private _handleMessageEnd(message: unknown): boolean {
536
+ let changed = false;
537
+ if (isMessageLike(message) && message.role === "assistant") {
538
+ changed = this._updateAssistantFromMessage(message) || changed;
539
+ for (const [toolCallId, index] of this.toolEntryIndexes.entries()) {
540
+ const entry = this.transcript[index];
541
+ if (entry?.role === "tool" && entry.state === "pending") {
542
+ this.transcript[index] = { ...entry, text: entry.text };
543
+ }
544
+ this.toolEntryIndexes.set(toolCallId, index);
545
+ }
546
+ }
547
+ this.streamingAssistantIndex = undefined;
548
+ this.streamingThinkingIndex = undefined;
549
+ return changed || isMessageLike(message);
550
+ }
551
+
552
+ private _updateAssistantFromMessage(message: { role?: unknown; content?: unknown; stopReason?: unknown; errorMessage?: unknown }): boolean {
553
+ const projection = projectAssistantContent(message.content);
554
+ let changed = false;
555
+ if (projection.thinking) {
556
+ changed = this._upsertStreamingText("thinking", projection.thinking) || changed;
557
+ }
558
+ if (projection.text) {
559
+ changed = this._upsertStreamingText("assistant", projection.text) || changed;
560
+ }
561
+ const stopReason = typeof message.stopReason === "string" ? message.stopReason : "";
562
+ if (stopReason === "aborted" || stopReason === "error") {
563
+ const errorText = typeof message.errorMessage === "string" && message.errorMessage
564
+ ? message.errorMessage
565
+ : stopReason === "aborted"
566
+ ? "Operation aborted"
567
+ : "Unknown error";
568
+ changed = this._failPendingToolEntries(errorText) || changed;
569
+ if (!projection.toolCalls.length) {
570
+ changed = this._upsertStreamingText("system", stopReason === "error" ? `Error: ${errorText}` : errorText) || changed;
571
+ }
572
+ }
573
+ for (const toolCall of projection.toolCalls) {
574
+ changed = this._upsertToolEntry(toolCall) || changed;
575
+ }
576
+ return changed;
577
+ }
578
+
579
+ private _upsertStreamingText(
580
+ role: "assistant" | "thinking" | "system",
581
+ text: string,
582
+ ): boolean {
583
+ if (!text) return false;
584
+ if (role === "system") {
585
+ this._upsertTextLastByRole("system", text);
586
+ return true;
587
+ }
588
+ const index = role === "assistant" ? this.streamingAssistantIndex : this.streamingThinkingIndex;
589
+ if (index !== undefined && this.transcript[index]?.role === role) {
590
+ if (this.transcript[index]?.text === text) return false;
591
+ this.transcript[index] = { role, text } as TranscriptEntry;
592
+ return true;
593
+ }
594
+ this.transcript.push({ role, text } as TranscriptEntry);
595
+ const nextIndex = this.transcript.length - 1;
596
+ if (role === "assistant") this.streamingAssistantIndex = nextIndex;
597
+ else this.streamingThinkingIndex = nextIndex;
598
+ return true;
599
+ }
600
+
601
+ private _absorbStageNotices(stage: StageSnapshot | undefined): boolean {
602
+ const notices = stage?.notices;
603
+ if (!notices) return false;
604
+ let changed = false;
605
+ for (const n of notices) {
606
+ if (this.seenNoticeIds.has(n.id)) continue;
607
+ this.seenNoticeIds.add(n.id);
608
+ changed = true;
609
+ this.transcript.push({
610
+ role: "notice",
611
+ noticeId: n.id,
612
+ kind: n.kind,
613
+ value: n.to,
614
+ from: n.from,
615
+ meta: n.meta,
616
+ text: noticeSummary(n),
617
+ });
618
+ }
619
+ return changed;
620
+ }
621
+
622
+ private _upsertTextLastByRole(
623
+ role: "user" | "assistant" | "thinking" | "system",
624
+ text: string,
625
+ ): void {
626
+ const last = this.transcript[this.transcript.length - 1];
627
+ if (last && last.role === role) {
628
+ this.transcript[this.transcript.length - 1] = { role, text } as TranscriptEntry;
629
+ } else {
630
+ this.transcript.push({ role, text } as TranscriptEntry);
631
+ }
632
+ }
633
+
634
+ private _appendTextDelta(
635
+ role: "assistant" | "thinking",
636
+ delta: string,
637
+ ): void {
638
+ const index = role === "assistant" ? this.streamingAssistantIndex : this.streamingThinkingIndex;
639
+ if (index !== undefined && this.transcript[index]?.role === role) {
640
+ const current = this.transcript[index];
641
+ this.transcript[index] = { role, text: current.text + delta } as TranscriptEntry;
642
+ return;
643
+ }
644
+ this.transcript.push({ role, text: delta } as TranscriptEntry);
645
+ const nextIndex = this.transcript.length - 1;
646
+ if (role === "assistant") this.streamingAssistantIndex = nextIndex;
647
+ else this.streamingThinkingIndex = nextIndex;
648
+ }
649
+
650
+ private _failPendingToolEntries(errorText: string): boolean {
651
+ let changed = false;
652
+ for (const [toolCallId, index] of this.toolEntryIndexes.entries()) {
653
+ const entry = this.transcript[index];
654
+ if (entry?.role !== "tool" || entry.state !== "pending") continue;
655
+ changed = this._upsertToolEntry({
656
+ toolCallId,
657
+ name: entry.name,
658
+ output: errorText,
659
+ state: "error",
660
+ }) || changed;
661
+ }
662
+ this.toolEntryIndexes.clear();
663
+ return changed;
664
+ }
665
+
666
+ private _upsertToolEntry(update: {
667
+ toolCallId?: string;
668
+ name: string;
669
+ args?: string;
670
+ output?: string;
671
+ state: "pending" | "success" | "error";
672
+ }): boolean {
673
+ const mappedIndex = update.toolCallId ? this.toolEntryIndexes.get(update.toolCallId) : undefined;
674
+ const index = mappedIndex ?? findToolEntryIndex(this.transcript, update.toolCallId, update.name);
675
+ const existing = index !== undefined && index >= 0 ? this.transcript[index] : undefined;
676
+ const previous = existing?.role === "tool" ? existing : undefined;
677
+ const output = update.output || previous?.output;
678
+ const name = previous?.name ?? update.name;
679
+ const args = update.args ?? previous?.args;
680
+ const summary = output ? truncateToWidth(output.replace(/\s+/g, " "), 80) : "";
681
+ const next: ToolEntry = {
682
+ role: "tool",
683
+ name,
684
+ toolCallId: previous?.toolCallId ?? update.toolCallId,
685
+ args,
686
+ output,
687
+ state: update.state,
688
+ text: summary
689
+ ? `← ${name} ${summary}`
690
+ : args
691
+ ? `→ ${name} ${args}`
692
+ : `→ ${name}`,
693
+ };
694
+ if (previous && shallowToolEntryEqual(previous, next)) return false;
695
+ if (index !== undefined && index >= 0) {
696
+ this.transcript[index] = next;
697
+ if (next.toolCallId) this.toolEntryIndexes.set(next.toolCallId, index);
698
+ } else {
699
+ this.transcript.push(next);
700
+ if (next.toolCallId) this.toolEntryIndexes.set(next.toolCallId, this.transcript.length - 1);
701
+ }
702
+ return true;
703
+ }
704
+
705
+ private _currentStage(): StageSnapshot | undefined {
706
+ const snap = this.store.snapshot();
707
+ const run = snap.runs.find((r) => r.id === this.runId);
708
+ return run?.stages.find((s) => s.id === this.stageId);
709
+ }
710
+
711
+ // -------------------------------------------------------------------------
712
+ // Frame sizing
713
+ // -------------------------------------------------------------------------
714
+
715
+ /**
716
+ * Number of rows the chat surface paints per frame. The mounted overlay
717
+ * passes `terminal.rows` through `getViewportRows`; direct unit renders
718
+ * fall back to the constant `VIEW_LINE_COUNT` so the legacy 32-row frame
719
+ * still applies to lightweight test mocks.
720
+ */
721
+ private _viewLineCount(): number {
722
+ const reported = this.getViewportRows?.();
723
+ if (typeof reported !== "number" || !Number.isFinite(reported)) {
724
+ return VIEW_LINE_COUNT;
725
+ }
726
+ return Math.max(VIEW_LINE_COUNT, Math.floor(reported));
727
+ }
728
+
729
+ private _isStreaming(): boolean {
730
+ return this.sdkBusy || Boolean(this.handle?.isStreaming);
731
+ }
732
+
733
+ private _hasPendingToolEntries(): boolean {
734
+ return this.transcript.some((entry) => entry.role === "tool" && entry.state === "pending");
735
+ }
736
+
737
+ private _syncAnimationTick(): void {
738
+ const shouldAnimate = this._isStreaming() || (this.sdkBusy && this._hasPendingToolEntries());
739
+ if (shouldAnimate && !this.animationTimer) {
740
+ this.animationTimer = setInterval(() => {
741
+ this.requestRender?.();
742
+ }, ANIMATION_FRAME_MS);
743
+ this.animationTimer.unref?.();
744
+ return;
745
+ }
746
+ if (!shouldAnimate && this.animationTimer) {
747
+ clearInterval(this.animationTimer);
748
+ this.animationTimer = undefined;
749
+ }
750
+ }
751
+
752
+ private _isBlocked(): boolean {
753
+ return this._currentStage()?.status === "blocked";
754
+ }
755
+
756
+ private _isSettled(stage: StageSnapshot | undefined): boolean {
757
+ if (!stage) return !this.handle;
758
+ return stage.status === "completed" || stage.status === "failed";
759
+ }
760
+
761
+ // -------------------------------------------------------------------------
762
+ // Top-level render — composes header / body / loader / editor / footer / hints
763
+ // -------------------------------------------------------------------------
764
+
765
+ render(width: number): string[] {
766
+ const w = Math.max(40, width);
767
+ const stage = this._currentStage();
768
+ const blocked = this._isBlocked();
769
+ const settled = this._isSettled(stage);
770
+ const streaming = this._isStreaming() && !blocked && !settled;
771
+ const paused = this.localPaused || stage?.status === "paused";
772
+
773
+ const headerLines = this._renderHeader(w, stage);
774
+ const sepLines = [this._sepRule(w)];
775
+ const loaderLines = streaming ? this._renderLoader(w, stage) : [];
776
+ // When the loader sits above the editor, the loader's bottom rule and
777
+ // the editor's top rule collapse into a single shared divider — matches
778
+ // the mockup's `pi-loader` + `pi-editor` stack and saves one row.
779
+ const editorLines = this._renderEditor(w, {
780
+ paused,
781
+ streaming,
782
+ settled,
783
+ blocked,
784
+ omitTopRule: loaderLines.length > 0,
785
+ });
786
+ const footerLines = this._renderFooter(w, stage, { paused, streaming, settled });
787
+ const hintsLines = this._renderHints(w, { paused, streaming, settled });
788
+
789
+ const fixed =
790
+ headerLines.length +
791
+ sepLines.length +
792
+ loaderLines.length +
793
+ editorLines.length +
794
+ footerLines.length +
795
+ hintsLines.length;
796
+ const totalRows = this._viewLineCount();
797
+ const bodyBudget = Math.max(1, totalRows - fixed);
798
+ const bodyLines = blocked
799
+ ? this._renderBlockedBody(w, bodyBudget, stage)
800
+ : this._renderBody(w, bodyBudget, stage, { paused, streaming, settled });
801
+
802
+ const lines = [
803
+ ...headerLines,
804
+ ...sepLines,
805
+ ...bodyLines,
806
+ ...loaderLines,
807
+ ...editorLines,
808
+ ...footerLines,
809
+ ...hintsLines,
810
+ ];
811
+ while (lines.length < totalRows) lines.push(this._blank(w));
812
+ if (lines.length > totalRows) lines.length = totalRows;
813
+ return lines;
814
+ }
815
+
816
+ // -------------------------------------------------------------------------
817
+ // Header
818
+ // -------------------------------------------------------------------------
819
+
820
+ private _renderHeader(width: number, stage: StageSnapshot | undefined): string[] {
821
+ const t = this.theme;
822
+ const stageName = stage?.name ?? "stage";
823
+ const status = stage?.status ?? (this.handle ? "pending" : "completed");
824
+
825
+ // Left side: `▎ STAGE <wf> / <stage>`
826
+ const left =
827
+ paint(" ▎ ", t.mauve, { bold: true }) +
828
+ paint("STAGE", t.textMuted, { bold: true }) +
829
+ " " +
830
+ paint(this.workflowName, t.textMuted) +
831
+ paint(" / ", t.dim) +
832
+ paint(stageName, t.text, { bold: true });
833
+
834
+ // Right side: stage meta · status pill
835
+ const meta = this._headerMeta(stage);
836
+ const pill = this._statusPill(status);
837
+ const right = (meta ? paint(meta, t.dim) + " " : "") + pill.styled + " ";
838
+
839
+ const leftW = visibleWidth(this.workflowName) + visibleWidth(stageName) + visibleWidth(" STAGE / ") + 1;
840
+ const rightW = visibleWidth(meta) + (meta ? 2 : 0) + pill.width + 1;
841
+ const gap = Math.max(1, width - leftW - rightW);
842
+ return [left + " ".repeat(gap) + right];
843
+ }
844
+
845
+ private _headerMeta(stage: StageSnapshot | undefined): string {
846
+ const parts: string[] = [];
847
+ if (stage) {
848
+ const dur = stageDurationText(stage);
849
+ if (dur) parts.push(dur);
850
+ }
851
+ const sid = this.handle?.sessionId ?? stage?.sessionId;
852
+ if (sid) parts.push(`session ${shortenId(sid)}`);
853
+ return parts.join(" · ");
854
+ }
855
+
856
+ /**
857
+ * Render an inline ` ● status ` pill with the status colour applied to a
858
+ * tinted background. Matches the mockup's `.status-pill` vocabulary.
859
+ */
860
+ private _statusPill(status: string): { styled: string; width: number } {
861
+ const t = this.theme;
862
+ const map: Record<string, { fg: string; bg: string; label: string }> = {
863
+ pending: { fg: t.dim, bg: blendBg(t.bg, t.dim, 0.18), label: "pending" },
864
+ running: { fg: t.accent, bg: blendBg(t.bg, t.accent, 0.18), label: "running" },
865
+ paused: { fg: t.warning, bg: blendBg(t.bg, t.warning, 0.18), label: "paused" },
866
+ blocked: { fg: t.warning, bg: blendBg(t.bg, t.warning, 0.18), label: "blocked" },
867
+ completed: { fg: t.success, bg: blendBg(t.bg, t.success, 0.18), label: "completed" },
868
+ failed: { fg: t.error, bg: blendBg(t.bg, t.error, 0.18), label: "failed" },
869
+ };
870
+ const cfg = map[status] ?? map.pending!;
871
+ const body = ` ● ${cfg.label} `;
872
+ return {
873
+ styled: hexBg(cfg.bg) + hexToAnsi(cfg.fg) + BOLD + body + RESET,
874
+ width: visibleWidth(body),
875
+ };
876
+ }
877
+
878
+ private _sepRule(width: number): string {
879
+ return hexToAnsi(this.theme.borderDim) + "─".repeat(width) + RESET;
880
+ }
881
+
882
+ // -------------------------------------------------------------------------
883
+ // Body — welcome panel / banner + transcript / blocked
884
+ // -------------------------------------------------------------------------
885
+
886
+ private _renderBlockedBody(width: number, budget: number, stage: StageSnapshot | undefined): string[] {
887
+ const t = this.theme;
888
+ const upstream = stage?.blockedByStageId ?? "upstream stage";
889
+ const lines: string[] = [];
890
+ // Yellow banner — uses the same chrome vocabulary as paused/completed.
891
+ lines.push(...this._bannerLines(width, "warning", "↑", "BLOCKED", `waiting on ${upstream}`));
892
+ lines.push(this._blank(width));
893
+ lines.push(
894
+ ...new Text(
895
+ paint("This stage is waiting for the upstream stage to resume.", t.textMuted),
896
+ 2,
897
+ 0,
898
+ ).render(width),
899
+ );
900
+ lines.push(
901
+ ...new Text(
902
+ paint("Press ", t.textMuted) +
903
+ paint("Ctrl+D", t.accent, { bold: true }) +
904
+ paint(" to return to the graph.", t.textMuted),
905
+ 2,
906
+ 0,
907
+ ).render(width),
908
+ );
909
+ while (lines.length < budget) lines.push(this._blank(width));
910
+ if (lines.length > budget) lines.length = budget;
911
+ return lines;
912
+ }
913
+
914
+ private _renderBody(
915
+ width: number,
916
+ budget: number,
917
+ stage: StageSnapshot | undefined,
918
+ flags: { paused: boolean; streaming: boolean; settled: boolean },
919
+ ): string[] {
920
+ // Empty + not paused + not settled + not streaming → welcome panel.
921
+ const transcriptEmpty = this.transcript.length === 0;
922
+ if (transcriptEmpty && !flags.paused && !flags.settled && !flags.streaming) {
923
+ return this._fitToBudget(this._renderWelcome(width, stage), budget, width);
924
+ }
925
+
926
+ const components: Component[] = [];
927
+ if (flags.paused) {
928
+ components.push(
929
+ this._banner(
930
+ "warning",
931
+ "❚❚",
932
+ "PAUSED",
933
+ "stopped between turns · type to resume, or Ctrl+P to release without input",
934
+ ),
935
+ );
936
+ components.push(new Spacer(1));
937
+ } else if (flags.settled && stage?.status === "completed") {
938
+ components.push(this._banner("success", "✓", "COMPLETED", this._completedMeta(stage)));
939
+ components.push(new Spacer(1));
940
+ } else if (flags.settled && stage?.status === "failed") {
941
+ components.push(
942
+ this._banner(
943
+ "error",
944
+ "✗",
945
+ "FAILED",
946
+ stage?.error?.replace(/\s+/g, " ") ?? "stage exited with an error",
947
+ ),
948
+ );
949
+ components.push(new Spacer(1));
950
+ }
951
+
952
+ // Base chat body: delegate transcript composition to the Pi-style
953
+ // transcript component so the attached stage chat uses the same message
954
+ // spacing and coding-agent message widgets as the main interactive chat.
955
+ if (this.transcript.length > 0) {
956
+ components.push(new PiChatTranscriptComponent(this.transcript, (entry) => this._renderEntry(entry)));
957
+ }
958
+
959
+ // Stream a static status message (e.g. "pausing…") as a dim trailing row.
960
+ if (this.statusMessage) {
961
+ components.push(new Spacer(1));
962
+ components.push(new Text(paint(this.statusMessage, this.theme.dim), 2, 0));
963
+ }
964
+
965
+ // Flatten from the tail + sticky-bottom — show the most recent content.
966
+ // This keeps the 80ms Pi-style spinner tick cheap even after long chats:
967
+ // off-screen history is not rebuilt just to be sliced away.
968
+ return this._renderComponentTail(components, width, budget);
969
+ }
970
+
971
+ private _renderComponentTail(components: Component[], width: number, budget: number): string[] {
972
+ const chunks: string[][] = [];
973
+ let lineCount = 0;
974
+ for (let i = components.length - 1; i >= 0; i--) {
975
+ const lines = components[i]!.render(width);
976
+ chunks.push(lines);
977
+ lineCount += lines.length;
978
+ if (lineCount >= budget) break;
979
+ }
980
+ const flat: string[] = [];
981
+ for (let i = chunks.length - 1; i >= 0; i--) flat.push(...chunks[i]!);
982
+ return this._fitToBudget(flat, budget, width);
983
+ }
984
+
985
+ private _fitToBudget(lines: string[], budget: number, width: number): string[] {
986
+ if (lines.length >= budget) return lines.slice(lines.length - budget);
987
+ const out = lines.slice();
988
+ while (out.length < budget) out.push(this._blank(width));
989
+ return out;
990
+ }
991
+
992
+ // -------------------------------------------------------------------------
993
+ // Welcome panel — first attach, no transcript yet
994
+ // -------------------------------------------------------------------------
995
+
996
+ private _renderWelcome(width: number, stage: StageSnapshot | undefined): string[] {
997
+ const t = this.theme;
998
+ const sessionId = this.handle?.sessionId ?? stage?.sessionId;
999
+ const sessionFile = this.handle?.sessionFile ?? stage?.sessionFile;
1000
+ const status = stage?.status ?? "pending";
1001
+
1002
+ const out: string[] = [];
1003
+ out.push(...new Spacer(1).render(width));
1004
+ out.push(centred(paint("▎", t.mauve, { bold: true }), width));
1005
+ out.push(
1006
+ centred(
1007
+ paint("Attached to ", t.text) +
1008
+ paint(this.workflowName, t.textMuted) +
1009
+ paint(" / ", t.dim) +
1010
+ paint(stage?.name ?? "stage", t.text, { bold: true }),
1011
+ width,
1012
+ ),
1013
+ );
1014
+ out.push(...new Spacer(1).render(width));
1015
+ const sub =
1016
+ "This stage is idle. Press ↵ to send the first prompt — the SDK session " +
1017
+ "will be created on submit. The workflow body keeps running in the " +
1018
+ "background; closing this overlay does not kill the run.";
1019
+ out.push(...new Text(paint(sub, t.textMuted), 4, 0).render(width));
1020
+ out.push(...new Spacer(1).render(width));
1021
+
1022
+ const grid: Array<[string, string]> = [
1023
+ ["session", sessionId ? shortenId(sessionId) : "(not yet realised)"],
1024
+ ["status", status],
1025
+ ];
1026
+ if (sessionFile) grid.push(["session file", shortenFile(sessionFile)]);
1027
+ for (const [k, v] of grid) {
1028
+ const row = paint(k.padEnd(13), t.dim) + paint(v, t.text);
1029
+ out.push(...new Text(row, 8, 0).render(width));
1030
+ }
1031
+ return out;
1032
+ }
1033
+
1034
+ // -------------------------------------------------------------------------
1035
+ // Transcript entry → pi/coding-agent Component. Stage chat deliberately uses
1036
+ // the same exported message/tool components as the main interactive chat
1037
+ // instead of maintaining parallel workflow-specific bubbles.
1038
+ // -------------------------------------------------------------------------
1039
+
1040
+ private _renderEntry(entry: TranscriptEntry): Component {
1041
+ switch (entry.role) {
1042
+ case "user":
1043
+ return this._userMessage(entry.text);
1044
+ case "assistant":
1045
+ return new AssistantMessageComponent(assistantMessageForText(entry.text));
1046
+ case "thinking":
1047
+ return new AssistantMessageComponent(assistantMessageForThinking(entry.text));
1048
+ case "tool":
1049
+ return this._toolExecution(entry);
1050
+ case "notice":
1051
+ return this._noticeRow(entry);
1052
+ case "system":
1053
+ return new Text(paint(entry.text, this.theme.dim), 2, 0);
1054
+ }
1055
+ }
1056
+
1057
+ private _userMessage(text: string): Component {
1058
+ const skillBlock = parseSkillBlock(text);
1059
+ if (!skillBlock) return new UserMessageComponent(text);
1060
+
1061
+ const container = new Container();
1062
+ container.addChild(new SkillInvocationMessageComponent(skillBlock));
1063
+ if (skillBlock.userMessage) {
1064
+ container.addChild(new UserMessageComponent(skillBlock.userMessage));
1065
+ }
1066
+ return container;
1067
+ }
1068
+
1069
+ private _toolExecution(entry: ToolEntry): Component {
1070
+ const component = new ToolExecutionComponent(
1071
+ entry.name,
1072
+ entry.toolCallId ?? `workflow-${entry.name}`,
1073
+ toolArgsForRender(entry),
1074
+ { showImages: true },
1075
+ undefined,
1076
+ this._toolTui(),
1077
+ process.cwd(),
1078
+ );
1079
+ if (entry.state !== "pending" || entry.output) {
1080
+ component.updateResult(
1081
+ {
1082
+ content: entry.output
1083
+ ? [{ type: "text", text: entry.output }]
1084
+ : [],
1085
+ isError: entry.state === "error",
1086
+ details: {},
1087
+ },
1088
+ entry.state === "pending",
1089
+ );
1090
+ }
1091
+ return component;
1092
+ }
1093
+
1094
+ private _toolTui(): TUI {
1095
+ return {
1096
+ requestRender: () => this.requestRender?.(),
1097
+ } as TUI;
1098
+ }
1099
+
1100
+ private _noticeRow(entry: NoticeEntry): Component {
1101
+ const t = this.theme;
1102
+ const fromPart = entry.from ? paint(` (was ${entry.from})`, t.dim) : "";
1103
+ const metaPart = entry.meta ? " " + paint(entry.meta, t.dim) : "";
1104
+ const line =
1105
+ paint("~ ", t.borderDim) +
1106
+ paint(entry.kind, t.mauve, { bold: true }) +
1107
+ paint(" → ", t.borderDim) +
1108
+ paint(entry.value, t.text) +
1109
+ fromPart +
1110
+ metaPart;
1111
+ return new Text(line, 2, 0);
1112
+ }
1113
+
1114
+ // -------------------------------------------------------------------------
1115
+ // Banners (paused / completed / failed / blocked)
1116
+ // -------------------------------------------------------------------------
1117
+
1118
+ private _banner(
1119
+ kind: "warning" | "success" | "error",
1120
+ glyph: string,
1121
+ label: string,
1122
+ meta: string,
1123
+ ): Component {
1124
+ const t = this.theme;
1125
+ const fg = kind === "warning" ? t.warning : kind === "success" ? t.success : t.error;
1126
+ const bg = blendBg(t.bg, fg, 0.10);
1127
+ const head =
1128
+ paintOnFill(glyph, fg, { bold: true }) +
1129
+ " " +
1130
+ paintOnFill(label, fg, { bold: true }) +
1131
+ " " +
1132
+ paintOnFill(stripAnsi(meta), t.dim);
1133
+ const box = new Box(2, 0, bgFn(bg));
1134
+ box.addChild(new Text(head, 0, 0));
1135
+ return box;
1136
+ }
1137
+
1138
+ /**
1139
+ * Banner rendered directly as string lines. Used by `_renderBlockedBody`
1140
+ * which builds its body out of raw rows rather than a Component[] stack.
1141
+ */
1142
+ private _bannerLines(
1143
+ width: number,
1144
+ kind: "warning" | "success" | "error",
1145
+ glyph: string,
1146
+ label: string,
1147
+ meta: string,
1148
+ ): string[] {
1149
+ return this._banner(kind, glyph, label, meta).render(width);
1150
+ }
1151
+
1152
+ // -------------------------------------------------------------------------
1153
+ // Loader — top rule + spinner row + bottom rule
1154
+ // -------------------------------------------------------------------------
1155
+
1156
+ private _renderLoader(width: number, stage: StageSnapshot | undefined): string[] {
1157
+ const t = this.theme;
1158
+ const rule = hexToAnsi(t.border) + "─".repeat(width) + RESET;
1159
+ const dur = stageDurationText(stage);
1160
+ const msg = `Working${dur ? " · " + dur : ""}`;
1161
+ const escapeHint = paint("Esc", t.text, { bold: true }) + " " + paint("interrupt", t.dim);
1162
+ const left = " " + paint(spinnerFrame(), t.accent, { bold: true }) + " " + paint(msg, t.textMuted) + " ";
1163
+ const leftW = visibleWidth(spinnerFrame()) + 4 + visibleWidth(msg);
1164
+ const rightW = visibleWidth("Esc interrupt");
1165
+ const gap = Math.max(1, width - leftW - rightW - 2);
1166
+ const body = left + " ".repeat(gap) + escapeHint + " ";
1167
+ // No closing rule — the editor's top rule (or the editor's body when
1168
+ // `omitTopRule: true`) sits directly underneath and provides the divider.
1169
+ return [rule, body];
1170
+ }
1171
+
1172
+ // -------------------------------------------------------------------------
1173
+ // Editor — top rule + ` ❯ … ` + bottom rule
1174
+ // -------------------------------------------------------------------------
1175
+
1176
+ private _renderEditor(
1177
+ width: number,
1178
+ flags: {
1179
+ paused: boolean;
1180
+ streaming: boolean;
1181
+ settled: boolean;
1182
+ blocked: boolean;
1183
+ /**
1184
+ * When `true`, drop the editor's top rule — the loader directly above
1185
+ * already paints a horizontal rule and we don't want a doubled border.
1186
+ */
1187
+ omitTopRule: boolean;
1188
+ },
1189
+ ): string[] {
1190
+ const t = this.theme;
1191
+ // Disabled (settled or blocked) uses surface1 rules + dim placeholder.
1192
+ const disabled = flags.settled || flags.blocked || !this.handle;
1193
+ if (!disabled && this.editor) {
1194
+ return this.editor.render(width);
1195
+ }
1196
+ const ruleHex = disabled ? t.borderDim : t.border;
1197
+ const rule = hexToAnsi(ruleHex) + "─".repeat(width) + RESET;
1198
+
1199
+ const glyphHex = disabled ? t.dim : t.accent;
1200
+ const placeholder = flags.blocked
1201
+ ? "blocked · upstream stage owns the prompt"
1202
+ : flags.settled || !this.handle
1203
+ ? "read-only · stage has no live handle"
1204
+ : flags.paused
1205
+ ? "type to resume, or Ctrl+P to release without input…"
1206
+ : flags.streaming
1207
+ ? "type to steer the current turn… (queues with ↵)"
1208
+ : "type a message…";
1209
+
1210
+ const value = this.inputBuffer
1211
+ ? paint(truncateToWidth(this.inputBuffer, Math.max(8, width - 6)), t.text) + paint("▌", t.text)
1212
+ : paint(placeholder, t.dim, { italic: true });
1213
+
1214
+ const tag = flags.streaming
1215
+ ? paint("streaming", t.accent, { bold: true })
1216
+ : flags.paused
1217
+ ? paint("paused", t.warning, { bold: true })
1218
+ : flags.settled
1219
+ ? paint("settled", t.success, { bold: true })
1220
+ : paint("idle", t.dim);
1221
+ const tagWidth = visibleWidth(stripAnsi(tag));
1222
+ const left = " " + paint("❯", glyphHex, { bold: true }) + " " + value;
1223
+ const valueWidth = visibleWidth(this.inputBuffer || placeholder);
1224
+ const leftWidth = 1 + 1 + 2 + valueWidth + (this.inputBuffer ? 1 : 0);
1225
+ const gap = Math.max(1, width - leftWidth - tagWidth - 2);
1226
+ const body = left + " ".repeat(gap) + tag + " ";
1227
+ return flags.omitTopRule ? [body, rule] : [rule, body, rule];
1228
+ }
1229
+
1230
+ // -------------------------------------------------------------------------
1231
+ // Footer — two dim lines mirroring Pi's FooterComponent
1232
+ // -------------------------------------------------------------------------
1233
+
1234
+ private _renderFooter(
1235
+ width: number,
1236
+ stage: StageSnapshot | undefined,
1237
+ flags: { paused: boolean; streaming: boolean; settled: boolean },
1238
+ ): string[] {
1239
+ const t = this.theme;
1240
+ const sessionId = this.handle?.sessionId ?? stage?.sessionId;
1241
+ const messages = this.handle?.messages.length ?? this.transcript.length;
1242
+ const dur = stageDurationText(stage) ?? "";
1243
+
1244
+ // Top line — left: workflow / stage tag; right: session id
1245
+ const lTop = paint(`pi-workflows/${this.workflowName}/${stage?.name ?? "stage"}`, t.dim);
1246
+ const rTop = sessionId
1247
+ ? paint("session ", t.dim) + paint(shortenId(sessionId), t.textMuted)
1248
+ : paint("session not yet realised", t.dim);
1249
+ const top = layoutRow(width, " ", " " + lTop, rTop + " ", t);
1250
+
1251
+ // Bottom line — left: messages / duration; right: caption
1252
+ const lBot =
1253
+ paint(`◇ ${messages} messages`, t.dim) +
1254
+ (dur ? " " + paint(`· ${dur}`, t.dim) : "");
1255
+ const rBot = flags.streaming
1256
+ ? paint("streaming · live", t.accent)
1257
+ : flags.paused
1258
+ ? paint("paused · ready to resume", t.warning)
1259
+ : flags.settled && stage?.status === "completed"
1260
+ ? paint("completed · session persisted", t.success)
1261
+ : flags.settled && stage?.status === "failed"
1262
+ ? paint("failed · see error", t.error)
1263
+ : paint(this.statusMessage || "idle · awaiting input", t.dim);
1264
+ const bot = layoutRow(width, " ", " " + lBot, rBot + " ", t);
1265
+ return [top, bot];
1266
+ }
1267
+
1268
+ // -------------------------------------------------------------------------
1269
+ // Hints — dashed rule + key bindings
1270
+ // -------------------------------------------------------------------------
1271
+
1272
+ private _renderHints(
1273
+ width: number,
1274
+ flags: { paused: boolean; streaming: boolean; settled: boolean },
1275
+ ): string[] {
1276
+ const t = this.theme;
1277
+ const dash = hexToAnsi(t.borderDim) + "╌".repeat(width) + RESET;
1278
+ const hints = this._hintSet(flags);
1279
+ const sep = paint(" · ", t.dim);
1280
+ const rendered = hints
1281
+ .map(({ key, label, emphasis }) =>
1282
+ paint(key, t.text, { bold: true }) +
1283
+ " " +
1284
+ paint(label, emphasis ? t.textMuted : t.dim, emphasis ? { bold: true } : {}),
1285
+ )
1286
+ .join(sep);
1287
+ const tagPlain = `pi-workflows/${this.workflowName}`;
1288
+ const renderedW = visibleWidth(stripAnsi(rendered));
1289
+ const tagW = visibleWidth(tagPlain);
1290
+ // Right-side tag is "nice to have". When the hint line + tag overflows
1291
+ // the chrome, drop the tag — the hints are the load-bearing affordance.
1292
+ if (renderedW + tagW + 3 > width) {
1293
+ const gap = Math.max(1, width - renderedW - 1);
1294
+ return [dash, " " + rendered + " ".repeat(gap)];
1295
+ }
1296
+ const tag = paint(tagPlain, t.dim);
1297
+ const gap = Math.max(1, width - renderedW - tagW - 2);
1298
+ return [dash, " " + rendered + " ".repeat(gap) + tag + " "];
1299
+ }
1300
+
1301
+ private _hintSet(flags: {
1302
+ paused: boolean;
1303
+ streaming: boolean;
1304
+ settled: boolean;
1305
+ }): Array<{ key: string; label: string; emphasis?: boolean }> {
1306
+ if (flags.settled) {
1307
+ return [
1308
+ { key: "Ctrl+D", label: "back to graph", emphasis: true },
1309
+ { key: "Esc", label: "close" },
1310
+ ];
1311
+ }
1312
+ if (flags.paused) {
1313
+ return [
1314
+ { key: "↵", label: "resume with message", emphasis: true },
1315
+ { key: "Ctrl+P", label: "resume empty" },
1316
+ { key: "Ctrl+D", label: "back" },
1317
+ { key: "Esc", label: "close" },
1318
+ ];
1319
+ }
1320
+ if (flags.streaming) {
1321
+ return [
1322
+ { key: "↵", label: "steer", emphasis: true },
1323
+ { key: "Ctrl+F", label: "follow-up", emphasis: true },
1324
+ { key: "Ctrl+P", label: "pause" },
1325
+ { key: "Ctrl+D", label: "back" },
1326
+ { key: "Esc", label: "interrupt" },
1327
+ ];
1328
+ }
1329
+ return [
1330
+ { key: "↵", label: "send", emphasis: true },
1331
+ { key: "Ctrl+F", label: "follow-up" },
1332
+ { key: "Ctrl+P", label: "pause" },
1333
+ { key: "Ctrl+D", label: "back" },
1334
+ { key: "Esc", label: "close" },
1335
+ ];
1336
+ }
1337
+
1338
+ // -------------------------------------------------------------------------
1339
+ // Small helpers
1340
+ // -------------------------------------------------------------------------
1341
+
1342
+ private _completedMeta(stage: StageSnapshot | undefined): string {
1343
+ const dur = stageDurationText(stage);
1344
+ const parts: string[] = ["stage settled"];
1345
+ if (dur) parts.push(dur);
1346
+ if (stage?.sessionFile) parts.push(`session ${shortenFile(stage.sessionFile)}`);
1347
+ return parts.join(" · ");
1348
+ }
1349
+
1350
+ private _blank(width: number): string {
1351
+ return " ".repeat(width);
1352
+ }
1353
+
1354
+ // -------------------------------------------------------------------------
1355
+ // Input
1356
+ // -------------------------------------------------------------------------
1357
+
1358
+ handleInput(data: string): boolean {
1359
+ if (data === "\x04") {
1360
+ this.onDetach();
1361
+ return true;
1362
+ }
1363
+ if (data === "\x1b") {
1364
+ if (this._isStreaming() && !this._isBlocked()) {
1365
+ void this._pause();
1366
+ } else {
1367
+ this.onClose();
1368
+ }
1369
+ return true;
1370
+ }
1371
+ const blocked = this._isBlocked();
1372
+ if (data === "\x10") {
1373
+ if (blocked) return true;
1374
+ void this._pause();
1375
+ return true;
1376
+ }
1377
+ if (data === "\x06") {
1378
+ if (blocked) return true;
1379
+ void this._submit("followUp");
1380
+ return true;
1381
+ }
1382
+ if (this.editor) {
1383
+ if (blocked) return true;
1384
+ this.editor.handleInput(data);
1385
+ return true;
1386
+ }
1387
+ if (data === "\r" || data === "\n") {
1388
+ if (blocked) return true;
1389
+ void this._submit("auto");
1390
+ return true;
1391
+ }
1392
+ if (data === "\x7f" || data === "\b") {
1393
+ if (blocked) return true;
1394
+ this.inputBuffer = this.inputBuffer.slice(0, -1);
1395
+ return true;
1396
+ }
1397
+ if (data.length === 1 && data >= " " && data <= "~") {
1398
+ if (blocked) return true;
1399
+ this.inputBuffer += data;
1400
+ return true;
1401
+ }
1402
+ return false;
1403
+ }
1404
+
1405
+ private async _pause(): Promise<void> {
1406
+ if (!this.handle) {
1407
+ this.statusMessage = "no live handle on this stage";
1408
+ this.requestRender?.();
1409
+ return;
1410
+ }
1411
+ this.localPaused = true;
1412
+ this.statusMessage = "pausing…";
1413
+ this.requestRender?.();
1414
+ try {
1415
+ await this.handle.pause();
1416
+ this.sdkBusy = false;
1417
+ this.statusMessage = "paused";
1418
+ } catch (err) {
1419
+ this.statusMessage = `pause failed: ${err instanceof Error ? err.message : String(err)}`;
1420
+ this.localPaused = false;
1421
+ } finally {
1422
+ this._syncAnimationTick();
1423
+ this.requestRender?.();
1424
+ }
1425
+ }
1426
+
1427
+ private async _submit(mode: "auto" | "followUp", submittedText?: string): Promise<void> {
1428
+ const text = (submittedText ?? this.inputBuffer).trim();
1429
+ if (!text) return;
1430
+ this.inputBuffer = "";
1431
+ this.editor?.setText("");
1432
+ if (!this.handle) {
1433
+ this.statusMessage = "no live handle on this stage";
1434
+ this.transcript.push({
1435
+ role: "system",
1436
+ text: "(no live handle — message dropped)",
1437
+ });
1438
+ this.requestRender?.();
1439
+ return;
1440
+ }
1441
+ this.transcript.push({ role: "user", text });
1442
+ this.optimisticUserSignatures.add(userMessageSignature(text));
1443
+ this.requestRender?.();
1444
+ try {
1445
+ if (this.localPaused) {
1446
+ this.sdkBusy = true;
1447
+ this._syncAnimationTick();
1448
+ await this.handle.resume(text);
1449
+ this.localPaused = false;
1450
+ this.statusMessage = "resumed";
1451
+ this.requestRender?.();
1452
+ return;
1453
+ }
1454
+ if (mode === "followUp") {
1455
+ await this.handle.followUp(text);
1456
+ return;
1457
+ }
1458
+ if (this.handle.isStreaming) {
1459
+ await this.handle.steer(text);
1460
+ } else {
1461
+ this.sdkBusy = true;
1462
+ this._syncAnimationTick();
1463
+ await this.handle.ensureAttached();
1464
+ await this.handle.prompt(text);
1465
+ }
1466
+ } catch (err) {
1467
+ this.sdkBusy = false;
1468
+ this.statusMessage = err instanceof Error ? err.message : String(err);
1469
+ this._syncAnimationTick();
1470
+ this.requestRender?.();
1471
+ }
1472
+ }
1473
+
1474
+ invalidate(): void {
1475
+ // Stateless render reads directly from snapshot + handle.
1476
+ }
1477
+
1478
+ dispose(): void {
1479
+ this._unsubscribeStore?.();
1480
+ this._unsubscribeStore = null;
1481
+ this._unsubscribeHandle?.();
1482
+ this._unsubscribeHandle = null;
1483
+ if (this.animationTimer) {
1484
+ clearInterval(this.animationTimer);
1485
+ this.animationTimer = undefined;
1486
+ }
1487
+ this.editor = undefined;
1488
+ }
1489
+
1490
+ // ---- Test seams ----
1491
+ get _inputBuffer(): string {
1492
+ return this.inputBuffer;
1493
+ }
1494
+ get _transcript(): readonly TranscriptEntry[] {
1495
+ return this.transcript;
1496
+ }
1497
+ get _statusMessage(): string {
1498
+ return this.statusMessage;
1499
+ }
1500
+ get _isLocalPaused(): boolean {
1501
+ return this.localPaused;
1502
+ }
1503
+ get _hasAnimationTick(): boolean {
1504
+ return this.animationTimer !== undefined;
1505
+ }
1506
+ }
1507
+
1508
+ // ---------------------------------------------------------------------------
1509
+ // Module-private helpers
1510
+ // ---------------------------------------------------------------------------
1511
+
1512
+ type AssistantComponentMessage = NonNullable<
1513
+ ConstructorParameters<typeof AssistantMessageComponent>[0]
1514
+ >;
1515
+
1516
+ function assistantMessageForText(text: string): AssistantComponentMessage {
1517
+ return {
1518
+ role: "assistant",
1519
+ content: [{ type: "text", text }],
1520
+ stopReason: "stop",
1521
+ } as AssistantComponentMessage;
1522
+ }
1523
+
1524
+ function assistantMessageForThinking(text: string): AssistantComponentMessage {
1525
+ return {
1526
+ role: "assistant",
1527
+ content: [{ type: "thinking", thinking: text }],
1528
+ stopReason: "stop",
1529
+ } as AssistantComponentMessage;
1530
+ }
1531
+
1532
+ function toolArgsForRender(entry: ToolEntry): Record<string, unknown> {
1533
+ if (!entry.args) return {};
1534
+ if (entry.name === "bash") {
1535
+ return { command: entry.args.replace(/^command=/, "") };
1536
+ }
1537
+ return { input: entry.args };
1538
+ }
1539
+
1540
+ function isMessageLike(message: unknown): message is { role?: unknown; content?: unknown; stopReason?: unknown; errorMessage?: unknown } {
1541
+ return message !== null && typeof message === "object" && "role" in message;
1542
+ }
1543
+
1544
+ function userMessageSignature(text: string): string {
1545
+ return text.trim();
1546
+ }
1547
+
1548
+ interface AssistantProjection {
1549
+ text: string;
1550
+ thinking: string;
1551
+ toolCalls: Array<{ toolCallId?: string; name: string; args?: string; state: "pending" }>;
1552
+ }
1553
+
1554
+ function assistantContentHasRenderablePayload(content: unknown): boolean {
1555
+ if (typeof content === "string") return content.length > 0;
1556
+ if (!Array.isArray(content)) return false;
1557
+ return content.some((item) => {
1558
+ if (typeof item === "string") return item.length > 0;
1559
+ if (item == null || typeof item !== "object") return false;
1560
+ const obj = item as { type?: unknown; text?: unknown; thinking?: unknown };
1561
+ return (obj.type === "text" && typeof obj.text === "string" && obj.text.length > 0) ||
1562
+ (obj.type === "thinking" && typeof obj.thinking === "string" && obj.thinking.length > 0) ||
1563
+ obj.type === "toolCall";
1564
+ });
1565
+ }
1566
+
1567
+ function projectAssistantContent(content: unknown): AssistantProjection {
1568
+ const projection: AssistantProjection = { text: "", thinking: "", toolCalls: [] };
1569
+ if (!Array.isArray(content)) {
1570
+ projection.text = typeof content === "string" ? content : "";
1571
+ return projection;
1572
+ }
1573
+ const textParts: string[] = [];
1574
+ const thinkingParts: string[] = [];
1575
+ for (const item of content) {
1576
+ if (item == null) continue;
1577
+ if (typeof item === "string") {
1578
+ textParts.push(item);
1579
+ continue;
1580
+ }
1581
+ if (typeof item !== "object") continue;
1582
+ const obj = item as {
1583
+ type?: unknown;
1584
+ text?: unknown;
1585
+ thinking?: unknown;
1586
+ id?: unknown;
1587
+ name?: unknown;
1588
+ arguments?: unknown;
1589
+ args?: unknown;
1590
+ };
1591
+ if (obj.type === "text" && typeof obj.text === "string") {
1592
+ textParts.push(obj.text);
1593
+ continue;
1594
+ }
1595
+ if (obj.type === "thinking" && typeof obj.thinking === "string") {
1596
+ thinkingParts.push(obj.thinking);
1597
+ continue;
1598
+ }
1599
+ if (obj.type === "toolCall") {
1600
+ const name = typeof obj.name === "string" ? obj.name : "tool";
1601
+ const toolCallId = typeof obj.id === "string" ? obj.id : undefined;
1602
+ const args = summariseArgs(obj.arguments ?? obj.args);
1603
+ projection.toolCalls.push({ toolCallId, name, args, state: "pending" });
1604
+ }
1605
+ }
1606
+ projection.text = textParts.join("");
1607
+ projection.thinking = thinkingParts.join("\n\n");
1608
+ return projection;
1609
+ }
1610
+
1611
+ function shallowToolEntryEqual(a: ToolEntry, b: ToolEntry): boolean {
1612
+ return a.role === b.role &&
1613
+ a.text === b.text &&
1614
+ a.name === b.name &&
1615
+ a.toolCallId === b.toolCallId &&
1616
+ a.args === b.args &&
1617
+ a.output === b.output &&
1618
+ a.state === b.state;
1619
+ }
1620
+
1621
+ function transcriptEntryFromSnapshotMessage(
1622
+ message: AgentSnapshotMessage,
1623
+ ): TranscriptEntry | undefined {
1624
+ switch (message.role) {
1625
+ case "user": {
1626
+ const text = extractMessageText(message.content);
1627
+ return text ? { role: "user", text } : undefined;
1628
+ }
1629
+ case "assistant": {
1630
+ const text = extractMessageText(message.content);
1631
+ return text ? { role: "assistant", text } : undefined;
1632
+ }
1633
+ case "toolResult": {
1634
+ const output = extractMessageText(message.content);
1635
+ const summary = output ? truncateToWidth(output.replace(/\s+/g, " "), 80) : "";
1636
+ return {
1637
+ role: "tool",
1638
+ name: message.toolName,
1639
+ output,
1640
+ state: message.isError ? "error" : "success",
1641
+ text: summary ? `← ${message.toolName} ${summary}` : `← ${message.toolName}`,
1642
+ };
1643
+ }
1644
+ case "bashExecution": {
1645
+ const state =
1646
+ message.cancelled || (message.exitCode !== undefined && message.exitCode !== 0)
1647
+ ? "error"
1648
+ : "success";
1649
+ const summary = message.output ? truncateToWidth(message.output.replace(/\s+/g, " "), 80) : "";
1650
+ return {
1651
+ role: "tool",
1652
+ name: "bash",
1653
+ args: truncateToWidth(message.command.replace(/\s+/g, " "), 60),
1654
+ output: message.output,
1655
+ state,
1656
+ text: summary ? `← bash ${summary}` : `→ bash ${message.command}`,
1657
+ };
1658
+ }
1659
+ case "custom": {
1660
+ if (!message.display) return undefined;
1661
+ const text = extractMessageText(message.content);
1662
+ return text ? { role: "system", text } : undefined;
1663
+ }
1664
+ case "branchSummary": {
1665
+ const text = `Branch summary: ${message.summary}`;
1666
+ return { role: "system", text };
1667
+ }
1668
+ case "compactionSummary": {
1669
+ const text = `Compaction summary: ${message.summary}`;
1670
+ return { role: "system", text };
1671
+ }
1672
+ default:
1673
+ // The SDK message union is extensible. Snapshot unknown roles must be
1674
+ // skipped here instead of being cast into `TranscriptEntry`; `_renderBody`
1675
+ // only flattens the closed set of components returned by `_renderEntry`.
1676
+ return undefined;
1677
+ }
1678
+ }
1679
+
1680
+ function isSessionMessageEntry(entry: unknown): entry is SessionMessageEntry {
1681
+ return entry !== null &&
1682
+ typeof entry === "object" &&
1683
+ (entry as { type?: unknown }).type === "message" &&
1684
+ "message" in entry;
1685
+ }
1686
+
1687
+ function extractMessageText(content: unknown): string {
1688
+ if (typeof content === "string") return content;
1689
+ if (!Array.isArray(content)) return "";
1690
+ const parts: string[] = [];
1691
+ for (const item of content) {
1692
+ if (item == null) continue;
1693
+ if (typeof item === "string") {
1694
+ parts.push(item);
1695
+ continue;
1696
+ }
1697
+ const obj = item as { type?: unknown; text?: unknown };
1698
+ if (typeof obj.text === "string") parts.push(obj.text);
1699
+ else if (obj.type === "text" && typeof obj.text === "string") parts.push(obj.text);
1700
+ }
1701
+ return parts.join("");
1702
+ }
1703
+
1704
+ function extractToolResultText(result: unknown): string {
1705
+ if (typeof result === "string") return result;
1706
+ if (result == null || typeof result !== "object") return "";
1707
+ const content = (result as { content?: unknown }).content;
1708
+ return extractMessageText(content);
1709
+ }
1710
+
1711
+ function findToolEntryIndex(
1712
+ entries: readonly TranscriptEntry[],
1713
+ toolCallId: string | undefined,
1714
+ name: string,
1715
+ ): number {
1716
+ if (toolCallId !== undefined) {
1717
+ for (let i = entries.length - 1; i >= 0; i--) {
1718
+ const entry = entries[i];
1719
+ if (entry?.role === "tool" && entry.toolCallId === toolCallId) return i;
1720
+ }
1721
+ }
1722
+ for (let i = entries.length - 1; i >= 0; i--) {
1723
+ const entry = entries[i];
1724
+ if (entry?.role === "tool" && entry.name === name && entry.state === "pending") return i;
1725
+ }
1726
+ return -1;
1727
+ }
1728
+
1729
+ function summariseArgs(input: unknown): string {
1730
+ if (input == null) return "";
1731
+ if (typeof input === "string") return truncateToWidth(input.replace(/\s+/g, " "), 60);
1732
+ if (typeof input !== "object") return String(input);
1733
+ const obj = input as Record<string, unknown>;
1734
+ const keys = Object.keys(obj);
1735
+ if (keys.length === 0) return "";
1736
+ const head = keys[0]!;
1737
+ const value = obj[head];
1738
+ const summary = typeof value === "string" ? value : JSON.stringify(value);
1739
+ const formatted = `${head}=${summary}`;
1740
+ return truncateToWidth(formatted.replace(/\s+/g, " "), 60);
1741
+ }
1742
+
1743
+ function noticeSummary(n: StageNotice): string {
1744
+ const base = `~ ${n.kind} → ${n.to}`;
1745
+ return n.from ? `${base} (was ${n.from})` : base;
1746
+ }
1747
+
1748
+ function stageDurationText(stage: StageSnapshot | undefined): string {
1749
+ if (!stage?.startedAt) return "";
1750
+ const end = stage.endedAt ?? Date.now();
1751
+ const ms = Math.max(0, end - stage.startedAt);
1752
+ return formatDuration(ms);
1753
+ }
1754
+
1755
+ function formatDuration(ms: number): string {
1756
+ if (ms < 1000) return `${ms}ms`;
1757
+ const s = Math.floor(ms / 1000);
1758
+ if (s < 60) return `${s}s`;
1759
+ const m = Math.floor(s / 60);
1760
+ const rs = s % 60;
1761
+ if (m < 60) return rs ? `${m}m ${rs}s` : `${m}m`;
1762
+ const h = Math.floor(m / 60);
1763
+ const rm = m % 60;
1764
+ return rm ? `${h}h ${rm}m` : `${h}h`;
1765
+ }
1766
+
1767
+ function shortenId(id: string): string {
1768
+ return id.length > 10 ? id.slice(0, 8) : id;
1769
+ }
1770
+
1771
+ function shortenFile(path: string): string {
1772
+ if (path.length <= 36) return path;
1773
+ // Keep the basename and an ellipsis prefix so the user can still recognise
1774
+ // which session file we're pointing at.
1775
+ const slash = path.lastIndexOf("/");
1776
+ if (slash < 0) return "…" + path.slice(-35);
1777
+ return "…" + path.slice(Math.max(slash - 12, 0));
1778
+ }
1779
+
1780
+ function spinnerFrame(): string {
1781
+ const idx = Math.floor(Date.now() / 80) % SPINNER_FRAMES.length;
1782
+ return SPINNER_FRAMES[idx]!;
1783
+ }
1784
+
1785
+ function bgFn(hex: string): (text: string) => string {
1786
+ const open = hexBg(hex);
1787
+ return (text: string) => open + text + RESET;
1788
+ }
1789
+
1790
+ function editorThemeFromGraphTheme(t: GraphTheme): EditorTheme {
1791
+ const selected = (text: string): string => hexBg(t.backgroundPanel) + hexToAnsi(t.text) + text + RESET;
1792
+ const normal = (text: string): string => hexToAnsi(t.text) + text + RESET;
1793
+ return {
1794
+ borderColor: (text: string) => hexToAnsi(t.border) + text + RESET,
1795
+ selectList: {
1796
+ selectedPrefix: selected,
1797
+ selectedText: selected,
1798
+ description: (text: string) => hexToAnsi(t.dim) + text + RESET,
1799
+ scrollInfo: (text: string) => hexToAnsi(t.dim) + text + RESET,
1800
+ noMatch: (text: string) => hexToAnsi(t.warning) + text + RESET,
1801
+ normal,
1802
+ },
1803
+ } as EditorTheme;
1804
+ }
1805
+
1806
+ interface PaintOpts {
1807
+ bold?: boolean;
1808
+ italic?: boolean;
1809
+ bg?: string;
1810
+ }
1811
+
1812
+ function paint(text: string, fg: string, opts: PaintOpts = {}): string {
1813
+ if (!text) return "";
1814
+ let out = hexToAnsi(fg);
1815
+ if (opts.bold) out += BOLD;
1816
+ if (opts.italic) out += ITALIC;
1817
+ if (opts.bg) out = hexBg(opts.bg) + out;
1818
+ return out + text + RESET;
1819
+ }
1820
+
1821
+ /**
1822
+ * Foreground styling for text that will be wrapped by a `Box` background.
1823
+ * A normal `RESET` would also clear the parent background, so close only the
1824
+ * inline foreground/weight/italic state and let `bgFn()` reset the row at end.
1825
+ */
1826
+ function paintOnFill(text: string, fg: string, opts: PaintOpts = {}): string {
1827
+ if (!text) return "";
1828
+ let out = hexToAnsi(fg);
1829
+ if (opts.bold) out += BOLD;
1830
+ if (opts.italic) out += ITALIC;
1831
+ let close = FG_RESET;
1832
+ if (opts.bold) close += WEIGHT_RESET;
1833
+ if (opts.italic) close += ITALIC_RESET;
1834
+ return out + text + close;
1835
+ }
1836
+
1837
+ function stripAnsi(s: string): string {
1838
+ return s.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
1839
+ }
1840
+
1841
+ function centred(content: string, width: number): string {
1842
+ const w = visibleWidth(stripAnsi(content));
1843
+ if (w >= width) return content;
1844
+ const left = Math.floor((width - w) / 2);
1845
+ const right = width - w - left;
1846
+ return " ".repeat(left) + content + " ".repeat(right);
1847
+ }
1848
+
1849
+ /**
1850
+ * Compose a two-column row of `${prefix}${left}…${right}` padded to width.
1851
+ * Used by the footer to lay out left/right slabs without losing ANSI runs.
1852
+ */
1853
+ function layoutRow(
1854
+ width: number,
1855
+ _prefix: string,
1856
+ left: string,
1857
+ right: string,
1858
+ _theme: GraphTheme,
1859
+ ): string {
1860
+ const lw = visibleWidth(stripAnsi(left));
1861
+ const rw = visibleWidth(stripAnsi(right));
1862
+ const gap = Math.max(1, width - lw - rw);
1863
+ return left + " ".repeat(gap) + right;
1864
+ }
1865
+
1866
+ /**
1867
+ * Approximate a tinted background by mixing the base canvas with a saturated
1868
+ * hue at low alpha. Used for status pills and tool-bar tints. Returns a hex
1869
+ * colour the renderer can feed to `hexBg`.
1870
+ */
1871
+ function blendBg(baseHex: string, tintHex: string, alpha: number): string {
1872
+ return lerpColor(baseHex, tintHex, Math.max(0, Math.min(1, alpha)));
1873
+ }