@bastani/atomic 0.9.11-alpha.9 → 0.9.11

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 (285) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +1 -1
  3. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/intercom/subagent-relay.ts +8 -5
  6. package/dist/builtin/mcp/CHANGELOG.md +10 -0
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/subagents/CHANGELOG.md +20 -0
  9. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  10. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  11. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  12. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  13. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  14. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  15. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  16. package/dist/builtin/subagents/agents/worker.md +2 -2
  17. package/dist/builtin/subagents/package.json +1 -1
  18. package/dist/builtin/subagents/src/extension/notification-content.ts +14 -9
  19. package/dist/builtin/subagents/src/runs/background/notify.ts +20 -4
  20. package/dist/builtin/web-access/CHANGELOG.md +20 -0
  21. package/dist/builtin/web-access/content-tools.ts +1 -0
  22. package/dist/builtin/web-access/flat-string.ts +37 -0
  23. package/dist/builtin/web-access/github-api.ts +2 -1
  24. package/dist/builtin/web-access/github-extract.ts +3 -2
  25. package/dist/builtin/web-access/package.json +2 -2
  26. package/dist/builtin/workflows/CHANGELOG.md +57 -0
  27. package/dist/builtin/workflows/builtin/goal-ledger.ts +6 -4
  28. package/dist/builtin/workflows/builtin/goal-runner.ts +2 -1
  29. package/dist/builtin/workflows/builtin/goal.ts +1 -1
  30. package/dist/builtin/workflows/builtin/ralph-core.ts +8 -9
  31. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +2 -4
  32. package/dist/builtin/workflows/builtin/ralph-models.ts +4 -4
  33. package/dist/builtin/workflows/builtin/ralph-runner.ts +5 -8
  34. package/dist/builtin/workflows/builtin/ralph.ts +2 -1
  35. package/dist/builtin/workflows/package.json +1 -1
  36. package/dist/builtin/workflows/src/durable/completed-catalog.ts +14 -8
  37. package/dist/builtin/workflows/src/durable/completed-inspection.ts +10 -11
  38. package/dist/builtin/workflows/src/durable/resume-catalog.ts +1 -1
  39. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +42 -0
  40. package/dist/builtin/workflows/src/durable/resume-runtime.ts +36 -37
  41. package/dist/builtin/workflows/src/durable/tool-primitive.ts +10 -2
  42. package/dist/builtin/workflows/src/engine/run.ts +1 -0
  43. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +12 -8
  44. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +5 -2
  45. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1966 -880
  46. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +6 -3
  47. package/dist/builtin/workflows/src/extension/postmortem-deps.ts +3 -2
  48. package/dist/builtin/workflows/src/extension/render-result.ts +4 -11
  49. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +17 -20
  50. package/dist/builtin/workflows/src/extension/runtime.ts +3 -3
  51. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +1 -1
  52. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +3 -7
  53. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +21 -24
  54. package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +38 -26
  55. package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +2 -1
  56. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +56 -64
  57. package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -2
  58. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +0 -5
  59. package/dist/builtin/workflows/src/extension/workflow-targets.ts +17 -20
  60. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +1 -1
  61. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +24 -51
  62. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +7 -29
  63. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +5 -13
  64. package/dist/builtin/workflows/src/extension/workflow-tool.ts +4 -13
  65. package/dist/builtin/workflows/src/runs/background/quit.ts +4 -3
  66. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +8 -8
  67. package/dist/builtin/workflows/src/runs/background/status.ts +2 -1
  68. package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +2 -1
  69. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
  70. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +4 -0
  71. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +2 -0
  72. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +23 -8
  73. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +6 -2
  74. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +5 -0
  75. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +56 -10
  76. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +6 -0
  77. package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +137 -6
  78. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +2 -0
  79. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +2 -0
  80. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +159 -10
  81. package/dist/builtin/workflows/src/shared/flat-string.ts +33 -0
  82. package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +144 -0
  83. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +2 -3
  84. package/dist/builtin/workflows/src/shared/run-id.ts +36 -0
  85. package/dist/builtin/workflows/src/shared/store-internal.ts +32 -3
  86. package/dist/builtin/workflows/src/shared/store-observation.ts +10 -0
  87. package/dist/builtin/workflows/src/shared/store-public-types.ts +8 -0
  88. package/dist/builtin/workflows/src/shared/store-run-methods.ts +14 -0
  89. package/dist/builtin/workflows/src/shared/store-types.ts +4 -0
  90. package/dist/builtin/workflows/src/shared/types.ts +2 -0
  91. package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +261 -0
  92. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +39 -23
  93. package/dist/builtin/workflows/src/tui/graph-canvas.ts +26 -9
  94. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +3 -0
  95. package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +95 -98
  96. package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -20
  97. package/dist/builtin/workflows/src/tui/graph-view-state.ts +160 -13
  98. package/dist/builtin/workflows/src/tui/layout.ts +8 -2
  99. package/dist/builtin/workflows/src/tui/node-card.ts +57 -45
  100. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -2
  101. package/dist/builtin/workflows/src/tui/prompt-card-render.ts +291 -21
  102. package/dist/builtin/workflows/src/tui/prompt-card.ts +1 -1
  103. package/dist/builtin/workflows/src/tui/renderers.ts +0 -1
  104. package/dist/builtin/workflows/src/tui/run-detail.ts +52 -35
  105. package/dist/builtin/workflows/src/tui/run-identity-rows.ts +97 -0
  106. package/dist/builtin/workflows/src/tui/session-overlays.ts +24 -3
  107. package/dist/builtin/workflows/src/tui/session-picker.ts +94 -41
  108. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +190 -26
  109. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +52 -18
  110. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +9 -5
  111. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +44 -4
  112. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +3 -1
  113. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -2
  114. package/dist/builtin/workflows/src/tui/status-list.ts +67 -45
  115. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -6
  116. package/dist/builtin/workflows/src/tui/widget.ts +49 -40
  117. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +15 -10
  118. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +21 -8
  119. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  120. package/dist/core/agent-session-accessors.js +5 -0
  121. package/dist/core/agent-session-accessors.js.map +1 -1
  122. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  123. package/dist/core/agent-session-auto-compaction.js +5 -0
  124. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  125. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  126. package/dist/core/agent-session-compaction.js +3 -0
  127. package/dist/core/agent-session-compaction.js.map +1 -1
  128. package/dist/core/agent-session-events.d.ts.map +1 -1
  129. package/dist/core/agent-session-events.js +2 -1
  130. package/dist/core/agent-session-events.js.map +1 -1
  131. package/dist/core/agent-session-message-queue.d.ts +3 -7
  132. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  133. package/dist/core/agent-session-message-queue.js +27 -13
  134. package/dist/core/agent-session-message-queue.js.map +1 -1
  135. package/dist/core/agent-session-methods.d.ts +5 -3
  136. package/dist/core/agent-session-methods.d.ts.map +1 -1
  137. package/dist/core/agent-session-methods.js.map +1 -1
  138. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  139. package/dist/core/agent-session-persistent-custom-messages.js +6 -3
  140. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  141. package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
  142. package/dist/core/agent-session-post-tool-compaction.js +11 -2
  143. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  144. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  145. package/dist/core/agent-session-runtime.js +5 -3
  146. package/dist/core/agent-session-runtime.js.map +1 -1
  147. package/dist/core/agent-session-tree.d.ts.map +1 -1
  148. package/dist/core/agent-session-tree.js +4 -0
  149. package/dist/core/agent-session-tree.js.map +1 -1
  150. package/dist/core/agent-session-types.d.ts +4 -0
  151. package/dist/core/agent-session-types.d.ts.map +1 -1
  152. package/dist/core/agent-session-types.js.map +1 -1
  153. package/dist/core/agent-session.d.ts +2 -1
  154. package/dist/core/agent-session.d.ts.map +1 -1
  155. package/dist/core/agent-session.js +1 -0
  156. package/dist/core/agent-session.js.map +1 -1
  157. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  158. package/dist/core/auth-storage-backends.js +12 -1
  159. package/dist/core/auth-storage-backends.js.map +1 -1
  160. package/dist/core/bounded-model-refresh.d.ts +14 -0
  161. package/dist/core/bounded-model-refresh.d.ts.map +1 -0
  162. package/dist/core/bounded-model-refresh.js +38 -0
  163. package/dist/core/bounded-model-refresh.js.map +1 -0
  164. package/dist/core/context-tool-pairing.d.ts +4 -4
  165. package/dist/core/context-tool-pairing.js +4 -4
  166. package/dist/core/context-tool-pairing.js.map +1 -1
  167. package/dist/core/http-dispatcher.d.ts.map +1 -1
  168. package/dist/core/http-dispatcher.js +26 -1
  169. package/dist/core/http-dispatcher.js.map +1 -1
  170. package/dist/core/messages.d.ts +18 -6
  171. package/dist/core/messages.d.ts.map +1 -1
  172. package/dist/core/messages.js +46 -18
  173. package/dist/core/messages.js.map +1 -1
  174. package/dist/core/model-refresh-timeout.d.ts +9 -0
  175. package/dist/core/model-refresh-timeout.d.ts.map +1 -0
  176. package/dist/core/model-refresh-timeout.js +9 -0
  177. package/dist/core/model-refresh-timeout.js.map +1 -0
  178. package/dist/core/model-runtime-snapshot.d.ts +3 -0
  179. package/dist/core/model-runtime-snapshot.d.ts.map +1 -1
  180. package/dist/core/model-runtime-snapshot.js +59 -0
  181. package/dist/core/model-runtime-snapshot.js.map +1 -1
  182. package/dist/core/model-runtime.d.ts +7 -1
  183. package/dist/core/model-runtime.d.ts.map +1 -1
  184. package/dist/core/model-runtime.js +72 -17
  185. package/dist/core/model-runtime.js.map +1 -1
  186. package/dist/core/sdk.d.ts.map +1 -1
  187. package/dist/core/sdk.js +4 -3
  188. package/dist/core/sdk.js.map +1 -1
  189. package/dist/core/session-manager-core.d.ts +1 -1
  190. package/dist/core/session-manager-core.d.ts.map +1 -1
  191. package/dist/core/session-manager-core.js +2 -2
  192. package/dist/core/session-manager-core.js.map +1 -1
  193. package/dist/core/session-manager-entries.d.ts +1 -1
  194. package/dist/core/session-manager-entries.d.ts.map +1 -1
  195. package/dist/core/session-manager-entries.js +2 -1
  196. package/dist/core/session-manager-entries.js.map +1 -1
  197. package/dist/core/session-manager-history.d.ts.map +1 -1
  198. package/dist/core/session-manager-history.js +2 -2
  199. package/dist/core/session-manager-history.js.map +1 -1
  200. package/dist/core/session-manager-types.d.ts +3 -0
  201. package/dist/core/session-manager-types.d.ts.map +1 -1
  202. package/dist/core/session-manager-types.js.map +1 -1
  203. package/dist/index.d.ts +1 -1
  204. package/dist/index.d.ts.map +1 -1
  205. package/dist/index.js.map +1 -1
  206. package/dist/main-app-mode.d.ts +1 -0
  207. package/dist/main-app-mode.d.ts.map +1 -1
  208. package/dist/main-app-mode.js +3 -0
  209. package/dist/main-app-mode.js.map +1 -1
  210. package/dist/main.d.ts.map +1 -1
  211. package/dist/main.js +8 -3
  212. package/dist/main.js.map +1 -1
  213. package/dist/modes/interactive/components/chat-session-host-actions.d.ts +7 -2
  214. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  215. package/dist/modes/interactive/components/chat-session-host-actions.js +13 -4
  216. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  217. package/dist/modes/interactive/components/chat-session-host-editor.d.ts +3 -1
  218. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  219. package/dist/modes/interactive/components/chat-session-host-editor.js +2 -2
  220. package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
  221. package/dist/modes/interactive/components/chat-session-host-events.d.ts +3 -0
  222. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  223. package/dist/modes/interactive/components/chat-session-host-events.js +5 -4
  224. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  225. package/dist/modes/interactive/components/chat-session-host.d.ts +16 -2
  226. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  227. package/dist/modes/interactive/components/chat-session-host.js +35 -5
  228. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  229. package/dist/modes/interactive/components/index.d.ts +1 -0
  230. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/index.js.map +1 -1
  232. package/dist/modes/interactive/components/model-selector.d.ts +0 -1
  233. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/model-selector.js +25 -35
  235. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  236. package/dist/modes/interactive/interactive-editor-actions.js +2 -1
  237. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  238. package/dist/modes/interactive/interactive-input-handling.js +1 -1
  239. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  240. package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -1
  241. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  242. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  243. package/dist/modes/interactive/interactive-model-routing.js +4 -1
  244. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  245. package/dist/modes/interactive/interactive-pause.d.ts +3 -1
  246. package/dist/modes/interactive/interactive-pause.d.ts.map +1 -1
  247. package/dist/modes/interactive/interactive-pause.js +5 -1
  248. package/dist/modes/interactive/interactive-pause.js.map +1 -1
  249. package/dist/modes/interactive/interactive-queueing.js +3 -3
  250. package/dist/modes/interactive/interactive-queueing.js.map +1 -1
  251. package/dist/modes/interactive-engine/engine-health.d.ts +6 -0
  252. package/dist/modes/interactive-engine/engine-health.d.ts.map +1 -1
  253. package/dist/modes/interactive-engine/engine-health.js +15 -1
  254. package/dist/modes/interactive-engine/engine-health.js.map +1 -1
  255. package/dist/modes/interactive-engine/isolated-auth.js +1 -1
  256. package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
  257. package/dist/modes/interactive-engine/isolated-runtime.d.ts +1 -0
  258. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  259. package/dist/modes/interactive-engine/isolated-runtime.js +7 -1
  260. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  261. package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
  262. package/dist/modes/interactive-engine/remote-model-catalog.js +5 -1
  263. package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
  264. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  265. package/dist/modes/rpc/rpc-command-handler.js +23 -7
  266. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  267. package/dist/modes/rpc/rpc-input-scheduler.js +1 -1
  268. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  269. package/dist/modes/rpc/rpc-types.d.ts +2 -1
  270. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  271. package/dist/modes/rpc/rpc-types.js.map +1 -1
  272. package/docs/custom-provider.md +3 -3
  273. package/docs/extensions.md +3 -1
  274. package/docs/index.md +10 -0
  275. package/docs/keybindings.md +1 -1
  276. package/docs/models.md +2 -0
  277. package/docs/providers.md +7 -3
  278. package/docs/quickstart.md +7 -1
  279. package/docs/session-format.md +2 -0
  280. package/docs/termux.md +3 -1
  281. package/docs/usage.md +3 -2
  282. package/docs/workflows.md +82 -46
  283. package/npm-shrinkwrap.json +59 -27
  284. package/package.json +3 -3
  285. package/dist/builtin/workflows/src/tui/toast.ts +0 -107
@@ -5,10 +5,8 @@
5
5
  * Visual contract:
6
6
  * - One rounded `DISPATCHED` panel.
7
7
  * - One status-coloured rounded run card:
8
- * title: runId8 · workflowName · ● running
8
+ * title: full runId · workflowName · ● running
9
9
  * body: compact `k=v · k=v · +N more` input summary when present
10
- * - One compact next-step hint:
11
- * ▸ /workflow connect <id> see agents working · chat with and steer each stage
12
10
  *
13
11
  * What we deliberately do NOT emit (was in the legacy 7-row layout):
14
12
  * - the `✓ submitted · /workflow <name>` echo line — pi already shows
@@ -16,8 +14,8 @@
16
14
  * - the `[ DISPATCHED ]` band — a single-card surface doesn't need a
17
15
  * band wrapper (bands frame multi-card surfaces like BACKGROUND /
18
16
  * WORKFLOWS);
19
- * - the `run id` muted caption beside the tag the bg-pill chip with
20
- * an 8-char hex string visually communicates "identifier";
17
+ * - the `run id` muted caption the bare identifier row already
18
+ * communicates identity without restoring legacy chrome;
21
19
  * - the `status starting…` body row — the `● running` badge on row 1
22
20
  * occupies the same semantic slot;
23
21
  * - the second hint row `▸ /workflow status` — that is a separate intent
@@ -34,13 +32,13 @@
34
32
  */
35
33
 
36
34
  import type { WorkflowInputValues } from "../shared/types.js";
37
- import { chatWidth, ELLIPSIS, renderHintRows, renderRoundedBox } from "./chat-surface.js";
35
+ import { chatWidth, ELLIPSIS, renderRoundedBox } from "./chat-surface.js";
38
36
  import { hexToAnsi, RESET } from "./color-utils.js";
39
37
  import type { GraphTheme } from "./graph-theme.js";
38
+ import { wrapIdentifierLines } from "./run-identity-rows.js";
40
39
  import { truncateToWidth, visibleWidth } from "./text-helpers.js";
41
40
 
42
41
  const INLINE_INPUT_LIMIT = 3;
43
- const SHORT_ID_LEN = 8;
44
42
 
45
43
  /**
46
44
  * Below this many cells, inline inputs on row 1 are unreadable. We wrap
@@ -51,9 +49,9 @@ const SHORT_ID_LEN = 8;
51
49
  const MIN_INLINE_INPUT_BUDGET = 16;
52
50
 
53
51
  export interface RenderDispatchConfirmOpts {
54
- /** Registered workflow name (rendered bold beside the run-id tag on row 1). */
52
+ /** Registered workflow name rendered beside the full run-id body row. */
55
53
  workflowName: string;
56
- /** Real run UUID; the renderer surfaces the first 8 chars in the tag. */
54
+ /** Real run UUID; every rendered occurrence keeps the complete value. */
57
55
  runId: string;
58
56
  /** Inputs merged from CLI tokens + picker output. */
59
57
  inputs: Readonly<WorkflowInputValues>;
@@ -72,8 +70,7 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
72
70
  const width = effectiveWidth(opts.width);
73
71
  const theme = opts.theme;
74
72
  const accent = theme?.warning ?? "#000000";
75
- const tag = shortRunId(opts.runId);
76
-
73
+ const tag = opts.runId;
77
74
  // Status badge — anchored to the right of row 1, in the running hue.
78
75
  // Mirrors the `● running` glyph used by every other live-run surface.
79
76
  const trailing = theme ? { text: "● running", fg: theme.warning } : { text: "● running" };
@@ -117,34 +114,53 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
117
114
  // the full body interior (width - body chrome) as the wider canvas.
118
115
  if (hasInputs && titleSuffix === undefined) {
119
116
  const BODY_PREFIX_W = 4; // " " — see renderTaggedCard body prefix
120
- const bodyBudget = Math.max(0, width - BODY_PREFIX_W - 1);
117
+ const bodyBudget = Math.max(0, width - 2 - BODY_PREFIX_W);
121
118
  const overflowSeg = renderInputsSegment(opts.inputs, bodyBudget, theme);
122
119
  if (overflowSeg) bodyRows.push(overflowSeg.rendered);
123
120
  }
124
121
 
125
122
  const inputRows =
126
123
  bodyRows.length > 0 ? bodyRows.map((row) => ` ${row} `) : [` ${titleSuffix ?? "started in background"} `];
127
- const titleLine = ` ● ${tag} ${opts.workflowName} ${trailing.text} `;
124
+ const identifierRows = renderIdentifierRows(opts.runId, width - 2, theme);
125
+ const titleLine = ` ● ${opts.workflowName} ${trailing.text} `;
128
126
 
129
- const hints = renderHintRows(
130
- [{ command: `/workflow connect ${tag}`, hint: "see agents working · chat with and steer each stage" }],
131
- theme,
132
- )
133
- .split("\n")
134
- .map((line) => ` ${line} `);
127
+ const hints = renderDispatchHintRows(opts.runId, width - 4, theme).map((line) => ` ${line} `);
135
128
 
136
129
  return renderRoundedBox({
137
130
  title: "DISPATCHED",
138
- bodyLines: [titleLine, ...inputRows, "", ...hints],
131
+ bodyLines: [...identifierRows, titleLine, ...inputRows, "", ...hints],
139
132
  accent,
140
133
  theme,
141
134
  width,
142
135
  });
143
136
  }
144
137
 
145
- /** First 8 chars of the run UUID — the canonical short form. */
146
- function shortRunId(runId: string): string {
147
- return runId.length > SHORT_ID_LEN ? runId.slice(0, SHORT_ID_LEN) : runId;
138
+ function renderIdentifierRows(id: string, width: number, theme?: GraphTheme): string[] {
139
+ const rows = wrapIdentifierLines(id, width, " ", " ");
140
+ if (!theme) return rows.map((row) => `${row.prefix}${row.chunk}`);
141
+ const accent = hexToAnsi(theme.accent);
142
+ return rows.map((row) => `${row.prefix}${accent}${row.chunk}${RESET}`);
143
+ }
144
+
145
+ function renderDispatchHintRows(id: string, width: number, theme?: GraphTheme): string[] {
146
+ const budget = Math.max(1, width);
147
+ const prefix = "▸ /workflow connect ";
148
+ const continuation = " ";
149
+ const rows = wrapIdentifierLines(id, budget, prefix, continuation);
150
+ const suffix = " see agents working · chat with and steer each stage";
151
+ const last = rows[rows.length - 1]!;
152
+ if (visibleWidth(`${last.prefix}${last.chunk}${suffix}`) <= budget) {
153
+ last.chunk += suffix;
154
+ } else {
155
+ rows.push({
156
+ prefix: continuation,
157
+ chunk: truncateToWidth(suffix.trimStart(), Math.max(1, budget - visibleWidth(continuation)), ELLIPSIS),
158
+ });
159
+ }
160
+ if (!theme) return rows.map((row) => `${row.prefix}${row.chunk}`);
161
+ const dim = hexToAnsi(theme.dim);
162
+ const accent = hexToAnsi(theme.accent);
163
+ return rows.map((row) => `${dim}${row.prefix}${RESET}${accent}${row.chunk}${RESET}`);
148
164
  }
149
165
 
150
166
  interface InputsSegment {
@@ -70,14 +70,27 @@ function glyphForDirs(dirs: Set<Dir>): string {
70
70
  return " ";
71
71
  }
72
72
 
73
+ export interface GraphCanvasBounds {
74
+ top: number;
75
+ bottom: number;
76
+ left: number;
77
+ right: number;
78
+ }
79
+
73
80
  export class GraphCanvas {
74
- /** rowIdx → colIdx → Cell. Sparse — empty cells render as a single space. */
75
81
  private rows: Map<number, Map<number, Cell>> = new Map();
76
82
  private maxRow = -1;
77
83
  private maxCol = -1;
78
84
 
85
+ constructor(private readonly bounds?: GraphCanvasBounds) {}
79
86
  setCell(row: number, col: number, ch: string, fg: string | null): void {
80
87
  if (row < 0 || col < 0) return;
88
+ if (
89
+ this.bounds &&
90
+ (row < this.bounds.top || row >= this.bounds.bottom || col < this.bounds.left || col >= this.bounds.right)
91
+ ) {
92
+ return;
93
+ }
81
94
  let cols = this.rows.get(row);
82
95
  if (!cols) {
83
96
  cols = new Map();
@@ -116,34 +129,38 @@ export class GraphCanvas {
116
129
  * different glyph at the final column (e.g. `>`-style arrowhead).
117
130
  */
118
131
  hline(row: number, fromCol: number, toCol: number, fg: string | null): void {
119
- const lo = Math.min(fromCol, toCol);
120
- const hi = Math.max(fromCol, toCol);
132
+ if (this.bounds && (row < this.bounds.top || row >= this.bounds.bottom)) return;
133
+ const lo = Math.max(Math.min(fromCol, toCol), this.bounds?.left ?? 0);
134
+ const hi = Math.min(Math.max(fromCol, toCol), (this.bounds?.right ?? Number.POSITIVE_INFINITY) - 1);
121
135
  for (let c = lo; c <= hi; c++) this.mergeCell(row, c, ["l", "r"], fg);
122
136
  }
123
137
 
124
138
  vline(col: number, fromRow: number, toRow: number, fg: string | null): void {
125
- const lo = Math.min(fromRow, toRow);
126
- const hi = Math.max(fromRow, toRow);
139
+ if (this.bounds && (col < this.bounds.left || col >= this.bounds.right)) return;
140
+ const lo = Math.max(Math.min(fromRow, toRow), this.bounds?.top ?? 0);
141
+ const hi = Math.min(Math.max(fromRow, toRow), (this.bounds?.bottom ?? Number.POSITIVE_INFINITY) - 1);
127
142
  for (let r = lo; r <= hi; r++) this.mergeCell(r, col, ["u", "d"], fg);
128
143
  }
129
144
 
130
145
  /** Materialise the canvas as one ANSI-styled string per row. */
131
146
  toLines(): string[] {
132
- if (this.maxRow < 0) return [];
147
+ const top = this.bounds?.top ?? 0;
148
+ const bottom = this.bounds?.bottom ?? this.maxRow + 1;
149
+ const left = this.bounds?.left ?? 0;
150
+ if (bottom <= top) return [];
133
151
  const lines: string[] = [];
134
- for (let r = 0; r <= this.maxRow; r++) {
152
+ for (let r = top; r < bottom; r++) {
135
153
  const cols = this.rows.get(r);
136
154
  if (!cols || cols.size === 0) {
137
155
  lines.push("");
138
156
  continue;
139
157
  }
140
158
  let buf = "";
141
- let cursorCol = 0;
159
+ let cursorCol = left;
142
160
  let activeFg: string | null = null;
143
161
  const sortedCols = Array.from(cols.keys()).sort((a, b) => a - b);
144
162
  for (const c of sortedCols) {
145
163
  if (c > cursorCol) {
146
- // Reset before emitting the gap so trailing styles don't bleed.
147
164
  if (activeFg !== null) {
148
165
  buf += RESET;
149
166
  activeFg = null;
@@ -37,6 +37,9 @@ export const OVERLAY_VERTICAL_MARGIN_ROWS = 1;
37
37
  * and the running-stage border lerps between `borderDim` and
38
38
  * `warning` without a key press. The host-supplied `requestRender`
39
39
  * gate prevents work while the overlay is hidden or unfocused.
40
+ * GraphView also skips ticks when nothing is animating — no running or
41
+ * awaiting_input stage and no prompt caret — so large idle graphs do not
42
+ * burn ~10 full paints/sec (#2100).
40
43
  */
41
44
  export const ANIMATION_TICK_MS = 100;
42
45
 
@@ -18,16 +18,14 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
18
18
  if (!run || this.cachedLayout.length === 0) {
19
19
  this.lastGraphViewport = null;
20
20
  this.graphNodeHitRects = [];
21
+ this.lastGraphTotalRows = 1;
21
22
  const dim = hexToAnsi(this.graphTheme.dim);
22
23
  return [this._centerCanvasContent(`${dim}waiting for stage events…${RESET}`, width)];
23
24
  }
24
25
 
25
26
  const graphInner = Math.max(1, width - 4);
26
- const canvasWidth = this.cachedLayout.reduce((max, node) => Math.max(max, node.x + NODE_W), 0);
27
- // Centre the whole graph horizontally when it fits; otherwise keep a
28
- // small gutter and reveal focused nodes by horizontally scrolling the
29
- // graph canvas. Do not switch to a compact list: the orchestrator pane
30
- // should always preserve the node-card graph view.
27
+ const { canvasWidth, totalRows, bands, edges } = this.cachedRenderGeometry;
28
+ this.lastGraphTotalRows = totalRows;
31
29
  const leftMargin = Math.max(2, canvasWidth <= graphInner ? Math.floor((graphInner - canvasWidth) / 2) : 2);
32
30
  const viewportWidth = Math.max(1, width - leftMargin);
33
31
  const fullCanvasWidth = Math.max(canvasWidth, viewportWidth);
@@ -37,73 +35,83 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
37
35
  this._scrollFocusedColumnIntoView(viewportWidth, fullCanvasWidth);
38
36
  }
39
37
 
40
- // Pulse phase ∈ [0, 1) derived from wall-clock time so cards lerp
41
- // their border colour on the same beat regardless of how often
42
- // render() fires. The animation tick (`ANIMATION_TICK_MS`) only
43
- // controls render cadence — it does not advance the phase.
44
- const pulsePhase = (Date.now() % PULSE_PERIOD_MS) / PULSE_PERIOD_MS;
38
+ const bodyRows = this._overlayBodyRows(this._overlayPanelLineCount());
39
+ if (totalRows > bodyRows) {
40
+ this._clampGraphScroll(totalRows, bodyRows);
41
+ if (this.pendingEnsureFocusedVisible) this._scrollFocusedIntoView(width, bodyRows, totalRows);
42
+ this._clampGraphScroll(totalRows, bodyRows);
43
+ } else {
44
+ this.graphScrollOffset = 0;
45
+ }
45
46
 
46
- // 1. Plot parent → child edges into a sparse char canvas so they
47
- // survive crisp through column gaps. Node-card bodies overwrite
48
- // edge cells per-row when we compose the final output below.
49
- const edgeCanvas = new GraphCanvas();
47
+ const viewportTop = this.graphScrollOffset;
48
+ const viewportBottom = Math.min(totalRows, viewportTop + bodyRows);
49
+ const viewportLeft = this.graphScrollColOffset;
50
+ const viewportRight = viewportLeft + viewportWidth;
51
+ const pulsePhase = (Date.now() % PULSE_PERIOD_MS) / PULSE_PERIOD_MS;
52
+ const edgeCanvas = new GraphCanvas({
53
+ top: viewportTop,
54
+ bottom: viewportBottom,
55
+ left: viewportLeft,
56
+ right: viewportRight,
57
+ });
50
58
  const edgeColor = this.graphTheme.borderDim;
51
- const nodeByStageId = new Map(this.cachedLayout.map((n) => [n.stage.id, n]));
52
- for (const node of this.cachedLayout) {
53
- for (const parentId of node.stage.parentIds) {
54
- const parent = nodeByStageId.get(parentId);
55
- if (!parent) continue;
56
- this._plotEdge(edgeCanvas, parent.x, parent.y, node.x, node.y, edgeColor);
59
+ for (const edge of edges) {
60
+ if (
61
+ edge.bottom <= viewportTop ||
62
+ edge.top >= viewportBottom ||
63
+ edge.right <= viewportLeft ||
64
+ edge.left >= viewportRight
65
+ ) {
66
+ continue;
57
67
  }
68
+ this._plotEdge(edgeCanvas, edge.parentX, edge.parentY, edge.childX, edge.childY, edgeColor);
58
69
  }
59
70
  const edgeLines = edgeCanvas.toLines();
60
-
61
- // 2. Render each node card and record its row span / start column.
62
- const placements: Map<number, Placement[]> = new Map();
63
- for (let ni = 0; ni < this.cachedLayout.length; ni++) {
64
- const node = this.cachedLayout[ni]!;
65
- const focused = ni === this.focusedIndex;
66
- const cardLines = renderNodeCard(node.stage, {
67
- width: NODE_W,
68
- height: NODE_H,
69
- focused,
70
- pulsePhase,
71
- theme: this.graphTheme,
72
- stages: this.cachedLayout.map((layoutNode) => layoutNode.stage),
73
- queuedMessageCount: this._stageQueuedMessageCount(node.stage),
74
- });
75
- for (let li = 0; li < cardLines.length; li++) {
76
- const rowIdx = node.y + li;
77
- let bucket = placements.get(rowIdx);
78
- if (!bucket) {
79
- bucket = [];
80
- placements.set(rowIdx, bucket);
71
+ const placements = new Map<number, Placement[]>();
72
+ for (let bandIndex = this._firstVisibleLayoutBand(viewportTop); bandIndex < bands.length; bandIndex++) {
73
+ const band = bands[bandIndex]!;
74
+ if (band.top >= viewportBottom) break;
75
+ for (const ni of band.nodeIndices) {
76
+ const node = this.cachedLayout[ni]!;
77
+ if (node.x + NODE_W <= viewportLeft || node.x >= viewportRight) continue;
78
+ const cardLines = renderNodeCard(node.stage, {
79
+ width: NODE_W,
80
+ height: NODE_H,
81
+ focused: ni === this.focusedIndex,
82
+ pulsePhase,
83
+ theme: this.graphTheme,
84
+ stages: this.cachedDisplayStages,
85
+ queuedMessageCount: this._stageQueuedMessageCount(node.stage),
86
+ });
87
+ const visibleLeft = Math.max(node.x, viewportLeft);
88
+ const visibleRight = Math.min(node.x + NODE_W, viewportRight);
89
+ for (let li = 0; li < cardLines.length; li++) {
90
+ const globalRow = node.y + li;
91
+ if (globalRow < viewportTop || globalRow >= viewportBottom) continue;
92
+ const localRow = globalRow - viewportTop;
93
+ let bucket = placements.get(localRow);
94
+ if (!bucket) {
95
+ bucket = [];
96
+ placements.set(localRow, bucket);
97
+ }
98
+ bucket.push({
99
+ startCol: visibleLeft - viewportLeft,
100
+ width: visibleRight - visibleLeft,
101
+ line: this._sliceColumns(cardLines[li]!, visibleLeft - node.x, visibleRight - node.x),
102
+ });
81
103
  }
82
- bucket.push({ startCol: node.x, width: NODE_W, line: cardLines[li]! });
83
104
  }
84
105
  }
85
106
 
86
- // 3. Compose each output row by interleaving edge segments with the
87
- // card placements at their assigned columns.
88
- const totalRows = Math.max(edgeLines.length, ...this.cachedLayout.map((n) => n.y + NODE_H));
89
- const composed: string[] = [];
90
- for (let rowIdx = 0; rowIdx < totalRows; rowIdx++) {
91
- const edgeRowChars = this._edgeRowToCells(edgeLines[rowIdx] ?? "");
92
- const cards = placements.get(rowIdx) ?? [];
93
- composed.push(this._composeRow(edgeRowChars, cards, edgeColor));
94
- }
95
-
96
- // Pad the full graph canvas, then crop a horizontal viewport when the
97
- // fan-out is wider than the terminal. Cards/edges only paint cells they
98
- // occupy; everywhere else needs explicit bg so default terminal colours
99
- // never leak through.
100
107
  const bg = hexBg(this.graphTheme.bg);
101
108
  const leftPad = `${bg}${" ".repeat(leftMargin)}${RESET}`;
102
- return composed.map((line) => {
103
- const full = this._padCanvas(line, fullCanvasWidth);
104
- const sliced = this._sliceColumns(full, this.graphScrollColOffset, this.graphScrollColOffset + viewportWidth);
105
- return `${leftPad}${this._padCanvas(sliced, viewportWidth)}`;
106
- });
109
+ const composed: string[] = [];
110
+ for (let localRow = 0; localRow < viewportBottom - viewportTop; localRow++) {
111
+ const line = this._composeRow(edgeLines[localRow] ?? "", placements.get(localRow) ?? [], edgeColor);
112
+ composed.push(`${leftPad}${this._padCanvas(line, viewportWidth)}`);
113
+ }
114
+ return composed;
107
115
  }
108
116
 
109
117
  protected _recordGraphNodeHitRects(graphStartRow: number, visibleRowCount: number): void {
@@ -118,52 +126,41 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
118
126
  const viewportLeft = viewport.leftMargin;
119
127
  const viewportRight = viewport.leftMargin + viewport.viewportWidth;
120
128
  const rects: typeof this.graphNodeHitRects = [];
121
-
122
- for (let index = 0; index < this.cachedLayout.length; index++) {
123
- const node = this.cachedLayout[index]!;
124
- const top = graphStartRow + node.y - this.graphScrollOffset;
125
- const bottom = top + NODE_H;
126
- const left = viewport.leftMargin + node.x - this.graphScrollColOffset;
127
- const right = left + NODE_W;
128
- const clippedTop = Math.max(visibleTop, top);
129
- const clippedBottom = Math.min(visibleBottom, bottom);
130
- const clippedLeft = Math.max(viewportLeft, left);
131
- const clippedRight = Math.min(viewportRight, right);
132
- if (clippedTop >= clippedBottom || clippedLeft >= clippedRight) continue;
133
- rects.push({
134
- index,
135
- top: clippedTop,
136
- bottom: clippedBottom,
137
- left: clippedLeft,
138
- right: clippedRight,
139
- });
129
+ const viewportTop = this.graphScrollOffset;
130
+ const viewportBottom = viewportTop + visibleRowCount;
131
+ const { bands } = this.cachedRenderGeometry;
132
+ for (let bandIndex = this._firstVisibleLayoutBand(viewportTop); bandIndex < bands.length; bandIndex++) {
133
+ const band = bands[bandIndex]!;
134
+ if (band.top >= viewportBottom) break;
135
+ for (const index of band.nodeIndices) {
136
+ const node = this.cachedLayout[index]!;
137
+ const top = graphStartRow + node.y - viewportTop;
138
+ const bottom = top + NODE_H;
139
+ const left = viewport.leftMargin + node.x - this.graphScrollColOffset;
140
+ const right = left + NODE_W;
141
+ const clippedTop = Math.max(visibleTop, top);
142
+ const clippedBottom = Math.min(visibleBottom, bottom);
143
+ const clippedLeft = Math.max(viewportLeft, left);
144
+ const clippedRight = Math.min(viewportRight, right);
145
+ if (clippedTop >= clippedBottom || clippedLeft >= clippedRight) continue;
146
+ rects.push({ index, top: clippedTop, bottom: clippedBottom, left: clippedLeft, right: clippedRight });
147
+ }
140
148
  }
141
149
  this.graphNodeHitRects = rects;
142
150
  }
143
151
 
144
152
  protected _visibleGraphLines(
145
153
  graphLines: string[],
146
- frameWidth: number,
154
+ _frameWidth: number,
147
155
  bodyRows: number,
148
156
  ): { lines: string[]; topPad: number } {
149
- if (graphLines.length <= bodyRows) {
150
- this.graphScrollOffset = 0;
151
- this.pendingEnsureFocusedVisible = false;
152
- return {
153
- lines: graphLines,
154
- topPad: Math.min(3, Math.max(0, Math.floor((bodyRows - graphLines.length) / 2))),
155
- };
156
- }
157
-
158
- this._clampGraphScroll(graphLines.length, bodyRows);
159
- if (this.pendingEnsureFocusedVisible) {
160
- this._scrollFocusedIntoView(frameWidth, bodyRows, graphLines.length);
161
- this.pendingEnsureFocusedVisible = false;
162
- }
163
- this._clampGraphScroll(graphLines.length, bodyRows);
157
+ this.pendingEnsureFocusedVisible = false;
164
158
  return {
165
- lines: graphLines.slice(this.graphScrollOffset, this.graphScrollOffset + bodyRows),
166
- topPad: 0,
159
+ lines: graphLines,
160
+ topPad:
161
+ this.lastGraphTotalRows <= bodyRows
162
+ ? Math.min(3, Math.max(0, Math.floor((bodyRows - this.lastGraphTotalRows) / 2)))
163
+ : 0,
167
164
  };
168
165
  }
169
166
  }
@@ -4,8 +4,6 @@ import { GraphViewGraphRenderer } from "./graph-view-graph-render.js";
4
4
  import { renderHeader, renderOutlinePill } from "./header.js";
5
5
  import { renderPromptCard } from "./prompt-card.js";
6
6
  import { renderSwitcher } from "./switcher.js";
7
- import { truncateToWidth } from "./text-helpers.js";
8
- import { renderToasts } from "./toast.js";
9
7
 
10
8
  /** Overlay/widget rendering orchestration for GraphView. */
11
9
  export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
@@ -68,7 +66,6 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
68
66
 
69
67
  this._renderSwitcherOverlay(lines, run, frameWidth, bodyTarget);
70
68
  this._renderPromptOverlay(lines, frameWidth, bodyTarget);
71
- this._renderToastOverlay(lines, frameWidth);
72
69
 
73
70
  // 5. Three-row statusline pinned to the bottom.
74
71
  lines.push(...this._renderStatusline(frameWidth));
@@ -146,37 +143,27 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
146
143
  // surface that interacts with the prompt. When the stage switcher is
147
144
  // open it owns the body/input, so hide the prompt card until it closes.
148
145
  if (!this.promptState || this.switcherOpen) return;
149
-
146
+ const run = this._getCurrentRun();
150
147
  const cardWidth = Math.min(72, Math.max(40, frameWidth - 6));
151
148
  const cardLines = renderPromptCard({
152
149
  state: this.promptState,
153
150
  theme: this.graphTheme,
154
151
  width: cardWidth,
155
152
  cursorOn: ((Date.now() / 530) | 0) % 2 === 0,
153
+ identity: run ? { runId: run.id, name: run.name } : undefined,
154
+ maxRows: bodyTarget,
156
155
  });
157
156
  const bodyStart = 3;
158
- const bodyEnd = 3 + bodyTarget;
157
+ const bodyEnd = bodyStart + bodyTarget;
159
158
  const slot = Math.max(bodyStart, bodyStart + Math.floor((bodyTarget - cardLines.length) / 2));
159
+ // The card renderer owns row budgeting, but keep this composition boundary
160
+ // defensive: a complete card fits wholly in the body or is not painted.
161
+ if (cardLines.length > bodyTarget || slot + cardLines.length > bodyEnd) return;
160
162
  const leftPad = Math.max(0, Math.floor((frameWidth - cardWidth) / 2));
161
163
  for (let i = 0; i < cardLines.length; i++) {
162
164
  const lineIdx = slot + i;
163
- if (lineIdx >= bodyEnd) break;
164
165
  const base = lines[lineIdx] ?? this._blankRow(frameWidth);
165
166
  lines[lineIdx] = this._overlayCard(base, cardLines[i]!, leftPad, frameWidth);
166
167
  }
167
168
  }
168
-
169
- private _renderToastOverlay(lines: string[], frameWidth: number): void {
170
- // Toast overlay — top-right of header band.
171
- const toastLines = renderToasts(this.toastManager.active(), {
172
- theme: this.graphTheme,
173
- });
174
- if (toastLines.length === 0) return;
175
-
176
- for (let i = 0; i < toastLines.length && i < lines.length; i++) {
177
- const existing = lines[i] ?? "";
178
- const merged = `${existing} ${toastLines[i]}`;
179
- lines[i] = truncateToWidth(merged, frameWidth, "", true);
180
- }
181
- }
182
169
  }