@robota-sdk/agent-ui-terminal 3.0.0-beta.82 → 3.0.0-beta.83

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 (67) hide show
  1. package/CHANGELOG.md +858 -0
  2. package/README.md +5 -1
  3. package/dist/node/index.d.ts +189 -54
  4. package/dist/node/index.d.ts.map +1 -1
  5. package/dist/node/index.js +21 -21
  6. package/dist/node/index.js.map +1 -1
  7. package/package.json +16 -21
  8. package/src/App.tsx +14 -3
  9. package/src/AppPresentation.tsx +1 -1
  10. package/src/PermissionPrompt.tsx +50 -17
  11. package/src/SessionPicker.tsx +27 -7
  12. package/src/SessionStatusBar.tsx +6 -2
  13. package/src/StatusBar.tsx +24 -0
  14. package/src/SupervisedSessionView.tsx +479 -157
  15. package/src/TuiInteractionChannel.ts +8 -53
  16. package/src/__tests__/SupervisedSessionView-render.test.tsx +28 -0
  17. package/src/__tests__/SupervisedSessionView.test.tsx +453 -82
  18. package/src/__tests__/TuiInteractionChannel.display-contract.test.ts +32 -22
  19. package/src/__tests__/TuiInteractionChannel.lifecycle.test.ts +10 -2
  20. package/src/__tests__/attached-app-session-switch.test.tsx +317 -0
  21. package/src/__tests__/checkpoint-store-per-channel.test.ts +81 -0
  22. package/src/__tests__/command-handoff-pty-e2e.test.ts +2 -2
  23. package/src/__tests__/default-tui-cli-adapter-host-settings.test.ts +3 -1
  24. package/src/__tests__/fixtures/command-handoff-driver.tsx +8 -5
  25. package/src/__tests__/grant-history-reaches-the-session.test.ts +31 -0
  26. package/src/__tests__/numbered-selection-chunk.test.tsx +41 -0
  27. package/src/__tests__/palette-consistency.test.ts +0 -1
  28. package/src/__tests__/permission-prompt-arming.test.tsx +153 -0
  29. package/src/__tests__/pty/screen-005-prompt-footers.ptytest.ts +2 -1
  30. package/src/__tests__/pty/screen-006-no-color.ptytest.ts +2 -0
  31. package/src/__tests__/pty/session-attach.ptytest.ts +144 -0
  32. package/src/__tests__/render-channel-options.test.ts +6 -6
  33. package/src/__tests__/sandbox-reaches-the-session.test.ts +37 -0
  34. package/src/__tests__/screen-reader-menus.test.tsx +1 -1
  35. package/src/__tests__/session-picker.test.tsx +93 -0
  36. package/src/__tests__/status-bar.test.tsx +8 -0
  37. package/src/__tests__/tui-channel-init-failure.test.ts +10 -2
  38. package/src/__tests__/wire-tui-channel.test.ts +1445 -0
  39. package/src/__tests__/wire-tui-client-commands.test.ts +243 -0
  40. package/src/app-view-model.ts +6 -2
  41. package/src/attached-session-connection.ts +14 -0
  42. package/src/hooks/__tests__/use-app-detach-key.test.tsx +72 -0
  43. package/src/hooks/__tests__/use-app-loop-escape.test.tsx +13 -0
  44. package/src/hooks/useAppController.ts +6 -2
  45. package/src/hooks/useAppInputBindings.ts +20 -2
  46. package/src/hooks/useAppInteractionState.ts +3 -0
  47. package/src/hooks/useAppOverlays.ts +20 -4
  48. package/src/hooks/useNumberedSelection.ts +19 -2
  49. package/src/hooks/useTuiChannel.ts +13 -1
  50. package/src/index.ts +17 -3
  51. package/src/render.tsx +218 -27
  52. package/src/short-session-id.ts +9 -0
  53. package/src/slash-command-input.ts +11 -0
  54. package/src/tui-app-channel-port.ts +29 -1
  55. package/src/tui-channel-options.ts +11 -2
  56. package/src/tui-session-event-projector.ts +0 -2
  57. package/src/tui-session-events.ts +6 -0
  58. package/src/tui-session-options.ts +9 -0
  59. package/src/tui-state-manager.ts +17 -3
  60. package/src/waiting-loop-stop-notice.ts +23 -0
  61. package/src/wire-history-sync.ts +159 -0
  62. package/src/wire-tui-channel.ts +962 -0
  63. package/src/wire-tui-client-commands.ts +142 -0
  64. package/src/wire-tui-projection.ts +136 -0
  65. package/dist/node/index.cjs +0 -32
  66. package/dist/node/index.d.cts +0 -1066
  67. package/dist/node/index.d.cts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["InkText","Text","formatStatusLabel","Text","Text","Text","Text","isPrintable","Text","Text","Text","MAX_VISIBLE","Text","isPrintable","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","readFromAdapter","Text","#schemaUrl","#onDiagnostic","#listeners","#snapshot","#disposed","#startPromise","#start","#watcher","#load","#scheduleReload","#reloadTimer","#publishInvalid","#publish"],"sources":["../../src/sanitize-terminal-text.ts","../../src/SafeText.tsx","../../src/theme/built-in-themes.ts","../../src/screen-reader-context.tsx","../../src/terminal-capabilities.ts","../../src/theme/theme-context.tsx","../../src/theme/theme-contracts.ts","../../src/theme/theme-styles.ts","../../src/theme/color-vision.ts","../../src/json-value.ts","../../src/theme/theme-document.ts","../../src/app-banner.tsx","../../src/attention/time-units.ts","../../src/attention/countdown.ts","../../src/status-glyph.ts","../../src/execution-workspace-view-model.ts","../../src/background-task-row-format.ts","../../src/hooks/useCountdownTick.ts","../../src/BackgroundTaskPanel.tsx","../../src/ContextWarningBanner.tsx","../../src/ExecutionWorkspaceDetailPane.tsx","../../src/flows/selection-flow.ts","../../src/execution-workspace-switcher-selection.ts","../../src/hooks/useNumberedSelection.ts","../../src/key-hint-footer.tsx","../../src/keybindings/keybinding-catalogue.ts","../../src/keybindings/keybinding-syntax.ts","../../src/keybindings/keybinding-registry.ts","../../src/keybindings/keybindings-context.tsx","../../src/numbered-list.tsx","../../src/ExecutionWorkspaceSwitcher.tsx","../../src/flows/cjk-cursor-motion.ts","../../src/flows/cjk-text-deletion.ts","../../src/flows/cjk-text-input-flow.ts","../../src/flows/defer-submit.ts","../../src/flows/real-cursor-flow.ts","../../src/hooks/useRealCursorPosition.ts","../../src/screen-reader-pacing-context.tsx","../../src/terminal-capabilities-context.tsx","../../src/CjkTextInput.tsx","../../src/external-prompt-notice.tsx","../../src/flows/input-area-flow.ts","../../src/history-search/history-search-flow.ts","../../src/HistorySearchOverlay.tsx","../../src/hooks/useAutocomplete.ts","../../src/history-search/useHistorySearch.ts","../../src/hooks/useInputAreaHistorySearch.ts","../../src/hooks/useInputAreaKeys.ts","../../src/utils/input-top-border.ts","../../src/input-area-rules.tsx","../../src/SlashAutocomplete.tsx","../../src/utils/paste-labels.ts","../../src/WaveText.tsx","../../src/InputArea.tsx","../../src/command-output-summary.ts","../../src/render-markdown.ts","../../src/hooks/useRenderMarkdown.ts","../../src/humanize-tool-name.ts","../../src/product-display-name-context.tsx","../../src/screen-reader-labels.ts","../../src/RoleLabel.tsx","../../src/tool-summary-status.ts","../../src/ToolCommandOutput.tsx","../../src/utils/tool-diff-summary.ts","../../src/ToolDiffBlock.tsx","../../src/UsageSummaryEntry.tsx","../../src/MessageList.tsx","../../src/ListPicker.tsx","../../src/MultiSelectList.tsx","../../src/flows/text-prompt-flow.ts","../../src/TextPrompt.tsx","../../src/PendingActionPrompt.tsx","../../src/flows/permission-prompt-flow.ts","../../src/PermissionPrompt.tsx","../../src/flows/confirm-prompt-flow.ts","../../src/ConfirmPrompt.tsx","../../src/hooks/usePluginScreenData.ts","../../src/MenuSelect.tsx","../../src/plugin-tui-handlers.ts","../../src/PluginTUI.tsx","../../src/SessionEventNotices.tsx","../../src/SessionPicker.tsx","../../src/status-activity.ts","../../src/StatusBar.tsx","../../src/tui-cli-adapter-context.tsx","../../src/SessionStatusBar.tsx","../../src/StreamingIndicator.tsx","../../src/ThemePicker.tsx","../../src/TransportTUI.tsx","../../src/UpdateNotice.tsx","../../src/AppPresentation.tsx","../../src/app-static-items.ts","../../src/shutdown-signal.ts","../../src/attention-bell.ts","../../src/terminal-marks.ts","../../src/hooks/useScreenReaderTurnSignals.ts","../../src/hooks/useAppInputBindings.ts","../../src/hooks/usePluginCallbacks.ts","../../src/hooks/useAppOverlays.ts","../../src/hooks/useAppSubmissionState.ts","../../src/hooks/useAppInteractionState.ts","../../src/use-terminal-title.ts","../../src/hooks/useTerminalHandoffSuspension.ts","../../src/hooks/useAppLifecycleState.ts","../../src/hooks/useAppearanceSettings.ts","../../src/hooks/useExecutionDetailPage.ts","../../src/flows/fork-attach-flow.ts","../../src/hooks/useForkAttach.ts","../../src/hooks/useBackgroundPanel.ts","../../src/flows/background-focus-flow.ts","../../src/hooks/useAppWorkspaceState.ts","../../src/hooks/useSideEffects.ts","../../src/hooks/useStatusLineSettings.ts","../../src/hooks/useAppScreenState.ts","../../src/theme/theme-registry.ts","../../src/hooks/useAppThemeState.ts","../../src/hooks/useTuiChannel.ts","../../src/hooks/useAppController.ts","../../src/AppView.tsx","../../src/App.tsx","../../src/attention/attention-tracker.ts","../../src/attention/focus-input-filter.ts","../../src/screen-reader-announcement.ts","../../src/screen-reader-pacing.ts","../../src/screen-reader-stdout.ts","../../src/terminal-focus-reporting.ts","../../src/terminal-handoff-controller.ts","../../src/attention/interval-recap.ts","../../src/attention/attention-coordinator.ts","../../src/flows/session-init-poller.ts","../../src/hooks/command-result-handler.ts","../../src/tui-channel-lifecycle-coordinator.ts","../../src/tui-interaction-queues.ts","../../src/attributed-user-echo.ts","../../src/tui-session-binding.ts","../../src/tui-session-event-projector.ts","../../src/tui-session-options.ts","../../src/tui-session-events.ts","../../src/tui-session-notice-store.ts","../../src/tui-state-manager.ts","../../src/TuiInteractionChannel.ts","../../src/render.tsx","../../src/SupervisedSessionView.tsx","../../src/create-default-tui-cli-adapter.ts","../../src/keybindings/node-keybindings-source.ts"],"sourcesContent":["/**\n * SEC-019 (issue #2022) - the one place untrusted text is made safe to put on a terminal.\n *\n * Model output, tool output, file contents and plugin text all reach Ink `<Text>`, and from there a\n * terminal. What that terminal actually receives was MEASURED against a real stream rather than\n * assumed, because the first version of this module assumed Ink passed everything through and that\n * is not true:\n *\n * stripped by Ink OSC 52 (clipboard), OSC 0 (title), CSI erase/cursor/alt-screen, DCS, APC,\n * 8-bit CSI\n * REACHES the tty SGR colour, OSC 8 hyperlink, a bare carriage return\n *\n * So through `<Text>` the live attack is a link whose visible text and target differ, and a `\\r`\n * that overwrites the line the transcript just printed. `useTerminalTitle` writes to stdout directly\n * and is subject to none of Ink's filtering, so everything reaches the terminal there.\n *\n * ## Why sanitize the whole class when Ink removes most of it\n *\n * Because Ink's removal is INCIDENTAL. It falls out of slicing text for layout; it is in no\n * contract, no test of Ink's asserts it, and a dependency upgrade can return the entire class\n * without a line changing here. A boundary that rests on another project's implementation detail is\n * a boundary in name. The two sequences that reach the terminal today prove the exposure is real;\n * the ones Ink happens to remove are the reason this must not be scoped to them.\n *\n * ## Allowlist, not denylist\n *\n * Everything is passed through EXCEPT the control range, and inside that range exactly two\n * characters survive - tab and newline. A denylist of \"known dangerous sequences\" is\n * wrong for the same reason it is wrong for shell metacharacters: the set belongs to the terminal\n * emulator, and a list written today is incomplete the next time one adds an escape.\n *\n * ## Ordering: sanitize BEFORE rendering, never after\n *\n * The renderer ADDS ANSI - colours, bold, code-block framing. Sanitizing its output would strip the\n * repository's own presentation along with the attacker's. Sanitizing its input removes the escapes\n * that arrived from outside and leaves the ones generated after. That ordering is the whole design,\n * and it is why this is a function on the untrusted string rather than a filter on the rendered one.\n *\n * ## Streaming\n *\n * A sequence split across two deltas - `\\x1b]5` then `2;c;...\\x07` - is invisible to a sanitizer that\n * sees each chunk alone. {@link createStreamingTerminalSanitizer} holds the incomplete tail back until\n * the chunk that completes it arrives. The TUI's own stream path accumulates before rendering\n * (`tui-state-manager` appends each delta to a buffer and renders the buffer), so the stateless\n * function is sufficient THERE - but a caller that sanitizes per chunk needs the stateful one, and\n * having only the stateless function available is how that caller would get it wrong.\n */\n\n/**\n * Tab and newline are content. Carriage return is NOT, and that is a correction.\n *\n * A bare `\\r` returns the cursor to column zero, so `safe text\\rEVIL` prints `EVIL` over what the\n * transcript said — the same \"what you read is not what happened\" attack as a deceptive hyperlink,\n * with no escape sequence involved. It was kept here as content in the first version of this module\n * on the assumption that CR is a line ending; measurement against a real terminal stream showed a\n * bare CR reaching it intact.\n *\n * `\\r\\n` IS a line ending, and is normalized to `\\n` before the control filter runs, so a document\n * written on Windows keeps its line structure and loses only the overwrite primitive.\n */\nconst KEPT_C0 = new Set(['\\t', '\\n']);\n\n/** `\\r\\n` is one line break written in two bytes; a `\\r` that is not part of one is a cursor move. */\nconst CRLF = /\\r\\n/g;\n\n/**\n * One escape sequence, or a lone control character.\n *\n * The alternation is ordered longest-context-first so a two-character C1 introducer (`ESC ]`) is\n * consumed as the start of its sequence rather than as a bare `ESC`, and every 8-bit C1 form is\n * matched with its BODY before the lone-C1 alternative can take the introducer on its own.\n *\n * Each introducer appears in both spellings, because a terminal accepts both: `ESC ]` and the single\n * byte `\\x9d` are the same OSC. An alternation that removed only the 7-bit spelling would leave the\n * 8-bit one's parameters standing as visible text.\n *\n * - `\\x1b][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?` OSC, terminated by BEL or ST - or unterminated at the end\n * of the input, which is the streaming case and must still be removed rather than left visible.\n * - `\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x9c|\\x1b\\\\)?` the same, 8-bit introducer, with `\\x9c` as a\n * third accepted terminator because 8-bit ST is one byte.\n * - `\\x1b[P^_X][\\s\\S]*?(?:\\x1b\\\\|\\x07)` DCS, PM, APC, SOS, terminated by ST or BEL. The\n * terminator is MANDATORY here, and that is the difference from the OSC alternative above. OSC's\n * body is `[^\\x07\\x1b]*`, which is greedy and cannot cross its own terminator, so an optional\n * terminator costs nothing. A DCS body is `[\\s\\S]*?`, which is lazy - and a lazy quantifier\n * followed by an OPTIONAL group prefers the empty match at every position, so the alternative\n * would consume the two-character introducer and nothing else, leaving `q#0;2;0;0;0` from a Sixel\n * sequence standing as visible text. Found in review of PR #2212, and the reason every case in\n * the test table now asserts the exact output rather than \"no control byte survives\" - the weaker\n * assertion holds for a sanitizer that strips only introducers.\n * - `\\x1b[P^_X][\\s\\S]*$` the same, unterminated at the end of the input. Written as its own\n * alternative rather than an optional terminator, for the reason directly above.\n * - `[\\x90\\x98\\x9e\\x9f][\\s\\S]*?(?:\\x9c|\\x1b\\\\|\\x07)` and `[\\x90\\x98\\x9e\\x9f][\\s\\S]*$`\n * DCS/SOS/PM/APC again, 8-bit introducers, terminated and unterminated.\n * - `\\x1b\\[[0-?]*[ -/]*[@-~]` CSI. The parameter class is the ECMA-48 range 0x30–0x3F written as\n * a range, not the digits-plus-`;`-plus-`?` an author reaches for from memory. That shorter class\n * omits `:`, `<`, `=` and `>`, and the omission is not academic: `ESC [ 38:2:255:0:0 m` is the\n * colon form of a truecolor SGR and `ESC [ < 0;10;20 M` is an SGR mouse report. Neither matched\n * any alternative, so the lone-control fallback took the ESC and left `38:2:255:0:0m` as visible\n * text. Found in review of PR #2212.\n * - `\\x9b[0-?]*[ -/]*[@-~]` the same, 8-bit introducer.\n * - `\\x1b[@-Z\\\\-_]` any other two-character escape.\n * - `[\\x00-\\x1f\\x7f]` a lone control character, including a bare ESC or BEL.\n * - `[\\x80-\\x9f]` a lone C1 control that started no sequence any alternative above matched.\n */\nconst CONTROL_SEQUENCE =\n // eslint-disable-next-line no-control-regex\n /\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?|\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x9c|\\x1b\\\\)?|\\x1b[P^_X][\\s\\S]*?(?:\\x1b\\\\|\\x07)|\\x1b[P^_X][\\s\\S]*$|[\\x90\\x98\\x9e\\x9f][\\s\\S]*?(?:\\x9c|\\x1b\\\\|\\x07)|[\\x90\\x98\\x9e\\x9f][\\s\\S]*$|\\x1b\\[[0-?]*[ -/]*[@-~]|\\x9b[0-?]*[ -/]*[@-~]|\\x1b[@-Z\\\\-_]|[\\x00-\\x1f\\x7f]|[\\x80-\\x9f]/g;\n\n/**\n * The longest prefix of `text` that could still become a control sequence if more text arrived.\n *\n * \"Could still become\" is the whole contract, and the DCS/PM/APC/SOS bodies are where it is easy to\n * lose. Written as a plain `[\\s\\S]*`, they match a sequence that is ALREADY terminated inside the\n * chunk — `'hello' ESC P … ESC \\ 'world'` anchors from the introducer to the end, so `world` is held\n * back and does not appear until the next `push` or the `flush`. Nothing dangerous escapes, but\n * ordinary text stops arriving, which for a streaming renderer is a stall.\n *\n * So each body excludes its own terminators through a negative lookahead: a sequence that has already\n * ended is not an incomplete tail. The OSC and CSI branches need no such guard — `[^\\x07\\x1b]*`\n * cannot cross BEL or ESC, and a CSI without its final byte is incomplete by construction. Found in\n * review of PR #2212, one round after the same asymmetry was fixed in {@link CONTROL_SEQUENCE}: the\n * two patterns describe the same grammar and have to be changed together.\n */\nconst INCOMPLETE_TAIL =\n // eslint-disable-next-line no-control-regex\n /(?:\\x1b(?:\\][^\\x07\\x1b]*|[P^_X](?:(?!\\x1b\\\\|\\x07)[\\s\\S])*|\\[[0-?]*[ -/]*|)?|\\x9d[^\\x07\\x1b\\x9c]*|[\\x90\\x98\\x9e\\x9f](?:(?!\\x9c|\\x1b\\\\|\\x07)[\\s\\S])*|\\x9b[0-?]*[ -/]*)$/;\n\n/**\n * `text` with every terminal control sequence removed, and tab/newline/carriage return preserved.\n *\n * Ordinary Unicode - including characters far outside ASCII - is untouched: the goal is to remove\n * what the TERMINAL acts on, not to restrict what a document may say.\n */\nexport function sanitizeTerminalText(text: string): string {\n return text\n .replace(CRLF, '\\n')\n .replace(CONTROL_SEQUENCE, (match) => (KEPT_C0.has(match) ? match : ''));\n}\n\n/** A sanitizer that carries an incomplete escape across chunk boundaries. */\nexport interface IStreamingTerminalSanitizer {\n /** Sanitize one chunk, holding back any tail that could still become an escape. */\n push(chunk: string): string;\n /** Emit whatever is held back, sanitized. Call when the stream ends. */\n flush(): string;\n}\n\n/**\n * A stateful sanitizer for callers that sanitize each chunk as it arrives.\n *\n * The held-back tail is bounded by the incomplete-prefix pattern rather than by a byte count: a\n * partial OSC can be arbitrarily long, and truncating the buffer would emit the middle of a sequence\n * as visible text while dropping the part that made it dangerous.\n */\nexport function createStreamingTerminalSanitizer(): IStreamingTerminalSanitizer {\n let pending = '';\n return {\n push(chunk: string): string {\n const combined = pending + chunk;\n const tail = INCOMPLETE_TAIL.exec(combined);\n const holdFrom = tail && tail[0].length > 0 ? tail.index : combined.length;\n pending = combined.slice(holdFrom);\n return sanitizeTerminalText(combined.slice(0, holdFrom));\n },\n flush(): string {\n const remaining = pending;\n pending = '';\n return sanitizeTerminalText(remaining);\n },\n };\n}\n","/**\n * `SafeText` — the ONE way a string reaches the terminal from this package (#2222).\n *\n * SEC-019 fixed every render site that was found to put untrusted text — model output, tool stdout,\n * file contents, plugin-supplied names — on the terminal through Ink's `<Text>`. Six review rounds\n * found three sites nobody had covered, because \"sanitized\" was a property of individual lines of\n * code, and a site added later by someone who never read SEC-019 is covered by nothing.\n *\n * This component makes the boundary structural: every string child is passed through\n * `sanitizeTerminalText` before Ink sees it, and the `tui-safe-text-boundary` scan refuses any other\n * module in this package that imports `Text` from `ink` — plain, aliased, or through a namespace.\n * A render site therefore CANNOT put a string on the terminal without passing through here. For the\n * many constant labels this is a no-op; that is the point — the boundary is not \"the sites judged\n * to carry untrusted text\", because that judgement is what already failed twice.\n *\n * This module is the only permitted importer of Ink's `Text`.\n */\n\nimport { Text as InkText } from 'ink';\nimport React from 'react';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { ReactNode } from 'react';\n\ntype TInkTextProps = React.ComponentProps<typeof InkText>;\n\nfunction sanitizeChildren(children: ReactNode): ReactNode {\n if (typeof children === 'string') return sanitizeTerminalText(children);\n if (typeof children === 'number' || typeof children === 'boolean') return children;\n if (children === null || children === undefined) return children;\n if (Array.isArray(children)) return children.map((child) => sanitizeChildren(child));\n // A nested element (another <Text>, a <Box>) sanitizes its own string children on its own render.\n return children;\n}\n\n/** Ink's `Text`, with every string child sanitized for the terminal. Same props, same output shape. */\nexport function SafeText(props: TInkTextProps): React.JSX.Element {\n const { children, ...rest } = props;\n return <InkText {...rest}>{sanitizeChildren(children)}</InkText>;\n}\n\n/**\n * Ink's `Text` for a string THIS PACKAGE'S OWN RENDERER produced from already-sanitized input.\n *\n * `renderMarkdown` sanitizes the untrusted markdown BEFORE `marked-terminal` styles it, and the\n * SGR colour in its output (the theme's diff pairs, heading emphasis) is the renderer's\n * own, not the model's. Routing that output through `SafeText` would strip the styling the renderer\n * just applied — the SCREEN-006 diff colours vanished exactly that way. The boundary still holds:\n * the only string that reaches this component is one the renderer built from sanitized text, and\n * the `tui-safe-text-boundary` scan keeps every other module off Ink's `Text`.\n *\n * PERMITTED here: a string this package's own renderer built from already-sanitized input — today\n * that is exactly `renderMarkdown(...)`, which sanitizes before it styles. FORBIDDEN here: any\n * string that has not passed a sanitizer — raw model output, tool stdout, file contents, plugin\n * names, a template that interpolates one of those — which goes through `SafeText`/`Text` instead.\n * The mirror rule holds too: `renderMarkdown(...)` must NOT be a child of `SafeText`/`Text`, whose\n * sanitizer would strip the renderer's own SGR. The `tui-safe-text-boundary` scan enforces both\n * directions, so neither is left to a reviewer noticing it.\n */\nexport function RenderedText(props: TInkTextProps): React.JSX.Element {\n return <InkText {...props} />;\n}\n\n/** Call sites keep writing `<Text>`; the name resolves to the boundary rather than to Ink. */\nexport { SafeText as Text };\n","/**\n * SCREEN-2002 — the four built-in themes, as data.\n *\n * `dark` is today's colour, gathered from the four places that used to decide it: `PALETTE`/`MOTION`\n * (Ink), `ANSI` (the markdown diff rows), `marked-terminal`'s own chalk defaults and `cli-highlight`'s\n * `DEFAULT_THEME`. Making those defaults explicit is what lets a theme change them at all; it is also\n * why a user who sets nothing sees the same colours as before.\n *\n * Over the 300-line file ceiling, deliberately. The file is ONE concern — four colour tables of the\n * same shape — and the ceiling exists to break up files that do several things. Splitting per theme\n * would put the dark and light values that must correspond in different files, which is the drift\n * the token model was built to stop.\n *\n * The daltonized pair is specified in hex rather than colour names on purpose: a name is whatever the\n * terminal says it is, so a colour-vision-deficiency simulation cannot compute it, and the guard\n * REFUSES what it cannot simulate. Their status and diff pairs avoid red/green entirely — blue for\n * \"good\", orange for \"bad\" — the distinction protanopes and deuteranopes retain.\n *\n * This module is the only file in the package allowed to hold a colour VALUE; the consistency test\n * ratchets on both that import and on direct `chalk.<colour>(` calls elsewhere.\n */\nimport type { ITuiTheme } from './theme-contracts.js';\n\n/** Today's values, name for name. */\nexport const DARK_THEME: ITuiTheme = {\n id: 'dark',\n name: 'Dark',\n appearance: 'dark',\n source: 'built-in',\n colors: {\n text: {\n accent: 'cyan',\n emphasis: 'white',\n success: 'green',\n warning: 'yellow',\n error: 'red',\n session: 'magenta',\n muted: 'gray',\n onAccent: 'black',\n },\n border: {\n attention: 'yellow',\n focused: 'cyan',\n active: 'green',\n muted: 'gray',\n error: 'red',\n },\n status: {\n running: 'yellow',\n success: 'green',\n error: 'red',\n denied: 'yellowBright',\n waiting: 'yellow',\n cancelled: 'yellow',\n idle: 'gray',\n },\n },\n // marked-terminal's own defaults, written down so a theme can change them.\n markdown: {\n heading: 'green',\n firstHeading: 'magenta',\n code: 'yellow',\n codespan: 'yellow',\n link: 'blue',\n href: 'blue',\n blockquote: 'gray',\n del: 'gray',\n html: 'gray',\n // The former `ANSI.lightGreen` / `lightRed` / `darkGreenBackground` / `darkRedBackground`.\n diffAdded: 'ansi256(120)',\n diffRemoved: 'ansi256(210)',\n diffAddedBackground: 'ansi256(22)',\n diffRemovedBackground: 'ansi256(52)',\n diffHunk: 'cyan',\n },\n // cli-highlight's DEFAULT_THEME, written down for the same reason.\n syntax: {\n keyword: 'blue',\n built_in: 'cyan',\n type: 'cyan',\n literal: 'blue',\n number: 'green',\n regexp: 'red',\n string: 'red',\n class: 'blue',\n function: 'yellow',\n comment: 'green',\n doctag: 'green',\n meta: 'gray',\n tag: 'gray',\n name: 'blue',\n attr: 'cyan',\n addition: 'green',\n deletion: 'red',\n },\n motion: { wave: ['#555555', '#777777', '#999999', '#bbbbbb'] },\n};\n\n/** The same roles against a light background: darker foregrounds, light diff backgrounds. */\nexport const LIGHT_THEME: ITuiTheme = {\n id: 'light',\n name: 'Light',\n appearance: 'light',\n source: 'built-in',\n colors: {\n text: {\n accent: 'ansi256(24)',\n emphasis: 'black',\n success: 'ansi256(28)',\n warning: 'ansi256(130)',\n error: 'ansi256(124)',\n session: 'ansi256(90)',\n muted: 'ansi256(243)',\n onAccent: 'white',\n },\n border: {\n attention: 'ansi256(130)',\n focused: 'ansi256(24)',\n active: 'ansi256(28)',\n muted: 'ansi256(249)',\n error: 'ansi256(124)',\n },\n status: {\n running: 'ansi256(130)',\n success: 'ansi256(28)',\n error: 'ansi256(124)',\n denied: 'ansi256(166)',\n waiting: 'ansi256(130)',\n cancelled: 'ansi256(130)',\n idle: 'ansi256(243)',\n },\n },\n markdown: {\n heading: 'ansi256(28)',\n firstHeading: 'ansi256(90)',\n code: 'ansi256(94)',\n codespan: 'ansi256(94)',\n link: 'ansi256(26)',\n href: 'ansi256(26)',\n blockquote: 'ansi256(243)',\n del: 'ansi256(243)',\n html: 'ansi256(243)',\n diffAdded: 'ansi256(22)',\n diffRemoved: 'ansi256(52)',\n diffAddedBackground: 'ansi256(194)',\n diffRemovedBackground: 'ansi256(224)',\n diffHunk: 'ansi256(24)',\n },\n syntax: {\n keyword: 'ansi256(26)',\n built_in: 'ansi256(24)',\n type: 'ansi256(24)',\n literal: 'ansi256(26)',\n number: 'ansi256(28)',\n regexp: 'ansi256(124)',\n string: 'ansi256(124)',\n class: 'ansi256(26)',\n function: 'ansi256(94)',\n comment: 'ansi256(28)',\n doctag: 'ansi256(28)',\n meta: 'ansi256(243)',\n tag: 'ansi256(243)',\n name: 'ansi256(26)',\n attr: 'ansi256(24)',\n addition: 'ansi256(22)',\n deletion: 'ansi256(52)',\n },\n motion: { wave: ['#bbbbbb', '#999999', '#777777', '#555555'] },\n};\n\n/** Blue \"good\" / orange \"bad\": the pair protanopes and deuteranopes keep apart. */\nexport const DARK_DALTONIZED_THEME: ITuiTheme = {\n id: 'dark-daltonized',\n name: 'Dark (daltonized)',\n appearance: 'dark',\n source: 'built-in',\n colors: {\n text: {\n accent: '#56b4e9',\n emphasis: '#ffffff',\n success: '#56b4e9',\n warning: '#e69f00',\n error: '#d55e00',\n session: '#cc79a7',\n muted: '#999999',\n onAccent: '#000000',\n },\n border: {\n attention: '#e69f00',\n focused: '#56b4e9',\n active: '#56b4e9',\n muted: '#999999',\n error: '#d55e00',\n },\n status: {\n running: '#e69f00',\n success: '#56b4e9',\n error: '#d55e00',\n denied: '#f0e442',\n waiting: '#e69f00',\n cancelled: '#cc79a7',\n idle: '#999999',\n },\n },\n markdown: {\n heading: '#56b4e9',\n firstHeading: '#cc79a7',\n code: '#e69f00',\n codespan: '#e69f00',\n link: '#56b4e9',\n href: '#56b4e9',\n blockquote: '#999999',\n del: '#999999',\n html: '#999999',\n diffAdded: '#8ecdf5',\n diffRemoved: '#ffb066',\n diffAddedBackground: '#00335c',\n diffRemovedBackground: '#5c3000',\n diffHunk: '#56b4e9',\n },\n syntax: {\n keyword: '#56b4e9',\n built_in: '#8ecdf5',\n type: '#8ecdf5',\n literal: '#56b4e9',\n number: '#e69f00',\n regexp: '#d55e00',\n string: '#d55e00',\n class: '#56b4e9',\n function: '#f0e442',\n comment: '#999999',\n doctag: '#999999',\n meta: '#999999',\n tag: '#999999',\n name: '#56b4e9',\n attr: '#8ecdf5',\n addition: '#8ecdf5',\n deletion: '#ffb066',\n },\n motion: { wave: ['#3d3d3d', '#5c5c5c', '#7a7a7a', '#999999'] },\n};\n\nexport const LIGHT_DALTONIZED_THEME: ITuiTheme = {\n id: 'light-daltonized',\n name: 'Light (daltonized)',\n appearance: 'light',\n source: 'built-in',\n colors: {\n text: {\n accent: '#0072b2',\n emphasis: '#000000',\n success: '#0072b2',\n warning: '#b35900',\n error: '#8c3d00',\n session: '#a8477f',\n muted: '#5c5c5c',\n onAccent: '#ffffff',\n },\n border: {\n attention: '#b35900',\n focused: '#0072b2',\n active: '#0072b2',\n muted: '#a3a3a3',\n error: '#8c3d00',\n },\n status: {\n running: '#b35900',\n success: '#0072b2',\n error: '#8c3d00',\n denied: '#8a7500',\n waiting: '#b35900',\n cancelled: '#a8477f',\n idle: '#5c5c5c',\n },\n },\n markdown: {\n heading: '#0072b2',\n firstHeading: '#a8477f',\n code: '#8a5a00',\n codespan: '#8a5a00',\n link: '#0072b2',\n href: '#0072b2',\n blockquote: '#5c5c5c',\n del: '#5c5c5c',\n html: '#5c5c5c',\n diffAdded: '#004c77',\n diffRemoved: '#7a3300',\n diffAddedBackground: '#d6ecf9',\n diffRemovedBackground: '#fbe5cd',\n diffHunk: '#0072b2',\n },\n syntax: {\n keyword: '#0072b2',\n built_in: '#004c77',\n type: '#004c77',\n literal: '#0072b2',\n number: '#b35900',\n regexp: '#8c3d00',\n string: '#8c3d00',\n class: '#0072b2',\n function: '#8a7500',\n comment: '#5c5c5c',\n doctag: '#5c5c5c',\n meta: '#5c5c5c',\n tag: '#5c5c5c',\n name: '#0072b2',\n attr: '#004c77',\n addition: '#004c77',\n deletion: '#7a3300',\n },\n motion: { wave: ['#c7c7c7', '#a3a3a3', '#7a7a7a', '#5c5c5c'] },\n};\n\nexport const BUILT_IN_THEMES: readonly ITuiTheme[] = [\n DARK_THEME,\n LIGHT_THEME,\n DARK_DALTONIZED_THEME,\n LIGHT_DALTONIZED_THEME,\n];\n\n/** The theme a run uses when nothing is configured: today's colours. */\nexport const DEFAULT_THEME_ID = DARK_THEME.id;\n\n/**\n * The catalogue, as a function rather than the array itself. A caller outside `src/theme/` — the\n * theme registry a picker reads — needs to ENUMERATE the built-ins, which is a different act from\n * reaching into one of them for a colour; the floor refuses the second by refusing the DATA's names,\n * so the first gets an accessor of its own instead of an exemption.\n */\nexport function listBuiltInThemes(): readonly ITuiTheme[] {\n return BUILT_IN_THEMES;\n}\n\n/**\n * The one place \"no theme was resolved\" is answered. A caller outside `src/theme/` asks this instead\n * of importing the built-in data, so the anti-drift floor can keep the colour DATA inside this\n * module and still let a non-React entry point — `renderMarkdown`, which is called with a theme or\n * without one — render at all.\n */\nexport function resolveTheme(theme: ITuiTheme | undefined): ITuiTheme {\n return theme ?? DARK_THEME;\n}\n","/**\n * CLI-2004 — the one place a component asks \"is screen-reader mode on?\".\n *\n * The mode is a single resolved boolean threaded from the CLI into `renderApp`, handed to Ink as\n * `isScreenReaderEnabled` AND published here. Two sources of truth for one fact is the hazard this\n * design accepts (Ink's option and this context); both are set from the same resolved value in\n * `render.tsx`, and no component takes the mode as a prop from its parent — it reads it here — so a\n * missed prop cannot silently leave one component in the wrong mode.\n *\n * Default `false`: a component rendered outside the provider (every existing test) behaves exactly\n * as it does today.\n */\n\nimport React, { createContext, useContext } from 'react';\n\nconst ScreenReaderContext = createContext<boolean>(false);\n\n/** Publish the resolved mode to the tree. */\nexport function ScreenReaderProvider({\n enabled,\n children,\n}: {\n enabled: boolean;\n children: React.ReactNode;\n}): React.ReactElement {\n return <ScreenReaderContext.Provider value={enabled}>{children}</ScreenReaderContext.Provider>;\n}\n\n/** Read the resolved mode. `false` outside a provider. */\nexport function useScreenReader(): boolean {\n return useContext(ScreenReaderContext);\n}\n","/**\n * Single source of truth for \"should this process emit color + motion to the\n * terminal?\" — used by both the markdown renderer (color) and WaveText (motion),\n * so the two never disagree (SCREEN-008).\n *\n * Precedence:\n * - `NO_COLOR` present (any value, including empty) → off (per the NO_COLOR convention).\n * - `FORCE_COLOR=0` → off; `FORCE_COLOR=<non-empty, non-0>` → on.\n * - otherwise → on only when stdout is an interactive TTY.\n */\nexport function isInteractiveColorTerminal(): boolean {\n if ('NO_COLOR' in process.env) return false;\n const force = process.env.FORCE_COLOR;\n if (force === '0') return false;\n if (force !== undefined && force !== '') return true;\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * CLI-062 — may this process position the REAL terminal cursor for IME composition?\n *\n * Precedence:\n * - Host override true → on (explicit opt-in for Terminal.app); false → off.\n * - `TERM_PROGRAM=Apple_Terminal` → off by default (invariant I5): the historical Korean-IME\n * SIGSEGV is an Apple-side `attributedSubstringFromRange:` bug; its trigger geometry is gone\n * (measured y + post-<Static> tiny frame), but Terminal.app stays opt-in until the manual\n * matrix passes on real hardware. The CLI-052 startup warning remains in place.\n * - otherwise → on only when stdout is an interactive TTY (ink writes no cursor sequences at\n * all on a non-TTY, so this is defense in depth).\n */\nexport function supportsImeCursorPositioning(\n options: { override?: boolean | undefined } = {},\n): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n if (process.env.TERM_PROGRAM === 'Apple_Terminal') return false; // I5\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * CLI-2004 — may this process write OSC 133 shell-integration marks at agent-turn boundaries?\n *\n * The marks are otherwise harmless: every emulator surveyed discards an unknown OSC sequence, so\n * this gate carries DOCUMENTED negatives, not a capability probe. Robota's support table (the TUI\n * package's docs/SPEC.md) is the user-facing half of the same list.\n *\n * Precedence:\n * - Host override true → on (opt back in on a terminal listed below); false → off.\n * - `TERM_PROGRAM=WezTerm` → off: WezTerm treats OSC 133 as shell-owned and a non-shell emitter\n * corrupts its own prompt tracking, so the marks are withheld rather than emitted uselessly.\n * - otherwise → on only when stdout is an interactive TTY. Nothing consumes the marks in a pipe,\n * and they would land in a captured transcript as noise.\n */\nexport function supportsTurnMarks(options: { override?: boolean | undefined } = {}): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n if (process.env.TERM_PROGRAM === 'WezTerm') return false;\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * SCREEN-1992 — may this process ask the terminal to report focus changes (DECSET 1004)?\n *\n * The mode is harmless where unimplemented (the request is discarded and no `CSI I`/`CSI O` ever\n * arrives), so the gate is the interactive-TTY check plus an override the product shell injects from\n * its own environment — this package reads no product-named literal for it.\n *\n * Precedence:\n * - `override === true` → on; `override === false` → off (kill switch).\n * - otherwise → on only when BOTH stdin and stdout are interactive TTYs: the sequences arrive on\n * stdin, and nothing sends them in a pipe.\n */\nexport function supportsFocusReporting(options: { override?: boolean | undefined } = {}): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);\n}\n","/**\n * SCREEN-2002 — the resolved theme, published to the tree.\n *\n * The `screen-reader-context` shape: one resolved value in a React context, defaulting to the `dark`\n * built-in outside a provider so a component rendered in a test behaves exactly as it did before the\n * theme existed. Components read `usePalette()` where they used to import `PALETTE`; nothing takes the\n * theme as a prop, so a missed prop cannot leave one component on another theme.\n */\nimport React, { createContext, useContext } from 'react';\n\nimport { DARK_THEME, resolveTheme } from './built-in-themes.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { isInteractiveColorTerminal } from '../terminal-capabilities.js';\n\nimport type { IThemeColors, IThemeMotion, ITuiTheme } from './theme-contracts.js';\n\nconst ThemeContext = createContext<ITuiTheme>(DARK_THEME);\nconst ReducedMotionContext = createContext<boolean>(false);\nconst SyntaxHighlightingContext = createContext<boolean>(true);\n\nexport function ThemeProvider({\n theme,\n reducedMotion = false,\n syntaxHighlighting = true,\n children,\n}: {\n theme?: ITuiTheme;\n reducedMotion?: boolean;\n syntaxHighlighting?: boolean;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ThemeContext.Provider value={resolveTheme(theme)}>\n <ReducedMotionContext.Provider value={reducedMotion}>\n <SyntaxHighlightingContext.Provider value={syntaxHighlighting}>\n {children}\n </SyntaxHighlightingContext.Provider>\n </ReducedMotionContext.Provider>\n </ThemeContext.Provider>\n );\n}\n\n/** The whole resolved theme. Most components want {@link usePalette} instead. */\nexport function useTheme(): ITuiTheme {\n return useContext(ThemeContext);\n}\n\n/** The colour tokens — the direct replacement for the former `PALETTE` import. */\nexport function usePalette(): IThemeColors {\n return useTheme().colors;\n}\n\n/** The animation ramp — the direct replacement for the former `MOTION` import. */\nexport function useMotionTokens(): IThemeMotion {\n return useTheme().motion;\n}\n\n/**\n * May this process ANIMATE? One owner for a three-input rule, with exactly one consumer (`WaveText`,\n * the package's only animation). Deliberately NOT gating the background countdown (a once-a-second\n * number is content — freezing it would show a stale `in 59s`) or the streaming indicator's\n * screen-reader collapse (that drops content, which a sighted reduced-motion user still wants).\n */\nexport function useMotion(): boolean {\n const screenReader = useScreenReader();\n const reducedMotion = useContext(ReducedMotionContext);\n // No `useMemo`: the value is a boolean, so memoizing it buys nothing — and a dependency array\n // would have to omit `isInteractiveColorTerminal()`, which reads process state rather than React\n // state. Hiding a non-reactive read inside a memo is how a stale gate gets written later.\n return isInteractiveColorTerminal() && !screenReader && !reducedMotion;\n}\n\n/**\n * SCREEN-2002: whether a fenced code block is highlighted. It travels with the theme rather than as\n * a prop because every markdown render site already reads the theme from here, and a setting that\n * reaches only some of them is worse than one that reaches none — `/theme syntax off` would report\n * success while a block somewhere kept its colours.\n */\nexport function useSyntaxHighlighting(): boolean {\n return useContext(SyntaxHighlightingContext);\n}\n","/**\n * SCREEN-2002 — the TUI's one theme token model.\n *\n * Before this, five things decided a colour: `PALETTE` (Ink names), `ANSI` (raw SGR for the markdown\n * pipeline), `marked-terminal`'s own chalk defaults, `cli-highlight`'s `DEFAULT_THEME`, and a handful\n * of `chalk.<name>` calls. A theme that rewrote only the first two would leave a daltonized run with\n * red/green code blocks, because `cli-highlight` falls back PER KEY to its own theme. So the model\n * carries every group a renderer reads, and the style builder derives every encoding from it through\n * chalk — which is exactly what Ink's own `colorize` does, so no mapping layer is invented.\n *\n * Values use INK'S colour grammar verbatim (`<chalk name> | #rrggbb | ansi256(n) | rgb(r,g,b)`), so a\n * theme file, a built-in and a component all speak one language and the grammar doubles as the\n * injection floor: no raw escape sequence can enter through a theme.\n */\n\n/** A colour in Ink's grammar. Validated at the boundary, never parsed twice. */\nexport type TThemeColor = string;\n\n/** Foreground and border roles every Ink component reads. */\nexport interface IThemeColors {\n readonly text: {\n readonly accent: TThemeColor;\n readonly emphasis: TThemeColor;\n readonly success: TThemeColor;\n readonly warning: TThemeColor;\n readonly error: TThemeColor;\n readonly session: TThemeColor;\n readonly muted: TThemeColor;\n readonly onAccent: TThemeColor;\n };\n readonly border: {\n readonly attention: TThemeColor;\n readonly focused: TThemeColor;\n readonly active: TThemeColor;\n readonly muted: TThemeColor;\n readonly error: TThemeColor;\n };\n readonly status: {\n readonly running: TThemeColor;\n readonly success: TThemeColor;\n readonly error: TThemeColor;\n readonly denied: TThemeColor;\n readonly waiting: TThemeColor;\n readonly cancelled: TThemeColor;\n readonly idle: TThemeColor;\n };\n}\n\n/**\n * Every key `marked-terminal` colours by default, including `html` — a key left out would keep that\n * dependency's own gray under every theme.\n *\n * Deliberately absent, because they carry no colour today and giving them one would change rendering\n * rather than theme it: `strong` (`chalk.bold`), `em` (`chalk.italic`), `listitem` and `hr`\n * (`chalk.reset`), and a diff's `diff `/`index ` metadata rows, which this package writes itself as\n * dim over the inherited foreground.\n */\nexport interface IThemeMarkdown {\n readonly heading: TThemeColor;\n readonly firstHeading: TThemeColor;\n readonly code: TThemeColor;\n readonly codespan: TThemeColor;\n readonly link: TThemeColor;\n readonly href: TThemeColor;\n readonly blockquote: TThemeColor;\n readonly del: TThemeColor;\n readonly html: TThemeColor;\n readonly diffAdded: TThemeColor;\n readonly diffRemoved: TThemeColor;\n readonly diffAddedBackground: TThemeColor;\n readonly diffRemovedBackground: TThemeColor;\n readonly diffHunk: TThemeColor;\n}\n\n/**\n * `cli-highlight`'s seventeen coloured keys, ALL REQUIRED. Its `colorizeNode` reads\n * `theme[token] || DEFAULT_THEME[token] || plain`, so one missing key restores that dependency's\n * red/green pair — the exact thing a daltonized theme exists to remove.\n */\nexport interface IThemeSyntax {\n readonly keyword: TThemeColor;\n readonly built_in: TThemeColor;\n readonly type: TThemeColor;\n readonly literal: TThemeColor;\n readonly number: TThemeColor;\n readonly regexp: TThemeColor;\n readonly string: TThemeColor;\n readonly class: TThemeColor;\n readonly function: TThemeColor;\n readonly comment: TThemeColor;\n readonly doctag: TThemeColor;\n readonly meta: TThemeColor;\n readonly tag: TThemeColor;\n readonly name: TThemeColor;\n readonly attr: TThemeColor;\n readonly addition: TThemeColor;\n readonly deletion: TThemeColor;\n}\n\n/** The colour ramp of the package's one animation. Cadence is not themed. */\nexport interface IThemeMotion {\n readonly wave: readonly [TThemeColor, TThemeColor, TThemeColor, TThemeColor];\n}\n\nexport type TThemeAppearance = 'dark' | 'light';\nexport type TThemeSource = 'built-in' | 'user' | 'plugin';\n\nexport interface ITuiTheme {\n /** `dark`, `light`, `dark-daltonized`, `light-daltonized`, `custom:<slug>`, `custom:<plugin>:<slug>`. */\n readonly id: string;\n readonly name: string;\n readonly appearance: TThemeAppearance;\n readonly source: TThemeSource;\n readonly colors: IThemeColors;\n readonly markdown: IThemeMarkdown;\n readonly syntax: IThemeSyntax;\n readonly motion: IThemeMotion;\n}\n\n/** The seventeen `syntax` keys, in the order `cli-highlight` declares them. */\nexport const THEME_SYNTAX_KEYS: readonly (keyof IThemeSyntax)[] = [\n 'keyword',\n 'built_in',\n 'type',\n 'literal',\n 'number',\n 'regexp',\n 'string',\n 'class',\n 'function',\n 'comment',\n 'doctag',\n 'meta',\n 'tag',\n 'name',\n 'attr',\n 'addition',\n 'deletion',\n];\n","/**\n * SCREEN-2002 — one theme, every encoding.\n *\n * Ink reads `colors` directly. `marked-terminal` wants chalk STYLE FUNCTIONS and `cli-highlight` wants\n * a token→function map, so both are built here from the same values. Ink's own `colorize` is this\n * mapping (`chalk[name]` / `chalk.hex` / `chalk.ansi256` / `chalk.rgb`), which is why deriving them is\n * not the \"name→SGR layer\" SCREEN-006 refused to invent — chalk is already the layer, and a direct\n * dependency.\n *\n * The STRUCTURE of a markdown or syntax token is fixed here, not themed: a heading is bold, a first\n * heading underlined and bold, a blockquote italic, a deletion dim and struck through, an href\n * underlined, and a syntax `type` dim — exactly what the two dependencies do today. A theme changes\n * the colour and nothing else, which is what keeps the `dark` theme's rendering the one users know.\n */\nimport chalk, { Chalk, backgroundColorNames, foregroundColorNames } from 'chalk';\n\nimport { THEME_SYNTAX_KEYS } from './theme-contracts.js';\n\nimport type { IThemeMarkdown, IThemeSyntax, ITuiTheme, TThemeColor } from './theme-contracts.js';\nimport type { ChalkInstance } from 'chalk';\n\nconst HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/iu;\nconst ANSI256 = /^ansi256\\((\\d{1,3})\\)$/iu;\nconst RGB = /^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/iu;\nconst ANSI256_MAX = 255;\n\n/** The grammar, written once: the validator, the refusal below and the file parser all quote it. */\nexport const THEME_COLOR_GRAMMAR = '<chalk name> | #rgb | #rrggbb | ansi256(n) | rgb(r,g,b)';\n\n/**\n * chalk's own name tables, which it exports; its styles are prototype getters, so a table has to be\n * built from the names rather than from `Object.keys(chalk)`. This is the same set Ink's `colorize`\n * accepts, which is why the grammar needs no list of its own.\n *\n * A `Map`, not an object literal: an object built by `Object.fromEntries` inherits `Object.prototype`,\n * so a lookup of `constructor`, `toString`, `valueOf` or `__proto__` answers TRUTHY and the\n * `undefined` guard below never fires — a value that is not a colour name would be handed back as\n * though it were a style. That value becomes reachable the moment a parsed user theme (work unit 3)\n * reaches this builder.\n */\nconst CHALK_STYLES = new Map<string, ChalkInstance>(\n [...foregroundColorNames, ...backgroundColorNames].map((name) => [name, chalk[name]]),\n);\nconst FOREGROUND_NAMES = new Set<string>(foregroundColorNames);\n\n/** A FOREGROUND chalk name Ink would accept — a `bg…` name is not a token value. */\nfunction isChalkColorName(value: string): boolean {\n return FOREGROUND_NAMES.has(value);\n}\n\n/** Whether a string is a colour in Ink's grammar. The one place the grammar is decided. */\nexport function isThemeColor(value: string): boolean {\n if (HEX.test(value)) return true;\n const ansi = ANSI256.exec(value);\n if (ansi) return Number(ansi[1]) <= ANSI256_MAX;\n const rgb = RGB.exec(value);\n if (rgb) return rgb.slice(1).every((part) => Number(part) <= ANSI256_MAX);\n return isChalkColorName(value);\n}\n\n/**\n * A value that reached the builder without being a colour is a DEFECT IN THIS PACKAGE, not in a\n * user's file: `parseThemeDocument` refuses a theme file whole, and every built-in value is checked\n * by TC-01. Before work unit 3 this case had two different silent answers — `foreground` handed\n * back its base, `background`'s helper handed back `chalk.reset` — so the same bad value rendered\n * as unstyled text in one place and as a reset in another, and neither said anything. Throwing is\n * the one behaviour that cannot render a theme nobody can see is wrong.\n */\nfunction refuseThemeColor(value: string): never {\n throw new Error(\n `Theme colour ${JSON.stringify(value)} is not in Ink's colour grammar (${THEME_COLOR_GRAMMAR}).`,\n );\n}\n\n/**\n * A foreground style for one theme value, optionally continuing a chain. The base matters: chalk\n * emits its SGR in chain order, so `chalk.dim` + a colour is not the same bytes as a colour + `.dim`,\n * and reproducing a dependency's own default means reproducing its order.\n */\nexport function foreground(color: TThemeColor, base: ChalkInstance = chalk): ChalkInstance {\n if (HEX.test(color)) return base.hex(color);\n const ansi = ANSI256.exec(color);\n if (ansi) return base.ansi256(Number(ansi[1]));\n const rgb = RGB.exec(color);\n if (rgb) return base.rgb(Number(rgb[1]), Number(rgb[2]), Number(rgb[3]));\n // Gated on the FOREGROUND names, not on the shared table: the table also holds the `bg…` entries\n // that `background()` needs, so asking it alone would answer `foreground('bgRed')` with a\n // background style — a name `isThemeColor` already refuses as a token value.\n const named = isChalkColorName(color) ? CHALK_STYLES.get(color) : undefined;\n if (named === undefined) refuseThemeColor(color);\n // A named colour continues the chain through chalk's own getter on the base instance.\n const chained = Reflect.get(base, color) as ChalkInstance | undefined;\n return chained ?? named;\n}\n\n/** A background style for one theme value. */\nexport function background(color: TThemeColor, base: ChalkInstance = chalk): ChalkInstance {\n if (HEX.test(color)) return base.bgHex(color);\n const ansi = ANSI256.exec(color);\n if (ansi) return base.bgAnsi256(Number(ansi[1]));\n const rgb = RGB.exec(color);\n if (rgb) return base.bgRgb(Number(rgb[1]), Number(rgb[2]), Number(rgb[3]));\n const capitalised = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;\n const chained = Reflect.get(base, capitalised) as ChalkInstance | undefined;\n return chained ?? refuseThemeColor(color);\n}\n\n/**\n * The diff rows are written by this package, not by a dependency, and their caller decides colour\n * with its own `color` flag — so a caller that asked for colour gets it even where chalk's own\n * detection says level 0. That is the behaviour the renderer had when it wrote the escapes by hand,\n * and the reason `renderMarkdown(md, { color: true })` still emits them off a TTY.\n *\n * What is forced is colour, NOT depth. Overriding a DETECTED level would downsample the rest of the\n * frame while these rows alone emit truecolor — visible on a 256-colour terminal with a hex-valued\n * theme, where `#56b4e9` is `ESC[38;5;117m` at level 2 and `ESC[38;2;86;180;233m` at level 3. So the\n * ambient level is kept whenever there is one, and only the OFF case is overridden.\n */\nconst TRUECOLOR = 3;\nconst FORCED_CHALK = new Chalk({ level: TRUECOLOR });\n\nfunction diffRowBase(): ChalkInstance {\n return chalk.level === 0 ? FORCED_CHALK : chalk;\n}\n\n/** The `marked-terminal` constructor options: theme colour + the structure that is not themed. */\nexport function markdownRendererOptions(markdown: IThemeMarkdown): Record<string, ChalkInstance> {\n return {\n // Each chain reproduces marked-terminal's own default order, so the `dark` theme is byte-identical.\n heading: foreground(markdown.heading).bold,\n firstHeading: foreground(markdown.firstHeading).underline.bold,\n code: foreground(markdown.code),\n codespan: foreground(markdown.codespan),\n link: foreground(markdown.link),\n href: foreground(markdown.href).underline,\n blockquote: foreground(markdown.blockquote).italic,\n del: foreground(markdown.del, chalk.dim).strikethrough,\n html: foreground(markdown.html),\n };\n}\n\n/** The `cli-highlight` theme: every one of its seventeen coloured keys, so none falls back. */\nexport function syntaxHighlightTheme(syntax: IThemeSyntax): Record<string, ChalkInstance> {\n const theme: Record<string, ChalkInstance> = {};\n for (const key of THEME_SYNTAX_KEYS) {\n // `type` is `<colour>.dim` in cli-highlight's own theme: the modifier is structure, the colour is\n // the theme's, and the order is the dependency's, so the `dark` theme is byte-identical.\n theme[key] = key === 'type' ? foreground(syntax[key]).dim : foreground(syntax[key]);\n }\n return theme;\n}\n\n/** The styles the diff renderer writes around a row: background and foreground composed per row. */\nexport interface IDiffRowStyles {\n readonly added: (row: string) => string;\n readonly removed: (row: string) => string;\n readonly hunk: (row: string) => string;\n /** Structure only: dim over the inherited foreground, never a theme colour. */\n readonly meta: (row: string) => string;\n}\n\nexport function diffRowStyles(theme: ITuiTheme): IDiffRowStyles {\n const { markdown } = theme;\n const base = diffRowBase();\n const addedBackground = background(markdown.diffAddedBackground, base);\n const addedForeground = foreground(markdown.diffAdded, base);\n const removedBackground = background(markdown.diffRemovedBackground, base);\n const removedForeground = foreground(markdown.diffRemoved, base);\n return {\n added: (row) => addedBackground(addedForeground(row)),\n removed: (row) => removedBackground(removedForeground(row)),\n hunk: foreground(markdown.diffHunk, base),\n // Dim over the INHERITED foreground, as this renderer always wrote it — colouring it would be a\n // rendering change dressed as a theme.\n meta: (row) => base.dim(row),\n };\n}\n","/**\n * SCREEN-2002 — colour-vision-deficiency simulation, for the guard over the daltonized built-ins.\n *\n * A daltonized theme's claim is that two colours a viewer must tell apart still differ AFTER their\n * protan or deutan confusion. That is measurable, so it is measured: simulate both deficiencies\n * (Viénot–Brettel–Mollon, the linear-RGB projection published for dichromats), convert to CIE Lab,\n * and take the pairwise distance.\n *\n * The guard REFUSES what it cannot simulate. A colour NAME is whatever the terminal says it is, so a\n * named value in a daltonized theme is an error rather than a pass — the one direction a guard may\n * never take silently.\n */\nimport type { TThemeColor } from './theme-contracts.js';\n\nexport type TColorVision = 'protanopia' | 'deuteranopia';\n\ninterface IRgb {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n}\n\nconst HEX_LONG = /^#([0-9a-f]{6})$/iu;\nconst HEX_SHORT = /^#([0-9a-f]{3})$/iu;\nconst ANSI256 = /^ansi256\\((\\d{1,3})\\)$/iu;\nconst RGB = /^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/iu;\nconst BYTE_MAX = 255;\nconst HEX_BASE = 16;\nconst HEX_PAIR = /[0-9a-f]{2}/giu;\nconst ANSI_CUBE_START = 16;\nconst ANSI_GRAY_START = 232;\nconst ANSI_CUBE_SIDE = 6;\n/**\n * Published numeric tables are kept as DATA — parsed from their published text rather than spelled\n * as literals in expressions, so the values stay verbatim and reviewable against their source.\n */\nfunction coefficients(published: string): number[] {\n return published.split(/\\s+/u).map(Number);\n}\n\n/** The xterm 6×6×6 cube's channel levels. */\nconst ANSI_CUBE_STEPS = coefficients('0 95 135 175 215 255');\nconst GRAY_STEP = 10;\nconst GRAY_BASE = 8;\n\n/** An Ink-grammar colour as 0–255 sRGB, or `undefined` when it cannot be simulated. */\nexport function toRgb(color: TThemeColor): IRgb | undefined {\n const long = HEX_LONG.exec(color);\n if (long?.[1]) {\n const [r, g, b] = (long[1].match(HEX_PAIR) ?? []).map((pair) =>\n Number.parseInt(pair, HEX_BASE),\n ) as [number, number, number];\n return { r, g, b };\n }\n const short = HEX_SHORT.exec(color);\n if (short?.[1]) {\n const [r, g, b] = [...short[1]].map((digit) => Number.parseInt(`${digit}${digit}`, HEX_BASE));\n return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n }\n const rgb = RGB.exec(color);\n if (rgb) return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) };\n const ansi = ANSI256.exec(color);\n if (ansi) return fromAnsi256(Number(ansi[1]));\n // A colour NAME resolves in the terminal, not here.\n return undefined;\n}\n\nfunction fromAnsi256(index: number): IRgb | undefined {\n if (index >= ANSI_GRAY_START) {\n const level = GRAY_BASE + (index - ANSI_GRAY_START) * GRAY_STEP;\n return { r: level, g: level, b: level };\n }\n if (index < ANSI_CUBE_START) return undefined; // the 16 system colours are terminal-defined\n const offset = index - ANSI_CUBE_START;\n const r = ANSI_CUBE_STEPS[Math.floor(offset / (ANSI_CUBE_SIDE * ANSI_CUBE_SIDE))];\n const g = ANSI_CUBE_STEPS[Math.floor(offset / ANSI_CUBE_SIDE) % ANSI_CUBE_SIDE];\n const b = ANSI_CUBE_STEPS[offset % ANSI_CUBE_SIDE];\n return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n}\n\nconst GAMMA_THRESHOLD = 0.04045;\nconst GAMMA_OFFSET = 0.055;\nconst GAMMA_SCALE = 1.055;\nconst GAMMA_EXPONENT = 2.4;\nconst GAMMA_LINEAR_DIVISOR = 12.92;\n\nfunction toLinear(channel: number): number {\n const value = channel / BYTE_MAX;\n return value <= GAMMA_THRESHOLD\n ? value / GAMMA_LINEAR_DIVISOR\n : ((value + GAMMA_OFFSET) / GAMMA_SCALE) ** GAMMA_EXPONENT;\n}\n\n/** Viénot–Brettel–Mollon dichromat projections, applied in linear RGB. */\nconst PROJECTION: Record<TColorVision, readonly number[][]> = {\n protanopia: [\n coefficients('0.1121 0.8853 -0.0005'),\n coefficients('0.1127 0.8897 -0.0001'),\n coefficients('0.0045 0.0 1.0019'),\n ],\n deuteranopia: [\n coefficients('0.292 0.7054 -0.0003'),\n coefficients('0.2934 0.7089 0.0'),\n coefficients('-0.0209 0.4614 0.5677'),\n ],\n};\n\n/** sRGB → CIE XYZ (D65), the standard matrix. */\nconst SRGB_TO_XYZ = [\n coefficients('0.4124 0.3576 0.1805'),\n coefficients('0.2126 0.7152 0.0722'),\n coefficients('0.0193 0.1192 0.9505'),\n];\n\nfunction project(matrix: readonly number[][], vector: readonly number[]): number[] {\n return matrix.map((row) =>\n row.reduce((sum, factor, index) => sum + factor * (vector[index] ?? 0), 0),\n );\n}\n\ninterface ILab {\n readonly l: number;\n readonly a: number;\n readonly b: number;\n}\n\nconst D65 = coefficients('0.95047 1 1.08883');\nconst LAB_EPSILON_NUMERATOR = 216;\nconst LAB_EPSILON_DENOMINATOR = 24389;\nconst LAB_KAPPA_DIVISOR = 27;\nconst LAB_EPSILON = LAB_EPSILON_NUMERATOR / LAB_EPSILON_DENOMINATOR;\nconst LAB_KAPPA = LAB_EPSILON_DENOMINATOR / LAB_KAPPA_DIVISOR;\nconst LAB_L_SCALE = 116;\nconst LAB_L_OFFSET = 16;\nconst LAB_A_SCALE = 500;\nconst LAB_B_SCALE = 200;\nconst THREE = 3;\nconst CUBE_ROOT = 1 / THREE;\n\nfunction toLab(linear: readonly number[]): ILab {\n const xyz = project(SRGB_TO_XYZ, linear).map((value, index) => value / (D65[index] ?? 1));\n const f = (value: number): number =>\n value > LAB_EPSILON ? value ** CUBE_ROOT : (LAB_KAPPA * value + LAB_L_OFFSET) / LAB_L_SCALE;\n const [fx, fy, fz] = xyz.map(f) as [number, number, number];\n return {\n l: LAB_L_SCALE * fy - LAB_L_OFFSET,\n a: LAB_A_SCALE * (fx - fy),\n b: LAB_B_SCALE * (fy - fz),\n };\n}\n\n/** One colour as a dichromat sees it, in Lab. `undefined` ⇒ the value cannot be simulated. */\nexport function simulate(color: TThemeColor, vision: TColorVision): ILab | undefined {\n const rgb = toRgb(color);\n if (rgb === undefined) return undefined;\n const linear = [toLinear(rgb.r), toLinear(rgb.g), toLinear(rgb.b)];\n return toLab(project(PROJECTION[vision], linear));\n}\n\n/** CIE76 distance between two simulated colours; `undefined` when either cannot be simulated. */\nexport function simulatedDistance(\n first: TThemeColor,\n second: TThemeColor,\n vision: TColorVision,\n): number | undefined {\n const a = simulate(first, vision);\n const b = simulate(second, vision);\n if (a === undefined || b === undefined) return undefined;\n const SQUARE = 2;\n return Math.sqrt((a.l - b.l) ** SQUARE + (a.a - b.a) ** SQUARE + (a.b - b.b) ** SQUARE);\n}\n","/**\n * The shape of a parsed JSON document, and the one guard that narrows it.\n *\n * Two user-owned files are read as JSON in this package — the keybindings document and, since\n * SCREEN-2002, a theme document — and both walk an arbitrary tree looking for the keys they know.\n * A second copy of this type would be a second answer to \"what can a parsed file hold\", which is\n * how two readers of the same kind of file start disagreeing about what a null or an array means.\n */\nexport type TJsonValue = string | number | boolean | null | TJsonValue[] | IJsonRecord;\n\nexport interface IJsonRecord {\n [key: string]: TJsonValue;\n}\n\n/** An object, not an array and not `null` — the distinction `typeof` alone does not make. */\nexport function isJsonRecord(value: TJsonValue): value is IJsonRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * SCREEN-2002 — the theme file boundary.\n *\n * A theme file is `{ name?, base?, overrides? }`: a name, a built-in to start from, and a SPARSE map\n * over the same token paths the built-ins use. It is refused WHOLE on the first unknown token or\n * invalid value, with a `$.`-path naming what was wrong — the keybindings document's contract, for\n * the same reason. A partially-applied theme is the state where a reader cannot tell which colours\n * are theirs and which the base's, and a file that silently loses its third line is worse than a\n * file that is skipped with its reason printed.\n *\n * The token paths are not listed here. They are WALKED from the base theme, so `ITuiTheme` gaining a\n * key makes it overridable and `ITuiTheme` losing one makes it refused — there is no second\n * declaration of the token model to drift from the first.\n *\n * Values go through `isThemeColor`, which is also the injection floor: a raw escape sequence is not\n * in Ink's grammar, so it cannot enter through a theme.\n */\nimport { isJsonRecord } from '../json-value.js';\nimport { sanitizeTerminalText } from '../sanitize-terminal-text.js';\nimport { BUILT_IN_THEMES, DEFAULT_THEME_ID } from './built-in-themes.js';\nimport { isThemeColor, THEME_COLOR_GRAMMAR } from './theme-styles.js';\n\nimport type { IJsonRecord, TJsonValue } from '../json-value.js';\nimport type { ITuiTheme, TThemeSource } from './theme-contracts.js';\n\nexport interface IThemeDocumentInput {\n /** The namespaced id the caller minted — `custom:<slug>` or `custom:<plugin>:<slug>`. */\n readonly id: string;\n /** The file's own name, for the `Skipped theme \"<file>\": …` line. */\n readonly fileName: string;\n readonly source: TThemeSource;\n readonly text: string;\n}\n\nexport type TThemeDocumentResult =\n { readonly ok: true; readonly theme: ITuiTheme } | { readonly ok: false; readonly error: string };\n\nconst DOCUMENT_KEYS = new Set(['name', 'base', 'overrides']);\nconst MAX_NAME_LENGTH = 60;\n// eslint-disable-next-line no-control-regex -- matching the control range IS the point here\nconst CONTROL_CHARACTER = /[\\u0000-\\u001F\\u007F-\\u009F]/u;\n\nconst CONTROL_CHARACTERS = new RegExp(CONTROL_CHARACTER.source, 'gu');\n\n/**\n * Untrusted text on its way into a line a terminal will print — the one escaping policy, exported\n * so the surfaces that build their OWN diagnostics use it rather than re-deriving it.\n *\n * A diagnostic is the one part of a refused file that reaches the terminal, and it quotes what was\n * wrong — so without this the injection floor the grammar provides is defeated by the message that\n * reports a violation of it. Escaped rather than stripped, because an author has to SEE what their\n * file contains (`\"\\u001b[31m\"`) instead of a value that looks fine.\n *\n * `JSON.stringify` alone is NOT enough, and that is the whole reason this is a function: it escapes\n * U+0000–U+001F and leaves U+007F and the C1 range literal — including U+009B and U+009D, the\n * single-byte spellings of CSI and OSC. A terminal accepts both spellings (`sanitize-terminal-text.ts`\n * says so and handles both), so quoting only the 7-bit one leaves the 8-bit one's parameters\n * standing as a live sequence.\n */\nexport function quoteThemeText(value: string): string {\n return JSON.stringify(value).replace(\n CONTROL_CHARACTERS,\n (character) => `\\\\u${(character.codePointAt(0) ?? 0).toString(16).padStart(4, '0')}`,\n );\n}\n\n/**\n * The same escaping WITHOUT the surrounding quotes, for a field a caller already quotes — so a\n * refused file does not print as `Skipped theme \"\"My Theme.json\"\": …`.\n */\nexport function escapeThemeText(value: string): string {\n return quoteThemeText(value).slice(1, -1);\n}\n\n/**\n * Prose a surface did not write — a dependency's message, a path from the environment. Sanitized\n * rather than quoted, because it is a sentence; `sanitizeTerminalText` removes both spellings of\n * every sequence, and the tab/newline it keeps are flattened so one diagnostic stays one line.\n */\nexport function sanitizeThemeProse(message: string): string {\n return sanitizeTerminalText(message).replaceAll('\\n', ' ').replaceAll('\\t', ' ').trim();\n}\n\n/** A path SEGMENT taken from the file. Plain keys read as themselves; anything else is quoted. */\nfunction pathSegment(key: string): string {\n return CONTROL_CHARACTER.test(key) ? quoteThemeText(key) : key;\n}\n\nfunction refuse(error: string): TThemeDocumentResult {\n return { ok: false, error };\n}\n\ntype TMergeResult =\n | { readonly ok: true; readonly value: TJsonValue }\n | { readonly ok: false; readonly error: string };\n\nfunction mergeColor(override: TJsonValue, path: string): TMergeResult {\n if (typeof override !== 'string')\n return { ok: false, error: `${path}: expected a colour string` };\n if (!isThemeColor(override)) {\n return {\n ok: false,\n error: `${path}: ${quoteThemeText(override)} is not a colour in Ink's grammar (${THEME_COLOR_GRAMMAR})`,\n };\n }\n return { ok: true, value: override };\n}\n\nfunction mergeArray(base: readonly TJsonValue[], override: TJsonValue, path: string): TMergeResult {\n if (!Array.isArray(override) || override.length !== base.length) {\n return { ok: false, error: `${path}: expected exactly ${base.length} colours` };\n }\n const merged: TJsonValue[] = [];\n for (const [index, item] of base.entries()) {\n const result = mergeNode(item, override[index] ?? null, `${path}[${index}]`);\n if (!result.ok) return result;\n merged.push(result.value);\n }\n return { ok: true, value: merged };\n}\n\nfunction mergeRecord(base: IJsonRecord, override: TJsonValue, path: string): TMergeResult {\n if (!isJsonRecord(override)) return { ok: false, error: `${path}: expected an object of tokens` };\n const merged: IJsonRecord = { ...base };\n for (const key of Object.keys(override)) {\n // Own-property only: a JSON document can carry a literal `__proto__` key, and the base theme\n // has no own property by that name, so the same check that catches a typo catches that too.\n if (!Object.prototype.hasOwnProperty.call(base, key)) {\n return { ok: false, error: `${path}.${pathSegment(key)} is not a theme token` };\n }\n const result = mergeNode(\n base[key] ?? null,\n override[key] ?? null,\n `${path}.${pathSegment(key)}`,\n );\n if (!result.ok) return result;\n merged[key] = result.value;\n }\n return { ok: true, value: merged };\n}\n\n/** One node of the token tree, matched against the SHAPE of the base theme's node. */\nfunction mergeNode(base: TJsonValue, override: TJsonValue, path: string): TMergeResult {\n if (typeof base === 'string') return mergeColor(override, path);\n if (Array.isArray(base)) return mergeArray(base, override, path);\n if (isJsonRecord(base)) return mergeRecord(base, override, path);\n return { ok: false, error: `${path} is not a theme token` };\n}\n\ntype TThemeTokens = Pick<ITuiTheme, 'colors' | 'markdown' | 'syntax' | 'motion'>;\n\n/**\n * The merged tree, read back as the token groups it was built from.\n *\n * A JSON round-trip rather than a type assertion, in both directions: the token interfaces have no\n * index signature, so neither shape is assignable to the other and an assertion between them is\n * what TypeScript rightly refuses. The conversion is sound by construction — `mergeNode` copies the\n * BASE theme's shape and replaces only string leaves with strings the grammar accepted, so every key\n * `ITuiTheme` declares is present and every leaf is a `TThemeColor`. An unknown key or a non-colour\n * leaf is a refusal above this line and never reaches it.\n */\nfunction asThemeTokens(value: TJsonValue): TThemeTokens {\n return JSON.parse(JSON.stringify(value)) as TThemeTokens;\n}\n\n/** The four token groups, as JSON — the base for the walk above. */\nfunction themeTokens(theme: ITuiTheme): IJsonRecord {\n return JSON.parse(\n JSON.stringify({\n colors: theme.colors,\n markdown: theme.markdown,\n syntax: theme.syntax,\n motion: theme.motion,\n }),\n ) as IJsonRecord;\n}\n\nfunction readDocument(text: string): TMergeResult {\n let parsed: TJsonValue;\n try {\n parsed = JSON.parse(text) as TJsonValue;\n } catch (cause) {\n return {\n ok: false,\n error: `$: ${cause instanceof Error ? sanitizeThemeProse(cause.message) : 'invalid JSON'}`,\n };\n }\n if (!isJsonRecord(parsed)) return { ok: false, error: '$: expected an object' };\n for (const key of Object.keys(parsed)) {\n if (!DOCUMENT_KEYS.has(key)) {\n return { ok: false, error: `$.${pathSegment(key)} is not a theme token` };\n }\n }\n return { ok: true, value: parsed };\n}\n\nfunction readName(document: IJsonRecord, fallback: string): TMergeResult {\n const name = document.name;\n // The FALLBACK is checked on the same terms. It is the id the loader minted, which is constrained\n // where it is minted — but \"the caller already made it safe\" is the assumption that put an\n // unchecked string on every list row once, and this is the function that renders the answer.\n if (name === undefined) {\n if (CONTROL_CHARACTER.test(fallback)) {\n return { ok: false, error: '$.name: the minted id must not contain control characters' };\n }\n if (fallback.length > MAX_NAME_LENGTH) {\n return {\n ok: false,\n error: `$.name: the minted id must be at most ${MAX_NAME_LENGTH} characters`,\n };\n }\n return { ok: true, value: fallback };\n }\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, error: '$.name: expected a non-empty string' };\n }\n // A name is APPLIED, so unlike a diagnostic it is rendered on every `/theme list` row and every\n // picker row for the whole session. A control character there is an escape the terminal acts on,\n // and a newline breaks the box the row is drawn in — so the file is refused rather than the name\n // repaired, which is this module's one policy.\n if (CONTROL_CHARACTER.test(name)) {\n return { ok: false, error: '$.name: must not contain control characters' };\n }\n if (name.length > MAX_NAME_LENGTH) {\n return { ok: false, error: `$.name: must be at most ${MAX_NAME_LENGTH} characters` };\n }\n return { ok: true, value: name };\n}\n\nfunction readBase(\n document: IJsonRecord,\n):\n | { readonly ok: true; readonly theme: ITuiTheme }\n | { readonly ok: false; readonly error: string } {\n // `=== undefined`, not `??`: an explicit `null` is a value the file states, and \"applied whole or\n // refused whole\" means it is refused rather than silently read as \"unset\".\n const id = document.base === undefined ? DEFAULT_THEME_ID : document.base;\n if (typeof id !== 'string') return { ok: false, error: '$.base: expected a built-in theme id' };\n const base = BUILT_IN_THEMES.find((theme) => theme.id === id);\n if (!base) return { ok: false, error: `$.base: ${quoteThemeText(id)} is not a built-in theme` };\n return { ok: true, theme: base };\n}\n\n/**\n * Parse one theme file. The `id`, the `source` and the `appearance` are NOT the document's to\n * decide: the id is minted from where the file was found so nothing can shadow a built-in, the\n * source is where it was read from, and the appearance follows the base a theme chose to extend.\n */\nexport function parseThemeDocument(input: IThemeDocumentInput): TThemeDocumentResult {\n const document = readDocument(input.text);\n if (!document.ok) return refuse(document.error);\n const fields = document.value as IJsonRecord;\n\n const base = readBase(fields);\n if (!base.ok) return refuse(base.error);\n\n const name = readName(fields, input.id);\n if (!name.ok) return refuse(name.error);\n\n const overrides = fields.overrides === undefined ? {} : fields.overrides;\n const tokens = mergeNode(themeTokens(base.theme), overrides, '$.overrides');\n if (!tokens.ok) return refuse(tokens.error);\n\n const { colors, markdown, syntax, motion } = asThemeTokens(tokens.value);\n return {\n ok: true,\n theme: {\n id: input.id,\n name: name.value as string,\n appearance: base.theme.appearance,\n source: input.source,\n colors,\n markdown,\n syntax,\n motion,\n },\n };\n}\n","/**\n * The startup banner, committed once to the terminal scrollback.\n *\n * Extracted from `App.tsx` under CLI-2004: the art is ASCII, not box-drawing, so a\n * no-box-drawing sweep passes over it unchanged — it needs its own suppression AND its own\n * assertion, and neither is reviewable while the glyphs live inline in a 600-line component.\n * `BANNER_GLYPHS` is the set a test asserts is absent in screen-reader mode.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\n/** The ASCII art itself. */\nexport const BANNER_ART = `\n ____ ___ ____ ___ _____ _\n | _ \\\\ / _ \\\\| __ ) / _ \\\\_ _|/ \\\\\n | |_) | | | | _ \\\\| | | || | / _ \\\\\n | _ <| |_| | |_) | |_| || |/ ___ \\\\\n |_| \\\\_\\\\\\\\___/|____/ \\\\___/ |_/_/ \\\\_\\\\\n`;\n\n/**\n * The characters the art is drawn from. None is a box-drawing character, which is exactly why the\n * banner needs a suppression test of its own.\n */\nexport const BANNER_GLYPHS: readonly string[] = ['_', '|', '\\\\', '/', '<'];\n\n/** The banner as it is committed to scrollback: the art plus the version line. */\nexport function AppBanner({ version }: { version: string }): React.ReactElement {\n const palette = usePalette();\n return (\n <Box flexDirection=\"column\" paddingX={1} marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {BANNER_ART}\n </Text>\n <Text dimColor> v{version}</Text>\n </Box>\n );\n}\n","/** The units the attention modules convert between; named so no arithmetic carries a bare literal. */\nexport const MS_PER_SECOND = 1000;\nexport const SECONDS_PER_MINUTE = 60;\nexport const MINUTES_PER_HOUR = 60;\nexport const MS_PER_MINUTE = MS_PER_SECOND * SECONDS_PER_MINUTE;\nexport const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;\n","import {\n MINUTES_PER_HOUR,\n MS_PER_SECOND,\n SECONDS_PER_HOUR,\n SECONDS_PER_MINUTE,\n} from './time-units.js';\n\n/**\n * SCREEN-1992: the time until a sleeping schedule fires, rendered against a given clock so the\n * row can tick once a second without the projection changing.\n */\nexport function formatCountdown(nextFireAt: string, now: Date): string | undefined {\n const target = Date.parse(nextFireAt);\n if (Number.isNaN(target)) return undefined;\n const remainingMs = target - now.getTime();\n if (remainingMs <= 0) return 'now';\n const totalSeconds = Math.floor(remainingMs / MS_PER_SECOND);\n const hours = Math.floor(totalSeconds / SECONDS_PER_HOUR);\n const minutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n if (hours > 0) return `in ${hours}h ${minutes}m`;\n if (minutes > 0) return `in ${minutes}m ${seconds}s`;\n return `in ${seconds}s`;\n}\n","/**\n * Single source of truth for status symbol + color across the TUI.\n *\n * Before SCREEN-005 the same conceptual status (e.g. \"running\") was rendered\n * three different ways — icons (⟳/✓/✗) in the streaming indicator, markers\n * (□/■) in the background-task panel, and color-only in the workspace detail\n * pane. This module gives every component one semantic status vocabulary and\n * one glyph map, so a \"running\" task looks the same everywhere and every status\n * pairs a SYMBOL with a color (never color alone — required for no-color\n * terminals and colorblind users).\n */\n\nimport type { IThemeColors } from './theme/index.js';\nimport type {\n TExecutionAttention,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nexport type TUiStatusKind =\n 'running' | 'success' | 'error' | 'denied' | 'waiting' | 'cancelled' | 'idle';\n\n/**\n * Canonical status → SYMBOL map. Used by every status-rendering component.\n *\n * SCREEN-2002 split the colour out: a symbol is the same in every theme, a colour is not, and a\n * module-level constant cannot follow a theme resolved at render time. The symbol stays here (this\n * module remains the SSOT for status kinds and their glyphs); the colour comes from the live theme\n * through {@link statusGlyphColor}, which every call site reaches with `usePalette()`, so the pairing\n * survives — no caller gets one without the other.\n */\nexport const STATUS_SYMBOL: Record<TUiStatusKind, string> = {\n running: '⟳',\n success: '✓',\n error: '✗',\n denied: '⊘',\n waiting: '◴',\n cancelled: '⊗',\n idle: '·',\n};\n\n/** The live theme's colour for a status kind. Always rendered beside {@link STATUS_SYMBOL}. */\nexport function statusGlyphColor(colors: IThemeColors, kind: TUiStatusKind): string {\n return colors.status[kind];\n}\n\n/** Map a tool-execution state to a semantic status kind. */\nexport function toolStateStatusKind(tool: IToolState): TUiStatusKind {\n if (tool.isRunning) return 'running';\n if (tool.result === 'error') return 'error';\n if (tool.result === 'denied') return 'denied';\n return 'success';\n}\n\nconst ACTIVE_WORKSPACE_STATUSES: readonly TExecutionWorkspaceStatus[] = [\n 'active',\n 'queued',\n 'running',\n 'sleeping',\n];\n\n/**\n * Map an execution-workspace entry's status (+ optional attention) to a semantic\n * status kind. `attention` takes precedence so an entry flagged failed/permission\n * classifies correctly even when its raw status hasn't caught up — this mirrors the\n * precedence the workspace color logic uses, so colour + glyph come from one rule.\n */\nexport function workspaceStatusKind(\n status: TExecutionWorkspaceStatus,\n attention?: TExecutionAttention,\n): TUiStatusKind {\n if (attention === 'failed' || status === 'failed') return 'error';\n if (attention === 'permission' || status === 'waiting_permission') return 'waiting';\n if (status === 'completed') return 'success';\n if (status === 'cancelled') return 'cancelled';\n if (ACTIVE_WORKSPACE_STATUSES.includes(status)) return 'running';\n return 'idle';\n}\n","import { workspaceStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type {\n IExecutionDetailRecord,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\n\n// \"Active\" for the in-flight COUNT (countActiveBackgroundWorkspaceEntries) —\n// intentionally includes `waiting_permission` (a task awaiting permission is still\n// in flight). This is a different question from the status KIND used for\n// colour/glyph (where `waiting_permission` maps to its own 'waiting' kind via\n// `workspaceStatusKind`), so the two lists differ on purpose.\nconst ACTIVE_STATUSES: readonly TExecutionWorkspaceStatus[] = [\n 'active',\n 'queued',\n 'running',\n 'waiting_permission',\n 'sleeping',\n];\nconst DETAIL_RECORD_TEXT_LIMIT = 160;\nconst PREVIEW_WHITESPACE = /\\s+/g;\nconst PREVIEW_SEPARATOR = ' ';\n\nexport interface IExecutionWorkspaceEntryRow {\n id: string;\n radio: '●' | '○';\n /** SCREEN-1992: the five-word state, rendered beside the radio and carried in the accessible text. */\n state: string;\n title: string;\n subtitle?: string;\n statusLabel: string;\n preview?: string;\n /** SCREEN-2002: the status KIND; the component resolves its colour from the live theme. */\n statusKind: TUiStatusKind;\n isSelected: boolean;\n accessibleText: string;\n}\n\nexport interface IExecutionWorkspaceEntryRowOptions {\n selectedEntryId?: string;\n}\n\nexport function getDefaultBackgroundWorkspaceEntries(\n snapshot: IExecutionWorkspaceSnapshot | null,\n): IExecutionWorkspaceEntry[] {\n return (snapshot?.entries ?? []).filter(\n (entry) => entry.kind === 'background_task' && entry.visibility === 'default',\n );\n}\n\nexport function countActiveBackgroundWorkspaceEntries(\n snapshot: IExecutionWorkspaceSnapshot | null,\n): number {\n return getDefaultBackgroundWorkspaceEntries(snapshot).filter((entry) =>\n ACTIVE_STATUSES.includes(entry.status),\n ).length;\n}\n\nexport function formatExecutionWorkspaceEntryRow(\n entry: IExecutionWorkspaceEntry,\n options: IExecutionWorkspaceEntryRowOptions = {},\n): IExecutionWorkspaceEntryRow {\n const isSelected = entry.id === options.selectedEntryId;\n const row = {\n id: entry.id,\n radio: isSelected ? '●' : '○',\n state: entry.state,\n title: formatEntryTitle(entry),\n subtitle: formatEntrySubtitle(entry),\n statusLabel: formatStatusLabel(entry.status),\n preview: trimPreview(entry.preview ?? entry.currentAction),\n statusKind: getEntryStatusKind(entry),\n isSelected,\n } satisfies Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>;\n return { ...row, accessibleText: formatAccessibleText(row) };\n}\n\nexport function formatExecutionDetailRecord(record: IExecutionDetailRecord): string {\n const text = record.text.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);\n if (!text) return record.kind;\n return text.length > DETAIL_RECORD_TEXT_LIMIT\n ? `${text.slice(0, DETAIL_RECORD_TEXT_LIMIT)}...`\n : text;\n}\n\nfunction formatEntryTitle(entry: IExecutionWorkspaceEntry): string {\n if (entry.kind === 'main_thread') return entry.title;\n if (entry.kind === 'background_group') return `${entry.title} group`;\n if (entry.taskKind === 'agent') return `${entry.title} agent`;\n if (entry.taskKind === 'process') return entry.title || 'Process';\n if (entry.taskKind === 'scheduled') return entry.title || 'Scheduled';\n return entry.title;\n}\n\nfunction formatEntrySubtitle(entry: IExecutionWorkspaceEntry): string | undefined {\n if (entry.kind === 'main_thread') return entry.subtitle;\n const parts = [\n entry.taskKind,\n entry.subtitle,\n entry.attention === 'none' ? undefined : entry.attention,\n ];\n return (\n parts\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · ') || undefined\n );\n}\n\nfunction formatStatusLabel(status: TExecutionWorkspaceStatus): string {\n return status.replace(/_/g, ' ');\n}\n\n// SCREEN-2002: the view model names the status KIND, not a colour — a colour belongs to the theme\n// resolved at render time, and the kind is what keeps an entry's colour matching the glyph shown for\n// the same status everywhere (SCREEN-007). The component resolves it through `statusGlyphColor`.\nfunction getEntryStatusKind(entry: IExecutionWorkspaceEntry): TUiStatusKind {\n return workspaceStatusKind(entry.status, entry.attention);\n}\n\nfunction trimPreview(value: string | undefined): string | undefined {\n const preview = value?.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);\n return preview || undefined;\n}\n\nfunction formatAccessibleText(row: Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>): string {\n const parts = [\n `${row.radio} ${row.state}`,\n row.title,\n row.statusLabel,\n row.subtitle,\n row.preview,\n ];\n return parts\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · ');\n}\n","import { formatCountdown } from './attention/countdown.js';\nimport { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';\nimport { STATUS_SYMBOL, workspaceStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\n/**\n * The row's leading connector. CLI-2004 adds the plain form: `├`/`└` draw a tree a sighted reader\n * follows at a glance and a screen reader announces as two more characters per row.\n */\nexport const BACKGROUND_TASK_CONNECTORS = { branch: '├', last: '└', plain: '-' } as const;\n\nexport type TBackgroundTaskConnector =\n (typeof BACKGROUND_TASK_CONNECTORS)[keyof typeof BACKGROUND_TASK_CONNECTORS];\n\nexport interface IBackgroundTaskRow {\n connector: TBackgroundTaskConnector;\n /** Shared status glyph (⟳ running, ✓ done, ✗ failed, …) — see status-glyph.ts. */\n marker: string;\n /** SCREEN-1992: the five-word state beside the glyph — the word a reader hears, not a colour. */\n state: string;\n /** SCREEN-2002: the status KIND; the component resolves its colour from the live theme. */\n statusKind: TUiStatusKind;\n label: string;\n segments: string[];\n preview?: string;\n /** SCREEN-1992: the entry's headline when it says more than the preview already does. */\n headline?: string;\n /** SCREEN-1992: `in 59s` while a schedule sleeps — rendered against `options.now`. */\n countdown?: string;\n accessibleText: string;\n}\n\nexport interface IBackgroundTaskRowOptions {\n isLast?: boolean;\n /** Screen-reader mode ⇒ the plain connector. */\n screenReader?: boolean;\n /** The clock the countdown is rendered against; the panel's tick advances it. */\n now?: Date;\n}\n\n/** The headline earns its place only when the row does not already say it (preview or subtitle). */\nfunction resolveHeadline(\n entry: IExecutionWorkspaceEntry,\n preview: string | undefined,\n subtitle: string | undefined,\n): string | undefined {\n const text = entry.headline?.text;\n if (text === undefined || text.length === 0 || text === preview) return undefined;\n if (subtitle !== undefined && subtitle.includes(text)) return undefined;\n return entry.headline?.kind === 'question' ? `? ${text}` : text;\n}\n\nfunction resolveConnector(options: IBackgroundTaskRowOptions): TBackgroundTaskConnector {\n if (options.screenReader === true) return BACKGROUND_TASK_CONNECTORS.plain;\n return options.isLast === false\n ? BACKGROUND_TASK_CONNECTORS.branch\n : BACKGROUND_TASK_CONNECTORS.last;\n}\n\nexport function formatBackgroundTaskRow(\n entry: IExecutionWorkspaceEntry,\n options: IBackgroundTaskRowOptions = {},\n): IBackgroundTaskRow {\n const row = formatExecutionWorkspaceEntryRow(entry);\n // Symbol from the same (status, attention) classification that drives row.statusKind,\n // so the marker's glyph and colour always agree (SCREEN-007).\n const marker = STATUS_SYMBOL[workspaceStatusKind(entry.status, entry.attention)];\n const segments = [row.statusLabel, row.subtitle].filter(\n (segment): segment is string => typeof segment === 'string' && segment.length > 0,\n );\n const connector = resolveConnector(options);\n const headline = resolveHeadline(entry, row.preview, row.subtitle);\n const countdown =\n entry.nextFireAt === undefined\n ? undefined\n : formatCountdown(entry.nextFireAt, options.now ?? new Date());\n return {\n connector,\n marker,\n state: entry.state,\n statusKind: row.statusKind,\n label: row.title,\n segments,\n preview: row.preview,\n headline,\n countdown,\n accessibleText: [\n `${connector} ${marker} ${entry.state} ${row.title}`,\n ...segments,\n headline,\n row.preview,\n countdown,\n ]\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · '),\n };\n}\n","import { useEffect, useState } from 'react';\n\nimport { MS_PER_SECOND } from '../attention/time-units.js';\n\n/** SCREEN-1992: one second, the resolution a `in 59s` countdown is read at. */\nconst COUNTDOWN_TICK_MS = MS_PER_SECOND;\n\n/**\n * The clock a sleeping schedule's countdown is rendered against. Re-renders once a second only while\n * `active` (an entry carries `nextFireAt`) and not in screen-reader mode — a reader would otherwise\n * hear the row change every second. When not ticking the clock is still the CURRENT one on every\n * render (never the mount-time seed): a reader gets a correct number, read once.\n */\nexport function useCountdownTick(active: boolean, screenReader: boolean): Date {\n const ticking = active && !screenReader;\n const [tickedAt, setTickedAt] = useState(() => new Date());\n useEffect(() => {\n if (!ticking) return undefined;\n setTickedAt(new Date());\n const timer = setInterval(() => setTickedAt(new Date()), COUNTDOWN_TICK_MS);\n return () => clearInterval(timer);\n }, [ticking]);\n return ticking ? tickedAt : new Date();\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { formatBackgroundTaskRow } from './background-task-row-format.js';\nimport { useCountdownTick } from './hooks/useCountdownTick.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\ninterface IProps {\n entries: IExecutionWorkspaceEntry[];\n /** Index of the keyboard-focused row (SCREEN-014), or null when the prompt input is focused. */\n focusedIndex?: number | null;\n}\n\nexport default function BackgroundTaskPanel({\n entries,\n focusedIndex = null,\n}: IProps): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n // SCREEN-1992: ticks once a second only while a schedule sleeps, never in screen-reader mode.\n const now = useCountdownTick(\n entries.some((entry) => entry.nextFireAt !== undefined),\n screenReader,\n );\n if (entries.length === 0) return null;\n\n // SCREEN-014: the hint is focus-aware — it tells you how to enter the list, then how to move/open.\n const hint =\n focusedIndex !== null ? ' · ↑↓ select · Enter open · Esc back' : ' · ↓ select · Ctrl+B all';\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={palette.text.accent} bold>\n Background work\n </Text>\n <Text dimColor>{hint}</Text>\n </Box>\n {entries.map((entry, index) => {\n const row = formatBackgroundTaskRow(entry, {\n isLast: index === entries.length - 1,\n screenReader,\n now,\n });\n const isFocused = index === focusedIndex;\n return (\n // SCREEN-011: one truncated line so the connector + glyph lead the row.\n // SCREEN-014: the keyboard-focused row is inverse-highlighted.\n <Text key={entry.id} wrap=\"truncate-end\" inverse={isFocused}>\n {`${row.connector} `}\n <Text\n color={statusGlyphColor(palette, row.statusKind)}\n >{`${row.marker} ${row.state}`}</Text>\n {` ${row.label}`}\n {row.segments.map((segment, segmentIndex) => (\n <Text key={`${segment}-${segmentIndex}`} dimColor>{` · ${segment}`}</Text>\n ))}\n {row.headline ? <Text dimColor>{` · ${row.headline}`}</Text> : null}\n {row.preview ? <Text dimColor>{` · ${row.preview}`}</Text> : null}\n {row.countdown ? (\n <Text color={statusGlyphColor(palette, row.statusKind)}>{` · ${row.countdown}`}</Text>\n ) : null}\n </Text>\n );\n })}\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\ninterface IProps {\n percentage: number;\n}\n\nconst COMPACT_SUGGESTION_THRESHOLD = 70;\nconst CRITICAL_THRESHOLD = 90;\n\nexport function ContextWarningBanner({ percentage }: IProps): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n if (percentage >= CRITICAL_THRESHOLD) {\n return (\n <Box\n {...(screenReader\n ? {}\n : { borderStyle: 'single' as const, borderColor: palette.border.error })}\n paddingX={1}\n >\n <Text color={palette.text.error} bold>\n ⚠ Context at {Math.round(percentage)}% — window nearly full. Run /compact to summarize the\n conversation.\n </Text>\n </Box>\n );\n }\n\n if (percentage >= COMPACT_SUGGESTION_THRESHOLD) {\n return (\n <Box paddingX={1}>\n <Text color={palette.text.warning}>\n Context at {Math.round(percentage)}% — consider running /compact to free up space.\n </Text>\n </Box>\n );\n }\n\n return null;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport {\n formatExecutionDetailRecord,\n formatExecutionWorkspaceEntryRow,\n} from './execution-workspace-view-model.js';\nimport { Text } from './SafeText.js';\nimport { STATUS_SYMBOL, statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n TExecutionDetailRecordKind,\n} from '@robota-sdk/agent-interface-execution';\n\nconst MAX_VISIBLE_DETAIL_RECORDS = 12;\n\ninterface IProps {\n entry: IExecutionWorkspaceEntry;\n page: IExecutionDetailPage | null;\n loading?: boolean;\n error?: string;\n}\n\nexport default function ExecutionWorkspaceDetailPane({\n entry,\n page,\n loading,\n error,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId: entry.id });\n const records = page?.records.slice(-MAX_VISIBLE_DETAIL_RECORDS) ?? [];\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {`Viewing ${row.title}`}\n </Text>\n <Text dimColor>\n {row.statusLabel}\n {row.subtitle ? ` · ${row.subtitle}` : ''}\n {row.preview ? ` · ${row.preview}` : ''}\n </Text>\n {loading ? <Text dimColor>Loading workspace detail...</Text> : null}\n {error ? <Text color={palette.text.error}>{error}</Text> : null}\n {!loading && !error && records.length === 0 ? <Text dimColor>No detail yet</Text> : null}\n {!loading &&\n !error &&\n records.map((record) => {\n const { symbol, color } = getDetailRecordGlyph(record.kind, palette);\n return (\n <Text key={record.id} color={color}>\n {symbol ? `${symbol} ` : ''}\n {formatExecutionDetailRecord(record)}\n </Text>\n );\n })}\n {page?.nextCursor ? <Text dimColor>... more detail available</Text> : null}\n </Box>\n );\n}\n\n/**\n * Symbol + color per detail-record kind. A symbol always accompanies the color\n * so status is legible without color (SCREEN-005 — no color-only encoding).\n */\nfunction getDetailRecordGlyph(\n kind: TExecutionDetailRecordKind,\n palette: IThemeColors,\n): {\n symbol: string;\n color: string | undefined;\n} {\n if (kind === 'error')\n return { symbol: STATUS_SYMBOL.error, color: statusGlyphColor(palette, 'error') };\n if (kind === 'result')\n return { symbol: STATUS_SYMBOL.success, color: statusGlyphColor(palette, 'success') };\n if (kind === 'group_summary') return { symbol: '▸', color: palette.text.accent };\n if (kind === 'process_output') return { symbol: '·', color: palette.text.emphasis };\n return { symbol: '', color: undefined };\n}\n","export interface ISelectionFlowState {\n selectedIndex: number;\n scrollOffset: number;\n resolved: boolean;\n}\n\nexport interface ISelectionInputKey {\n escape?: boolean;\n return?: boolean;\n upArrow?: boolean;\n downArrow?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n}\n\nexport type TSelectionInputAction = 'cancel' | 'select' | 'previous' | 'next';\n\nexport type TSelectionEffect =\n | { type: 'none' }\n | { type: 'cancel' }\n | { type: 'select'; index: number }\n // CLI-2004: a checklist toggles many times and then commits; only the numbered branch emits it.\n | { type: 'confirm' };\n\nexport interface ISelectionFlowOptions {\n itemCount: number;\n maxVisible?: number;\n wrap?: boolean;\n enabled?: boolean;\n}\n\n/** `initialIndex` seeds the highlight (a request's declared default); the caller clamps it. */\nexport function createSelectionFlowState(initialIndex = 0): ISelectionFlowState {\n return { selectedIndex: initialIndex, scrollOffset: 0, resolved: false };\n}\n\nexport function getVerticalSelectionInputAction(\n key: ISelectionInputKey,\n): TSelectionInputAction | undefined {\n if (key.escape === true) return 'cancel';\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n if (key.return === true) return 'select';\n return undefined;\n}\n\nexport function getDirectionalSelectionInputAction(\n key: ISelectionInputKey,\n): TSelectionInputAction | undefined {\n if (key.escape === true) return 'cancel';\n if (key.leftArrow === true || key.upArrow === true) return 'previous';\n if (key.rightArrow === true || key.downArrow === true) return 'next';\n if (key.return === true) return 'select';\n return undefined;\n}\n\n/**\n * CLI-2004 — the typed-number branch of a selection.\n *\n * Screen-reader mode replaces arrow-key navigation with a numbered list and a typed answer, but the\n * SELECTION itself must stay owned by this module: two reducers deciding what \"selected\" means is\n * how a menu ends up resolving differently depending on how you drove it. `applyNumericSelection`\n * accumulates digits and resolves on Enter; everything downstream is the same `TSelectionEffect`.\n */\nexport interface INumericSelectionState {\n /** Digits typed so far, before Enter. */\n buffer: string;\n /** The last Enter was outside `1..itemCount` (or empty) — the caller re-prints the prompt. */\n invalid: boolean;\n resolved: boolean;\n}\n\nexport interface INumericSelectionOptions {\n itemCount: number;\n /** Escape resolves as a cancel only when the menu offers one. */\n cancellable?: boolean;\n /**\n * A checklist: a number TOGGLES its row and the menu stays open, so Enter on an empty buffer is\n * the commit rather than an invalid entry. A single-choice menu resolves on the first number.\n */\n multi?: boolean;\n /** A persistent view: selecting one row does not resolve the input. */\n repeatable?: boolean;\n}\n\nexport function createNumericSelectionState(): INumericSelectionState {\n return { buffer: '', invalid: false, resolved: false };\n}\n\n/**\n * Apply one keystroke to a numbered selection. Digits accumulate; Backspace edits; Enter resolves or\n * marks the entry invalid; Escape cancels when the menu is cancellable. A non-digit character is\n * ignored rather than treated as an answer — the invalid signal is reserved for a completed entry.\n */\nexport function applyNumericSelection(\n state: INumericSelectionState,\n input: string,\n key: ISelectionInputKey & { backspace?: boolean; delete?: boolean },\n options: INumericSelectionOptions,\n): { state: INumericSelectionState; effect: TSelectionEffect } {\n if (state.resolved) return { state, effect: { type: 'none' } };\n if (key.escape === true && options.cancellable === true) {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (key.backspace === true || key.delete === true) {\n return { state: { ...state, buffer: state.buffer.slice(0, -1) }, effect: { type: 'none' } };\n }\n if (key.return === true) {\n // A checklist commits on an empty Enter; a single-choice menu has nothing to commit, so the\n // same keystroke there is an entry that named no row.\n if (options.multi === true && state.buffer === '') {\n return { state: { ...state, invalid: false, resolved: true }, effect: { type: 'confirm' } };\n }\n const index = Number.parseInt(state.buffer, 10) - 1;\n if (!Number.isInteger(index) || index < 0 || index >= options.itemCount) {\n return { state: { ...state, buffer: '', invalid: true }, effect: { type: 'none' } };\n }\n return {\n // A toggle leaves the menu open — only a single-choice selection resolves it.\n state: { ...state, buffer: '', invalid: false, resolved: options.multi !== true && options.repeatable !== true },\n effect: { type: 'select', index },\n };\n }\n if (/^[0-9]+$/.test(input)) {\n return {\n state: { ...state, buffer: state.buffer + input, invalid: false },\n effect: { type: 'none' },\n };\n }\n return { state, effect: { type: 'none' } };\n}\n\nexport function applySelectionInput(\n state: ISelectionFlowState,\n action: TSelectionInputAction,\n options: ISelectionFlowOptions,\n): { state: ISelectionFlowState; effect: TSelectionEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (action === 'cancel') {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (options.enabled === false || options.itemCount === 0) {\n return { state, effect: { type: 'none' } };\n }\n if (action === 'select') {\n const index = clampIndex(state.selectedIndex, options.itemCount);\n return {\n state: { ...state, selectedIndex: index, resolved: true },\n effect: { type: 'select', index },\n };\n }\n const selectedIndex = moveSelection(state.selectedIndex, action, options);\n const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);\n return { state: { ...state, selectedIndex, scrollOffset }, effect: { type: 'none' } };\n}\n\nexport function normalizeSelectionState(\n state: ISelectionFlowState,\n options: ISelectionFlowOptions,\n): ISelectionFlowState {\n if (options.itemCount === 0) {\n return { ...state, selectedIndex: 0, scrollOffset: 0 };\n }\n const selectedIndex = clampIndex(state.selectedIndex, options.itemCount);\n const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);\n if (selectedIndex === state.selectedIndex && scrollOffset === state.scrollOffset) {\n return state;\n }\n return {\n ...state,\n selectedIndex,\n scrollOffset,\n };\n}\n\nfunction moveSelection(\n selectedIndex: number,\n action: TSelectionInputAction,\n options: ISelectionFlowOptions,\n): number {\n if (action === 'previous') {\n if (options.wrap === true && selectedIndex === 0) return options.itemCount - 1;\n return Math.max(0, selectedIndex - 1);\n }\n if (options.wrap === true && selectedIndex === options.itemCount - 1) return 0;\n return Math.min(options.itemCount - 1, selectedIndex + 1);\n}\n\nfunction resolveScrollOffset(\n selectedIndex: number,\n scrollOffset: number,\n options: ISelectionFlowOptions,\n): number {\n const maxVisible = options.maxVisible ?? options.itemCount;\n if (maxVisible <= 0) return 0;\n if (selectedIndex < scrollOffset) return selectedIndex;\n if (selectedIndex >= scrollOffset + maxVisible) return selectedIndex - maxVisible + 1;\n return Math.max(0, scrollOffset);\n}\n\nfunction clampIndex(index: number, itemCount: number): number {\n return Math.min(Math.max(index, 0), itemCount - 1);\n}\n","/**\n * The switcher's selection state: where the cursor is, which slice of entries is on screen, and what\n * a keypress does to both.\n *\n * Split out of `ExecutionWorkspaceSwitcher.tsx` (CLI-1994 + CLI-2004 both grew that file past its\n * size budget) along the seam that was already there: this half owns \"what is selected\", the\n * component owns \"what is drawn\".\n */\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type React from 'react';\n\n/** How many entries the list shows at once; the rest scroll. Read only by this module. */\nconst MAX_VISIBLE_WORKSPACE_ENTRIES = 8;\n\nexport interface IUseWorkspaceSwitcherSelectionInput {\n entries: IExecutionWorkspaceEntry[];\n selectedEntryId?: string;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n}\n\nexport function useWorkspaceSwitcherSelection({\n entries,\n selectedEntryId,\n onSelect,\n onClose,\n}: IUseWorkspaceSwitcherSelectionInput): {\n normalized: ISelectionFlowState;\n visibleEntries: IExecutionWorkspaceEntry[];\n applyAction: (action: TSelectionInputAction) => void;\n} {\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n\n useEffect(() => {\n const selectedIndex = Math.max(\n 0,\n entries.findIndex((entry) => entry.id === selectedEntryId),\n );\n const nextState = createNormalizedSelection({ selectedIndex, itemCount: entries.length });\n stateRef.current = nextState;\n setState(nextState);\n }, [entries.length, selectedEntryId]);\n\n const normalized = createNormalizedSelection({\n selectedIndex: state.selectedIndex,\n scrollOffset: state.scrollOffset,\n itemCount: entries.length,\n });\n if (normalized !== state) stateRef.current = normalized;\n return {\n normalized,\n visibleEntries: entries.slice(\n normalized.scrollOffset,\n normalized.scrollOffset + MAX_VISIBLE_WORKSPACE_ENTRIES,\n ),\n applyAction: createApplyAction({ entries, stateRef, setState, onSelect, onClose }),\n };\n}\n\nfunction createApplyAction({\n entries,\n stateRef,\n setState,\n onSelect,\n onClose,\n}: {\n entries: IExecutionWorkspaceEntry[];\n stateRef: React.MutableRefObject<ISelectionFlowState>;\n setState: React.Dispatch<React.SetStateAction<ISelectionFlowState>>;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n}): (action: TSelectionInputAction) => void {\n return (action): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: entries.length,\n maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES,\n });\n const nextState =\n result.effect.type === 'select' || result.effect.type === 'cancel'\n ? { ...result.state, resolved: false }\n : result.state;\n stateRef.current = nextState;\n setState(nextState);\n if (result.effect.type === 'cancel') {\n onClose();\n } else if (result.effect.type === 'select') {\n const entry = entries[result.effect.index];\n if (entry) onSelect(entry.id);\n }\n };\n}\n\nfunction createNormalizedSelection(input: {\n selectedIndex: number;\n scrollOffset?: number;\n itemCount: number;\n}): ISelectionFlowState {\n return normalizeSelectionState(\n {\n selectedIndex: input.selectedIndex,\n scrollOffset: input.scrollOffset ?? 0,\n resolved: false,\n },\n { itemCount: input.itemCount, maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES },\n );\n}\n","/**\n * CLI-2004 — the typed-number input wiring shared by every numbered menu.\n *\n * The reducer lives in `flows/selection-flow.ts` (one owner of \"what is selected\"); this hook is the\n * Ink adapter around it, so six components get the same behaviour without six copies of the same\n * `useInput` block. Inactive when the mode is off, so the arrow-key handlers keep the terminal.\n */\n\nimport { useInput } from 'ink';\nimport { useCallback, useRef, useState } from 'react';\n\nimport {\n applyNumericSelection,\n createNumericSelectionState,\n type INumericSelectionState,\n} from '../flows/selection-flow.js';\n\nexport interface IUseNumberedSelectionInputs {\n /** Screen-reader mode. Off ⇒ the hook reads nothing. */\n enabled: boolean;\n itemCount: number;\n cancellable?: boolean;\n /** A checklist: a number toggles a row, the menu stays open, and an empty Enter commits. */\n multi?: boolean;\n /** Keep a single-row selection open so the user can select another row later. */\n repeatable?: boolean;\n onSelect: (index: number) => void;\n onCancel?: () => void;\n /** Required with `multi` — what an empty Enter commits. */\n onConfirm?: () => void;\n}\n\nexport interface INumberedSelection {\n buffer: string;\n invalid: boolean;\n clear: () => void;\n}\n\n/** Read a typed selection for a numbered list. */\nexport function useNumberedSelection(inputs: IUseNumberedSelectionInputs): INumberedSelection {\n const [state, setState] = useState<INumericSelectionState>(createNumericSelectionState);\n const stateRef = useRef(state);\n const { enabled, itemCount, cancellable, multi, repeatable, onSelect, onCancel, onConfirm } = inputs;\n\n const clear = useCallback((): void => {\n const empty = createNumericSelectionState();\n stateRef.current = empty;\n setState(empty);\n }, []);\n\n const handle = useCallback(\n (input: string, key: Parameters<typeof applyNumericSelection>[2]): void => {\n const result = applyNumericSelection(stateRef.current, input, key, {\n itemCount,\n ...(cancellable === true ? { cancellable: true } : {}),\n ...(multi === true ? { multi: true } : {}),\n ...(repeatable === true ? { repeatable: true } : {}),\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'select') onSelect(result.effect.index);\n else if (result.effect.type === 'cancel') onCancel?.();\n else if (result.effect.type === 'confirm') onConfirm?.();\n },\n [itemCount, cancellable, multi, repeatable, onSelect, onCancel, onConfirm],\n );\n\n useInput(\n (input, key) => {\n handle(input, key);\n },\n { isActive: enabled },\n );\n\n return { buffer: state.buffer, invalid: state.invalid, clear };\n}\n","/**\n * Key-hint footer SSOT (SCREEN-005).\n *\n * Single source of truth for prompt-footer key hints and the selection-row indicator across the\n * package's Ink prompt/selection components. Every footer call site declares its hints as\n * `IKeyHint[]` and renders them through `formatKeyHints` / `KeyHintFooter`, so exactly one grammar\n * exists (no per-component footer dialects) and a drift cannot re-appear silently — the\n * `key-hint-consistency` test asserts every declared hint set round-trips through this module.\n *\n * This module ships MECHANICS only: the separator, the pair grammar, the indicator constants.\n * Verb vocabulary (Navigate/Select/Confirm/…) is supplied by callers — the package stays a\n * content-neutral library ingredient. No config surface: per-call-site suppression is passing an\n * empty hint list.\n *\n * Affordance contract (see docs/SPEC.md \"Interaction affordance contract\"): the footer lists\n * exactly the keys that do something — a prompt that suppresses Esc in its flow omits Esc here.\n */\n\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** One key hint: the key(s) and the verb label, e.g. `{ keys: '↑↓', label: 'Navigate' }`. */\nexport interface IKeyHint {\n keys: string;\n label: string;\n}\n\n/** Separator between key-hint pairs. Changing the footer separator is a one-constant edit. */\nexport const KEY_HINT_SEPARATOR = ' · ';\n\n/** Selection cursor rendered in front of the focused row. */\nexport const SELECTION_INDICATOR = '> ';\n\n/** Same-width blank rendered in front of non-focused rows so columns stay aligned. */\nexport const SELECTION_INDICATOR_NONE = ' ';\n\n/** Pure formatter: joins `keys label` pairs with {@link KEY_HINT_SEPARATOR}. Empty list → ''. */\nexport function formatKeyHints(hints: readonly IKeyHint[]): string {\n return hints.map((hint) => `${hint.keys} ${hint.label}`).join(KEY_HINT_SEPARATOR);\n}\n\n/**\n * Dim one-line footer: leading pad + formatted hints. Renders nothing for an empty hint list\n * (per-call-site suppression without a config surface).\n */\nexport function KeyHintFooter({\n hints,\n}: {\n hints: readonly IKeyHint[];\n}): React.ReactElement | null {\n if (hints.length === 0) return null;\n return <Text dimColor> {formatKeyHints(hints)}</Text>;\n}\n","export const KEYBINDING_ACTIONS = {\n app: ['open-workspace-switcher'],\n thinking: ['abort'],\n 'background-detail': ['return-to-main'],\n recovery: ['retry'],\n 'chat-input': [\n 'submit',\n 'history-previous',\n 'history-next',\n 'history-search',\n 'cursor-left',\n 'cursor-right',\n 'delete-backward',\n 'delete-word',\n 'delete-line',\n ],\n 'text-input': [\n 'submit',\n 'cursor-left',\n 'cursor-right',\n 'cursor-up',\n 'cursor-down',\n 'delete-backward',\n 'delete-word',\n 'delete-line',\n ],\n 'autocomplete-menu': ['previous', 'next', 'close', 'accept', 'execute'],\n 'queued-prompt': ['cancel'],\n 'confirm-prompt': ['previous', 'next', 'confirm', 'choose-yes', 'choose-no'],\n 'permission-prompt': [\n 'previous',\n 'next',\n 'confirm',\n 'allow-once',\n 'allow-session',\n 'allow-project',\n 'deny',\n ],\n 'text-prompt': ['submit', 'cancel', 'delete-backward'],\n 'list-picker': ['previous', 'next', 'select', 'cancel'],\n 'menu-select': ['previous', 'next', 'select', 'back'],\n 'multi-select': ['previous', 'next', 'toggle', 'confirm', 'cancel'],\n 'workspace-switcher': ['previous', 'next', 'select', 'close', 'attach'],\n 'background-list': ['previous', 'next', 'open', 'close'],\n 'transport-settings': ['previous', 'next', 'toggle', 'close'],\n /** SCREEN-1993: the reverse prompt-history search overlay. */\n 'history-search': ['previous', 'next', 'cycle-scope', 'insert', 'execute', 'cancel'],\n /**\n * SCREEN-2002: the theme picker. Navigation PREVIEWS; only `select` applies. Printable keys are\n * fine here because the picker takes no text.\n */\n 'theme-picker': ['previous', 'next', 'select', 'toggle-syntax', 'toggle-motion', 'cancel'],\n} as const;\n\nexport type TKeybindingContext = keyof typeof KEYBINDING_ACTIONS;\nexport type TKeybindingAction<TContext extends TKeybindingContext = TKeybindingContext> =\n (typeof KEYBINDING_ACTIONS)[TContext][number];\n\ntype TDefaultKeybindings = {\n readonly [TContext in TKeybindingContext]: Readonly<\n Record<TKeybindingAction<TContext>, readonly string[]>\n >;\n};\n\nexport const DEFAULT_KEYBINDINGS = {\n app: { 'open-workspace-switcher': ['ctrl+b'] },\n thinking: { abort: ['escape'] },\n 'background-detail': { 'return-to-main': ['escape'] },\n recovery: { retry: ['enter'] },\n 'chat-input': {\n submit: ['enter'],\n 'history-previous': ['up'],\n 'history-next': ['down'],\n 'history-search': ['ctrl+r'],\n 'cursor-left': ['left'],\n 'cursor-right': ['right'],\n 'delete-backward': ['backspace', 'delete'],\n 'delete-word': ['ctrl+w'],\n 'delete-line': ['ctrl+u'],\n },\n 'text-input': {\n submit: ['enter'],\n 'cursor-left': ['left'],\n 'cursor-right': ['right'],\n 'cursor-up': ['up'],\n 'cursor-down': ['down'],\n 'delete-backward': ['backspace', 'delete'],\n 'delete-word': ['ctrl+w'],\n 'delete-line': ['ctrl+u'],\n },\n 'autocomplete-menu': {\n previous: ['up'],\n next: ['down'],\n close: ['escape'],\n accept: ['tab'],\n execute: ['enter'],\n },\n 'queued-prompt': { cancel: ['backspace', 'delete'] },\n 'confirm-prompt': {\n previous: ['left', 'up'],\n next: ['right', 'down'],\n confirm: ['enter'],\n 'choose-yes': ['y'],\n 'choose-no': ['n'],\n },\n 'permission-prompt': {\n previous: ['left', 'up'],\n next: ['right', 'down'],\n confirm: ['enter'],\n 'allow-once': ['y'],\n 'allow-session': ['s', 'a'],\n 'allow-project': ['p'],\n deny: ['n', 'd'],\n },\n 'text-prompt': {\n submit: ['enter'],\n cancel: ['escape'],\n 'delete-backward': ['backspace', 'delete'],\n },\n 'list-picker': { previous: ['up'], next: ['down'], select: ['enter'], cancel: ['escape'] },\n 'menu-select': { previous: ['up'], next: ['down'], select: ['enter'], back: ['escape'] },\n 'multi-select': {\n previous: ['up'],\n next: ['down'],\n toggle: ['space'],\n confirm: ['enter'],\n cancel: ['escape'],\n },\n 'workspace-switcher': {\n previous: ['up'],\n next: ['down'],\n select: ['enter'],\n close: ['ctrl+b', 'escape'],\n attach: ['a'],\n },\n 'background-list': {\n previous: ['up'],\n next: ['down'],\n open: ['enter'],\n close: ['escape'],\n },\n 'transport-settings': {\n previous: ['up'],\n next: ['down'],\n toggle: ['space'],\n close: ['enter', 'escape'],\n },\n 'history-search': {\n previous: ['up'],\n next: ['down', 'ctrl+r'],\n 'cycle-scope': ['ctrl+s'],\n insert: ['enter', 'tab'],\n execute: ['ctrl+e'],\n cancel: ['escape'],\n },\n 'theme-picker': {\n previous: ['up'],\n next: ['down'],\n select: ['enter'],\n 'toggle-syntax': ['s'],\n 'toggle-motion': ['m'],\n cancel: ['escape'],\n },\n} as const satisfies TDefaultKeybindings;\n\nexport const TEXT_ENTRY_CONTEXTS: ReadonlySet<TKeybindingContext> = new Set([\n 'chat-input',\n 'text-input',\n 'text-prompt',\n 'history-search',\n]);\n","export interface IKeyInput {\n readonly ctrl?: boolean;\n readonly shift?: boolean;\n readonly meta?: boolean;\n readonly return?: boolean;\n readonly escape?: boolean;\n readonly tab?: boolean;\n readonly upArrow?: boolean;\n readonly downArrow?: boolean;\n readonly leftArrow?: boolean;\n readonly rightArrow?: boolean;\n readonly backspace?: boolean;\n readonly delete?: boolean;\n}\n\nconst MODIFIER_ALIASES: Readonly<Record<string, 'ctrl' | 'alt' | 'meta' | 'shift'>> = {\n ctrl: 'ctrl',\n control: 'ctrl',\n alt: 'alt',\n option: 'alt',\n meta: 'meta',\n cmd: 'meta',\n command: 'meta',\n shift: 'shift',\n};\nconst MODIFIER_ORDER = ['ctrl', 'alt', 'meta', 'shift'] as const;\nconst KEY_ALIASES: Readonly<Record<string, string>> = {\n return: 'enter',\n enter: 'enter',\n esc: 'escape',\n escape: 'escape',\n tab: 'tab',\n space: 'space',\n spacebar: 'space',\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n backspace: 'backspace',\n delete: 'delete',\n};\n\nexport function normalizeBinding(raw: string): string | undefined {\n const strokes = raw.trim().split(/\\s+/u);\n if (strokes.length < 1 || strokes.length > 2) return undefined;\n const normalized = strokes.map(normalizeStroke);\n return normalized.every((stroke): stroke is string => stroke !== undefined)\n ? normalized.join(' ')\n : undefined;\n}\n\nfunction normalizeStroke(raw: string): string | undefined {\n if (!raw || raw.includes(' ')) return undefined;\n const parts = raw.split('+');\n if (parts.some((part) => part.length === 0)) return undefined;\n const rawKey = parts.at(-1);\n if (rawKey === undefined) return undefined;\n const modifiers = new Set<'ctrl' | 'alt' | 'meta' | 'shift'>();\n for (const rawModifier of parts.slice(0, -1)) {\n const modifier = MODIFIER_ALIASES[rawModifier.toLowerCase()];\n if (modifier === undefined || modifiers.has(modifier)) return undefined;\n modifiers.add(modifier);\n }\n let key = KEY_ALIASES[rawKey.toLowerCase()];\n if (key === undefined) {\n if ([...rawKey].length !== 1 || /\\s/u.test(rawKey)) return undefined;\n key = rawKey.toLowerCase();\n if (parts.length === 1 && rawKey !== key) modifiers.add('shift');\n }\n return [...MODIFIER_ORDER.filter((modifier) => modifiers.has(modifier)), key].join('+');\n}\n\nfunction keyStrokeFromInput(input: string, key: IKeyInput): string | undefined {\n // Ink preserves LF as a one-character input string instead of setting `key.return`. In terminal\n // raw mode that byte is Ctrl+J; normalize it before printable-text handling can treat it as paste.\n if (/^[\\r\\n]*\\n[\\r\\n]*$/u.test(input)) return 'ctrl+j';\n const special = key.return\n ? 'enter'\n : key.escape\n ? 'escape'\n : key.tab\n ? 'tab'\n : key.upArrow\n ? 'up'\n : key.downArrow\n ? 'down'\n : key.leftArrow\n ? 'left'\n : key.rightArrow\n ? 'right'\n : key.backspace\n ? 'backspace'\n : key.delete\n ? 'delete'\n : input === ' '\n ? 'space'\n : [...input].length === 1\n ? input.toLowerCase()\n : undefined;\n if (special === undefined) return undefined;\n const modifiers = [\n ...(key.ctrl ? ['ctrl'] : []),\n ...(key.meta ? ['meta'] : []),\n ...(key.shift ? ['shift'] : []),\n ];\n return [...modifiers, special].join('+');\n}\n\n/**\n * Terminal protocols collapse several control characters onto named keys. Keep the configured\n * spelling for hints, but offer every spelling that the same received byte can represent.\n */\nexport function keyStrokesFromInput(input: string, key: IKeyInput): readonly string[] {\n const primary = keyStrokeFromInput(input, key);\n if (primary === undefined) return [];\n if (primary === 'enter') return ['enter', 'ctrl+m'];\n if (primary === 'tab') return ['tab', 'ctrl+i'];\n if (primary === 'escape') return ['escape', 'ctrl+['];\n return [primary];\n}\n\nexport function keybindingDeliverySignature(binding: string): string {\n return binding\n .split(' ')\n .map((stroke) => {\n if (stroke === 'ctrl+m') return 'enter';\n if (stroke === 'ctrl+i') return 'tab';\n if (stroke === 'ctrl+[') return 'escape';\n return stroke;\n })\n .join(' ');\n}\n\nexport function displayBinding(binding: string): string {\n const displayStroke = (stroke: string): string =>\n stroke\n .split('+')\n .map(\n (part) =>\n ({\n ctrl: 'Ctrl',\n alt: 'Alt',\n meta: 'Meta',\n shift: 'Shift',\n escape: 'Esc',\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n })[part] ??\n (part.length === 1 ? part.toUpperCase() : `${part[0]?.toUpperCase()}${part.slice(1)}`),\n )\n .join('+');\n return binding.split(' ').map(displayStroke).join(' ');\n}\n","import { isJsonRecord } from '../json-value.js';\nimport {\n DEFAULT_KEYBINDINGS,\n KEYBINDING_ACTIONS,\n TEXT_ENTRY_CONTEXTS,\n type TKeybindingAction,\n type TKeybindingContext,\n} from './keybinding-catalogue.js';\nimport {\n displayBinding,\n keybindingDeliverySignature,\n keyStrokesFromInput,\n normalizeBinding,\n type IKeyInput,\n} from './keybinding-syntax.js';\n\nimport type { TJsonValue } from '../json-value.js';\n\nexport { DEFAULT_KEYBINDINGS } from './keybinding-catalogue.js';\nexport type { IKeyInput } from './keybinding-syntax.js';\nexport type { TKeybindingAction, TKeybindingContext } from './keybinding-catalogue.js';\n\nexport interface IKeybindingWarning {\n readonly code: 'multiplexer-conflict' | 'modifier-delivery';\n readonly path: string;\n readonly message: string;\n}\n\nexport interface IKeybindingDiagnostic {\n readonly file: string;\n readonly path: string;\n readonly message: string;\n}\n\nexport interface IKeybindingSnapshot {\n readonly generation: number;\n readonly sourcePath: string;\n readonly bindings: Readonly<\n Record<TKeybindingContext, Readonly<Record<string, readonly string[]>>>\n >;\n readonly warnings: readonly IKeybindingWarning[];\n readonly diagnostic?: IKeybindingDiagnostic;\n}\n\nexport type TParseKeybindingsResult =\n | { readonly ok: true; readonly snapshot: IKeybindingSnapshot }\n | { readonly ok: false; readonly diagnostic: IKeybindingDiagnostic };\n\nconst CHORD_TIMEOUT_MS = 1_000;\n\nfunction diagnostic(file: string, path: string, message: string): TParseKeybindingsResult {\n return { ok: false, diagnostic: { file, path, message } };\n}\n\nfunction cloneDefaults(): Record<TKeybindingContext, Record<string, string[]>> {\n return Object.fromEntries(\n Object.entries(DEFAULT_KEYBINDINGS).map(([context, actions]) => [\n context,\n Object.fromEntries(\n Object.entries(actions).map(([action, bindings]) => [action, [...bindings]]),\n ),\n ]),\n ) as Record<TKeybindingContext, Record<string, string[]>>;\n}\n\nfunction bindingValues(value: TJsonValue): readonly string[] | null | undefined {\n if (value === null) return null;\n if (typeof value === 'string') return [value];\n if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === 'string')) {\n return value;\n }\n return undefined;\n}\n\nfunction validateContextBindings(\n context: TKeybindingContext,\n bindings: Record<string, string[]>,\n file: string,\n): TParseKeybindingsResult | undefined {\n const owners = new Map<string, string>();\n const prefixes = new Map<string, string>();\n for (const [action, values] of Object.entries(bindings)) {\n for (const binding of values) {\n const deliveryBinding = keybindingDeliverySignature(binding);\n const owner = owners.get(deliveryBinding);\n const actionPath = `$.bindings.${context}.${action}`;\n if (owner !== undefined && owner !== action) {\n return diagnostic(file, actionPath, `Binding duplicates action ${owner}.`);\n }\n const strokes = deliveryBinding.split(' ');\n if (strokes.length > 1 && TEXT_ENTRY_CONTEXTS.has(context) && isPrintable(strokes[0] ?? '')) {\n return diagnostic(\n file,\n actionPath,\n 'Printable text cannot begin a chord in a text context.',\n );\n }\n owners.set(deliveryBinding, action);\n if (strokes.length > 1) prefixes.set(strokes[0] ?? '', action);\n }\n }\n for (const [binding, action] of owners) {\n const prefixOwner = prefixes.get(binding);\n if (prefixOwner !== undefined) {\n return diagnostic(\n file,\n `$.bindings.${context}.${prefixOwner}`,\n `Chord prefix collides with action ${action}.`,\n );\n }\n }\n return undefined;\n}\n\nfunction isPrintable(stroke: string): boolean {\n return !stroke.includes('+') && [...stroke].length === 1;\n}\n\nfunction warningFor(binding: string, path: string): IKeybindingWarning[] {\n const warnings: IKeybindingWarning[] = [];\n if (binding.split(' ').includes('ctrl+b')) {\n warnings.push({\n code: 'multiplexer-conflict',\n path,\n message: 'Ctrl+B may be captured by a terminal multiplexer.',\n });\n }\n if (/ctrl\\+shift\\+[a-z]/u.test(binding)) {\n warnings.push({\n code: 'modifier-delivery',\n path,\n message: 'This terminal may not distinguish Ctrl+Shift from Ctrl.',\n });\n }\n return warnings;\n}\n\nexport function parseKeybindingsDocument(text: string, file: string): TParseKeybindingsResult {\n let value: TJsonValue;\n try {\n value = JSON.parse(text) as TJsonValue;\n } catch (cause) {\n return diagnostic(file, '$', cause instanceof Error ? cause.message : 'Invalid JSON.');\n }\n if (!isJsonRecord(value)) return diagnostic(file, '$', 'Expected an object.');\n if (value.version !== 1) return diagnostic(file, '$.version', 'Expected version 1.');\n if (!isJsonRecord(value.bindings)) return diagnostic(file, '$.bindings', 'Expected an object.');\n\n const effective = cloneDefaults();\n const warnings: IKeybindingWarning[] = [];\n for (const [rawContext, rawActions] of Object.entries(value.bindings)) {\n if (!(rawContext in KEYBINDING_ACTIONS)) {\n return diagnostic(file, `$.bindings.${rawContext}`, 'Unknown keybinding context.');\n }\n const context = rawContext as TKeybindingContext;\n if (!isJsonRecord(rawActions)) {\n return diagnostic(file, `$.bindings.${context}`, 'Expected an action object.');\n }\n for (const [action, rawBindings] of Object.entries(rawActions)) {\n if (!(KEYBINDING_ACTIONS[context] as readonly string[]).includes(action)) {\n return diagnostic(file, `$.bindings.${context}.${action}`, 'Unknown action.');\n }\n const values = bindingValues(rawBindings);\n const actionPath = `$.bindings.${context}.${action}`;\n if (values === undefined) {\n return diagnostic(file, actionPath, 'Expected a binding, binding list, or null.');\n }\n if (values === null) {\n effective[context][action] = [];\n continue;\n }\n const normalized: string[] = [];\n for (const rawBinding of values) {\n const binding = normalizeBinding(rawBinding);\n if (binding === undefined) return diagnostic(file, actionPath, 'Invalid binding syntax.');\n if (binding.split(' ').includes('ctrl+c')) {\n return diagnostic(file, actionPath, 'Ctrl+C is reserved for two-stage shutdown.');\n }\n normalized.push(binding);\n warnings.push(...warningFor(binding, actionPath));\n }\n effective[context][action] = normalized;\n }\n }\n for (const context of Object.keys(effective) as TKeybindingContext[]) {\n const invalid = validateContextBindings(context, effective[context], file);\n if (invalid !== undefined) return invalid;\n }\n const bindings = Object.freeze(\n Object.fromEntries(\n Object.entries(effective).map(([context, actions]) => [\n context,\n Object.freeze(\n Object.fromEntries(\n Object.entries(actions).map(([action, bindings]) => [action, Object.freeze(bindings)]),\n ),\n ),\n ]),\n ),\n ) as IKeybindingSnapshot['bindings'];\n return {\n ok: true,\n snapshot: Object.freeze({\n generation: 0,\n sourcePath: file,\n bindings,\n warnings: Object.freeze(warnings),\n }),\n };\n}\n\nexport interface IChordState {\n readonly context?: TKeybindingContext;\n readonly generation?: number;\n readonly strokes: readonly string[];\n readonly deadline?: number;\n}\n\nexport function createEmptyChordState(): IChordState {\n return { strokes: [] };\n}\n\nfunction actionBindings(\n snapshot: IKeybindingSnapshot,\n context: TKeybindingContext,\n): [TKeybindingAction, readonly string[]][] {\n return Object.entries(snapshot.bindings[context]) as [TKeybindingAction, readonly string[]][];\n}\n\nexport function resolveKeybindingInput(options: {\n readonly snapshot: IKeybindingSnapshot;\n readonly state: IChordState;\n readonly context: TKeybindingContext;\n readonly input: string;\n readonly key: IKeyInput;\n readonly now: number;\n}): {\n readonly state: IChordState;\n readonly actions: readonly TKeybindingAction[];\n readonly consumed: boolean;\n} {\n const strokes = keyStrokesFromInput(options.input, options.key);\n if (strokes.length === 0) {\n return { state: createEmptyChordState(), actions: [], consumed: false };\n }\n const stateValid =\n options.state.context === options.context &&\n options.state.generation === options.snapshot.generation &&\n (options.state.deadline === undefined || options.now <= options.state.deadline) &&\n !options.key.escape;\n const prior = stateValid ? options.state.strokes : [];\n const entries = actionBindings(options.snapshot, options.context);\n for (const stroke of strokes) {\n const sequence = [...prior, stroke].join(' ');\n const exact = entries\n .filter(([, bindings]) => bindings.includes(sequence))\n .map(([action]) => action);\n if (exact.length > 0) {\n return { state: createEmptyChordState(), actions: exact, consumed: true };\n }\n if (\n entries.some(([, bindings]) => bindings.some((binding) => binding.startsWith(`${sequence} `)))\n ) {\n return {\n state: {\n context: options.context,\n generation: options.snapshot.generation,\n strokes: [...prior, stroke],\n deadline: options.now + CHORD_TIMEOUT_MS,\n },\n actions: [],\n consumed: true,\n };\n }\n }\n if (prior.length > 0) {\n return resolveKeybindingInput({ ...options, state: createEmptyChordState() });\n }\n return { state: createEmptyChordState(), actions: [], consumed: false };\n}\n\nexport function keybindingHints<TContext extends TKeybindingContext>(\n snapshot: IKeybindingSnapshot,\n context: TContext,\n actions: readonly (readonly [\n TKeybindingAction<TContext> | readonly TKeybindingAction<TContext>[],\n string,\n ])[],\n): { keys: string; label: string }[] {\n return actions.flatMap(([actionOrActions, label]) => {\n const grouped = Array.isArray(actionOrActions);\n const actionList = grouped ? actionOrActions : [actionOrActions];\n const bindings = actionList.flatMap((action) => {\n const values = snapshot.bindings[context][action] ?? [];\n return grouped ? values.slice(0, 1) : values;\n });\n const displayed = bindings.map(displayBinding);\n const keys = displayed.every((binding) => /^[↑↓←→]$/u.test(binding))\n ? displayed.join('')\n : displayed.join('/');\n return bindings.length === 0 ? [] : [{ keys, label }];\n });\n}\n","import { useInput } from 'ink';\nimport React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport {\n createEmptyChordState,\n keybindingHints,\n parseKeybindingsDocument,\n resolveKeybindingInput,\n type IChordState,\n type IKeybindingSnapshot,\n type IKeyInput,\n type TKeybindingAction,\n type TKeybindingContext,\n} from './keybinding-registry.js';\n\nimport type { IKeybindingsSource } from './node-keybindings-source.js';\n\nconst parsedDefault = parseKeybindingsDocument('{\"version\":1,\"bindings\":{}}', '<defaults>');\nif (!parsedDefault.ok) throw new Error(parsedDefault.diagnostic.message);\nconst DEFAULT_SNAPSHOT = parsedDefault.snapshot;\n\nconst KeybindingsContext = createContext<IKeybindingSnapshot>(DEFAULT_SNAPSHOT);\n\nexport function KeybindingsProvider({\n source,\n children,\n}: {\n source?: IKeybindingsSource;\n children: React.ReactNode;\n}): React.ReactElement {\n const [snapshot, setSnapshot] = useState(() => source?.getSnapshot() ?? DEFAULT_SNAPSHOT);\n useEffect(() => {\n if (source === undefined) return;\n setSnapshot(source.getSnapshot());\n return source.subscribe(setSnapshot);\n }, [source]);\n return <KeybindingsContext.Provider value={snapshot}>{children}</KeybindingsContext.Provider>;\n}\n\nexport function useKeybindings(): IKeybindingSnapshot {\n return useContext(KeybindingsContext);\n}\n\nexport function useKeybindingHints<TContext extends TKeybindingContext>(\n context: TContext,\n actions: readonly (readonly [\n TKeybindingAction<TContext> | readonly TKeybindingAction<TContext>[],\n string,\n ])[],\n): { keys: string; label: string }[] {\n return keybindingHints(useKeybindings(), context, actions);\n}\n\nexport function useKeybindingActions<\n TContext extends TKeybindingContext,\n TFallback extends TKeybindingContext = TContext,\n>(\n context: TContext,\n handler: (\n actions: readonly (TKeybindingAction<TContext> | TKeybindingAction<TFallback>)[],\n input: string,\n key: IKeyInput,\n consumed: boolean,\n resolvedContext: TContext | TFallback,\n ) => void,\n options: {\n readonly isActive?: boolean;\n /**\n * A key `context` does not bind is resolved again here, so a surface layered over an editor (the\n * autocomplete menu over the chat input) leaves every key it does not use to the editor's bindings.\n */\n readonly fallbackContext?: TFallback;\n } = {},\n): void {\n const snapshot = useKeybindings();\n const stateRef = useRef<IChordState>(createEmptyChordState());\n const fallbackStateRef = useRef<IChordState>(createEmptyChordState());\n const { fallbackContext } = options;\n useEffect(() => {\n stateRef.current = createEmptyChordState();\n fallbackStateRef.current = createEmptyChordState();\n }, [context, fallbackContext, snapshot.generation]);\n useInput(\n (input, key) => {\n const result = resolveKeybindingInput({\n snapshot,\n state: stateRef.current,\n context,\n input,\n key,\n now: Date.now(),\n });\n stateRef.current = result.state;\n if (fallbackContext === undefined || result.consumed) {\n fallbackStateRef.current = createEmptyChordState();\n handler(\n result.actions as readonly TKeybindingAction<TContext>[],\n input,\n key,\n result.consumed,\n context,\n );\n return;\n }\n const fallback = resolveKeybindingInput({\n snapshot,\n state: fallbackStateRef.current,\n context: fallbackContext,\n input,\n key,\n now: Date.now(),\n });\n fallbackStateRef.current = fallback.state;\n handler(\n fallback.actions as readonly TKeybindingAction<TFallback>[],\n input,\n key,\n fallback.consumed,\n fallbackContext,\n );\n },\n { isActive: options.isActive ?? true },\n );\n}\n","/**\n * CLI-2004 — the numbered-list menu (SSOT for its grammar).\n *\n * Every arrow-key menu becomes this in screen-reader mode: `N. <option>` lines followed by one\n * prompt naming the valid range. Arrow navigation and the `> ` cursor are position cues a reader\n * cannot follow — a spoken \"3\" is unambiguous, and the range in the prompt is the affordance.\n *\n * The prompt LITERAL is authored here, not copied from any reference: the surveyed documentation\n * publishes the prompt's NAME and the cancel suffix, never the composed string. This module is the\n * one place it exists, so the menus cannot drift into per-component dialects — the same reason\n * `key-hint-footer.tsx` owns the footer grammar.\n *\n * No colour props: the mode drops colour rather than substituting values, so the palette floor stays\n * green and nothing here depends on a cue a reader cannot hear.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** Appended to the prompt when Escape actually cancels the menu. */\nexport const NUMBERED_LIST_CANCEL_SUFFIX = ' or Escape to cancel';\n\n/** The prompt literal. `Enter selection (1-3)`, plus the cancel suffix where it applies. */\nexport function formatNumberedSelectionPrompt(itemCount: number, cancellable: boolean): string {\n const base = `Enter selection (1-${itemCount})`;\n return cancellable ? `${base}${NUMBERED_LIST_CANCEL_SUFFIX}` : base;\n}\n\n/** One row's number prefix: `1. `, `2. `, … */\nexport function numberedRowPrefix(index: number): string {\n return `${index + 1}. `;\n}\n\nexport interface INumberedSelectionPromptProps {\n /** How many rows the prompt names. Zero ⇒ nothing is rendered: `(1-0)` names no answer. */\n itemCount: number;\n cancellable?: boolean;\n /** Digits typed so far, echoed after the prompt. */\n buffer?: string;\n /** The last entry was out of range ⇒ the same literal is re-printed. */\n invalid?: boolean;\n}\n\n/**\n * The prompt line, separated from `NumberedList` so a menu that renders its own rows — the\n * checklist, whose rows carry a checkbox — asks its question in the same words rather than\n * inventing a dialect.\n */\nexport function NumberedSelectionPrompt({\n itemCount,\n cancellable = false,\n buffer = '',\n invalid = false,\n}: INumberedSelectionPromptProps): React.ReactElement | null {\n if (itemCount === 0) return null;\n const prompt = formatNumberedSelectionPrompt(itemCount, cancellable);\n return (\n <>\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {/* An out-of-range answer re-prints the SAME literal: the range is the correction. */}\n {invalid && <Text>{prompt}</Text>}\n </>\n );\n}\n\nexport interface INumberedListProps {\n /** Heading line, e.g. the menu title or the permission question. */\n title?: string;\n /** Extra context line under the title. */\n description?: string;\n /** The selectable options, in order. */\n options: readonly string[];\n /** Escape cancels ⇒ the prompt says so. */\n cancellable?: boolean;\n /** Digits typed so far, echoed after the prompt. */\n buffer?: string;\n /** The last entry was out of range ⇒ the same literal is re-printed. */\n invalid?: boolean;\n}\n\n/** Render the numbered list and its prompt. */\nexport function NumberedList({\n title,\n description,\n options,\n cancellable = false,\n buffer = '',\n invalid = false,\n}: INumberedListProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n {title !== undefined && title.length > 0 && <Text>{title}</Text>}\n {description !== undefined && description.length > 0 && <Text>{description}</Text>}\n {options.map((option, index) => (\n <Text key={`${index}-${option}`}>\n {numberedRowPrefix(index)}\n {option}\n </Text>\n ))}\n <NumberedSelectionPrompt\n itemCount={options.length}\n cancellable={cancellable}\n buffer={buffer}\n invalid={invalid}\n />\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { useWorkspaceSwitcherSelection } from './execution-workspace-switcher-selection.js';\nimport { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type {\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\n\n/** Footer for the workspace switcher (order: navigate → primary → dismiss). */\nexport const EXECUTION_WORKSPACE_SWITCHER_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Switch' },\n { keys: 'Ctrl+B/Esc', label: 'Close' },\n];\n\n/**\n * CLI-1994: the extra hint shown only while a forked entry is focused. Appended rather than always\n * present, because a key that does nothing on most rows reads as a broken key, not an unused one.\n */\nexport const EXECUTION_WORKSPACE_ATTACH_HINT: IKeyHint = { keys: 'a', label: 'Attach' };\n\ninterface IProps {\n snapshot: IExecutionWorkspaceSnapshot | null;\n selectedEntryId?: string;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n /**\n * CLI-1994: attach to the focused entry's forked session — a VIEW switch onto that record, never\n * a merge. Absent when the surface cannot switch sessions; the control is then not offered.\n */\n onAttach?: (entry: IExecutionWorkspaceEntry) => void;\n}\n\n/** CLI-1994: `attach` is offered by the projection; the switcher only reads what it was handed. */\nfunction offersAttach(entry: IExecutionWorkspaceEntry | undefined): boolean {\n return entry?.controls.includes('attach') === true;\n}\n\nexport default function ExecutionWorkspaceSwitcher({\n snapshot,\n selectedEntryId,\n onSelect,\n onClose,\n onAttach,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const entries = [...(snapshot?.entries ?? [])];\n const { normalized, visibleEntries, applyAction } = useWorkspaceSwitcherSelection({\n entries,\n selectedEntryId,\n onSelect,\n onClose,\n });\n const focusedEntry = entries[normalized.selectedIndex];\n const canAttach = onAttach !== undefined && offersAttach(focusedEntry);\n\n // CLI-2004: the switcher is an arrow-key menu, so the mode gives it numbers and a typed answer.\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && entries.length > 0,\n itemCount: entries.length,\n cancellable: true,\n onSelect: (index) => {\n const entry = entries[index];\n if (entry) onSelect(entry.id);\n },\n onCancel: onClose,\n });\n\n useKeybindingActions(\n 'workspace-switcher',\n (actions) => {\n for (const action of actions) {\n if (action === 'attach' && canAttach && focusedEntry) {\n // Attaching replaces what the terminal is looking at, so the switcher has nothing left to\n // switch between — it closes itself rather than making every caller remember to.\n onAttach(focusedEntry);\n onClose();\n } else if (action === 'previous' || action === 'next' || action === 'select') {\n applyAction(action);\n } else if (action === 'close') {\n applyAction('cancel');\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('workspace-switcher', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Switch'],\n ['close', 'Close'],\n ]);\n const attachHint = useKeybindingHints('workspace-switcher', [['attach', 'Attach']]);\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.focused })}\n paddingX={1}\n >\n <Text color={palette.text.accent} bold>\n Execution workspace\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n {visibleEntries.length === 0 ? (\n <Text dimColor>No workspace entries</Text>\n ) : screenReader ? (\n entries.map((entry, index) => (\n <ExecutionWorkspaceSwitcherRow\n key={entry.id}\n entry={entry}\n isFocused={false}\n selectedEntryId={selectedEntryId}\n rowNumber={index}\n />\n ))\n ) : (\n visibleEntries.map((entry, index) => (\n <ExecutionWorkspaceSwitcherRow\n key={entry.id}\n entry={entry}\n isFocused={normalized.scrollOffset + index === normalized.selectedIndex}\n selectedEntryId={selectedEntryId}\n />\n ))\n )}\n </Box>\n {screenReader ? (\n <ScreenReaderSelectionPrompt\n itemCount={entries.length}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n ) : (\n <KeyHintFooter hints={canAttach ? [...footerHints, ...attachHint] : footerHints} />\n )}\n </Box>\n );\n}\n\n/** The typed-selection prompt, re-printed verbatim after an out-of-range answer. */\nfunction ScreenReaderSelectionPrompt({\n itemCount,\n buffer,\n invalid,\n}: {\n itemCount: number;\n buffer: string;\n invalid: boolean;\n}): React.ReactElement {\n const prompt = formatNumberedSelectionPrompt(itemCount, true);\n return (\n <Box flexDirection=\"column\">\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {invalid && <Text>{prompt}</Text>}\n </Box>\n );\n}\n\nfunction ExecutionWorkspaceSwitcherRow({\n entry,\n isFocused,\n selectedEntryId,\n rowNumber,\n}: {\n entry: IExecutionWorkspaceEntry;\n isFocused: boolean;\n selectedEntryId?: string;\n rowNumber?: number;\n}): React.ReactElement {\n const palette = usePalette();\n const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId });\n return (\n <Text>\n <Text color={isFocused ? palette.text.accent : undefined} bold={isFocused}>\n {rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isFocused\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE}\n </Text>\n <Text color={statusGlyphColor(palette, row.statusKind)}>{`${row.radio} ${row.state}`}</Text>\n <Text\n color={isFocused ? palette.text.accent : undefined}\n bold={isFocused}\n >{` ${row.title}`}</Text>\n <Text dimColor>{` · ${row.statusLabel}`}</Text>\n {row.subtitle ? <Text dimColor>{` · ${row.subtitle}`}</Text> : null}\n {row.preview ? <Text dimColor>{` · ${row.preview}`}</Text> : null}\n </Text>\n );\n}\n","/**\n * CJK-aware cursor geometry and motion.\n *\n * Display columns and character indices are different things when a character is two columns wide;\n * these functions are the translation between them, and the two cursor moves built on it.\n *\n * Extracted from `cjk-text-input-flow.ts` (CLI-2004) by responsibility — that module routes\n * keystrokes, this one measures and moves.\n */\n\nimport stringWidth from 'string-width';\n\nimport type { ICjkTextInputFlowState, TCjkTextInputEffect } from './cjk-text-input-types.js';\n\ninterface ICjkMotionResult {\n state: ICjkTextInputFlowState;\n effect: TCjkTextInputEffect;\n}\n\nexport function displayOffset(chars: string[], charIndex: number, width: number): number {\n let offset = 0;\n for (let i = 0; i < charIndex && i < chars.length; i++) {\n const w = stringWidth(chars[i]!);\n const col = offset % width;\n if (col + w > width) offset += width - col;\n offset += w;\n }\n return offset;\n}\n\nexport function charIndexAtDisplayOffset(chars: string[], target: number, width: number): number {\n let offset = 0;\n for (let i = 0; i < chars.length; i++) {\n if (offset >= target) return i;\n const w = stringWidth(chars[i]!);\n const col = offset % width;\n if (col + w > width) offset += width - col;\n offset += w;\n }\n return chars.length;\n}\n\nexport function moveCursorVertically(\n state: ICjkTextInputFlowState,\n direction: 'up' | 'down',\n availableWidth: number | undefined,\n): ICjkMotionResult {\n if (!availableWidth || availableWidth <= 0) {\n return { state, effect: { type: 'none' } };\n }\n const chars = [...state.value];\n const offset = displayOffset(chars, state.cursor, availableWidth);\n const target = direction === 'up' ? offset - availableWidth : offset + availableWidth;\n if (target < 0) {\n return { state, effect: { type: 'none' } };\n }\n const cursor = charIndexAtDisplayOffset(chars, target, availableWidth);\n if (cursor === state.cursor) {\n return { state, effect: { type: 'none' } };\n }\n return { state: { ...state, cursor }, effect: { type: 'render' } };\n}\n\nexport function moveCursorHorizontally(\n state: ICjkTextInputFlowState,\n direction: 'left' | 'right',\n): ICjkMotionResult {\n if (direction === 'left' && state.cursor > 0) {\n return { state: { ...state, cursor: state.cursor - 1 }, effect: { type: 'render' } };\n }\n if (direction === 'right' && state.cursor < state.value.length) {\n return { state: { ...state, cursor: state.cursor + 1 }, effect: { type: 'render' } };\n }\n return { state, effect: { type: 'none' } };\n}\n","/**\n * CLI-2004 — deletion, and what was deleted.\n *\n * A reader announces the line it is on. When a whole word or the whole line disappears, the only\n * thing it can say is what is LEFT — which is exactly the information you do not need. These helpers\n * return the removed text alongside the new value so the input can say what went.\n *\n * Word and line deletion are added here as well as reported: the flow had only single-character\n * backspace, so `Ctrl+W`/`Ctrl+U` previously fell through to the printable-input path.\n *\n * Extracted from `cjk-text-input-flow.ts` by responsibility — that module routes keystrokes, this\n * one computes edits.\n */\n\n/** The result of one deletion: the new buffer, the new cursor, and the text that was removed. */\nexport interface ICjkDeletion {\n value: string;\n cursor: number;\n deleted: string;\n}\n\n/** Remove the character before the cursor. `undefined` at the start of the buffer. */\nexport function deleteCharBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n return {\n value: value.slice(0, cursor - 1) + value.slice(cursor),\n cursor: cursor - 1,\n deleted: value.slice(cursor - 1, cursor),\n };\n}\n\n/**\n * Remove the word before the cursor — trailing spaces first, then the run of non-space characters,\n * which is the behaviour every shell's `Ctrl+W` has.\n */\nexport function deleteWordBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n let start = cursor;\n while (start > 0 && /\\s/u.test(value[start - 1] ?? '')) start -= 1;\n while (start > 0 && !/\\s/u.test(value[start - 1] ?? '')) start -= 1;\n if (start === cursor) return undefined;\n return {\n value: value.slice(0, start) + value.slice(cursor),\n cursor: start,\n deleted: value.slice(start, cursor),\n };\n}\n\n/** Remove everything before the cursor (`Ctrl+U`). */\nexport function deleteLineBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n return { value: value.slice(cursor), cursor: 0, deleted: value.slice(0, cursor) };\n}\n","import { moveCursorHorizontally, moveCursorVertically } from './cjk-cursor-motion.js';\nimport {\n deleteCharBeforeCursor,\n deleteLineBeforeCursor,\n deleteWordBeforeCursor,\n type ICjkDeletion,\n} from './cjk-text-deletion.js';\nimport type {\n ICjkTextInputFlowState,\n TCjkDeletionScope,\n TCjkTextInputEffect,\n} from './cjk-text-input-types.js';\n\nconst PASTE_START = '[200~';\nconst PASTE_END = '[201~';\nconst LAST_ASCII_CONTROL_CODE = 0x1f;\nconst DELETE_CONTROL_CODE = 0x7f;\n\nexport { charIndexAtDisplayOffset, displayOffset } from './cjk-cursor-motion.js';\nexport type {\n ICjkTextInputFlowState,\n TCjkDeletionScope,\n TCjkTextInputEffect,\n} from './cjk-text-input-types.js';\n\nexport interface ICjkTextInputKey {\n ctrl?: boolean;\n tab?: boolean;\n shift?: boolean;\n upArrow?: boolean;\n downArrow?: boolean;\n return?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n backspace?: boolean;\n delete?: boolean;\n}\n\nexport interface ICjkTextInputFlowOptions {\n availableWidth?: number;\n canPaste: boolean;\n enableVerticalNavigation?: boolean;\n}\n\ninterface ICjkTextInputFlowResult {\n state: ICjkTextInputFlowState;\n effect: TCjkTextInputEffect;\n}\n\nexport function createCjkTextInputFlowState(value: string): ICjkTextInputFlowState {\n return { value, cursor: value.length, isPasting: false, pasteBuffer: '' };\n}\n\nexport function syncCjkTextInputFlowState(\n state: ICjkTextInputFlowState,\n value: string,\n cursorHint: number | null,\n): ICjkTextInputFlowState {\n if (value === state.value) {\n return state;\n }\n return {\n ...state,\n value,\n cursor: cursorHint != null ? Math.min(cursorHint, value.length) : value.length,\n };\n}\n\nexport function applyCjkTextInput(\n state: ICjkTextInputFlowState,\n input: string,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n const pasteResult = applyPasteBoundaryInput(state, input, options);\n if (pasteResult !== undefined) return pasteResult;\n const controlResult = applyControlInput(state, input, key, options);\n if (controlResult !== undefined) return controlResult;\n const cursorResult = applyCursorInput(state, key, options);\n if (cursorResult !== undefined) return cursorResult;\n return insertPrintableInput(state, input);\n}\n\nexport function applyCjkTextPaste(\n state: ICjkTextInputFlowState,\n text: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n const normalizedText = text.replace(/\\r\\n?/g, '\\n');\n if (normalizedText.length === 0) {\n return { state, effect: { type: 'none' } };\n }\n if (normalizedText.includes('\\n') && options.canPaste) {\n return { state, effect: { type: 'paste', text: normalizedText, cursor: state.cursor } };\n }\n return insertPrintableInput(state, normalizedText);\n}\n\nfunction applyPasteBoundaryInput(\n state: ICjkTextInputFlowState,\n input: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if (input === PASTE_START || input.startsWith(PASTE_START)) {\n return startBracketedPaste(state, input);\n }\n if (state.isPasting) {\n return continueBracketedPaste(state, input, options);\n }\n return undefined;\n}\n\nfunction applyControlInput(\n state: ICjkTextInputFlowState,\n input: string,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if ((key.ctrl === true && input === 'c') || key.tab === true) {\n return { state, effect: { type: 'none' } };\n }\n if (key.ctrl === true && (input === 'w' || input === 'u')) {\n const scope: TCjkDeletionScope = input === 'w' ? 'word' : 'line';\n const remove = scope === 'word' ? deleteWordBeforeCursor : deleteLineBeforeCursor;\n return applyDeletion(state, remove(state.value, state.cursor), scope);\n }\n if (key.return === true) {\n return { state, effect: { type: 'submit', value: state.value } };\n }\n if (input.length > 1 && (input.includes('\\n') || input.includes('\\r')) && options.canPaste) {\n return {\n state,\n effect: { type: 'paste', text: input.replace(/\\r\\n?/g, '\\n'), cursor: state.cursor },\n };\n }\n return undefined;\n}\n\nfunction applyCursorInput(\n state: ICjkTextInputFlowState,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if (key.upArrow === true || key.downArrow === true) {\n if (options.enableVerticalNavigation === false) {\n return { state, effect: { type: 'none' } };\n }\n return moveCursorVertically(\n state,\n key.upArrow === true ? 'up' : 'down',\n options.availableWidth,\n );\n }\n if (key.leftArrow === true) {\n return moveCursorHorizontally(state, 'left');\n }\n if (key.rightArrow === true) {\n return moveCursorHorizontally(state, 'right');\n }\n if (key.backspace === true || key.delete === true) {\n return applyDeletion(state, deleteCharBeforeCursor(state.value, state.cursor));\n }\n return undefined;\n}\n\nexport function filterPrintable(input: string | null | undefined): string {\n if (!input || input.length === 0) return '';\n let output = '';\n for (const char of input) {\n const code = char.charCodeAt(0);\n if (code > LAST_ASCII_CONTROL_CODE && code !== DELETE_CONTROL_CODE) {\n output += char;\n }\n }\n return output;\n}\n\nexport function insertAtCursor(\n value: string,\n cursor: number,\n input: string,\n): { value: string; cursor: number } {\n const next = value.slice(0, cursor) + input + value.slice(cursor);\n return { value: next, cursor: cursor + input.length };\n}\n\nfunction startBracketedPaste(\n state: ICjkTextInputFlowState,\n input: string,\n): ICjkTextInputFlowResult {\n return {\n state: { ...state, isPasting: true, pasteBuffer: input.slice(PASTE_START.length) },\n effect: { type: 'none' },\n };\n}\n\nfunction continueBracketedPaste(\n state: ICjkTextInputFlowState,\n input: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n if (input !== PASTE_END && !input.includes(PASTE_END)) {\n return {\n state: { ...state, pasteBuffer: state.pasteBuffer + input },\n effect: { type: 'none' },\n };\n }\n const beforeMarker = input.split(PASTE_END)[0] ?? '';\n const nextState = { ...state, isPasting: false, pasteBuffer: '' };\n return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);\n}\n\n/**\n * Apply a deletion result. A word/line delete carries the removed text so the caller can announce\n * it; a character delete does not — the reader already heard the character go.\n */\nfunction applyDeletion(\n state: ICjkTextInputFlowState,\n deletion: ICjkDeletion | undefined,\n scope?: TCjkDeletionScope,\n): ICjkTextInputFlowResult {\n if (deletion === undefined) {\n return { state, effect: { type: 'none' } };\n }\n return {\n state: { ...state, value: deletion.value, cursor: deletion.cursor },\n effect: {\n type: 'change',\n value: deletion.value,\n ...(scope !== undefined ? { deleted: deletion.deleted, deletedScope: scope } : {}),\n },\n };\n}\n\nfunction insertPrintableInput(\n state: ICjkTextInputFlowState,\n input: string,\n): ICjkTextInputFlowResult {\n const printable = filterPrintable(input);\n if (printable.length === 0) {\n return { state, effect: { type: 'none' } };\n }\n const result = insertAtCursor(state.value, state.cursor, printable);\n return {\n state: { ...state, value: result.value, cursor: result.cursor },\n effect: { type: 'change', value: result.value },\n };\n}\n","/**\n * CLI-061 — deferred submit for the CJK-IME last-character-drop race.\n *\n * IME composition is finalized by the terminal, which writes the final composed character into the pty as its\n * OWN stdin event that arrives JUST AFTER the confirming `\\r`. If we submit synchronously on Enter, the trailing\n * syllable has not yet been applied to the input state, so it is dropped (`안녕하세요` → `안녕하세`). Deferring the\n * submit a few milliseconds lets that trailing event land, after which we re-read the LATEST value and submit it.\n *\n * This is the same app-layer fix Gemini CLI applied on the same Ink (PR #4987/#7556); no framework change and\n * no upstream Ink dependency (Ink #759 stays a watch-only item).\n *\n * Injectable `schedule`/`clear` (defaulting to the real timer) keep this unit deterministically testable —\n * there is NO fake timer shipped in `src`; a test injects its own scheduler.\n */\n\n/**\n * Defer window before a submit fires. Larger = safer against a slow/remote pty delivering the trailing event\n * late, but adds this latency to EVERY submit (including plain ASCII). 50ms matches Gemini's default — long\n * enough for a local IME finalize, short enough to feel instant. This NARROWS the race; on a very slow pty the\n * trailing event can still land after the window (residual, accepted).\n */\nexport const IME_SUBMIT_DEFER_MS = 50;\n\ntype TTimer = ReturnType<typeof setTimeout>;\n\nexport interface IDeferSubmitState {\n timer: TTimer | null;\n /** Gates a SECOND submit / Enter only — never the input pipeline (the trailing char must still be processed). */\n isSubmitting: boolean;\n}\n\nexport function createDeferSubmitState(): IDeferSubmitState {\n return { timer: null, isSubmitting: false };\n}\n\n/**\n * Schedule a deferred submit. At fire time it reads `readLatest()` (the LIVE input value, not a value captured\n * at Enter time) and submits it. A submit already in flight is ignored (no double-submit) — but note the caller\n * MUST keep its input pipeline live during the window so `readLatest()` sees the trailing character.\n */\nexport function scheduleDeferredSubmit(\n state: IDeferSubmitState,\n readLatest: () => string,\n submit: (value: string) => void,\n deferMs: number = IME_SUBMIT_DEFER_MS,\n schedule: (fn: () => void, ms: number) => TTimer = setTimeout,\n): void {\n if (state.isSubmitting) return; // second Enter within the window — ignore, do not double-submit\n state.isSubmitting = true;\n state.timer = schedule(() => {\n state.timer = null;\n state.isSubmitting = false;\n submit(readLatest());\n }, deferMs);\n}\n\n/** Cancel a pending deferred submit (call on unmount so no submit fires after teardown and no timer leaks). */\nexport function cancelDeferredSubmit(\n state: IDeferSubmitState,\n clear: (timer: TTimer) => void = clearTimeout,\n): void {\n if (state.timer !== null) {\n clear(state.timer);\n state.timer = null;\n }\n state.isSubmitting = false;\n}\n","/**\n * CLI-062 — pure flow for real-terminal-cursor positioning during CJK IME composition.\n *\n * Maps the input box's absolute frame-space origin plus the live (value, cursor) to the exact\n * screen cell the hardware cursor must occupy, and guards WHEN positioning is allowed at all.\n * Contract: .design/investigations/2026-07-25-cli-062-ime-cursor-design.md.\n *\n * The wrap math reuses `displayOffset` — the same wrap-aware column accounting the input's\n * vertical navigation already trusts — so the computed cell always matches where ink actually\n * rendered the character under the cursor.\n */\n\nimport { displayOffset } from './cjk-text-input-flow.js';\n\nexport interface ICursorCellInput {\n /** Absolute frame-space column of the input box's first cell. */\n absX: number;\n /** Absolute frame-space row of the input box's first line. */\n absY: number;\n /** Current input value. */\n value: string;\n /** Cursor as a character index into the value (clamped to the value length). */\n cursor: number;\n /** Wrap width in columns; unset/non-positive means the value renders unwrapped. */\n availableWidth?: number;\n}\n\nexport interface ICursorCell {\n x: number;\n y: number;\n}\n\nexport interface IRealCursorGuardInput {\n /** Whether the input box has been measured in the CURRENT yoga layout. */\n hasMeasured: boolean;\n /** Target cursor row (absolute frame space). */\n y: number;\n /** Height of the live frame (ink-root yoga height). */\n frameHeight: number;\n /** Terminal viewport rows. */\n viewportRows: number;\n /** Terminal capability + focus gate (supportsImeCursorPositioning() && focused && showCursor). */\n capability: boolean;\n}\n\n/** Compute the hardware-cursor cell for the current composition point. */\nexport function computeCursorCell(input: ICursorCellInput): ICursorCell {\n const chars = [...input.value];\n const cursor = Math.min(Math.max(input.cursor, 0), chars.length);\n const width =\n input.availableWidth !== undefined && input.availableWidth > 0\n ? input.availableWidth\n : undefined;\n const offset = displayOffset(chars, cursor, width ?? Number.MAX_SAFE_INTEGER);\n if (width === undefined) {\n return { x: input.absX + offset, y: input.absY };\n }\n return { x: input.absX + (offset % width), y: input.absY + Math.floor(offset / width) };\n}\n\n/**\n * Crash-avoidance guard — the SIGSEGV invariants from the CLI-062 contract:\n *\n * - I1: never a GUESSED row — position only with a y measured from the current yoga layout\n * (`hasMeasured`); the historical hardcoded `y: 0` pointed the cursor at the logo area, where\n * Terminal.app's Korean IME `attributedSubstringFromRange:` null-derefs (Apple-side bug).\n * - I2: never into an overflowing frame — when `frameHeight >= viewportRows` ink's bottom-anchor\n * assumption breaks (upstream defect, present in ink 7.0.5 AND 7.1.1: the cursor lands one row\n * high in the fullscreen path, and an overflowing frame clamps `cursorUp` at the screen top —\n * the original crash geometry). Same for a y outside `[0, frameHeight)`.\n * - I5 lives in `supportsImeCursorPositioning()` and arrives here folded into `capability`.\n *\n * I3 (all movement rides ink's synchronized frame writes) and I4 (guard fail → no call → today's\n * rendering) are properties of the hook that consumes this guard (useRealCursorPosition).\n */\nexport function shouldPositionRealCursor(input: IRealCursorGuardInput): boolean {\n if (!input.capability) return false;\n if (!input.hasMeasured) return false; // I1\n if (input.viewportRows <= 0 || input.frameHeight <= 0) return false;\n if (input.frameHeight >= input.viewportRows) return false; // I2 (fullscreen / overflow)\n if (input.y < 0 || input.y >= input.frameHeight) return false; // I2 (out of frame)\n return true;\n}\n","/**\n * CLI-062 — position the REAL terminal cursor at the CJK composition point.\n *\n * Mechanism (contract: .design/investigations/2026-07-25-cli-062-ime-cursor-design.md, POC-PASS):\n * the input box's absolute frame-space origin comes from walking the yoga `parentNode` chain from\n * a `<Box ref>` (the `5195e326b` approach, minus its debug logging); the cell is handed to ink's\n * `useCursor().setCursorPosition`, which converts it into a relative `cursorUp` from the frame\n * bottom INSIDE the same atomic synchronized frame write (invariant I3: never out-of-band — all\n * cursor movement rides ink's ≤30fps frame writes; this hook itself writes NOTHING to any stream,\n * the PoC row-accounting lesson).\n *\n * Measurement happens post-commit (layout is valid there) and is stored in state so the NEXT\n * commit's insertion effect propagates a fresh position with its frame; `useBoxMetrics` provides\n * the layout-change subscription so sibling-driven moves re-measure too. A stale origin self-heals\n * within one frame (ink recomputes layout before onRender and notifies layout listeners).\n */\n\nimport { useBoxMetrics, useCursor, useWindowSize } from 'ink';\nimport { useEffect, useState } from 'react';\n\nimport { computeCursorCell, shouldPositionRealCursor } from '../flows/real-cursor-flow.js';\n\nimport type { DOMElement } from 'ink';\nimport type { RefObject } from 'react';\n\nexport interface IUseRealCursorPositionOptions {\n /** Ref attached to the `<Box>` wrapping the input's text. */\n boxRef: RefObject<DOMElement | null>;\n /** Capability + focus gate: supportsImeCursorPositioning() && focus && showCursor. */\n enabled: boolean;\n /** Live input value (from the input's state ref — fresh at render time). */\n value: string;\n /** Live cursor character index. */\n cursor: number;\n /** Wrap width in columns (matches the input's own wrap math). */\n availableWidth?: number;\n}\n\nexport interface IUseRealCursorPositionResult {\n /** True while the hardware cursor is being positioned — the drawn cursor must be suppressed. */\n realCursorActive: boolean;\n}\n\ninterface IMeasuredOrigin {\n x: number;\n y: number;\n frameHeight: number;\n}\n\n/** Walk the yoga `parentNode` chain to the absolute frame-space origin, plus the root frame height. */\nfunction measureAbsoluteOrigin(element: DOMElement): IMeasuredOrigin | undefined {\n if (element.yogaNode === undefined) return undefined;\n let x = 0;\n let y = 0;\n let current: DOMElement | undefined = element;\n let root: DOMElement = element;\n while (current !== undefined) {\n const layout = current.yogaNode?.getComputedLayout();\n x += layout?.left ?? 0;\n y += layout?.top ?? 0;\n root = current;\n current = current.parentNode;\n }\n const frameHeight = root.yogaNode?.getComputedLayout().height ?? 0;\n return { x, y, frameHeight };\n}\n\nfunction sameOrigin(a: IMeasuredOrigin | undefined, b: IMeasuredOrigin | undefined): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.x === b.x && a.y === b.y && a.frameHeight === b.frameHeight;\n}\n\nexport function useRealCursorPosition(\n options: IUseRealCursorPositionOptions,\n): IUseRealCursorPositionResult {\n const { setCursorPosition } = useCursor();\n // Layout-change subscription: useBoxMetrics re-renders this component whenever the tracked\n // box's metrics change on a root layout commit, and exposes hasMeasured for invariant I1.\n const metrics = useBoxMetrics(options.boxRef);\n const { rows: viewportRows } = useWindowSize();\n const [origin, setOrigin] = useState<IMeasuredOrigin | undefined>(undefined);\n\n // I1: never a guessed row — measure the absolute origin ONLY from a committed yoga layout\n // (post-commit effect; ink ran calculateLayout before onRender, passive effects run after).\n // No dependency array on purpose: any render may follow a layout change, and the setState\n // bails out when the origin is unchanged, so this settles instead of looping.\n useEffect(() => {\n const element = options.boxRef.current;\n const next = element === null ? undefined : measureAbsoluteOrigin(element);\n setOrigin((previous) => (sameOrigin(previous, next) ? previous : next));\n });\n\n const cell =\n origin !== undefined && options.enabled\n ? computeCursorCell({\n absX: origin.x,\n absY: origin.y,\n value: options.value,\n cursor: options.cursor,\n ...(options.availableWidth !== undefined\n ? { availableWidth: options.availableWidth }\n : {}),\n })\n : undefined;\n\n const realCursorActive =\n cell !== undefined &&\n origin !== undefined &&\n shouldPositionRealCursor({\n hasMeasured: metrics.hasMeasured, // I1 — `origin` is already narrowed non-undefined above\n y: cell.y,\n frameHeight: origin.frameHeight, // I2 checked inside the guard\n viewportRows,\n capability: options.enabled, // I5 folded in via supportsImeCursorPositioning()\n });\n\n // Render-phase call is safe: useCursor only records the position in a ref; propagation happens\n // in its insertion effect, inside ink's synchronized frame write (I3). I4: when the guard fails\n // (blur, fullscreen frame, not measured yet) we pass `undefined` — ink hides the hardware cursor\n // and the caller falls back to today's drawn-cursor rendering; on unmount, useCursor's cleanup\n // propagates `undefined` by itself.\n setCursorPosition(realCursorActive && cell !== undefined ? { x: cell.x, y: cell.y } : undefined);\n\n return { realCursorActive };\n}\n","/**\n * SCREEN-2670 — the one place a component reaches the owned write path.\n *\n * Mirrors `screen-reader-context.tsx`: the port is published from `render.tsx`, no component takes\n * it as a prop from its parent, so a missed prop cannot silently leave one component writing past\n * the queue. It also keeps a live writer handle out of the presentation tree, which declares itself\n * free of channels, sessions and mutable state.\n *\n * Outside a provider (every existing test, and every run with the mode off) the port is inert:\n * arming does nothing and writes go straight to the process's own stdout, exactly as today.\n */\n\nimport React, { createContext, useContext } from 'react';\n\nexport interface IScreenReaderPacingPort {\n /** Arm the echo flag for the batch the current keystroke's commit will open. */\n armEchoRelease(): void;\n /** Write a positional sequence (an OSC 133 mark) through the same ordered path. */\n write(text: string): void;\n}\n\nconst inertPort: IScreenReaderPacingPort = {\n armEchoRelease(): void {},\n write(text: string): void {\n process.stdout.write(text);\n },\n};\n\nconst ScreenReaderPacingContext = createContext<IScreenReaderPacingPort>(inertPort);\n\n/** Publish the owned write path to the tree. */\nexport function ScreenReaderPacingProvider({\n port,\n children,\n}: {\n port: IScreenReaderPacingPort;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ScreenReaderPacingContext.Provider value={port}>{children}</ScreenReaderPacingContext.Provider>\n );\n}\n\n/** Read the port. Inert outside a provider. */\nexport function useScreenReaderPacing(): IScreenReaderPacingPort {\n return useContext(ScreenReaderPacingContext);\n}\n","import React, { createContext, useContext } from 'react';\n\n/** Host-selected overrides; absent values leave terminal detection in charge. */\nexport interface ITerminalCapabilityOverrides {\n readonly imeCursorPositioning?: boolean | undefined;\n readonly turnMarks?: boolean | undefined;\n}\n\nconst TerminalCapabilitiesContext = createContext<ITerminalCapabilityOverrides>({});\n\nexport function TerminalCapabilitiesProvider({\n overrides,\n children,\n}: {\n overrides: ITerminalCapabilityOverrides | undefined;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <TerminalCapabilitiesContext.Provider value={overrides ?? {}}>\n {children}\n </TerminalCapabilitiesContext.Provider>\n );\n}\n\nexport function useTerminalCapabilityOverrides(): ITerminalCapabilityOverrides {\n return useContext(TerminalCapabilitiesContext);\n}\n","/**\n * CJK-aware TextInput component for Ink.\n *\n * Replaces ink-text-input with proper wide character support:\n * - Uses string-width for display width calculation\n * - Cursor position based on character index (not display columns)\n * - Renders CJK characters correctly (2 columns each)\n * - Uses refs for value/cursor to avoid React state batching issues\n * (IME sends multiple keystrokes synchronously, state updates are async)\n *\n * Drop-in replacement: same props as ink-text-input.\n */\n\nimport chalk from 'chalk';\nimport { Box, usePaste } from 'ink';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n applyCjkTextInput,\n applyCjkTextPaste,\n createCjkTextInputFlowState,\n syncCjkTextInputFlowState,\n type ICjkTextInputFlowState,\n} from './flows/cjk-text-input-flow.js';\nimport {\n cancelDeferredSubmit,\n createDeferSubmitState,\n scheduleDeferredSubmit,\n type IDeferSubmitState,\n} from './flows/defer-submit.js';\nimport { useRealCursorPosition } from './hooks/useRealCursorPosition.js';\nimport { useKeybindingActions } from './keybindings/keybindings-context.js';\nimport { RenderedText } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReaderPacing } from './screen-reader-pacing-context.js';\nimport { supportsImeCursorPositioning } from './terminal-capabilities.js';\nimport { useTerminalCapabilityOverrides } from './terminal-capabilities-context.js';\nimport { foreground, usePalette } from './theme/index.js';\n\nimport type { IKeyInput, TKeybindingContext } from './keybindings/keybinding-registry.js';\nimport type { DOMElement } from 'ink';\n\ninterface IProps {\n value: string;\n onChange: (value: string) => void;\n onSubmit?: (value: string) => void;\n onPaste?: (text: string, cursorPosition: number) => void;\n placeholder?: string;\n focus?: boolean;\n showCursor?: boolean;\n /** Available width in columns for visual line wrapping navigation */\n availableWidth?: number;\n /** Cursor position hint for external value changes. null = end (default). */\n cursorHint?: number | null;\n /** When false, parent flows own up/down arrow behavior. */\n enableVerticalNavigation?: boolean;\n /**\n * CLI-2004: called with the text a word/line delete removed. A reader announces what is LEFT on\n * the line, so the removed text is the one thing it cannot tell you.\n */\n onDeletedText?: (deleted: string) => void;\n /** Semantic key context; autocomplete consumes its own actions without invoking text actions. */\n keybindingContext?: 'chat-input' | 'text-input' | 'autocomplete-menu';\n}\n\ninterface IInputHandlerOptions {\n stateRef: React.MutableRefObject<ICjkTextInputFlowState>;\n onChange: (value: string) => void;\n onSubmit?: (value: string) => void;\n onPaste?: (text: string, cursorPosition: number) => void;\n availableWidth?: number;\n focus: boolean;\n enableVerticalNavigation: boolean;\n onDeletedText?: (deleted: string) => void;\n forceRender: React.Dispatch<React.SetStateAction<number>>;\n /** CLI-061: deferred-submit state (timer + submit guard). The input pipeline stays live during the window. */\n deferState: IDeferSubmitState;\n keybindingContext: 'chat-input' | 'text-input' | 'autocomplete-menu';\n}\n\nexport default function CjkTextInput({\n value,\n onChange,\n onSubmit,\n onPaste,\n placeholder = '',\n focus = true,\n showCursor = true,\n availableWidth,\n cursorHint = null,\n enableVerticalNavigation = true,\n onDeletedText,\n keybindingContext = 'text-input',\n}: IProps): React.ReactElement {\n const stateRef = useRef<ICjkTextInputFlowState>(createCjkTextInputFlowState(value));\n const [, forceRender] = useState(0);\n // CLI-061: deferred-submit state so a trailing IME character (a stdin event arriving just after Enter) is\n // included in the submitted value. The timer is cancelled on unmount so no submit fires after teardown.\n const deferRef = useRef<IDeferSubmitState>(createDeferSubmitState());\n useEffect(() => () => cancelDeferredSubmit(deferRef.current), []);\n\n // Sync ref when value changes from parent (e.g., setValue(''), tab completion, paste)\n stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);\n\n useCjkTextInputHandlers({\n stateRef,\n onChange,\n onSubmit,\n onPaste,\n availableWidth,\n focus,\n enableVerticalNavigation,\n ...(onDeletedText !== undefined ? { onDeletedText } : {}),\n forceRender,\n deferState: deferRef.current,\n keybindingContext,\n });\n\n // CLI-062: real terminal cursor positioning so the OS IME composition window appears AT the\n // input position. The historical Terminal.app SIGSEGV came from a hardcoded `y: 0` (logo area);\n // the hook only positions with a y measured from the live yoga layout and refuses the ink\n // fullscreen geometry (invariants I1–I5 — see flows/real-cursor-flow.ts and the hook itself).\n // SCREEN-2002: the placeholder's muted colour comes from the resolved theme, not a chalk literal.\n const palette = usePalette();\n const mutedPlaceholder = useMemo(() => foreground(palette.text.muted), [palette.text.muted]);\n const boxRef = useRef<DOMElement | null>(null);\n const terminalCapabilities = useTerminalCapabilityOverrides();\n const { realCursorActive } = useRealCursorPosition({\n boxRef,\n enabled:\n focus &&\n showCursor &&\n supportsImeCursorPositioning({ override: terminalCapabilities.imeCursorPositioning }),\n value: stateRef.current.value,\n cursor: stateRef.current.cursor,\n ...(availableWidth !== undefined ? { availableWidth } : {}),\n });\n\n return (\n <Box ref={boxRef}>\n <RenderedText>\n {renderWithCursor(\n stateRef.current.value,\n stateRef.current.cursor,\n placeholder,\n // I4: the drawn inverse cursor is suppressed ONLY while real positioning is active;\n // any guard failure falls back to exactly today's rendering.\n showCursor && focus && !realCursorActive,\n mutedPlaceholder,\n )}\n </RenderedText>\n </Box>\n );\n}\n\n/**\n * SCREEN-2670: which keys arm the echo exemption. Text insertion and deletion only — a keystroke\n * whose commit repaints the composer. Submit and execute run through this same handler\n * (`translateCjkInput` returns `key.return` for them) and their commit appends the user's prompt\n * line to the transcript, which is exactly the content the park exists to pace; cursor moves and\n * tab commit no text of the composer's own.\n */\nfunction mutatesComposerText(\n action: string | undefined,\n translated: { input: string; key: IKeyInput },\n): boolean {\n if (action === 'delete-backward' || action === 'delete-word' || action === 'delete-line')\n return true;\n return (\n translated.key.return !== true &&\n translated.key.ctrl !== true &&\n translated.key.tab !== true &&\n translated.input.length > 0\n );\n}\n\nfunction useCjkTextInputHandlers(options: IInputHandlerOptions): void {\n const pacing = useScreenReaderPacing();\n usePaste(\n (text) => {\n pacing.armEchoRelease();\n applyCjkFlowSafely(options, () =>\n applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options)),\n );\n },\n { isActive: options.focus },\n );\n\n useKeybindingActions(\n options.keybindingContext,\n (actions, input, key, consumed, resolvedContext) => {\n const translated = translateCjkInput(resolvedContext, actions, input, key, consumed);\n if (translated === undefined) return;\n if (mutatesComposerText(actions[0], translated)) pacing.armEchoRelease();\n applyCjkFlowSafely(options, () =>\n applyCjkTextInput(\n options.stateRef.current,\n translated.input,\n translated.key,\n createFlowOptions(options),\n ),\n );\n },\n {\n isActive: options.focus,\n // #3125: the menu owns only its own keys; editing keys keep the chat input's bindings.\n ...(options.keybindingContext === 'autocomplete-menu'\n ? { fallbackContext: 'chat-input' as const }\n : {}),\n },\n );\n}\n\nfunction translateCjkInput(\n context: TKeybindingContext,\n actions: readonly string[],\n input: string,\n key: IKeyInput,\n consumed: boolean,\n): { input: string; key: IKeyInput } | undefined {\n const action = actions[0];\n if (action === 'submit' || action === 'execute') return { input: '', key: { return: true } };\n if (context === 'autocomplete-menu' && consumed) return undefined;\n if (action === 'cursor-left') return { input: '', key: { leftArrow: true } };\n if (action === 'cursor-right') return { input: '', key: { rightArrow: true } };\n if (action === 'cursor-up') return { input: '', key: { upArrow: true } };\n if (action === 'cursor-down') return { input: '', key: { downArrow: true } };\n if (action === 'delete-backward') return { input: '', key: { backspace: true } };\n if (action === 'delete-word') return { input: 'w', key: { ctrl: true } };\n if (action === 'delete-line') return { input: 'u', key: { ctrl: true } };\n if (consumed) return undefined;\n const rawText =\n key.ctrl !== true &&\n key.meta !== true &&\n !key.return &&\n !key.escape &&\n !key.tab &&\n !key.upArrow &&\n !key.downArrow &&\n !key.leftArrow &&\n !key.rightArrow &&\n !key.backspace &&\n !key.delete;\n return rawText || (key.ctrl === true && input === 'c') || key.tab ? { input, key } : undefined;\n}\n\nfunction createFlowOptions(options: IInputHandlerOptions): {\n availableWidth?: number;\n canPaste: boolean;\n enableVerticalNavigation: boolean;\n} {\n return {\n availableWidth: options.availableWidth,\n canPaste: options.onPaste !== undefined,\n enableVerticalNavigation: options.enableVerticalNavigation,\n };\n}\n\nfunction applyCjkFlowSafely(\n options: IInputHandlerOptions,\n run: () => ReturnType<typeof applyCjkTextInput>,\n): void {\n try {\n const result = run();\n options.stateRef.current = result.state;\n applyCjkTextInputEffect(options, result.effect);\n } catch {\n // allow-fallback: Korean IME in raw mode can produce unexpected byte sequences\n }\n}\n\nfunction applyCjkTextInputEffect(\n options: IInputHandlerOptions,\n effect: ReturnType<typeof applyCjkTextInput>['effect'],\n): void {\n if (effect.type === 'change') {\n if (effect.deleted !== undefined) options.onDeletedText?.(effect.deleted);\n options.onChange(effect.value);\n } else if (effect.type === 'submit') {\n // CLI-061: DEFER the submit and re-read the LIVE `stateRef.current.value` at fire time — never the stale\n // `effect.value` captured at Enter. The input pipeline (this same handler) stays live during the window, so\n // a trailing IME character's stdin event is applied to `stateRef` before the deferred read. The guard only\n // blocks a SECOND submit — it must not gate the input pipeline (that would drop the trailing char).\n const onSubmit = options.onSubmit;\n if (onSubmit) {\n scheduleDeferredSubmit(options.deferState, () => options.stateRef.current.value, onSubmit);\n }\n } else if (effect.type === 'paste') {\n options.onPaste?.(effect.text, effect.cursor);\n } else if (effect.type === 'render') {\n options.forceRender((n) => n + 1);\n }\n}\n\n/**\n * Render text with an inverse-style cursor at the correct position.\n *\n * #2222: the input is sanitized BEFORE the cursor is drawn, and the result renders through\n * `RenderedText` — the SGR here (inverse cursor, muted placeholder) is this component's own styling,\n * and `SafeText` would strip it along with everything else, which is exactly how the drawn cursor\n * disappeared (CLI-062 byte-identical rendering).\n */\nfunction renderWithCursor(\n rawValue: string,\n cursorOffset: number,\n rawPlaceholder: string,\n showCursor: boolean,\n mutedPlaceholder: (text: string) => string,\n): string {\n const value = sanitizeTerminalText(rawValue);\n const placeholder = sanitizeTerminalText(rawPlaceholder);\n if (!showCursor) {\n return value.length > 0 ? value : placeholder ? mutedPlaceholder(placeholder) : '';\n }\n\n if (value.length === 0) {\n if (placeholder.length > 0) {\n return chalk.inverse(placeholder[0]) + mutedPlaceholder(placeholder.slice(1));\n }\n return chalk.inverse(' ');\n }\n\n const chars = [...value];\n let rendered = '';\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i] ?? '';\n rendered += i === cursorOffset ? chalk.inverse(char) : char;\n }\n\n if (cursorOffset >= chars.length) {\n rendered += chalk.inverse(' ');\n }\n\n return rendered;\n}\n","/**\n * FLOW-2006: the line that says a prompt did not come from the person at the keyboard.\n *\n * A deep link can fill the composer, so the provenance has to be visible for as long as the composer\n * holds exactly what the link supplied — otherwise a prompt the user never typed reads exactly like\n * one they did. The moment that text is edited, sent or cleared the label goes: what is there is then\n * the user's own, and a label that outlived the text would be claiming otherwise. Above the long\n * threshold it also carries the character count and says to read the whole thing, because a long\n * prompt pushes its own instructions off screen.\n */\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** Above this many characters the notice escalates from a label to a review instruction. */\nexport const EXTERNAL_PROMPT_REVIEW_THRESHOLD = 1000;\nexport const EXTERNAL_PROMPT_NOTICE = 'Prompt from an external link';\n\nexport interface IExternalPromptNoticeProps {\n /** The composer's current text, so the notice can size its warning to what is actually there. */\n readonly value: string;\n}\n\n/** The notice text for a value, or `undefined` when there is nothing to say. */\nexport function externalPromptNotice(value: string): string | undefined {\n if (value.length === 0) return undefined;\n return value.length > EXTERNAL_PROMPT_REVIEW_THRESHOLD\n ? `${EXTERNAL_PROMPT_NOTICE} — ${value.length} characters; scroll and review the full text before sending`\n : EXTERNAL_PROMPT_NOTICE;\n}\n\nexport default function ExternalPromptNotice({\n value,\n}: IExternalPromptNoticeProps): React.ReactElement | null {\n const notice = externalPromptNotice(value);\n if (notice === undefined) return null;\n return <Text dimColor>{notice}</Text>;\n}\n","import { isSlashCommand, tokeniseSlashCommand } from '@robota-sdk/agent-framework';\n\nimport type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nexport interface IAutocompleteInputKey {\n upArrow?: boolean;\n downArrow?: boolean;\n escape?: boolean;\n tab?: boolean;\n backspace?: boolean;\n delete?: boolean;\n}\n\nexport type TAutocompletePopupAction = 'previous' | 'next' | 'close' | 'complete';\nexport type TPendingPromptInputAction = 'cancelQueue';\nexport type TPromptHistoryInputAction = 'previous' | 'next';\n\nexport type TCommandSelectionResult =\n { type: 'insert'; value: string; selectedIndex?: number } | { type: 'submit'; value: string };\n\nexport interface IPasteLabelChange {\n value: string;\n cursorHint: number;\n label: string;\n lineCount: number;\n}\n\nexport interface IPromptHistoryNavigationState {\n selectedIndex: number | null;\n draft: string;\n}\n\nexport interface IPromptHistoryNavigationResult {\n value: string;\n cursorHint: number;\n state: IPromptHistoryNavigationState;\n}\n\nexport function getAutocompletePopupAction(\n key: IAutocompleteInputKey,\n): TAutocompletePopupAction | undefined {\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n if (key.escape === true) return 'close';\n if (key.tab === true) return 'complete';\n return undefined;\n}\n\nexport function getPendingPromptInputAction(\n key: IAutocompleteInputKey,\n): TPendingPromptInputAction | undefined {\n if (key.backspace === true || key.delete === true) {\n return 'cancelQueue';\n }\n return undefined;\n}\n\nexport function getPromptHistoryInputAction(\n key: IAutocompleteInputKey,\n): TPromptHistoryInputAction | undefined {\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n return undefined;\n}\n\nexport function createPromptHistoryNavigationState(): IPromptHistoryNavigationState {\n return { selectedIndex: null, draft: '' };\n}\n\nexport function navigatePromptHistory(\n value: string,\n history: readonly string[],\n state: IPromptHistoryNavigationState,\n action: TPromptHistoryInputAction,\n): IPromptHistoryNavigationResult {\n if (history.length === 0) {\n return { value, cursorHint: value.length, state };\n }\n\n if (action === 'previous') {\n const selectedIndex =\n state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);\n const nextValue = history[selectedIndex] ?? value;\n return {\n value: nextValue,\n cursorHint: nextValue.length,\n state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft },\n };\n }\n\n if (state.selectedIndex === null) {\n return { value, cursorHint: value.length, state };\n }\n\n if (state.selectedIndex < history.length - 1) {\n const selectedIndex = state.selectedIndex + 1;\n const nextValue = history[selectedIndex] ?? value;\n return {\n value: nextValue,\n cursorHint: nextValue.length,\n state: { ...state, selectedIndex },\n };\n }\n\n return {\n value: state.draft,\n cursorHint: state.draft.length,\n state: createPromptHistoryNavigationState(),\n };\n}\n\nexport function appendPromptHistory(history: readonly string[], value: string): string[] {\n const prompt = value.trim();\n if (prompt.length === 0) return [...history];\n if (history[history.length - 1] === prompt) return [...history];\n return [...history, prompt];\n}\n\nexport function extractPromptHistory(entries: readonly IHistoryEntry[]): string[] {\n let prompts: string[] = [];\n for (const entry of entries) {\n if (entry.category !== 'chat' || entry.type !== 'user') continue;\n const data = entry.data as Record<string, TUniversalValue> | undefined;\n if (typeof data?.content !== 'string') continue;\n prompts = appendPromptHistory(prompts, data.content);\n }\n return prompts;\n}\n\nexport function moveAutocompleteSelection(\n selectedIndex: number,\n commandCount: number,\n direction: 'previous' | 'next',\n): number {\n if (commandCount === 0) return 0;\n if (direction === 'previous') {\n return selectedIndex > 0 ? selectedIndex - 1 : commandCount - 1;\n }\n return selectedIndex < commandCount - 1 ? selectedIndex + 1 : 0;\n}\n\nexport function resolveTabCompletion(value: string, command: ICommand): TCommandSelectionResult {\n // Subcommand mode: '/parent filter' — space present after command name\n if (isSlashCommand(value) && value.slice(1).includes(' ')) {\n const { name } = tokeniseSlashCommand(value);\n return { type: 'insert', value: `/${name} ${command.name} ` };\n }\n if (command.subcommands && command.subcommands.length > 0) {\n return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };\n }\n return { type: 'insert', value: `/${command.name} ` };\n}\n\nexport function resolveEnterCommandSelection(\n value: string,\n command: ICommand,\n): TCommandSelectionResult {\n // Subcommand mode: '/parent filter' — space present after command name\n if (isSlashCommand(value) && value.slice(1).includes(' ')) {\n const { name } = tokeniseSlashCommand(value);\n return { type: 'submit', value: `/${name} ${command.name}` };\n }\n if (command.subcommands && command.subcommands.length > 0 && command.runsBare !== true) {\n return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };\n }\n return { type: 'submit', value: `/${command.name}` };\n}\n\nexport function createPasteLabelChange(\n value: string,\n cursorPosition: number,\n pasteId: number,\n text: string,\n): IPasteLabelChange {\n const lineCount = text.split('\\n').length;\n const label = `[Pasted text #${pasteId} +${lineCount} lines]`;\n return {\n value: value.slice(0, cursorPosition) + label + value.slice(cursorPosition),\n cursorHint: cursorPosition + label.length,\n label,\n lineCount,\n };\n}\n\nexport function shouldSubmitInput(text: string): boolean {\n return text.trim().length > 0;\n}\n","/**\n * SCREEN-1993 — the pure half of reverse prompt-history search: filtering with match ranges,\n * collapse-to-newest dedup, the scope cycle and the run split a match is rendered from.\n */\nimport type { IPromptHistoryEntry } from '@robota-sdk/agent-interface-session';\n\nexport type THistorySearchScope = 'all' | 'session' | 'project';\n\n/** `all → session → project → all`: the widest first, narrowing on each press. */\nconst HISTORY_SEARCH_SCOPES: readonly THistorySearchScope[] = ['all', 'session', 'project'];\n\nexport function cycleScope(scope: THistorySearchScope): THistorySearchScope {\n const index = HISTORY_SEARCH_SCOPES.indexOf(scope);\n return HISTORY_SEARCH_SCOPES[(index + 1) % HISTORY_SEARCH_SCOPES.length] ?? 'all';\n}\n\nexport interface IHistorySearchScopeContext {\n readonly sessionId: string;\n readonly project: string;\n}\n\n/** Whether an entry belongs to the scope for this session and project. */\nexport function inScope(\n entry: IPromptHistoryEntry,\n scope: THistorySearchScope,\n context: IHistorySearchScopeContext,\n): boolean {\n switch (scope) {\n case 'all':\n return true;\n case 'session':\n return entry.sessionId === context.sessionId;\n case 'project':\n return entry.project === context.project;\n }\n}\n\n/** Search-time dedup: the first (newest) occurrence of a trimmed text wins; order is preserved. */\nexport function collapseToNewest(entries: readonly IPromptHistoryEntry[]): IPromptHistoryEntry[] {\n const seen = new Set<string>();\n const collapsed: IPromptHistoryEntry[] = [];\n for (const entry of entries) {\n const key = entry.text.trim();\n if (seen.has(key)) continue;\n seen.add(key);\n collapsed.push(entry);\n }\n return collapsed;\n}\n\nexport interface IHistorySearchMatch {\n readonly entry: IPromptHistoryEntry;\n /** `[start, end)` of the query inside `entry.text`; empty when the query is empty. */\n readonly ranges: readonly (readonly [number, number])[];\n}\n\n/** Case-insensitive substring filter; an empty query matches everything with no ranges. */\nexport function filterPrompts(\n entries: readonly IPromptHistoryEntry[],\n query: string,\n): IHistorySearchMatch[] {\n const needle = query.toLowerCase();\n if (needle.length === 0) return entries.map((entry) => ({ entry, ranges: [] }));\n const matches: IHistorySearchMatch[] = [];\n for (const entry of entries) {\n const haystack = entry.text.toLowerCase();\n const ranges: (readonly [number, number])[] = [];\n let from = 0;\n for (;;) {\n const index = haystack.indexOf(needle, from);\n if (index === -1) break;\n ranges.push([index, index + needle.length]);\n from = index + needle.length;\n }\n if (ranges.length > 0) matches.push({ entry, ranges });\n }\n return matches;\n}\n\n/** Text split into plain and highlighted runs, for rendering a match without colour dependence. */\nexport function splitByRanges(\n text: string,\n ranges: readonly (readonly [number, number])[],\n): { readonly text: string; readonly matched: boolean }[] {\n const runs: { text: string; matched: boolean }[] = [];\n let cursor = 0;\n for (const [start, end] of ranges) {\n if (start > cursor) runs.push({ text: text.slice(cursor, start), matched: false });\n runs.push({ text: text.slice(start, end), matched: true });\n cursor = end;\n }\n if (cursor < text.length) runs.push({ text: text.slice(cursor), matched: false });\n return runs;\n}\n","/**\n * SCREEN-1993 — the reverse prompt-history search overlay (Ctrl+R), rendered inside the input area\n * on the `SlashAutocomplete` pattern. The query line, the scope label, the matches newest-first with\n * the query highlighted, the load state and the footer of live keys.\n *\n * The highlight survives `NO_COLOR`: when the process emits no colour, a matched run is wrapped in\n * visible brackets instead of relying on an SGR attribute nothing would render.\n */\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { splitByRanges, type IHistorySearchMatch } from './history-search/history-search-flow.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IHistorySearchView } from './history-search/useHistorySearch.js';\n\n/** Footer for the overlay, in the default bindings. */\nexport const HISTORY_SEARCH_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Ctrl+S', label: 'Scope' },\n { keys: 'Enter/Tab', label: 'Insert' },\n { keys: 'Ctrl+E', label: 'Run' },\n { keys: 'Esc', label: 'Close' },\n];\n\nconst MAX_VISIBLE = 8;\n\nfunction scrollOffset(selectedIndex: number, total: number): number {\n if (total <= MAX_VISIBLE || selectedIndex < MAX_VISIBLE) return 0;\n return Math.min(selectedIndex - MAX_VISIBLE + 1, total - MAX_VISIBLE);\n}\n\n/** `1 unreadable line skipped` / `3 unreadable lines skipped`. */\nfunction formatSkippedLines(count: number): string {\n return `${count} unreadable ${count === 1 ? 'line' : 'lines'} skipped`;\n}\n\nfunction MatchRow(props: {\n readonly match: IHistorySearchMatch;\n readonly selected: boolean;\n readonly rowNumber: number | undefined;\n readonly color: boolean;\n}): React.ReactElement {\n const palette = usePalette();\n const { match, selected, rowNumber, color } = props;\n const indicator =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : selected\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n // A stored prompt may span lines; one visible glyph per newline keeps the row a row and the\n // match ranges aligned (same length).\n const runs = splitByRanges(\n match.entry.text.replace(/\\r\\n|[\\r\\n]/gu, (br) => (br.length === 2 ? ' ↵' : '↵')),\n match.ranges,\n );\n return (\n <Text\n color={selected ? palette.text.accent : undefined}\n dimColor={!selected}\n wrap=\"truncate-end\"\n >\n {indicator}\n {runs.map((run, index) =>\n run.matched ? (\n <Text key={index} bold inverse={color}>\n {color ? run.text : `[${run.text}]`}\n </Text>\n ) : (\n <Text key={index}>{run.text}</Text>\n ),\n )}\n </Text>\n );\n}\n\nfunction StateLine({ view }: { readonly view: IHistorySearchView }): React.ReactElement {\n const palette = usePalette();\n if (view.error !== undefined) {\n return <Text color={palette.text.error}>History could not be read: {view.error}</Text>;\n }\n const parts: string[] = [];\n if (view.matches.length === 0) {\n parts.push(\n view.loading ? 'loading…' : view.query.length === 0 ? 'no stored prompts yet' : 'no match',\n );\n } else {\n parts.push(`${view.matches.length} match${view.matches.length === 1 ? '' : 'es'}`);\n if (view.loading) parts.push('loading…');\n }\n if (view.skippedLines > 0) parts.push(formatSkippedLines(view.skippedLines));\n return <Text dimColor>{parts.join(' · ')}</Text>;\n}\n\nexport default function HistorySearchOverlay({\n view,\n}: {\n readonly view: IHistorySearchView;\n}): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const color = isInteractiveColorTerminal();\n const footerHints = useKeybindingHints('history-search', [\n [['previous', 'next'], 'Navigate'],\n ['cycle-scope', 'Scope'],\n ['insert', 'Insert'],\n ['execute', 'Run'],\n ['cancel', 'Close'],\n ]);\n if (!view.open) return null;\n const offset = scrollOffset(view.selectedIndex, view.matches.length);\n const visible = view.matches.slice(offset, offset + MAX_VISIBLE);\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.muted })}\n paddingX={1}\n >\n <Text>\n <Text color={palette.text.accent}>(reverse-i-search)</Text> scope: {view.scope} · query:{' '}\n {view.query}\n </Text>\n {visible.map((match, index) => (\n <MatchRow\n key={`${match.entry.at}:${offset + index}`}\n match={match}\n selected={offset + index === view.selectedIndex}\n rowNumber={screenReader ? offset + index : undefined}\n color={color}\n />\n ))}\n <StateLine view={view} />\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","/**\n * Slash command autocomplete hook.\n * Extracted from InputArea.tsx for single-responsibility.\n */\n\nimport React, { useState, useMemo, useEffect } from 'react';\n\nimport type { ITuiCommandQueryPort } from '../tui-app-channel-port.js';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\n/** Parse input to determine autocomplete state */\nfunction parseSlashInput(value: string): {\n isSlash: boolean;\n parentCommand: string;\n filter: string;\n} {\n if (!value.startsWith('/')) return { isSlash: false, parentCommand: '', filter: '' };\n const afterSlash = value.slice(1);\n const spaceIndex = afterSlash.indexOf(' ');\n if (spaceIndex === -1) return { isSlash: true, parentCommand: '', filter: afterSlash };\n const parent = afterSlash.slice(0, spaceIndex);\n const rest = afterSlash.slice(spaceIndex + 1);\n return { isSlash: true, parentCommand: parent, filter: rest };\n}\n\n/** Hook: manage autocomplete state */\nexport function useAutocomplete(\n value: string,\n commandQueryPort: ITuiCommandQueryPort | undefined,\n): {\n showPopup: boolean;\n filteredCommands: ICommand[];\n selectedIndex: number;\n setSelectedIndex: React.Dispatch<React.SetStateAction<number>>;\n isSubcommandMode: boolean;\n setShowPopup: React.Dispatch<React.SetStateAction<boolean>>;\n} {\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [dismissed, setDismissed] = useState(false);\n\n // Reset dismissed when input changes\n const prevValueRef = React.useRef(value);\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (dismissed) setDismissed(false);\n }\n\n const parsed = parseSlashInput(value);\n const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;\n\n const previousParentCommandRef = React.useRef(parsed.parentCommand);\n useEffect(() => {\n if (previousParentCommandRef.current === parsed.parentCommand) return;\n previousParentCommandRef.current = parsed.parentCommand;\n setSelectedIndex(0);\n }, [parsed.parentCommand]);\n\n const filteredCommands = useMemo(() => {\n if (!commandQueryPort || !parsed.isSlash || dismissed) return [];\n if (isSubcommandMode) {\n const subs = commandQueryPort.getSubcommands(parsed.parentCommand);\n if (subs.length === 0) return [];\n if (!parsed.filter) return subs;\n const lower = parsed.filter.toLowerCase();\n return subs.filter((c) => c.name.toLowerCase().startsWith(lower));\n }\n return commandQueryPort.getCommands(parsed.filter);\n }, [\n commandQueryPort,\n parsed.isSlash,\n parsed.parentCommand,\n parsed.filter,\n dismissed,\n isSubcommandMode,\n ]);\n\n const showPopup = parsed.isSlash && filteredCommands.length > 0 && !dismissed;\n\n // Clamp selectedIndex\n if (selectedIndex >= filteredCommands.length && filteredCommands.length > 0) {\n setSelectedIndex(filteredCommands.length - 1);\n }\n\n return {\n showPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n isSubcommandMode,\n setShowPopup: (val) => {\n if (typeof val === 'function') {\n setDismissed((prev) => {\n const nextVal = (val as (prev: boolean) => boolean)(!prev);\n return !nextVal;\n });\n } else {\n setDismissed(!val);\n }\n },\n };\n}\n","/**\n * SCREEN-1993 — the reverse prompt-history search overlay's state.\n *\n * One loader per open: opening starts one read of the injected source under one `AbortController`,\n * and every close — insert, execute or cancel — aborts it. Blocks arrive newest-first and are appended\n * as they land, so the first frame shows the newest prompts before the rest of the file is read. The\n * `session` scope never touches the file: it is the live prompt list the composer already holds.\n *\n * The composer's draft is restored by construction, not by copying: the overlay never writes the\n * input while it is open, so a cancel leaves text and cursor exactly as they were.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport {\n collapseToNewest,\n cycleScope,\n filterPrompts,\n inScope,\n type IHistorySearchMatch,\n type IHistorySearchScopeContext,\n type THistorySearchScope,\n} from './history-search-flow.js';\n\nimport type { IKeyInput } from '../keybindings/keybinding-registry.js';\nimport type {\n IPromptHistoryEntry,\n IPromptHistorySource,\n} from '@robota-sdk/agent-interface-session';\n\n/** What the surface injects to turn the overlay on; absent ⇒ `ctrl+r` does nothing. */\nexport interface IHistorySearchSurface {\n readonly source: IPromptHistorySource;\n readonly sessionId: string;\n readonly project: string;\n}\n\nexport interface IUseHistorySearchOptions {\n readonly surface: IHistorySearchSurface | undefined;\n /** The composer's live prompt list, oldest-first — the `session` scope. */\n readonly sessionPrompts: readonly string[];\n readonly onInsert: (text: string) => void;\n readonly onExecute: (text: string) => void;\n readonly onOpenChange?: ((open: boolean) => void) | undefined;\n}\n\nexport interface IHistorySearchView {\n readonly open: boolean;\n readonly query: string;\n readonly scope: THistorySearchScope;\n readonly selectedIndex: number;\n readonly matches: readonly IHistorySearchMatch[];\n readonly loading: boolean;\n readonly skippedLines: number;\n readonly error: string | undefined;\n}\n\ninterface ILoadedState {\n readonly entries: readonly IPromptHistoryEntry[];\n readonly skippedLines: number;\n}\n\nconst EMPTY_LOADED: ILoadedState = { entries: [], skippedLines: 0 };\n\nfunction isPrintable(input: string, key: IKeyInput): boolean {\n const special =\n key.ctrl === true ||\n key.meta === true ||\n Boolean(key.return || key.escape || key.tab) ||\n Boolean(key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) ||\n Boolean(key.backspace || key.delete);\n return input.length > 0 && !special;\n}\n\nfunction liveEntries(\n prompts: readonly string[],\n context: IHistorySearchScopeContext,\n): IPromptHistoryEntry[] {\n return [...prompts]\n .reverse()\n .map((text) => ({ at: '', sessionId: context.sessionId, project: context.project, text }));\n}\n\ninterface ILoader {\n readonly loaded: ILoadedState;\n readonly loading: boolean;\n readonly error: string | undefined;\n readonly start: () => void;\n readonly stop: () => void;\n /** Screen-reader mode: the list is published only here — per keystroke or at load end. */\n readonly publish: () => void;\n}\n\ninterface ILoaderRun {\n readonly source: IPromptHistorySource;\n readonly signal: AbortSignal;\n readonly pending: { current: ILoadedState };\n readonly publishEachBlock: boolean;\n readonly publish: () => void;\n readonly setError: (message: string) => void;\n readonly setLoading: (loading: boolean) => void;\n}\n\n/**\n * A real source reads synchronously and yields blocks with only a microtask between them; Ink's\n * throttled write never runs inside that, so without this the whole file lands in one frame.\n */\nconst yieldToTerminal = (): Promise<void> => new Promise((resolve) => setImmediate(resolve));\n\nasync function runLoader(run: ILoaderRun): Promise<void> {\n try {\n for await (const block of run.source.read({ signal: run.signal })) {\n if (run.signal.aborted) return;\n run.pending.current = {\n entries: [...run.pending.current.entries, ...block.entries],\n skippedLines: run.pending.current.skippedLines + block.skippedLines,\n };\n if (run.publishEachBlock) {\n run.publish();\n await yieldToTerminal();\n }\n }\n if (!run.signal.aborted) run.publish();\n } catch (cause) {\n if (!run.signal.aborted) run.setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n if (!run.signal.aborted) run.setLoading(false);\n }\n}\n\n/** Reads the source once per open under one abort signal. */\nfunction useHistoryLoader(\n surface: IHistorySearchSurface | undefined,\n screenReader: boolean,\n): ILoader {\n const [loaded, setLoaded] = useState<ILoadedState>(EMPTY_LOADED);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const controller = useRef<AbortController | undefined>(undefined);\n const pending = useRef<ILoadedState>(EMPTY_LOADED);\n const publish = useCallback(() => setLoaded(pending.current), []);\n const stop = useCallback(() => {\n controller.current?.abort();\n controller.current = undefined;\n setLoading(false);\n }, []);\n const start = useCallback(() => {\n stop();\n pending.current = EMPTY_LOADED;\n setLoaded(EMPTY_LOADED);\n setError(undefined);\n if (surface === undefined) return;\n const own = new AbortController();\n controller.current = own;\n setLoading(true);\n void runLoader({\n source: surface.source,\n signal: own.signal,\n pending,\n publishEachBlock: !screenReader,\n publish,\n setError,\n setLoading,\n });\n }, [publish, screenReader, stop, surface]);\n useEffect(() => stop, [stop]);\n return { loaded, loading, error, start, stop, publish };\n}\n\ninterface ISearchKeysDeps {\n readonly open: boolean;\n readonly matches: readonly IHistorySearchMatch[];\n readonly selected: number;\n readonly scope: THistorySearchScope;\n readonly screenReader: boolean;\n readonly loader: ILoader;\n readonly close: () => void;\n readonly setScope: (scope: THistorySearchScope) => void;\n readonly setSelectedIndex: (index: number) => void;\n readonly setQuery: (update: (current: string) => string) => void;\n readonly onInsert: (text: string) => void;\n readonly onExecute: (text: string) => void;\n}\n\n/** The overlay's keys: the bound actions, and otherwise plain typing into the query. */\nfunction useHistorySearchKeys(deps: ISearchKeysDeps): void {\n const editQuery = (update: (current: string) => string): void => {\n deps.setQuery(update);\n deps.setSelectedIndex(0);\n if (deps.screenReader) deps.loader.publish();\n };\n const take = (action: 'insert' | 'execute'): void => {\n const text = deps.matches[deps.selected]?.entry.text;\n if (text === undefined) return;\n deps.close();\n (action === 'insert' ? deps.onInsert : deps.onExecute)(text);\n };\n const move = (step: number): void => {\n if (deps.matches.length === 0) return;\n deps.setSelectedIndex((deps.selected + step + deps.matches.length) % deps.matches.length);\n };\n useKeybindingActions(\n 'history-search',\n (actions, input, key, consumed) => {\n const action = actions[0];\n if (action === 'cancel') deps.close();\n else if (action === 'insert' || action === 'execute') take(action);\n else if (action === 'previous') move(-1);\n else if (action === 'next') move(1);\n else if (action === 'cycle-scope') {\n deps.setScope(cycleScope(deps.scope));\n deps.setSelectedIndex(0);\n } else if (!consumed && (key.backspace || key.delete)) editQuery((q) => q.slice(0, -1));\n else if (!consumed && isPrintable(input, key)) editQuery((q) => q + input);\n },\n { isActive: deps.open },\n );\n}\n\n/** The candidate list for the scope: the live list for `session`, the collapsed file otherwise. */\nfunction useCandidates(\n open: boolean,\n surface: IHistorySearchSurface | undefined,\n scope: THistorySearchScope,\n sessionPrompts: readonly string[],\n entries: readonly IPromptHistoryEntry[],\n): IPromptHistoryEntry[] {\n // Keyed on the surface's values, not its identity: the app rebuilds the object every render.\n const sessionId = surface?.sessionId;\n const project = surface?.project;\n return useMemo(() => {\n if (!open || sessionId === undefined || project === undefined) return [];\n const context = { sessionId, project };\n const scoped =\n scope === 'session'\n ? liveEntries(sessionPrompts, context)\n : entries.filter((entry) => inScope(entry, scope, context));\n return collapseToNewest(scoped);\n }, [entries, open, project, scope, sessionId, sessionPrompts]);\n}\n\ninterface IQueryState {\n readonly query: string;\n readonly scope: THistorySearchScope;\n readonly selectedIndex: number;\n}\n\nconst FRESH_QUERY: IQueryState = { query: '', scope: 'all', selectedIndex: 0 };\n\n/** The query, scope and selection as one state, with the setters the key handler needs. */\nfunction useQueryState(): {\n readonly state: IQueryState;\n readonly reset: () => void;\n readonly setters: Pick<ISearchKeysDeps, 'setScope' | 'setSelectedIndex' | 'setQuery'>;\n} {\n const [state, setState] = useState<IQueryState>(FRESH_QUERY);\n const reset = useCallback(() => setState(FRESH_QUERY), []);\n const setters = useMemo(\n () => ({\n setScope: (scope: THistorySearchScope): void => setState((s) => ({ ...s, scope })),\n setSelectedIndex: (selectedIndex: number): void => setState((s) => ({ ...s, selectedIndex })),\n setQuery: (update: (current: string) => string): void =>\n setState((s) => ({ ...s, query: update(s.query) })),\n }),\n [],\n );\n return { state, reset, setters };\n}\n\n/** Open and close: one loader start per open, one stop per close, the app told of both. */\nfunction useOverlayVisibility(\n loader: ILoader,\n canOpen: boolean,\n reset: () => void,\n onOpenChange: ((open: boolean) => void) | undefined,\n): { readonly open: boolean; readonly openSearch: () => void; readonly closeSearch: () => void } {\n const [open, setOpen] = useState(false);\n const openSearch = useCallback(() => {\n if (!canOpen || open) return;\n reset();\n setOpen(true);\n onOpenChange?.(true);\n loader.start();\n }, [canOpen, loader, onOpenChange, open, reset]);\n const closeSearch = useCallback(() => {\n loader.stop();\n setOpen(false);\n onOpenChange?.(false);\n }, [loader, onOpenChange]);\n return { open, openSearch, closeSearch };\n}\n\nexport function useHistorySearch(options: IUseHistorySearchOptions): {\n readonly view: IHistorySearchView;\n readonly openSearch: () => void;\n readonly closeSearch: () => void;\n} {\n const screenReader = useScreenReader();\n const loader = useHistoryLoader(options.surface, screenReader);\n const { state, reset, setters } = useQueryState();\n const { query, scope } = state;\n const { surface, onInsert, onExecute } = options;\n const { open, openSearch, closeSearch } = useOverlayVisibility(\n loader,\n surface !== undefined,\n reset,\n options.onOpenChange,\n );\n\n const candidates = useCandidates(\n open,\n surface,\n scope,\n options.sessionPrompts,\n loader.loaded.entries,\n );\n const matches = useMemo(() => filterPrompts(candidates, query), [candidates, query]);\n const selected = Math.min(state.selectedIndex, Math.max(0, matches.length - 1));\n useHistorySearchKeys({\n open,\n matches,\n selected,\n scope,\n screenReader,\n loader,\n close: closeSearch,\n onInsert,\n onExecute,\n ...setters,\n });\n\n const { loading, error, loaded } = loader;\n const view = { ...state, open, selectedIndex: selected, matches, loading, error };\n return { view: { ...view, skippedLines: loaded.skippedLines }, openSearch, closeSearch };\n}\n","/**\n * SCREEN-1993 — the input area's side of prompt-history search: the `chat-input.history-search`\n * open binding, and what insert and execute do to the composer.\n *\n * Insert replaces the composer text with the match (cursor at the end); execute submits the match\n * through the same path Enter takes, so it is recorded in the live prompt list like any prompt. While\n * the overlay is open the composer is untouched, which is what makes cancel restore the draft exactly.\n */\nimport { useCallback, useEffect } from 'react';\n\nimport {\n useHistorySearch,\n type IHistorySearchSurface,\n} from '../history-search/useHistorySearch.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { IHistorySearchView } from '../history-search/useHistorySearch.js';\n\n/** What the input area receives from the app; absent ⇒ the feature is off and `ctrl+r` is inert. */\nexport interface IInputAreaHistorySearch extends IHistorySearchSurface {\n /** Lets the app block its own bindings (Esc abort, the switcher) while the overlay is open. */\n readonly onOpenChange?: ((open: boolean) => void) | undefined;\n}\n\nexport interface IUseInputAreaHistorySearchOptions {\n readonly historySearch: IInputAreaHistorySearch | undefined;\n readonly sessionPrompts: readonly string[];\n /** The composer can take keys: no popup, not disabled, nothing queued. */\n readonly composerActive: boolean;\n readonly setValue: (value: string) => void;\n readonly setCursorHint: (hint: number | null) => void;\n readonly submitPrompt: (prompt: string) => void;\n}\n\nexport function useInputAreaHistorySearch(\n options: IUseInputAreaHistorySearchOptions,\n): IHistorySearchView {\n const { historySearch, setValue, setCursorHint, submitPrompt, composerActive } = options;\n const onInsert = useCallback(\n (text: string) => {\n setValue(text);\n setCursorHint(null);\n },\n [setCursorHint, setValue],\n );\n const onExecute = useCallback(\n (text: string) => {\n setValue('');\n submitPrompt(text);\n },\n [setValue, submitPrompt],\n );\n const { view, openSearch, closeSearch } = useHistorySearch({\n surface: historySearch,\n sessionPrompts: options.sessionPrompts,\n onInsert,\n onExecute,\n onOpenChange: historySearch?.onOpenChange,\n });\n useKeybindingActions(\n 'chat-input',\n (actions) => {\n if (actions.includes('history-search')) openSearch();\n },\n { isActive: composerActive && !view.open && historySearch !== undefined },\n );\n // Another overlay or a disabled composer takes the keys: the search cannot stay open under it.\n useEffect(() => {\n if (view.open && !composerActive) closeSearch();\n }, [closeSearch, composerActive, view.open]);\n return view;\n}\n","/**\n * The input area's three key bindings, in one place.\n *\n * Extracted from `InputArea.tsx` (CLI-2004) by responsibility: the component owns the input's state\n * and its rendering; these three `useInput` registrations are the routing between the two, and each\n * is gated on a different combination of the same flags — which is the shape that gets copied wrong\n * when it is inline among the render code.\n */\n\nimport {\n moveAutocompleteSelection,\n navigatePromptHistory,\n type IPromptHistoryNavigationState,\n} from '../flows/input-area-flow.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nexport interface IUseInputAreaKeysInputs {\n value: string;\n isDisabled: boolean;\n /** SCREEN-1993: the history-search overlay owns the keys while it is open. */\n searchOpen: boolean;\n isQueueCancellationDisabled: boolean;\n pendingPrompt?: string | null;\n showPopup: boolean;\n setShowPopup: (shown: boolean) => void;\n filteredCommands: ICommand[];\n selectedIndex: number;\n setSelectedIndex: (update: (previous: number) => number) => void;\n tabCompleteCommand: (command: ICommand) => void;\n promptHistory: string[];\n historyState: IPromptHistoryNavigationState;\n setValue: (value: string) => void;\n setCursorHint: (hint: number | null) => void;\n setHistoryState: (state: IPromptHistoryNavigationState) => void;\n onRequestFocusBackgroundList?: (() => void) | undefined;\n onCancelQueue?: (() => void) | undefined;\n}\n\n/** The autocomplete popup's own keys: move the selection, close it, or take the completion. */\nfunction useAutocompletePopupKeys(inputs: IUseInputAreaKeysInputs): void {\n const { showPopup, isDisabled, filteredCommands, selectedIndex } = inputs;\n useKeybindingActions(\n 'autocomplete-menu',\n (actions) => {\n if (!showPopup) return;\n for (const action of actions) {\n if (action === 'previous' || action === 'next') {\n inputs.setSelectedIndex((previous) =>\n moveAutocompleteSelection(previous, filteredCommands.length, action),\n );\n } else if (action === 'close') {\n inputs.setShowPopup(false);\n } else if (action === 'accept') {\n const command = filteredCommands[selectedIndex];\n if (command) inputs.tabCompleteCommand(command);\n }\n }\n },\n { isActive: showPopup && !isDisabled },\n );\n}\n\n/** Register the autocomplete-popup, prompt-history and queued-prompt bindings. */\nexport function useInputAreaKeys(inputs: IUseInputAreaKeysInputs): void {\n const { showPopup, isDisabled, isQueueCancellationDisabled, pendingPrompt, searchOpen } = inputs;\n\n useAutocompletePopupKeys(inputs);\n\n useKeybindingActions(\n 'chat-input',\n (actions) => {\n const action = actions.find(\n (candidate): candidate is 'history-previous' | 'history-next' =>\n candidate === 'history-previous' || candidate === 'history-next',\n );\n if (action === undefined) return;\n const direction = action === 'history-previous' ? 'previous' : 'next';\n // SCREEN-014: ↓ on an empty input that is not browsing history falls through into the\n // background-work list (where it is a no-op for the input today). The parent decides whether\n // there is a list to focus.\n if (\n direction === 'next' &&\n inputs.historyState.selectedIndex === null &&\n inputs.value.length === 0\n ) {\n inputs.onRequestFocusBackgroundList?.();\n return;\n }\n const result = navigatePromptHistory(\n inputs.value,\n inputs.promptHistory,\n inputs.historyState,\n direction,\n );\n inputs.setValue(result.value);\n inputs.setCursorHint(result.cursorHint);\n inputs.setHistoryState(result.state);\n },\n { isActive: !showPopup && !isDisabled && !pendingPrompt && !searchOpen },\n );\n\n // Backspace cancels queued prompt\n useKeybindingActions(\n 'queued-prompt',\n (actions) => {\n if (actions.includes('cancel') && pendingPrompt) {\n inputs.onCancelQueue?.();\n }\n },\n { isActive: !!pendingPrompt && !isQueueCancellationDisabled },\n );\n}\n","/** Pure builder for the input area's top border line (optional right-aligned session title). */\n\nexport interface IInputTopBorder {\n left: string;\n label: string;\n right: string;\n}\n\nconst TITLE_RIGHT_PAD = 2;\n\n/** Build the top border with an optional session-name title, right-aligned 2 chars from the edge. */\nexport function buildInputTopBorder(innerWidth: number, sessionName?: string): IInputTopBorder {\n if (sessionName) {\n const label = ` \"${sessionName}\" `;\n const leftLen = Math.max(0, innerWidth - label.length - TITLE_RIGHT_PAD);\n return { left: '─'.repeat(leftLen), label, right: '─'.repeat(TITLE_RIGHT_PAD) };\n }\n return { left: '─'.repeat(innerWidth), label: '', right: '' };\n}\n","/**\n * The input area's horizontal rules, and the deletion announcement that replaces them.\n *\n * Extracted from `InputArea.tsx` (CLI-2004). The rules are hand-drawn `<Text>` lines rather than a\n * Yoga-synthesized `Box` border because a synthesized border drops glyphs during rapid re-render at\n * full terminal height (SCREEN-003) — which is exactly why they are not covered by the `borderStyle`\n * suppression the mode applies elsewhere and need their own branch.\n */\n\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\nimport { buildInputTopBorder } from './utils/input-top-border.js';\n\n/** The announcement's exact shape, so the input and its test cannot disagree about it. */\nexport function formatDeletionAnnouncement(deleted: string): string {\n return `[deleted: ${deleted}]`;\n}\n\n/** Top rule, carrying the optional right-aligned session title. Nothing in screen-reader mode. */\nexport function InputTopRule({\n screenReader,\n innerWidth,\n borderColor,\n sessionName,\n}: {\n screenReader: boolean;\n innerWidth: number;\n borderColor: string;\n sessionName?: string;\n}): React.ReactElement | null {\n const palette = usePalette();\n if (screenReader) return null;\n const topBorder = buildInputTopBorder(innerWidth, sessionName);\n return (\n <Text color={borderColor}>\n {topBorder.left}\n {topBorder.label ? (\n <Text backgroundColor={borderColor} color={palette.text.onAccent} bold>\n {topBorder.label}\n </Text>\n ) : null}\n {topBorder.right}\n </Text>\n );\n}\n\n/** Bottom rule (mirrors the top). Nothing in screen-reader mode. */\nexport function InputBottomRule({\n screenReader,\n innerWidth,\n borderColor,\n}: {\n screenReader: boolean;\n innerWidth: number;\n borderColor: string;\n}): React.ReactElement | null {\n if (screenReader) return null;\n return <Text color={borderColor}>{'─'.repeat(innerWidth)}</Text>;\n}\n\n/** `[deleted: <text>]`, rendered once for the most recent word/line delete. */\nexport function DeletionAnnouncement({\n deleted,\n}: {\n deleted: string | null;\n}): React.ReactElement | null {\n if (deleted === null) return null;\n return <Text>{formatDeletionAnnouncement(deleted)}</Text>;\n}\n","import { Box, useStdout } from 'ink';\nimport React, { useState, useEffect } from 'react';\n\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\n/** Footer for the autocomplete popup. */\nexport const SLASH_AUTOCOMPLETE_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Tab', label: 'Complete' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Close' },\n];\n\ninterface IProps {\n /** Filtered list of commands to display */\n commands: ICommand[];\n /** Currently highlighted item index */\n selectedIndex: number;\n /** Whether to show the popup */\n visible: boolean;\n /** Whether showing subcommands (no slash prefix) */\n isSubcommandMode?: boolean;\n}\n\nconst MAX_VISIBLE = 8;\n// border(1×2) + paddingX(1×2) consumed by outer box\nconst OUTER_CHROME = 4;\nconst MIN_ROW_WIDTH = 40;\nconst NAME_COL_MAX = 20;\n\nfunction useRowWidth(): number {\n const { stdout } = useStdout();\n const measure = (): number => Math.max(MIN_ROW_WIDTH, (stdout.columns ?? 80) - OUTER_CHROME);\n const [width, setWidth] = useState(measure);\n\n useEffect(() => {\n const onResize = (): void => setWidth(measure());\n stdout.on('resize', onResize);\n return () => {\n stdout.off('resize', onResize);\n };\n }, [stdout]);\n\n return width;\n}\n\nfunction capName(name: string, colWidth: number): string {\n return name.length > colWidth ? `${name.slice(0, colWidth - 1)}…` : name.padEnd(colWidth);\n}\n\n/** Render a single command row */\nfunction CommandRow(props: {\n cmd: ICommand;\n isSelected: boolean;\n showSlash: boolean;\n rowWidth: number;\n nameColWidth: number;\n /** CLI-2004: row number in the mode, replacing the `> ` cursor. */\n rowNumber?: number;\n}): React.ReactElement {\n const palette = usePalette();\n const { cmd, isSelected, showSlash, rowWidth, nameColWidth, rowNumber } = props;\n const indicator =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isSelected\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n const nameColor = isSelected ? palette.text.accent : undefined;\n const dimmed = !isSelected;\n const namePart = capName(cmd.name, nameColWidth);\n const text = showSlash\n ? `${indicator}/${namePart} ${cmd.description ?? ''}`\n : `${indicator}${namePart} ${cmd.description ?? ''}`;\n\n return (\n <Box width={rowWidth}>\n <Text color={nameColor} dimColor={dimmed} wrap=\"truncate-end\">\n {text}\n </Text>\n </Box>\n );\n}\n\n/** Autocomplete popup showing matching slash commands */\nexport default function SlashAutocomplete({\n commands,\n selectedIndex,\n visible,\n isSubcommandMode,\n}: IProps): React.ReactElement | null {\n const palette = usePalette();\n const rowWidth = useRowWidth();\n const screenReader = useScreenReader();\n const footerHints = useKeybindingHints('autocomplete-menu', [\n [['previous', 'next'], 'Navigate'],\n ['accept', 'Complete'],\n ['execute', 'Select'],\n ['close', 'Close'],\n ]);\n\n if (!visible || commands.length === 0) return null;\n\n const scrollOffset = computeScrollOffset(selectedIndex, commands.length);\n const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);\n\n const nameColWidth = Math.min(\n NAME_COL_MAX,\n Math.max(...visibleCommands.map((c) => c.name.length)),\n );\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.muted })}\n paddingX={1}\n >\n {visibleCommands.map((cmd, i) => (\n <CommandRow\n key={cmd.name}\n cmd={cmd}\n isSelected={scrollOffset + i === selectedIndex}\n showSlash={!isSubcommandMode}\n rowWidth={rowWidth}\n nameColWidth={nameColWidth}\n {...(screenReader ? { rowNumber: scrollOffset + i } : {})}\n />\n ))}\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n\n/** Compute scroll offset to keep selectedIndex visible */\nfunction computeScrollOffset(selectedIndex: number, total: number): number {\n if (total <= MAX_VISIBLE) return 0;\n if (selectedIndex < MAX_VISIBLE) return 0;\n const maxOffset = total - MAX_VISIBLE;\n return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);\n}\n","const PASTE_LABEL_RE = /\\[Pasted text #(\\d+)(?: \\+\\d+ lines)?\\]/g;\n\n/**\n * Replace paste label placeholders with their original content from the store.\n * Labels that have no matching store entry are replaced with empty string.\n */\nexport function expandPasteLabels(text: string, store: Map<number, string>): string {\n return text.replace(PASTE_LABEL_RE, (_, id: string) => store.get(Number(id)) ?? '');\n}\n","/**\n * WaveText — renders text with a subtle wave color animation.\n * Groups of 3-4 characters share the same color, creating a soft flowing effect.\n * The ramp comes from the resolved theme's motion tokens (SCREEN-006, SCREEN-2002): a calm span\n * wide enough to be perceptible (#555→#bbb) at an unhurried 400ms cadence.\n */\n\nimport React, { useState, useEffect } from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { useMotion, useMotionTokens, usePalette } from './theme/index.js';\n\n/** Cadence is not themed: a theme changes colour, not how often the terminal repaints. */\nconst MOTION_INTERVAL_MS = 400;\nconst MOTION_CHARS_PER_GROUP = 4;\n\ninterface IProps {\n text: string;\n}\n\nexport default function WaveText({ text }: IProps): React.ReactElement {\n // Animate only on an interactive color terminal (shared with the markdown color\n // gate via terminal-capabilities). Non-TTY / NO_COLOR / FORCE_COLOR=0 → static,\n // no interval, no motion (SCREEN-006/008).\n //\n // CLI-2004: screen-reader mode is a gate on the same interval, not a second mechanism. A colour\n // ramp is silent to a reader, but the repaint it causes is not — every tick re-emits the line, and\n // the reader announces it again. SCREEN-2002 added `reducedMotion` as a third input to that rule.\n const screenReader = useScreenReader();\n const palette = usePalette();\n const motionTokens = useMotionTokens();\n // SCREEN-2002: the colour gate, screen-reader mode and the reduced-motion setting are ONE rule\n // with one owner. This is its only consumer — the package's only animation.\n const animate = useMotion();\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n if (!animate) return undefined;\n const timer = setInterval(() => {\n setTick((prev) => prev + 1);\n }, MOTION_INTERVAL_MS);\n return () => clearInterval(timer);\n }, [animate]);\n\n if (screenReader) {\n // No colour prop at all: the mode drops the cue rather than substituting a value.\n return <Text>{text}</Text>;\n }\n\n if (!animate) {\n return <Text color={palette.text.muted}>{text}</Text>;\n }\n\n const chars = [...text];\n\n return (\n <Text>\n {chars.map((char, i) => {\n const group = Math.floor(i / MOTION_CHARS_PER_GROUP);\n const colorIndex = (tick + group) % motionTokens.wave.length;\n return (\n <Text key={i} color={motionTokens.wave[colorIndex]}>\n {char}\n </Text>\n );\n })}\n </Text>\n );\n}\n","import { Box, useWindowSize } from 'ink';\nimport React, { useState, useCallback, useEffect, useRef, useMemo } from 'react';\n\nimport CjkTextInput from './CjkTextInput.js';\nimport ExternalPromptNotice from './external-prompt-notice.js';\nimport {\n appendPromptHistory,\n createPasteLabelChange,\n createPromptHistoryNavigationState,\n extractPromptHistory,\n resolveEnterCommandSelection,\n resolveTabCompletion,\n shouldSubmitInput,\n} from './flows/input-area-flow.js';\nimport HistorySearchOverlay from './HistorySearchOverlay.js';\nimport { useAutocomplete } from './hooks/useAutocomplete.js';\nimport { useInputAreaHistorySearch } from './hooks/useInputAreaHistorySearch.js';\nimport { useInputAreaKeys } from './hooks/useInputAreaKeys.js';\nimport { DeletionAnnouncement, InputBottomRule, InputTopRule } from './input-area-rules.js';\nimport { KeyHintFooter } from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport SlashAutocomplete from './SlashAutocomplete.js';\nimport { usePalette } from './theme/index.js';\nimport { expandPasteLabels } from './utils/paste-labels.js';\nimport WaveText from './WaveText.js';\n\nimport type { IInputAreaHistorySearch } from './hooks/useInputAreaHistorySearch.js';\nimport type { ITuiCommandQueryPort } from './tui-app-channel-port.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nconst PENDING_PROMPT_DISPLAY_MAX = 50;\nconst PENDING_PROMPT_TAIL_KEEP = 47;\n\ninterface IProps {\n /** FLOW-2006: a deep link's prefill, seeded once. */\n initialValue?: string | undefined;\n /** Told to the controller the first time the prefill is taken. */\n consumeInitialValue?: (() => void) | undefined;\n /** True while the composer's text came from a deep link: renders the provenance notice. */\n externalPromptOrigin?: boolean | undefined;\n onSubmit: (value: string) => void;\n onCancelQueue?: () => void;\n isDisabled: boolean;\n isQueueCancellationDisabled?: boolean;\n isAborting?: boolean;\n pendingPrompt?: string | null;\n /** REMOTE-014 E5: total queued turns (owner + co-drivers); >1 surfaces a co-driver-queued hint. */\n pendingCount?: number;\n commandQueryPort?: ITuiCommandQueryPort;\n sessionName?: string;\n history?: readonly IHistoryEntry[];\n /**\n * SCREEN-014: called when ↓ is pressed on an empty input at the bottom of prompt history, so the\n * parent can move focus into the background-work list (a no-op for the input today).\n */\n onRequestFocusBackgroundList?: () => void;\n /** SCREEN-1993: the stored-prompt search surface; absent ⇒ `ctrl+r` is inert. */\n historySearch?: IInputAreaHistorySearch | undefined;\n}\n\n/**\n * Known limitation: Korean IME last character may be dropped on Enter.\n * This is an Ink raw mode limitation — no compositionstart/compositionend\n * events are available in terminal raw mode.\n * Reference: https://github.com/anthropics/claude-code/issues/3045\n */\n/**\n * Layout constants for InputArea (columns).\n * Used to compute available text width from terminal columns.\n *\n * Side borders removed — only top/bottom horizontal lines remain.\n * paddingLeft={1} adds 1 column inside the box.\n * Prompt \"> \" takes 2 columns.\n */\nconst BORDER_HORIZONTAL = 0;\nconst PADDING_LEFT = 1;\nconst PROMPT_WIDTH = 2;\nconst INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;\nconst DEFAULT_TERMINAL_COLUMNS = 80;\n\nexport default function InputArea({\n onSubmit,\n onCancelQueue,\n isDisabled,\n isQueueCancellationDisabled,\n isAborting,\n pendingPrompt,\n pendingCount,\n commandQueryPort,\n sessionName,\n history,\n onRequestFocusBackgroundList,\n historySearch,\n initialValue,\n consumeInitialValue,\n externalPromptOrigin,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n // FLOW-2006: the deep link's prefill arrives as a prop and is taken exactly once; the controller\n // then drops it, so a remount (the handoff-suspend path) never re-seeds a cleared prompt.\n const [value, setValue] = useState(initialValue ?? '');\n // The text the link supplied, held for as long as it is still what the composer holds. The\n // provenance belongs to THIS TEXT, not to the session: once it is sent or cleared, the next\n // prompt is the user's own and must not be labelled as if a link had written it.\n const [seededValue] = useState(externalPromptOrigin === true ? (initialValue ?? '') : '');\n useEffect(() => {\n if (initialValue === undefined || initialValue.length === 0) return;\n consumeInitialValue?.();\n }, [initialValue, consumeInitialValue]);\n const [cursorHint, setCursorHint] = useState<number | null>(null);\n // CLI-2004: the text the last word/line delete removed, announced once and cleared on the next\n // ordinary edit. A reader announces the line it is ON — what left it is otherwise unrecoverable.\n const screenReader = useScreenReader();\n const [deletedText, setDeletedText] = useState<string | null>(null);\n const [historyState, setHistoryState] = useState(createPromptHistoryNavigationState);\n const [localPromptHistory, setLocalPromptHistory] = useState<string[]>([]);\n const restoredPromptHistory = useMemo(() => extractPromptHistory(history ?? []), [history]);\n const promptHistory = useMemo(\n () =>\n localPromptHistory.reduce<string[]>(\n (prompts, prompt) => appendPromptHistory(prompts, prompt),\n restoredPromptHistory,\n ),\n [restoredPromptHistory, localPromptHistory],\n );\n const pasteStore = useRef<Map<number, string>>(new Map());\n const { columns } = useWindowSize();\n const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;\n const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);\n const pasteIdRef = useRef(0);\n const submitHint = useKeybindingHints('chat-input', [['submit', 'Submit']]);\n\n const {\n showPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n isSubcommandMode,\n setShowPopup,\n } = useAutocomplete(value, commandQueryPort);\n\n const handlePaste = useCallback((text: string, cursorPosition: number) => {\n pasteIdRef.current += 1;\n const id = pasteIdRef.current;\n pasteStore.current.set(id, text);\n setValue((prev) => {\n const change = createPasteLabelChange(prev, cursorPosition, id, text);\n setCursorHint(change.cursorHint);\n return change.value;\n });\n }, []);\n\n const resetHistoryNavigation = useCallback(() => {\n setHistoryState(createPromptHistoryNavigationState());\n }, []);\n\n const recordPromptHistory = useCallback((prompt: string): void => {\n setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));\n }, []);\n\n const submitPrompt = useCallback(\n (prompt: string): void => {\n recordPromptHistory(prompt);\n resetHistoryNavigation();\n onSubmit(prompt);\n },\n [onSubmit, recordPromptHistory, resetHistoryNavigation],\n );\n\n /** Tab: insert command into input field without executing */\n const tabCompleteCommand = useCallback(\n (cmd: ICommand): void => {\n const result = resolveTabCompletion(value, cmd);\n if (result.type === 'insert') {\n setValue(result.value);\n if (result.selectedIndex !== undefined) {\n setSelectedIndex(result.selectedIndex);\n }\n }\n },\n [value, setSelectedIndex],\n );\n\n /** Enter: insert and execute command immediately */\n const enterSelectCommand = useCallback(\n (cmd: ICommand): void => {\n const result = resolveEnterCommandSelection(value, cmd);\n if (result.type === 'insert') {\n setValue(result.value);\n if (result.selectedIndex !== undefined) {\n setSelectedIndex(result.selectedIndex);\n }\n return;\n }\n if (result.type === 'submit') {\n setValue('');\n submitPrompt(result.value);\n }\n },\n [value, submitPrompt, setSelectedIndex],\n );\n\n const handleSubmit = useCallback(\n (text: string): void => {\n if (!shouldSubmitInput(text)) return;\n\n if (showPopup && filteredCommands[selectedIndex]) {\n enterSelectCommand(filteredCommands[selectedIndex]);\n return;\n }\n\n // Expand paste labels before submitting\n const expanded = expandPasteLabels(text.trim(), pasteStore.current);\n\n setValue('');\n // Reset paste state\n pasteStore.current.clear();\n pasteIdRef.current = 0;\n\n submitPrompt(expanded);\n },\n [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt],\n );\n\n const search = useInputAreaHistorySearch({\n historySearch,\n sessionPrompts: promptHistory,\n composerActive: !showPopup && !isDisabled && !pendingPrompt,\n setValue,\n setCursorHint,\n submitPrompt,\n });\n\n useInputAreaKeys({\n value,\n isDisabled,\n searchOpen: search.open,\n isQueueCancellationDisabled: isQueueCancellationDisabled ?? isDisabled,\n pendingPrompt,\n showPopup,\n setShowPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n tabCompleteCommand,\n promptHistory,\n historyState,\n setValue,\n setCursorHint,\n setHistoryState,\n onRequestFocusBackgroundList,\n onCancelQueue,\n });\n\n const borderColor = isAborting\n ? palette.border.attention\n : pendingPrompt\n ? palette.border.focused\n : isDisabled\n ? palette.border.muted\n : palette.border.active;\n const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);\n\n return (\n <Box flexDirection=\"column\">\n {showPopup && (\n <SlashAutocomplete\n commands={filteredCommands}\n selectedIndex={selectedIndex}\n visible={showPopup}\n isSubcommandMode={isSubcommandMode}\n />\n )}\n <HistorySearchOverlay view={search} />\n <DeletionAnnouncement deleted={screenReader ? deletedText : null} />\n <InputTopRule\n screenReader={screenReader}\n innerWidth={innerWidth}\n borderColor={borderColor}\n {...(sessionName !== undefined ? { sessionName } : {})}\n />\n <Box paddingLeft={1}>\n {isAborting ? (\n <Text color={palette.text.warning}> Interrupting...</Text>\n ) : pendingPrompt ? (\n <Text color={palette.text.accent}>\n {' '}\n Queued:{' '}\n {pendingPrompt.length > PENDING_PROMPT_DISPLAY_MAX\n ? pendingPrompt.slice(0, PENDING_PROMPT_TAIL_KEEP) + '...'\n : pendingPrompt}{' '}\n {typeof pendingCount === 'number' && pendingCount > 1 && (\n <Text dimColor>(+{pendingCount - 1} co-driver queued) </Text>\n )}\n <Text dimColor>(Backspace to cancel)</Text>\n </Text>\n ) : isDisabled ? (\n <WaveText text=\" Waiting for response... (ESC to interrupt)\" />\n ) : (\n <Box>\n <Text color={palette.text.success} bold>\n {'> '}\n </Text>\n <CjkTextInput\n value={value}\n onChange={(v) => {\n setValue(v);\n resetHistoryNavigation();\n setCursorHint(null); // reset after normal typing\n }}\n onDeletedText={setDeletedText}\n onSubmit={handleSubmit}\n onPaste={handlePaste}\n placeholder=\"Type a message or /help\"\n availableWidth={availableWidth}\n cursorHint={cursorHint}\n enableVerticalNavigation={false}\n keybindingContext={showPopup ? 'autocomplete-menu' : 'chat-input'}\n focus={!search.open}\n />\n </Box>\n )}\n </Box>\n {!screenReader && !isDisabled && !pendingPrompt && !search.open && (\n <KeyHintFooter hints={submitHint} />\n )}\n <InputBottomRule\n screenReader={screenReader}\n innerWidth={innerWidth}\n borderColor={borderColor}\n />\n {seededValue.length > 0 && value === seededValue && <ExternalPromptNotice value={value} />}\n </Box>\n );\n}\n","import { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\n\nconst MAX_PREVIEW_LINES = 4;\nconst SUCCESS_EXIT_CODE = 0;\nconst COMMAND_TOOL_NAMES = new Set(['Shell', 'Bash', 'BackgroundProcess']);\n\nexport interface ICommandOutputInput {\n toolName: string;\n firstArg?: string;\n isRunning?: boolean;\n result?: string;\n toolResultData?: string;\n}\n\nexport interface ICommandOutputSummary {\n status: 'success' | 'error';\n statusLabel: string;\n previewLines: string[];\n omittedLineCount: number;\n transcriptHint?: string;\n}\n\nexport function formatCommandOutputSummary(\n tool: ICommandOutputInput,\n): ICommandOutputSummary | undefined {\n if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return undefined;\n\n const parsed = parseToolResultData(tool.toolResultData);\n const exitCode = getNumberValue(parsed, 'exitCode');\n const successValue = getBooleanValue(parsed, 'success');\n const output = buildOutputText(tool.toolResultData, parsed);\n const lines = trimTrailingBlankLines(splitOutputLines(output));\n // SEC-019: these lines ARE the command's stdout and stderr — the most directly attacker-chosen\n // text in the whole surface. Sanitized where the summary is BUILT rather than where it is\n // rendered, because this function exists to produce display text and a second render site would\n // otherwise have to remember. `statusLabel` and `transcriptHint` are built from numbers and need\n // nothing.\n const previewLines = lines.slice(0, MAX_PREVIEW_LINES).map(sanitizeTerminalText);\n const omittedLineCount = Math.max(0, lines.length - previewLines.length);\n const isFailed =\n tool.result === 'error' ||\n successValue === false ||\n (exitCode !== undefined && exitCode !== SUCCESS_EXIT_CODE);\n\n return {\n status: isFailed ? 'error' : 'success',\n statusLabel: formatStatusLabel(isFailed, exitCode),\n previewLines,\n omittedLineCount,\n transcriptHint:\n omittedLineCount > 0\n ? `... +${omittedLineCount} lines (full output in session transcript)`\n : undefined,\n };\n}\n\nfunction parseToolResultData(value: string): TUniversalValue {\n try {\n return JSON.parse(value) as TUniversalValue;\n } catch {\n return value;\n }\n}\n\nfunction buildOutputText(raw: string, parsed: TUniversalValue): string {\n if (!isUniversalObject(parsed)) return raw;\n\n const output = getStringValue(parsed, 'output');\n if (output !== undefined) return output;\n\n const stdout = getStringValue(parsed, 'stdout');\n const stderr = getStringValue(parsed, 'stderr');\n const error = getStringValue(parsed, 'error');\n const lines: string[] = [];\n if (stdout) lines.push(stdout);\n if (stderr) lines.push(prefixLines(stderr, '[stderr] '));\n if (!stdout && !stderr && error) lines.push(error);\n return lines.join('\\n');\n}\n\nfunction formatStatusLabel(isFailed: boolean, exitCode: number | undefined): string {\n if (exitCode !== undefined && exitCode !== SUCCESS_EXIT_CODE) return `exit ${exitCode}`;\n return isFailed ? 'error' : 'ok';\n}\n\nfunction splitOutputLines(output: string): string[] {\n if (!output) return [];\n return output.replace(/\\r\\n/g, '\\n').split('\\n');\n}\n\nfunction trimTrailingBlankLines(lines: string[]): string[] {\n let end = lines.length;\n while (end > 0 && lines[end - 1]!.trim().length === 0) {\n end -= 1;\n }\n return lines.slice(0, end);\n}\n\nfunction prefixLines(value: string, prefix: string): string {\n return splitOutputLines(value)\n .map((line) => `${prefix}${line}`)\n .join('\\n');\n}\n\nfunction isUniversalObject(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction getStringValue(source: TUniversalValue, key: string): string | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction getNumberValue(source: TUniversalValue, key: string): number | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction getBooleanValue(source: TUniversalValue, key: string): boolean | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n","import { marked } from 'marked';\n// @ts-expect-error — marked-terminal has no type declarations\nimport TerminalRenderer from 'marked-terminal';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport {\n diffRowStyles,\n markdownRendererOptions,\n resolveTheme,\n syntaxHighlightTheme,\n} from './theme/index.js';\n\nimport type { IDiffRowStyles, ITuiTheme } from './theme/index.js';\nimport type { Renderer } from 'marked';\n\nconst CODE_BLOCK_INDENT = ' ';\n\nexport interface IRenderMarkdownOptions {\n color?: boolean;\n codeBlockWidth?: number;\n /**\n * SCREEN-2002: the resolved theme. Absent ⇒ the `dark` built-in, which is byte-identical to what\n * this renderer produced before themes existed.\n */\n theme?: ITuiTheme;\n /** SCREEN-2002: `false` renders code blocks as plain indented text, with no highlight SGR. */\n syntaxHighlighting?: boolean;\n /**\n * CLI-2004: flatten tables to `Header: value` sentences. A box-drawn grid is unreadable aloud —\n * a reader announces the rules, not the relationship between a heading and its cell.\n */\n screenReader?: boolean;\n}\n\ninterface IHighlightOptions {\n ignoreIllegals?: boolean;\n /** The cli-highlight theme marked-terminal forwards; absent ⇒ its red/green defaults. */\n theme?: Record<string, (text: string) => string>;\n}\n\ntype TTerminalRendererConstructor = new (\n /** `marked-terminal`'s style options: one chalk instance per token it colours. */\n options?: Record<string, (text: string) => string>,\n highlightOptions?: IHighlightOptions,\n) => Renderer;\n\nconst TerminalRendererConstructor = TerminalRenderer as TTerminalRendererConstructor;\n\nfunction shouldUseColor(option: boolean | undefined): boolean {\n if (option !== undefined) {\n return option;\n }\n return isInteractiveColorTerminal();\n}\n\nfunction isDiffLanguage(language: string | undefined): boolean {\n return language?.trim().toLowerCase() === 'diff';\n}\n\nfunction styleAddedOrRemovedDiffRow(\n line: string,\n rowWidth: number,\n color: boolean,\n styles: IDiffRowStyles,\n): string {\n const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);\n if (!color) {\n return row.trimEnd();\n }\n if (line.startsWith('+')) return styles.added(row);\n if (line.startsWith('-')) return styles.removed(row);\n return row.trimEnd();\n}\n\nfunction colorizeDiffLine(\n line: string,\n color: boolean,\n rowWidth: number,\n styles: IDiffRowStyles,\n): string {\n if (line.startsWith('+') || line.startsWith('-')) {\n return styleAddedOrRemovedDiffRow(line, rowWidth, color, styles);\n }\n const row = `${CODE_BLOCK_INDENT}${line}`;\n if (!color) {\n return row;\n }\n if (line.startsWith('@@')) return styles.hunk(row);\n if (line.startsWith('diff ') || line.startsWith('index ')) return styles.meta(row);\n return row;\n}\n\nfunction resolveDiffRowWidth(lines: readonly string[], requestedWidth: number | undefined): number {\n const minimumWidth = lines.reduce(\n (maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),\n 0,\n );\n if (requestedWidth === undefined) {\n return minimumWidth;\n }\n return Math.max(minimumWidth, requestedWidth);\n}\n\nfunction renderDiffCodeBlock(\n code: string,\n color: boolean,\n codeBlockWidth: number | undefined,\n styles: IDiffRowStyles,\n): string {\n const lines = code.split('\\n');\n const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);\n const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth, styles)).join('\\n');\n return `${body}\\n\\n`;\n}\n\n/**\n * CLI-2004 — flatten a table to `Header: value` lines.\n *\n * The cell and row hooks tag their output with separators marked's own renderers never emit, so the\n * table hook can recover the grid it was given. Reconstructing it from the RENDERED strings is the\n * alternative, and those already carry the SGR the inline renderers added.\n */\nconst CELL_MARK = '\\u0000';\nconst ROW_MARK = '\\u0001';\n\nfunction splitTableCells(row: string): string[] {\n const cells = row.split(ROW_MARK).join('').split(CELL_MARK);\n // The trailing separator leaves one empty tail entry; the cells themselves may legitimately be ''.\n return cells.slice(0, -1);\n}\n\nfunction installTableFlattening(renderer: Renderer): void {\n renderer.tablecell = (content: string): string => `${content}${CELL_MARK}`;\n renderer.tablerow = (content: string): string => `${content}${ROW_MARK}`;\n renderer.table = (header: string, body: string): string => {\n const headers = splitTableCells(header);\n const rows = body\n .split(ROW_MARK)\n .filter((row) => row.length > 0)\n .map((row) => splitTableCells(`${row}${ROW_MARK}`));\n if (headers.length === 0 || rows.length === 0) return '';\n const blocks = rows.map((cells) =>\n headers.map((head, index) => `${head}: ${cells[index] ?? ''}`).join('\\n'),\n );\n // A blank line between rows: the row boundary is the only structure left to hear.\n return `${blocks.join('\\n\\n')}\\n\\n`;\n };\n}\n\nfunction createTerminalRenderer(\n color: boolean,\n codeBlockWidth: number | undefined,\n screenReader: boolean,\n theme: ITuiTheme,\n syntaxHighlighting: boolean,\n): Renderer {\n // SCREEN-2002: both dependencies are told what to colour with. Left to their own defaults, a\n // daltonized theme would still get green/red code blocks from cli-highlight's own theme.\n const renderer = new TerminalRendererConstructor(markdownRendererOptions(theme.markdown), {\n ignoreIllegals: true,\n theme: syntaxHighlightTheme(theme.syntax),\n });\n const renderCode = renderer.code.bind(renderer);\n const styles = diffRowStyles(theme);\n\n renderer.code = (code: string, language: string | undefined, escaped: boolean): string => {\n if (isDiffLanguage(language)) {\n return renderDiffCodeBlock(code, color, codeBlockWidth, styles);\n }\n // Highlighting off: the block renders as plain indented text, the shape the diff path uses.\n if (!syntaxHighlighting) {\n return `${code\n .split('\\n')\n .map((line) => `${CODE_BLOCK_INDENT}${line}`)\n .join('\\n')}\\n\\n`;\n }\n return renderCode(code, language, escaped);\n };\n\n if (screenReader) installTableFlattening(renderer);\n\n return renderer;\n}\n\n/**\n * Render markdown to a terminal-formatted string with colors, bold, etc.\n * Returns the rendered string (may include ANSI escape codes).\n */\nexport function renderMarkdown(md: string, options: IRenderMarkdownOptions = {}): string {\n // SEC-019 (issue #2022): the untrusted string is sanitized BEFORE parsing, never after. The\n // renderer ADDS ANSI — colours, bold, code-block framing — so filtering its output would strip the\n // repository's own presentation along with the attacker's. Filtering its input removes what\n // arrived from outside and leaves what is generated after.\n //\n // This is the choke point every render path shares (MessageList, StreamingIndicator,\n // ToolDiffBlock), which is why it is here rather than at each of the three.\n const safe = sanitizeTerminalText(md);\n const result = marked.parse(safe, {\n renderer: createTerminalRenderer(\n shouldUseColor(options.color),\n options.codeBlockWidth,\n options.screenReader === true,\n resolveTheme(options.theme),\n options.syntaxHighlighting !== false,\n ),\n });\n return typeof result === 'string' ? result.trimEnd() : safe;\n}\n","import { renderMarkdown } from '../render-markdown.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { useSyntaxHighlighting, useTheme } from '../theme/index.js';\n\nimport type { IRenderMarkdownOptions } from '../render-markdown.js';\n\n/** What a call site still decides. The three appearance inputs are no longer among them. */\nexport type TRenderMarkdownExtras = Omit<\n IRenderMarkdownOptions,\n 'theme' | 'syntaxHighlighting' | 'screenReader'\n>;\n\n/**\n * SCREEN-2002 — markdown rendered with this surface's resolved appearance.\n *\n * The three inputs every render site needs — the theme, the syntax-highlighting setting and\n * screen-reader mode — are bound here instead of at each call. That is a deliberate removal of an\n * obligation rather than a convenience.\n *\n * The obligation was forgotten once and guarded four times. `/theme syntax off` shipped persisting\n * a setting no call site passed, so the command reported success and changed nothing; each guard\n * written afterwards measured a PROXY for \"every call site passes it\" — the prop appears in the\n * file, the file matched the regex, the identifier occurs N times — and each held for exactly one\n * review round. A binding removes the thing being proxied: there is no per-call-site argument left\n * to forget, and the floor collapses to a single-consumer ratchet (nothing outside this module\n * imports `renderMarkdown`), which is a property rather than a proxy for one.\n *\n * Screen-reader mode is bound too, uniformly. `StreamingIndicator` previously omitted it, which was\n * correct but only incidentally: it returns its collapsed line before reaching any markdown, so the\n * flag never applied. Binding it removes that coincidence as something a reader has to reconstruct.\n */\nexport function useRenderMarkdown(): (md: string, extras?: TRenderMarkdownExtras) => string {\n const theme = useTheme();\n const syntaxHighlighting = useSyntaxHighlighting();\n const screenReader = useScreenReader();\n return (md, extras = {}) =>\n renderMarkdown(md, { ...extras, theme, syntaxHighlighting, screenReader });\n}\n","/**\n * SCREEN-012: turn a provider-safe projected tool id into the natural command name for display.\n *\n * Slash commands invoked by the model are projected to provider-safe tool ids prefixed with\n * the host-selected prefix (or the neutral default), optionally suffixed with a `_<8 hex>` hash when\n * the name would otherwise exceed the provider length limit (see model-command-tool-projection.ts).\n * For display we recover the command name: strip the prefix and a trailing projection hash. Tool\n * names that are not command projections (e.g. `Shell`, `Read`) are returned unchanged.\n *\n * ## SEC-019: this is where a tool label is made safe, not at each render site\n *\n * A tool name and its first argument come from the model, from an MCP server's manifest, or from a\n * plugin — all untrusted — and they are put on the terminal as text. Sanitizing at the render sites\n * was tried and failed within one review round: `MessageList` has two branches that show a tool name\n * and only one of them got the call, so a name carrying OSC 52 still reached `<Text>` whenever the\n * tool's content happened to parse as a structured summary. Three more sites had the same gap\n * (`StreamingIndicator`, and the label `tool-summary-status` composes for the history view).\n *\n * The defect is not that a call was forgotten. It is that \"make this displayable\" and \"make this\n * safe to display\" were two separate steps, so every new render site had to remember the second one.\n * They are one step here, and a fifth caller is covered because it cannot obtain the display text\n * without going through the function that sanitizes it.\n */\nimport { MODEL_COMMAND_TOOL_PREFIX } from '@robota-sdk/agent-framework';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\n/** A trailing `_<8 lowercase hex>` projection hash appended to over-long command tool names. */\nconst PROJECTION_HASH_SUFFIX = /_[0-9a-f]{8}$/;\n\nexport function humanizeToolName(\n toolName: string,\n prefix: string = MODEL_COMMAND_TOOL_PREFIX,\n): string {\n if (!toolName.startsWith(prefix)) return sanitizeTerminalText(toolName);\n const body = toolName.slice(prefix.length);\n const withoutHash = body.replace(PROJECTION_HASH_SUFFIX, '');\n return sanitizeTerminalText(withoutHash.length > 0 ? withoutHash : toolName);\n}\n\n/**\n * The first argument of a tool call, as display text.\n *\n * A separate function rather than a bare `sanitizeTerminalText` at the two sites that show it: the\n * argument is the other half of a tool label, it is equally untrusted — a path, a shell command, a\n * URL the model chose — and pairing it with {@link humanizeToolName} is what makes the boundary\n * findable. A render site that reaches for one naturally reaches for the other.\n */\nexport function humanizeToolArgument(firstArg: string | undefined): string {\n return firstArg === undefined ? '' : sanitizeTerminalText(firstArg);\n}\n","import React, { createContext, useContext } from 'react';\n\nconst ProductDisplayNameContext = createContext('Assistant');\nconst ModelCommandToolPrefixContext = createContext<string | undefined>(undefined);\n\nexport function ProductDisplayNameProvider({\n name,\n modelCommandToolPrefix,\n children,\n}: {\n name?: string;\n modelCommandToolPrefix?: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ProductDisplayNameContext.Provider value={name?.trim() || 'Assistant'}>\n <ModelCommandToolPrefixContext.Provider value={modelCommandToolPrefix}>\n {children}\n </ModelCommandToolPrefixContext.Provider>\n </ProductDisplayNameContext.Provider>\n );\n}\n\nexport function useProductDisplayName(): string {\n return useContext(ProductDisplayNameContext);\n}\n\nexport function useModelCommandToolPrefix(): string | undefined {\n return useContext(ModelCommandToolPrefixContext);\n}\n","/**\n * CLI-2004 — the screen-reader label vocabulary (SSOT).\n *\n * A screen reader has no equivalent of `aria-live`: the only channel to it is which characters land\n * in the terminal buffer, in what order. A searchable prefix on every transcript line is therefore\n * the whole of the \"who is speaking\" affordance, and it has to be stable enough to grep for.\n *\n * PROVIDER-INVARIANT BY CONSTRUCTION. The label derives from the message ROLE, never from the\n * vendor behind it — `assistant:`, not the provider's product name — so the transcript reads\n * identically under every `agent-provider-*`. `screenReaderLabelValues()` exists so a test can\n * assert that negative mechanically instead of trusting review.\n *\n * All nine are lowercase. The reference this vocabulary is modelled on Title-Cases two of its nine;\n * that split is not reproduced, because a reader announces case and the inconsistency is noise.\n */\n\nimport type { TUniversalMessage } from '@robota-sdk/agent-core';\n\n/** The nine label kinds. */\nexport type TScreenReaderLabelKind =\n | 'user'\n | 'assistant'\n | 'thinking'\n | 'tool'\n | 'toolError'\n | 'error'\n | 'warning'\n | 'permissionRequired'\n | 'cost';\n\n/** The label vocabulary. Every spoken prefix in the mode comes from exactly this map. */\nexport const SCREEN_READER_LABELS: Readonly<Record<TScreenReaderLabelKind, string>> = {\n user: 'you:',\n assistant: 'assistant:',\n thinking: 'thinking:',\n tool: 'tool:',\n toolError: 'tool error:',\n error: 'error:',\n warning: 'warning:',\n permissionRequired: 'permission required:',\n cost: 'cost:',\n};\n\n/** Every label value, for mechanical assertions over the vocabulary. */\nexport function screenReaderLabelValues(): readonly string[] {\n return Object.values(SCREEN_READER_LABELS);\n}\n\n/**\n * Map a transcript message role onto the vocabulary. `system` speaks as `warning:` — it is the role\n * the TUI already renders in the warning colour, and the mode may not lean on colour.\n */\nexport function screenReaderLabelForRole(role: TUniversalMessage['role']): string {\n switch (role) {\n case 'user':\n return SCREEN_READER_LABELS.user;\n case 'assistant':\n return SCREEN_READER_LABELS.assistant;\n case 'tool':\n return SCREEN_READER_LABELS.tool;\n case 'system':\n return SCREEN_READER_LABELS.warning;\n }\n}\n","import { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { screenReaderLabelForRole } from './screen-reader-labels.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { TUniversalMessage } from '@robota-sdk/agent-core';\n\n/**\n * Who a transcript line is attributed to, and in what colour. Extracted from MessageList when\n * PEER-007 gave the `user` case a second thing to decide: the label stopped being a constant per\n * role, so it is its own responsibility rather than four inline returns.\n */\n\n/**\n * PEER-007 (issue #1915): the operator's own turns stay `You:`; a turn driven by anyone else is\n * labelled with WHO drove it, so a co-driven transcript can be read after the fact. `owner` is the\n * operator, so it reads as `You:` too; anything else prints as itself (`peer:<session-id>`, `agent`).\n */\nfunction driverLabel(driverId: string): string {\n return driverId === OWNER_DRIVER_ID ? 'You' : driverId;\n}\n\nexport function RoleLabel({\n role,\n driverId,\n}: {\n role: TUniversalMessage['role'];\n driverId?: string;\n}): React.ReactElement {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const productDisplayName = useProductDisplayName();\n if (screenReader) {\n // CLI-2004: the label comes from the ROLE, never the vendor — `assistant:` under every\n // agent-provider-*. No colour: in this mode the searchable word is the whole cue.\n const label =\n role === 'user' && driverId !== undefined && driverId !== OWNER_DRIVER_ID\n ? `${driverId}:`\n : screenReaderLabelForRole(role);\n return <Text>{label} </Text>;\n }\n switch (role) {\n case 'user':\n return (\n <Text color={palette.text.success} bold>\n {driverId !== undefined ? driverLabel(driverId) : 'You'}:{' '}\n </Text>\n );\n case 'assistant':\n return (\n <Text color={palette.text.accent} bold>\n {productDisplayName}:{' '}\n </Text>\n );\n case 'system':\n return (\n <Text color={palette.text.warning} bold>\n System:{' '}\n </Text>\n );\n case 'tool':\n return (\n <Text color={palette.text.emphasis} bold>\n Tool:{' '}\n </Text>\n );\n }\n}\n","/**\n * Persisted tool-summary → status-SSOT mapping (SCREEN-006).\n *\n * MessageList used to hand-roll its own glyph/color mapping for tool summaries, which drifted\n * from `status-glyph.ts` (denied rendered plain yellow vs the SSOT's yellowBright). These\n * helpers route the persisted summary items through `toolStateStatusKind` + `STATUS_SYMBOL`\n * so tool status renders one way everywhere.\n */\n\nimport { humanizeToolArgument, humanizeToolName } from './humanize-tool-name.js';\nimport { STATUS_SYMBOL, toolStateStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type { IToolCallSummary } from './utils/tool-call-extractor.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\n/** A tool item as persisted in a `tool-summary` history entry (loosely-typed session data). */\nexport type TToolSummaryItem = {\n toolName: string;\n firstArg?: string;\n isRunning?: boolean;\n result?: string;\n diffLines?: IToolCallSummary['diffLines'];\n diffFile?: string;\n toolResultData?: string;\n};\n\n/**\n * Map a persisted tool-summary item to the semantic status kind — via the `status-glyph.ts`\n * SSOT. A command-output error (non-zero exit surfaced by `formatCommandOutputSummary`)\n * overrides the recorded result; the caller passes that flag so this module stays free of\n * the command-output dependency.\n */\nexport function toolSummaryStatusKind(\n tool: TToolSummaryItem,\n hasCommandOutputError: boolean,\n): TUiStatusKind {\n if (hasCommandOutputError) return 'error';\n const result: IToolState['result'] =\n tool.result === 'success' || tool.result === 'error' || tool.result === 'denied'\n ? tool.result\n : undefined;\n return toolStateStatusKind({\n toolName: tool.toolName,\n firstArg: tool.firstArg ?? '',\n isRunning: tool.isRunning ?? false,\n result,\n });\n}\n\n/** One-line summary label: SSOT glyph + humanized tool name + first argument. */\nexport function getToolSummaryLabel(\n tool: TToolSummaryItem,\n kind: TUiStatusKind,\n prefix?: string,\n): string {\n const argument = humanizeToolArgument(tool.firstArg);\n return `${STATUS_SYMBOL[kind]} ${humanizeToolName(tool.toolName, prefix)}${argument ? `(${argument})` : ''}`;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { formatCommandOutputSummary } from './command-output-summary.js';\nimport { Text } from './SafeText.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ICommandOutputInput } from './command-output-summary.js';\n\ninterface IProps {\n tool: ICommandOutputInput;\n}\n\nexport default function ToolCommandOutput({ tool }: IProps): React.ReactElement | null {\n const palette = usePalette();\n const summary = formatCommandOutputSummary(tool);\n if (!summary) return null;\n\n // A successful command with no output renders nothing here: the parent\n // (MessageList ToolSummaryEntry) already shows a \"✓ tool(args)\" summary line for\n // every tool, so a second \"✓ ok\" line was a duplicate success marker (SCREEN-007).\n if (\n summary.statusLabel === 'ok' &&\n summary.previewLines.length === 0 &&\n !summary.transcriptHint\n ) {\n return null;\n }\n const color =\n summary.status === 'error' ? statusGlyphColor(palette, 'error') : palette.text.emphasis;\n\n return (\n <Box flexDirection=\"column\" marginLeft={4}>\n {summary.statusLabel !== 'ok' && (\n <Text color={color} dimColor>\n {summary.statusLabel}\n </Text>\n )}\n {summary.previewLines.map((line, index) => (\n <Text key={`${line}-${index}`} color={color} dimColor>\n {line}\n </Text>\n ))}\n {summary.transcriptHint && <Text dimColor>{summary.transcriptHint}</Text>}\n </Box>\n );\n}\n","import type { IDiffLine } from './edit-diff.js';\n\nconst MAX_DIFF_LINES = 12;\nconst TRUNCATED_SHOW = 10;\n\nexport interface IToolDiffSummaryInput {\n file?: string;\n lines: readonly IDiffLine[];\n}\n\nexport interface IToolDiffSummary {\n file?: string;\n markdown: string;\n truncated: boolean;\n remainingLineCount: number;\n}\n\nexport function buildToolDiffSummary(input: IToolDiffSummaryInput): IToolDiffSummary {\n const visibleLines =\n input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;\n const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString()\n .length;\n const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));\n const truncated = input.lines.length > MAX_DIFF_LINES;\n\n return {\n file: input.file,\n markdown: ['```diff', ...body, '```'].join('\\n'),\n truncated,\n remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0,\n };\n}\n\nfunction formatDiffLine(line: IDiffLine, lineNumberWidth: number): string {\n if (line.type === 'hunk') return line.text;\n const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, ' ');\n if (line.type === 'remove') return `- ${lineNumber} | ${line.text}`;\n if (line.type === 'add') return `+ ${lineNumber} | ${line.text}`;\n return ` ${lineNumber} | ${line.text}`;\n}\n\nfunction selectVisibleDiffLines(lines: readonly IDiffLine[]): readonly IDiffLine[] {\n const groups = groupByHunk(lines);\n const visible: IDiffLine[] = [];\n\n for (const group of groups) {\n if (visible.length === 0 && group.length > TRUNCATED_SHOW) {\n return group.slice(0, TRUNCATED_SHOW);\n }\n if (visible.length + group.length > TRUNCATED_SHOW) break;\n visible.push(...group);\n }\n\n return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);\n}\n\nfunction groupByHunk(lines: readonly IDiffLine[]): IDiffLine[][] {\n const groups: IDiffLine[][] = [];\n let current: IDiffLine[] = [];\n\n for (const line of lines) {\n if (line.type === 'hunk' && current.length > 0) {\n groups.push(current);\n current = [line];\n continue;\n }\n current.push(line);\n }\n\n if (current.length > 0) {\n groups.push(current);\n }\n\n return groups;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\nimport { buildToolDiffSummary } from './utils/tool-diff-summary.js';\n\nimport type { IDiffLine } from './utils/edit-diff.js';\n\ninterface IProps {\n file?: string;\n lines: readonly IDiffLine[];\n}\n\nexport default function ToolDiffBlock({ file, lines }: IProps): React.ReactElement {\n const palette = usePalette();\n const renderMarkdown = useRenderMarkdown();\n const summary = buildToolDiffSummary({ file, lines });\n // CLI-2004: the `\\u2502` gutter is a vertical rule a reader announces on every line it prefixes.\n // The mode drops it — indentation already separates the block from the surrounding transcript.\n const screenReader = useScreenReader();\n const gutter = screenReader ? '' : '\\u2502 ';\n\n return (\n <Box flexDirection=\"column\" marginLeft={4}>\n {summary.file && (\n <Text color={palette.text.emphasis} dimColor>\n {gutter}\n {sanitizeTerminalText(summary.file)}\n </Text>\n )}\n {/* `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours. */}\n <RenderedText>{renderMarkdown(summary.markdown)}</RenderedText>\n {summary.truncated && (\n <Text color={palette.text.emphasis} dimColor>\n {gutter}... and {summary.remainingLineCount} more lines\n </Text>\n )}\n </Box>\n );\n}\n","import { formatTokenCount } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IUsageSnapshot } from '@robota-sdk/agent-interface-analytics';\n\nconst TOKEN_COMPACT_THRESHOLD = 1000;\n\nexport default function UsageSummaryEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const usage = entry.data as IUsageSnapshot | undefined;\n if (!usage) return <></>;\n const prompt = usage.promptTokens !== undefined ? formatUsageTokenCount(usage.promptTokens) : '?';\n const completion =\n usage.completionTokens !== undefined ? formatUsageTokenCount(usage.completionTokens) : '?';\n const total = formatUsageTokenCount(usage.totalTokens);\n // Usage another unit spent (the advisor, a background task) is named, and has no context window\n // of the main thread to report.\n const source =\n usage.source !== undefined && usage.source.scope !== 'main' ? usage.source : undefined;\n const sourceLabel = source ? (source.label ?? source.id ?? source.scope) : undefined;\n const context = source\n ? ''\n : ` · Context ${Math.round(usage.contextUsedPercentage)}% (${formatTokenCount(\n usage.contextUsedTokens,\n )}/${formatTokenCount(usage.contextMaxTokens)})`;\n const costLabel = usage.costStatus === 'unknown' ? 'cost unknown' : `cost ${usage.costStatus}`;\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={palette.text.emphasis} bold>\n {sourceLabel ? `Usage (${sourceLabel}):` : 'Usage:'}{' '}\n </Text>\n <Text dimColor>\n {usage.kind} {total} tokens (in {prompt} / out {completion}){context} · {costLabel}\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction formatUsageTokenCount(tokens: number): string {\n return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : formatTokenCount(tokens);\n}\n","import { isToolMessage, isAssistantMessage } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { formatCommandOutputSummary } from './command-output-summary.js';\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { humanizeToolName } from './humanize-tool-name.js';\nimport { useModelCommandToolPrefix } from './product-display-name-context.js';\nimport { RoleLabel } from './RoleLabel.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS, type TScreenReaderLabelKind } from './screen-reader-labels.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\nimport { getToolSummaryLabel, toolSummaryStatusKind } from './tool-summary-status.js';\nimport ToolCommandOutput from './ToolCommandOutput.js';\nimport ToolDiffBlock from './ToolDiffBlock.js';\nimport UsageSummaryEntry from './UsageSummaryEntry.js';\n\nimport type { TToolSummaryItem } from './tool-summary-status.js';\nimport type { IToolCallSummary } from './utils/tool-call-extractor.js';\nimport type { IHistoryEntry, TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';\n\ninterface IProps {\n history: IHistoryEntry[];\n}\n\n/**\n * CLI-2004: one transcript label. In screen-reader mode it speaks the shared vocabulary and drops\n * the colour — the searchable word IS the cue there — and outside it renders exactly as before.\n */\nfunction EntryLabel({\n kind,\n text,\n color,\n}: {\n kind: TScreenReaderLabelKind;\n text: string;\n color: string;\n}): React.ReactElement {\n if (useScreenReader()) return <Text>{SCREEN_READER_LABELS[kind]} </Text>;\n return (\n <Text color={color} bold>\n {text}\n </Text>\n );\n}\n\nfunction ToolMessage({ message }: { message: TUniversalMessage }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n if (!isToolMessage(message)) {\n return <></>;\n }\n const toolName = message.name;\n const content = message.content;\n\n // Try to parse structured tool summaries (with diff info)\n let summaries: IToolCallSummary[] | null = null;\n try {\n const parsed = JSON.parse(content) as IToolCallSummary[];\n if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === 'string') {\n summaries = parsed as IToolCallSummary[];\n }\n } catch {\n // Not JSON — fall back to plain text lines\n }\n\n if (summaries) {\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n {toolName && (\n <Text color={palette.text.emphasis} dimColor>\n [{humanizeToolName(toolName, modelCommandToolPrefix)}]\n </Text>\n )}\n </Box>\n <Text> </Text>\n {summaries.map((s, i) => (\n <Box key={i} flexDirection=\"column\">\n <Text color={palette.text.success}>\n {' '}\n {'✓'} {sanitizeTerminalText(s.line)}\n </Text>\n {s.diffLines && s.diffLines.length > 0 && (\n <ToolDiffBlock file={s.diffFile} lines={s.diffLines} />\n )}\n </Box>\n ))}\n </Box>\n );\n }\n\n // Fallback: plain text lines\n const lines = content.split('\\n').filter((l) => l.trim());\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n {toolName && (\n <Text color={palette.text.emphasis} dimColor>\n [{sanitizeTerminalText(toolName)}]\n </Text>\n )}\n </Box>\n <Text> </Text>\n {lines.map((line, i) => (\n <Text key={i} color={palette.text.success}>\n {' '}\n {'✓'} {sanitizeTerminalText(line)}\n </Text>\n ))}\n </Box>\n );\n}\n\n/** ERR-001 G2: a failed turn renders as a styled error block, not a plain system note. */\nfunction ErrorEntryBlock({ message }: { message: TUniversalMessage }): React.ReactElement {\n const palette = usePalette();\n // SEC-019: an error message is untrusted text — it carries provider responses, tool stderr and\n // file contents — and it reaches `<Text>` without going through the markdown renderer, so it does\n // not inherit that path's sanitization.\n const content = sanitizeTerminalText((message.content ?? '').replace(/^Error:\\s*/, ''));\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"error\" text=\"✖ Error: \" color={palette.text.error} />\n </Box>\n <Box marginLeft={2} flexDirection=\"column\">\n <Text color={palette.text.error} wrap=\"wrap\">\n {content}\n </Text>\n <Text dimColor wrap=\"wrap\">\n The session is still alive — type your next prompt when ready.\n </Text>\n </Box>\n </Box>\n );\n}\n\nconst MessageItem = React.memo(function MessageItem({\n message,\n}: {\n message: TUniversalMessage;\n}): React.ReactElement {\n // Every hook this component uses runs BEFORE the early returns: a message whose role or kind\n // changes in place would otherwise render a different NUMBER of hooks and React would throw.\n const renderMarkdown = useRenderMarkdown();\n\n if (isToolMessage(message)) {\n return <ToolMessage message={message} />;\n }\n\n if (message.role === 'system' && message.metadata?.kind === 'error') {\n return <ErrorEntryBlock message={message} />;\n }\n\n const content = message.content ?? '';\n const isInterrupted = message.state === 'interrupted';\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <RoleLabel\n role={message.role}\n {...(typeof message.metadata?.driverId === 'string'\n ? { driverId: message.metadata.driverId }\n : {})}\n />\n </Box>\n <Text> </Text>\n <Box marginLeft={2}>\n {isAssistantMessage(message) ? (\n // `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours.\n <RenderedText wrap=\"wrap\">\n {renderMarkdown(content + (isInterrupted ? '\\n\\n_(interrupted)_' : ''))}\n </RenderedText>\n ) : (\n <Text wrap=\"wrap\">{content}</Text>\n )}\n </Box>\n </Box>\n );\n});\n\nfunction ToolSummaryEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n const data = entry.data as\n | {\n summary?: string;\n tools?: TToolSummaryItem[];\n }\n | undefined;\n const tools = data?.tools;\n const lines = data?.summary?.split('\\n') ?? [];\n\n if (tools && tools.length > 0) {\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n </Box>\n <Text> </Text>\n {tools.map((tool, i) => {\n const kind = toolSummaryStatusKind(\n tool,\n formatCommandOutputSummary(tool)?.status === 'error',\n );\n return (\n <Box key={i} flexDirection=\"column\">\n <Text color={statusGlyphColor(palette, kind)}>\n {' '}\n {getToolSummaryLabel(tool, kind, modelCommandToolPrefix)}\n </Text>\n <ToolCommandOutput tool={tool} />\n {tool.diffLines && tool.diffLines.length > 0 && (\n <ToolDiffBlock file={tool.diffFile} lines={tool.diffLines} />\n )}\n </Box>\n );\n })}\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n </Box>\n <Text> </Text>\n {lines.map((line, i) => (\n <Text key={i} color={palette.text.success}>\n {' '}\n {sanitizeTerminalText(line)}\n </Text>\n ))}\n </Box>\n );\n}\n\nfunction EventEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const eventData = entry.data as Record<string, TUniversalValue> | undefined;\n // SEC-019: an event message is BUILT by a neutral package from untrusted parts — a skill name that\n // may come from a plugin, a memory topic the model chose — and interpolated into a sentence. The\n // formatter cannot sanitize for a terminal (the same string reaches the GUI, where escapes are not\n // the threat and stripping them would be arbitrary), so this render site is the boundary.\n const text =\n typeof eventData?.message === 'string'\n ? eventData.message\n : typeof eventData?.content === 'string'\n ? eventData.content\n : undefined;\n // An event without a message is a record kept for persistence or telemetry (a provider-call trace,\n // a usage observation), not a notice; printing its type name only adds noise to the transcript.\n if (text === undefined) return <></>;\n const eventMessage = sanitizeTerminalText(text);\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"warning\" text=\"System: \" color={palette.text.warning} />\n </Box>\n <Text> </Text>\n <Box marginLeft={2}>\n <Text wrap=\"wrap\">{eventMessage}</Text>\n </Box>\n </Box>\n );\n}\n\nexport function EntryItem({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n if (entry.category === 'chat') {\n const message = entry.data as TUniversalMessage;\n return <MessageItem message={message} />;\n }\n\n if (entry.type === 'tool-summary') {\n return <ToolSummaryEntry entry={entry} />;\n }\n\n if (entry.type === 'usage-summary') {\n return <UsageSummaryEntry entry={entry} />;\n }\n\n // tool-start/tool-end are recorded in history for persistence but not rendered\n // (StreamingIndicator shows them during streaming, tool-summary shows them after)\n if (entry.type === 'tool-start' || entry.type === 'tool-end') {\n return <></>;\n }\n\n return <EventEntry entry={entry} />;\n}\n\nexport default function MessageList({ history }: IProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n {history.map((entry) => (\n <EntryItem key={entry.id} entry={entry} />\n ))}\n </Box>\n );\n}\n","/**\n * Generic list picker with arrow-key navigation and viewport scrolling.\n * Renders items via a user-supplied renderItem function.\n * Shows a limited number of items at a time; scrolls as the cursor moves.\n */\n\nimport { Box } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { KeyHintFooter, type IKeyHint } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\n\n/** Default number of visible items */\nconst DEFAULT_MAX_VISIBLE = 3;\n\nexport interface IListPickerProps<T> {\n /** Items to display in the list */\n items: T[];\n /** Render function for each item — receives the item and whether it is currently selected */\n renderItem: (item: T, isSelected: boolean) => React.ReactElement;\n /** Called when the user presses Enter on the highlighted item */\n onSelect: (item: T) => void;\n /** Called when the user presses Escape */\n onCancel: () => void;\n /** Maximum number of items visible at once (default: 10) */\n maxVisible?: number;\n /** Key-hint footer shown below the list (default: navigate/select/cancel; `[]` suppresses it) */\n footerHints?: readonly IKeyHint[];\n /** The item highlighted first (default: the first). Out-of-range values fall back to the first. */\n initialIndex?: number;\n}\n\n/** Default affordance footer — Enter always selects and Esc always cancels. */\nexport const LIST_PICKER_DEFAULT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Cancel' },\n];\n\nexport default function ListPicker<T>({\n items,\n renderItem,\n onSelect,\n onCancel,\n maxVisible = DEFAULT_MAX_VISIBLE,\n footerHints = LIST_PICKER_DEFAULT_FOOTER_HINTS,\n initialIndex = 0,\n}: IListPickerProps<T>): React.ReactElement {\n const [state, setState] = useState<ISelectionFlowState>(() => {\n const index = initialIndex >= 0 && initialIndex < items.length ? initialIndex : 0;\n const seeded = createSelectionFlowState(index);\n // Keep the seeded row on screen when it sits past the first window.\n return { ...seeded, scrollOffset: Math.max(0, index - maxVisible + 1) };\n });\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TSelectionInputAction): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: items.length,\n maxVisible,\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onCancel();\n } else if (result.effect.type === 'select') {\n const item = items[result.effect.index];\n if (item !== undefined) {\n onSelect(item);\n }\n }\n },\n [items, maxVisible, onCancel, onSelect],\n );\n\n // CLI-2004: the viewport is a sighted affordance — `↑ N more above` tells a reader nothing about\n // where it is. The mode lists every item with its number and asks for one.\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && items.length > 0,\n itemCount: items.length,\n cancellable: true,\n onSelect: (index) => {\n const item = items[index];\n if (item !== undefined) onSelect(item);\n },\n onCancel,\n });\n\n useKeybindingActions('list-picker', (actions) => actions.forEach(applyAction), {\n isActive: !screenReader,\n });\n const effectiveFooterHints = useKeybindingHints('list-picker', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Select'],\n ['cancel', 'Cancel'],\n ]);\n\n if (items.length === 0) {\n return <Box />;\n }\n\n if (screenReader) {\n const prompt = formatNumberedSelectionPrompt(items.length, true);\n return (\n <Box flexDirection=\"column\">\n {items.map((item, index) => (\n <Box key={index}>\n <Text>{numberedRowPrefix(index)}</Text>\n {renderItem(item, false)}\n </Box>\n ))}\n <Text>\n {prompt}\n {numbered.buffer.length > 0 ? ` ${numbered.buffer}` : ''}\n </Text>\n {numbered.invalid && <Text>{prompt}</Text>}\n </Box>\n );\n }\n\n const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });\n if (normalizedState !== state) {\n stateRef.current = normalizedState;\n }\n const { selectedIndex, scrollOffset } = normalizedState;\n const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = scrollOffset + maxVisible < items.length;\n const hasLess = scrollOffset > 0;\n\n return (\n <Box flexDirection=\"column\">\n {hasLess && <Text dimColor> ↑ {scrollOffset} more above</Text>}\n {visibleItems.map((item, index) => (\n <Box key={scrollOffset + index} marginBottom={1}>\n {renderItem(item, scrollOffset + index === selectedIndex)}\n </Box>\n ))}\n {hasMore && <Text dimColor> ↓ {items.length - scrollOffset - maxVisible} more below</Text>}\n <KeyHintFooter\n hints={\n footerHints === LIST_PICKER_DEFAULT_FOOTER_HINTS ? effectiveFooterHints : footerHints\n }\n />\n </Box>\n );\n}\n","/**\n * Multi-select checklist (CMD-004): arrow-key navigation, Space toggles, Enter confirms once the\n * selection count is within [minSelect, maxSelect], Esc cancels. Used by PendingActionPrompt for an\n * IActionRequest whose `maxSelect` > 1.\n *\n * Note: unlike ListPicker, this renders all options without a scroll viewport — `IActionRequest`'s\n * optional `maxVisible` hint is not yet honored here (multi-select lists are typically short). Adding a\n * viewport is a follow-up if long multi-select lists appear.\n */\n\nimport { Box } from 'ink';\nimport React, { useState } from 'react';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IActionOption } from '@robota-sdk/agent-core';\n\n/**\n * Footer hints for the multi-select checklist. The Enter hint carries a dynamic `(min N)` segment\n * until the selection is confirmable.\n */\nexport function getMultiSelectFooterHints(input: {\n canConfirm: boolean;\n minSelect: number;\n}): readonly IKeyHint[] {\n return [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Space', label: 'Toggle' },\n { keys: 'Enter', label: input.canConfirm ? 'Confirm' : `Confirm (min ${input.minSelect})` },\n { keys: 'Esc', label: 'Cancel' },\n ];\n}\n\n/**\n * One checklist row. The mode swaps the position cue — a spoken number for the `> ` cursor and its\n * colour — and nothing else: the checkbox is what says selected, in both.\n */\nfunction ChecklistRow({\n label,\n index,\n isCursor,\n isChecked,\n screenReader,\n}: {\n label: string;\n index: number;\n isCursor: boolean;\n isChecked: boolean;\n screenReader: boolean;\n}): React.ReactElement {\n const palette = usePalette();\n const box = isChecked ? '[x] ' : '[ ] ';\n if (screenReader) {\n return (\n <Text>\n {numberedRowPrefix(index)}\n {box}\n {label}\n </Text>\n );\n }\n return (\n <Text color={isCursor ? palette.text.accent : undefined}>\n {isCursor ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {box}\n {label}\n </Text>\n );\n}\n\nexport interface IMultiSelectListProps {\n title: string;\n description?: string;\n options: readonly IActionOption[];\n minSelect: number;\n maxSelect: number;\n defaultValues?: readonly string[];\n onConfirm: (values: string[]) => void;\n onCancel: () => void;\n}\n\n/** The arrow-key driver: cursor motion, Space toggles, Enter confirms. Inactive in the mode. */\nfunction useChecklistArrowKeys(inputs: {\n active: boolean;\n itemCount: number;\n setCursor: React.Dispatch<React.SetStateAction<number>>;\n onToggleAtCursor: () => void;\n onConfirm: () => void;\n onCancel: () => void;\n}): void {\n const { active, itemCount, setCursor } = inputs;\n useKeybindingActions(\n 'multi-select',\n (actions) => {\n for (const action of actions) {\n if (action === 'previous') {\n setCursor((c) => (c <= 0 ? itemCount - 1 : c - 1));\n } else if (action === 'next') {\n setCursor((c) => (c >= itemCount - 1 ? 0 : c + 1));\n } else if (action === 'toggle') {\n inputs.onToggleAtCursor();\n } else if (action === 'confirm') {\n inputs.onConfirm();\n } else if (action === 'cancel') {\n inputs.onCancel();\n }\n }\n },\n { isActive: active },\n );\n}\n\n/** How the checklist asks for an answer: a spoken range in the mode, the key hints otherwise. */\nfunction ChecklistAnswer(props: {\n screenReader: boolean;\n itemCount: number;\n numbered: { buffer: string; invalid: boolean };\n canConfirm: boolean;\n minSelect: number;\n}): React.ReactElement {\n const hints = useKeybindingHints('multi-select', [\n [['previous', 'next'], 'Navigate'],\n ['toggle', 'Toggle'],\n ['confirm', props.canConfirm ? 'Confirm' : `Confirm (min ${props.minSelect})`],\n ['cancel', 'Cancel'],\n ]);\n if (props.screenReader) {\n return (\n <NumberedSelectionPrompt\n itemCount={props.itemCount}\n cancellable\n buffer={props.numbered.buffer}\n invalid={props.numbered.invalid}\n />\n );\n }\n return <KeyHintFooter hints={hints} />;\n}\n\n/** The rendered checklist: the rows, and whichever answer affordance the mode calls for. */\nfunction ChecklistView(props: {\n title: string;\n description?: string;\n options: readonly IActionOption[];\n cursor: number;\n selected: ReadonlySet<string>;\n screenReader: boolean;\n canConfirm: boolean;\n minSelect: number;\n numbered: { buffer: string; invalid: boolean };\n}): React.ReactElement {\n const palette = usePalette();\n const { title, description, options, cursor, selected, screenReader, numbered } = props;\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.attention })}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n {description !== undefined && description.length > 0 && <Text dimColor>{description}</Text>}\n {options.map((option, index) => (\n <ChecklistRow\n key={option.value}\n label={option.label}\n index={index}\n isCursor={index === cursor}\n isChecked={selected.has(option.value)}\n screenReader={screenReader}\n />\n ))}\n <ChecklistAnswer\n screenReader={screenReader}\n itemCount={options.length}\n numbered={numbered}\n canConfirm={props.canConfirm}\n minSelect={props.minSelect}\n />\n </Box>\n );\n}\n\n/** The checklist's own state: which row the cursor is on, what is ticked, and how it commits. */\nfunction useChecklistSelection(inputs: {\n options: readonly IActionOption[];\n minSelect: number;\n maxSelect: number;\n defaultValues?: readonly string[];\n onConfirm: (values: string[]) => void;\n}): {\n cursor: number;\n setCursor: React.Dispatch<React.SetStateAction<number>>;\n selected: ReadonlySet<string>;\n toggleAt: (index: number) => void;\n confirmIfAllowed: () => void;\n} {\n const { options, minSelect, maxSelect, defaultValues, onConfirm } = inputs;\n const [cursor, setCursor] = useState(0);\n const [selected, setSelected] = useState<ReadonlySet<string>>(() => new Set(defaultValues ?? []));\n\n const toggleAt = (index: number): void => {\n const option = options[index];\n if (option === undefined) return;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(option.value)) {\n next.delete(option.value);\n } else if (next.size < maxSelect) {\n next.add(option.value);\n }\n return next;\n });\n };\n const confirmIfAllowed = (): void => {\n if (selected.size >= minSelect) onConfirm([...selected]);\n };\n\n return { cursor, setCursor, selected, toggleAt, confirmIfAllowed };\n}\n\nexport default function MultiSelectList({\n title,\n description,\n options,\n minSelect,\n maxSelect,\n defaultValues,\n onConfirm,\n onCancel,\n}: IMultiSelectListProps): React.ReactElement {\n // CLI-2004: the checklist keeps its own reducer (Space toggles, Enter confirms) — what the mode\n // changes is the ROW: a spoken number replaces the `> ` cursor, and typing that number toggles it.\n const screenReader = useScreenReader();\n const { cursor, setCursor, selected, toggleAt, confirmIfAllowed } = useChecklistSelection({\n options,\n minSelect,\n maxSelect,\n defaultValues,\n onConfirm,\n });\n\n // CLI-2004: the checklist answers by number in the mode, through the SAME reducer every other\n // numbered menu uses — `multi` is what keeps it open after a toggle and makes an empty Enter the\n // commit. A local digit branch would have capped it at nine rows and skipped the shared prompt.\n const numbered = useNumberedSelection({\n enabled: screenReader,\n itemCount: options.length,\n cancellable: true,\n multi: true,\n onSelect: toggleAt,\n onConfirm: confirmIfAllowed,\n onCancel,\n });\n\n useChecklistArrowKeys({\n active: !screenReader,\n itemCount: options.length,\n setCursor,\n onToggleAtCursor: () => toggleAt(cursor),\n onConfirm: confirmIfAllowed,\n onCancel,\n });\n\n const canConfirm = selected.size >= minSelect;\n\n return (\n <ChecklistView\n title={title}\n {...(description !== undefined ? { description } : {})}\n options={options}\n cursor={cursor}\n selected={selected}\n screenReader={screenReader}\n canConfirm={canConfirm}\n minSelect={minSelect}\n numbered={numbered}\n />\n );\n}\n","export interface ITextPromptFlowState {\n value: string;\n error?: string;\n resolved: boolean;\n}\n\nexport interface ITextPromptInputKey {\n escape?: boolean;\n return?: boolean;\n backspace?: boolean;\n delete?: boolean;\n ctrl?: boolean;\n meta?: boolean;\n}\n\nexport type TTextPromptInputAction =\n { type: 'cancel' } | { type: 'submit' } | { type: 'delete' } | { type: 'insert'; value: string };\n\nexport type TTextPromptEffect =\n { type: 'none' } | { type: 'cancel' } | { type: 'submit'; value: string };\n\nexport interface ITextPromptFlowOptions {\n allowEmpty: boolean;\n validate?: (value: string) => string | undefined;\n}\n\nexport function createTextPromptFlowState(): ITextPromptFlowState {\n return { value: '', resolved: false };\n}\n\nexport function getTextPromptInputAction(\n input: string,\n key: ITextPromptInputKey,\n): TTextPromptInputAction | undefined {\n if (key.escape === true) {\n return { type: 'cancel' };\n }\n if (key.return === true) {\n return { type: 'submit' };\n }\n if (key.backspace === true || key.delete === true) {\n return { type: 'delete' };\n }\n if (input && key.ctrl !== true && key.meta !== true) {\n return { type: 'insert', value: input };\n }\n return undefined;\n}\n\nexport function applyTextPromptInput(\n state: ITextPromptFlowState,\n action: TTextPromptInputAction,\n options: ITextPromptFlowOptions,\n): { state: ITextPromptFlowState; effect: TTextPromptEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (action.type === 'cancel') {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (action.type === 'delete') {\n return {\n state: { ...state, value: state.value.slice(0, -1), error: undefined },\n effect: { type: 'none' },\n };\n }\n if (action.type === 'insert') {\n return {\n state: { ...state, value: state.value + action.value, error: undefined },\n effect: { type: 'none' },\n };\n }\n return submitTextPromptValue(state, options);\n}\n\nfunction submitTextPromptValue(\n state: ITextPromptFlowState,\n options: ITextPromptFlowOptions,\n): { state: ITextPromptFlowState; effect: TTextPromptEffect } {\n const trimmed = state.value.trim();\n if (!trimmed && !options.allowEmpty) {\n const emptyError = options.validate?.(trimmed);\n return {\n state: emptyError ? { ...state, error: emptyError } : state,\n effect: { type: 'none' },\n };\n }\n const error = options.validate?.(trimmed);\n if (error !== undefined) {\n return { state: { ...state, error }, effect: { type: 'none' } };\n }\n return { state: { ...state, resolved: true }, effect: { type: 'submit', value: trimmed } };\n}\n","import { Box } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applyTextPromptInput,\n createTextPromptFlowState,\n type ITextPromptFlowState,\n type TTextPromptInputAction,\n} from './flows/text-prompt-flow.js';\nimport { KeyHintFooter, type IKeyHint } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** Footer for the free-text prompt. */\nexport const TEXT_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: 'Enter', label: 'Submit' },\n { keys: 'Esc', label: 'Cancel' },\n];\n\ninterface IProps {\n title: string;\n description?: string;\n placeholder?: string;\n onSubmit: (value: string) => void;\n onCancel: () => void;\n validate?: (value: string) => string | undefined;\n allowEmpty?: boolean;\n masked?: boolean;\n}\n\nexport default function TextPrompt({\n title,\n description,\n placeholder,\n onSubmit,\n onCancel,\n validate,\n allowEmpty = false,\n masked = false,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ITextPromptFlowState>(() => createTextPromptFlowState());\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TTextPromptInputAction): void => {\n const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onCancel();\n } else if (result.effect.type === 'submit') {\n onSubmit(result.effect.value);\n }\n },\n [allowEmpty, validate, onCancel, onSubmit],\n );\n\n const screenReader = useScreenReader();\n\n useKeybindingActions('text-prompt', (actions, input, key, consumed) => {\n for (const action of actions) {\n if (action === 'submit') applyAction({ type: 'submit' });\n else if (action === 'cancel') applyAction({ type: 'cancel' });\n else if (action === 'delete-backward') applyAction({ type: 'delete' });\n }\n if (actions.length === 0 && !consumed && input && key.ctrl !== true && key.meta !== true) {\n applyAction({ type: 'insert', value: input });\n }\n });\n const footerHints = useKeybindingHints('text-prompt', [\n ['submit', 'Submit'],\n ['cancel', 'Cancel'],\n ]);\n\n // CLI-2004: the box is dropped rather than restyled — the mode omits the prop, so no colour\n // literal is introduced and the palette floor stays green.\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.attention })}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n <PromptDescription description={description} />\n <Box marginTop={1}>\n <Text color={palette.text.accent}>&gt; </Text>\n {state.value ? (\n <Text>{masked ? '*'.repeat(state.value.length) : state.value}</Text>\n ) : placeholder ? (\n <Text dimColor>{placeholder}</Text>\n ) : null}\n <Text color={palette.text.accent}>█</Text>\n </Box>\n {state.error && <Text color={palette.text.error}>{state.error}</Text>}\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n\nfunction PromptDescription({ description }: { description?: string }): React.ReactElement | null {\n if (description === undefined || description.length === 0) {\n return null;\n }\n\n return <Text dimColor>{description}</Text>;\n}\n","/**\n * PendingActionPrompt (CMD-004) — the TUI renderer for the unified `IActionRequest`. It derives the\n * presentation from the request's fields (not a kind switch):\n * - no options → free-text (TextPrompt, honoring `masked`/`placeholder`/`allowEmpty`)\n * - options, `maxSelect` > 1 → multi-select checklist (MultiSelectList)\n * - options, `maxSelect` <= 1 → single-select list (ListPicker); with `allowFreeText`, a synthetic\n * \"Type a custom answer…\" entry switches to a text field\n *\n * It always resolves the caller's `onAnswer` with a `TActionResponse` (answer or cancelled).\n */\n\nimport { Box } from 'ink';\nimport React, { useState } from 'react';\n\nimport { SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport ListPicker from './ListPicker.js';\nimport MultiSelectList from './MultiSelectList.js';\nimport { Text } from './SafeText.js';\nimport TextPrompt from './TextPrompt.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IActionOption, IActionRequest, TActionResponse } from '@robota-sdk/agent-core';\n\ninterface IPendingActionPromptProps {\n request: IActionRequest;\n onAnswer: (response: TActionResponse) => void;\n}\n\n/**\n * Synthetic \"type a custom answer\" entry for a single-select with free text. Routed by object identity\n * (reference equality), not by a magic value — so it can never collide with a real option's `value`.\n */\nconst FREE_TEXT_OPTION: IActionOption = { value: '', label: 'Type a custom answer…' };\n\nexport default function PendingActionPrompt({\n request,\n onAnswer,\n}: IPendingActionPromptProps): React.ReactElement {\n const palette = usePalette();\n const [freeTextMode, setFreeTextMode] = useState(false);\n\n const options = request.options ?? [];\n const hasOptions = options.length > 0;\n const maxSelect = request.maxSelect ?? 1;\n const cancel = (): void => onAnswer({ type: 'cancelled' });\n\n // Pure free-text (no options) — or the user chose \"type a custom answer\" on a single-select.\n if (!hasOptions || freeTextMode) {\n return (\n <TextPrompt\n key={`text:${request.id}`}\n title={request.title}\n description={request.description}\n placeholder={request.placeholder}\n allowEmpty={request.allowEmpty}\n masked={request.masked}\n onSubmit={(value) => onAnswer({ type: 'answer', values: [], text: value })}\n onCancel={freeTextMode ? () => setFreeTextMode(false) : cancel}\n />\n );\n }\n\n // Multi-select.\n if (maxSelect > 1) {\n return (\n <MultiSelectList\n title={request.title}\n description={request.description}\n options={options}\n minSelect={request.minSelect ?? 1}\n maxSelect={maxSelect}\n defaultValues={request.default?.values}\n onConfirm={(values) => onAnswer({ type: 'answer', values })}\n onCancel={cancel}\n />\n );\n }\n\n // Single-select, optionally with a \"type your own\" entry (routed by identity, see FREE_TEXT_OPTION).\n const pickerItems: IActionOption[] = request.allowFreeText\n ? [...options, FREE_TEXT_OPTION]\n : [...options];\n // BEHAVIOR-2437: a request's declared default is where the highlight STARTS — for a write\n // confirmation built with `defaultYes: false`, a bare Enter must answer No, not Yes.\n const defaultValue = request.default?.values?.[0];\n const initialIndex =\n defaultValue === undefined\n ? 0\n : Math.max(\n 0,\n pickerItems.findIndex((option) => option.value === defaultValue),\n );\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>{request.title}</Text>\n {request.description !== undefined && request.description.length > 0 && (\n <Text dimColor>{request.description}</Text>\n )}\n <ListPicker<IActionOption>\n items={pickerItems}\n initialIndex={initialIndex}\n maxVisible={request.maxVisible}\n renderItem={(option, isSelected) => (\n <Text color={isSelected ? palette.text.accent : undefined}>\n {isSelected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {option.label}\n </Text>\n )}\n onSelect={(option) => {\n if (option === FREE_TEXT_OPTION) {\n setFreeTextMode(true);\n } else {\n onAnswer({ type: 'answer', values: [option.value] });\n }\n }}\n onCancel={cancel}\n />\n </Box>\n );\n}\n","import {\n applySelectionInput,\n getDirectionalSelectionInputAction,\n type ISelectionFlowState,\n type ISelectionInputKey,\n type TSelectionInputAction,\n} from './selection-flow.js';\n\nexport const PERMISSION_PROMPT_OPTIONS = [\n 'Allow [y]',\n 'Allow always (this session) [s]',\n 'Allow always (this project) [p]',\n 'Deny [n]',\n] as const;\n\n/**\n * Issue #2351: the \"always\" options name the SCOPE they grant — the consent pattern the enforcer\n * remembers (`Bash(git *)`, `Read(/w/src/**)`, `WebFetch(https://h/**)`), not the whole tool.\n */\nexport function permissionPromptOptionsFor(\n scope: string,\n canPersistProjectPermission = true,\n): readonly string[] {\n return [\n PERMISSION_PROMPT_OPTIONS[0],\n `Allow ${scope} always (this session) [s]`,\n canPersistProjectPermission\n ? `Allow ${scope} always (this project) [p]`\n : 'Project-wide approval unavailable',\n PERMISSION_PROMPT_OPTIONS[3],\n ];\n}\n\nexport type TPermissionPromptDecision = true | 'allow-session' | 'allow-project' | false;\nexport type TPermissionPromptInputAction =\n TSelectionInputAction | { type: 'shortcut'; index: number };\n\nexport type TPermissionPromptEffect =\n { type: 'none' } | { type: 'resolve'; decision: TPermissionPromptDecision };\n\nexport function getPermissionPromptInputAction(\n input: string,\n key: ISelectionInputKey,\n): TPermissionPromptInputAction | undefined {\n const action = getDirectionalSelectionInputAction({ ...key, escape: false });\n if (action !== undefined) {\n return action;\n }\n if (input === 'y' || input === '1') {\n return { type: 'shortcut', index: 0 };\n }\n if (input === 's' || input === 'a' || input === '2') {\n return { type: 'shortcut', index: 1 };\n }\n if (input === 'p' || input === '3') {\n return { type: 'shortcut', index: 2 };\n }\n if (input === 'n' || input === 'd' || input === '4') {\n return { type: 'shortcut', index: 3 };\n }\n return undefined;\n}\n\nexport function applyPermissionPromptInput(\n state: ISelectionFlowState,\n action: TPermissionPromptInputAction,\n canPersistProjectPermission = true,\n): { state: ISelectionFlowState; effect: TPermissionPromptEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (typeof action !== 'string') {\n if (action.index === 2 && !canPersistProjectPermission) {\n return { state, effect: { type: 'none' } };\n }\n return resolvePermissionIndex(state, action.index);\n }\n const result = applySelectionInput(state, action, {\n itemCount: PERMISSION_PROMPT_OPTIONS.length,\n });\n if (result.effect.type !== 'select') {\n return { state: result.state, effect: { type: 'none' } };\n }\n if (result.effect.index === 2 && !canPersistProjectPermission) {\n return { state: result.state, effect: { type: 'none' } };\n }\n return {\n state: result.state,\n effect: { type: 'resolve', decision: getPermissionDecision(result.effect.index) },\n };\n}\n\nfunction getPermissionDecision(index: number): TPermissionPromptDecision {\n if (index === 0) return true;\n if (index === 1) return 'allow-session';\n if (index === 2) return 'allow-project';\n return false;\n}\n\nfunction resolvePermissionIndex(\n state: ISelectionFlowState,\n index: number,\n): { state: ISelectionFlowState; effect: TPermissionPromptEffect } {\n return {\n state: { ...state, selectedIndex: index, resolved: true },\n effect: { type: 'resolve', decision: getPermissionDecision(index) },\n };\n}\n","import { consentScopeFor } from '@robota-sdk/agent-framework';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport {\n applyPermissionPromptInput,\n permissionPromptOptionsFor,\n type TPermissionPromptInputAction,\n} from './flows/permission-prompt-flow.js';\nimport { createSelectionFlowState, type ISelectionFlowState } from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedList } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS } from './screen-reader-labels.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IPendingPermissionRequest } from './types.js';\nimport type { TToolArgs } from '@robota-sdk/agent-core';\n\ninterface IProps {\n request: IPendingPermissionRequest;\n}\n\n/**\n * Footer for the permission prompt — identical to the confirm prompt's (same horizontal row, same\n * reducer). Esc is deliberately suppressed by the flow (`escape: false` — a permission ask must\n * resolve explicitly; Esc-dismissal would be an implicit deny), so the footer omits it.\n */\nexport const PERMISSION_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '←→', label: 'Navigate' },\n { keys: 'Enter', label: 'Confirm' },\n];\n\nfunction formatArgs(args: TToolArgs): string {\n const entries = Object.entries(args);\n if (entries.length === 0) return '(no arguments)';\n return entries\n .map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)\n .join(', ');\n}\n\nexport default function PermissionPrompt({ request }: IProps): React.ReactElement {\n const palette = usePalette();\n const canPersistProjectPermission = request.canPersistProjectPermission !== false;\n const [state, setState] = React.useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = React.useRef(state);\n const prevRequestRef = React.useRef(request);\n\n if (prevRequestRef.current !== request) {\n prevRequestRef.current = request;\n const nextState = createSelectionFlowState();\n stateRef.current = nextState;\n setState(nextState);\n }\n\n const applyAction = React.useCallback(\n (action: TPermissionPromptInputAction): void => {\n const result = applyPermissionPromptInput(\n stateRef.current,\n action,\n canPersistProjectPermission,\n );\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'resolve') {\n request.resolve(result.effect.decision);\n }\n },\n [request, canPersistProjectPermission],\n );\n\n const screenReader = useScreenReader();\n const options = permissionPromptOptionsFor(\n consentScopeFor(request.toolName, request.toolArgs),\n canPersistProjectPermission,\n );\n const numbered = useNumberedSelection({\n enabled: screenReader,\n itemCount: options.length,\n onSelect: (index) => {\n const result = applyPermissionPromptInput(\n stateRef.current,\n {\n type: 'shortcut',\n index,\n },\n canPersistProjectPermission,\n );\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'resolve') request.resolve(result.effect.decision);\n },\n });\n\n useKeybindingActions(\n 'permission-prompt',\n (actions) => {\n const shortcutIndex = {\n 'allow-once': 0,\n 'allow-session': 1,\n 'allow-project': 2,\n deny: 3,\n } as const;\n for (const action of actions) {\n if (action in shortcutIndex) {\n applyAction({\n type: 'shortcut',\n index: shortcutIndex[action as keyof typeof shortcutIndex],\n });\n } else if (action === 'previous' || action === 'next' || action === 'confirm') {\n applyAction(action === 'confirm' ? 'select' : action);\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('permission-prompt', [\n [['previous', 'next'], 'Navigate'],\n ['confirm', 'Confirm'],\n ]);\n\n if (screenReader) {\n return (\n <NumberedList\n title={SCREEN_READER_LABELS.permissionRequired}\n description={`${request.toolName} — ${formatArgs(request.toolArgs)}${\n request.requestedByPeer !== undefined\n ? ` (requested by another session: ${request.requestedByPeer})`\n : ''\n }`}\n options={options}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={palette.border.attention}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n [Permission Required]\n </Text>\n <Text>\n Tool:{' '}\n <Text color={palette.text.accent} bold>\n {request.toolName}\n </Text>\n </Text>\n <Text dimColor> {formatArgs(request.toolArgs)}</Text>\n {request.requestedByPeer !== undefined ? (\n <Text color={palette.text.warning}>\n Requested by another session: {request.requestedByPeer}\n </Text>\n ) : null}\n {/* Issue #2351: the \"always\" options carry the consent scope, so they no longer fit one row\n of the prompt box — a row wrapped `Allow [y]` across two lines. One option per line. */}\n <Box marginTop={1} flexDirection=\"column\">\n {options.map((opt, i) => (\n <Box key={opt}>\n <Text\n color={i === state.selectedIndex ? palette.text.accent : undefined}\n bold={i === state.selectedIndex}\n >\n {i === state.selectedIndex ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {opt}\n </Text>\n </Box>\n ))}\n </Box>\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","import {\n applySelectionInput,\n getDirectionalSelectionInputAction,\n type ISelectionFlowState,\n type ISelectionInputKey,\n type TSelectionEffect,\n type TSelectionInputAction,\n} from './selection-flow.js';\n\nexport type TConfirmPromptInputAction = TSelectionInputAction | { type: 'shortcut'; index: number };\n\nexport function getConfirmPromptInputAction(\n input: string,\n key: ISelectionInputKey,\n optionCount: number,\n): TConfirmPromptInputAction | undefined {\n const action = getDirectionalSelectionInputAction({ ...key, escape: false });\n if (action !== undefined) {\n return action;\n }\n if (optionCount === 2 && input === 'y') {\n return { type: 'shortcut', index: 0 };\n }\n if (optionCount === 2 && input === 'n') {\n return { type: 'shortcut', index: 1 };\n }\n return undefined;\n}\n\n/**\n * CLI-2004 — the typed yes/no answer.\n *\n * A two-option arrow menu is the worst shape for a reader: both rows are announced, and which one is\n * selected differs only by a cursor glyph and a colour. In screen-reader mode the prompt asks for a\n * typed answer instead — `y`, `n`, `yes` or `no`, then Enter. Anything else clears the buffer and\n * resolves nothing, so a mistyped answer cannot be read as consent.\n */\nexport interface ITypedConfirmState {\n buffer: string;\n resolved: boolean;\n}\n\nexport function createTypedConfirmState(): ITypedConfirmState {\n return { buffer: '', resolved: false };\n}\n\nconst YES_ANSWERS = ['y', 'yes'];\nconst NO_ANSWERS = ['n', 'no'];\n\nexport function applyTypedConfirmInput(\n state: ITypedConfirmState,\n input: string,\n key: ISelectionInputKey & { backspace?: boolean; delete?: boolean },\n): { state: ITypedConfirmState; effect: TSelectionEffect } {\n if (state.resolved) return { state, effect: { type: 'none' } };\n if (key.backspace === true || key.delete === true) {\n return { state: { ...state, buffer: state.buffer.slice(0, -1) }, effect: { type: 'none' } };\n }\n if (key.return === true) {\n const answer = state.buffer.trim().toLowerCase();\n if (YES_ANSWERS.includes(answer)) {\n return { state: { buffer: '', resolved: true }, effect: { type: 'select', index: 0 } };\n }\n if (NO_ANSWERS.includes(answer)) {\n return { state: { buffer: '', resolved: true }, effect: { type: 'select', index: 1 } };\n }\n return { state: { ...state, buffer: '' }, effect: { type: 'none' } };\n }\n if (/^[a-zA-Z]+$/.test(input)) {\n return { state: { ...state, buffer: state.buffer + input }, effect: { type: 'none' } };\n }\n return { state, effect: { type: 'none' } };\n}\n\nexport function applyConfirmPromptInput(\n state: ISelectionFlowState,\n action: TConfirmPromptInputAction,\n optionCount: number,\n): { state: ISelectionFlowState; effect: TSelectionEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (typeof action !== 'string') {\n return {\n state: { ...state, selectedIndex: action.index, resolved: true },\n effect: { type: 'select', index: action.index },\n };\n }\n return applySelectionInput(state, action, { itemCount: optionCount });\n}\n","/**\n * Reusable confirmation prompt with arrow-key selection.\n * Used by model change, permission prompts, and other yes/no confirmations.\n */\n\nimport { Box, useInput } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applyConfirmPromptInput,\n applyTypedConfirmInput,\n createTypedConfirmState,\n type ITypedConfirmState,\n type TConfirmPromptInputAction,\n} from './flows/confirm-prompt-flow.js';\nimport { createSelectionFlowState, type ISelectionFlowState } from './flows/selection-flow.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** The typed-answer prompt. Authored here beside the reducer that accepts it. */\nexport const CONFIRM_PROMPT_TYPED_LITERAL = 'Answer y or n and press Enter';\n\n/**\n * Footer for the confirm prompt. Names the canonical keys for a horizontal row (←→; the reducer\n * also accepts ↑↓ as aliases). Esc is deliberately suppressed by the flow (`escape: false` — the\n * prompt must resolve explicitly), so per the affordance contract the footer omits it.\n */\nexport const CONFIRM_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '←→', label: 'Navigate' },\n { keys: 'Enter', label: 'Confirm' },\n];\n\ninterface IProps {\n /** Message to display above the options */\n message: string;\n /** Options to select from (default: ['Yes', 'No']) */\n options?: string[];\n /** Callback with the selected index */\n onSelect: (index: number) => void;\n}\n\nexport default function ConfirmPrompt({\n message,\n options = ['Yes', 'No'],\n onSelect,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TConfirmPromptInputAction): void => {\n const result = applyConfirmPromptInput(stateRef.current, action, options.length);\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'select') {\n onSelect(result.effect.index);\n }\n },\n [onSelect, options.length],\n );\n\n const screenReader = useScreenReader();\n const [typed, setTyped] = useState<ITypedConfirmState>(createTypedConfirmState);\n const typedRef = useRef(typed);\n\n useInput(\n (input, key) => {\n const result = applyTypedConfirmInput(typedRef.current, input, key);\n typedRef.current = result.state;\n setTyped(result.state);\n if (result.effect.type === 'select') onSelect(result.effect.index);\n },\n { isActive: screenReader },\n );\n\n useKeybindingActions(\n 'confirm-prompt',\n (actions) => {\n for (const action of actions) {\n if (action === 'choose-yes' && options.length === 2) {\n applyAction({ type: 'shortcut', index: 0 });\n } else if (action === 'choose-no' && options.length === 2) {\n applyAction({ type: 'shortcut', index: 1 });\n } else if (action === 'previous' || action === 'next' || action === 'confirm') {\n applyAction(action === 'confirm' ? 'select' : action);\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('confirm-prompt', [\n [['previous', 'next'], 'Navigate'],\n ['confirm', 'Confirm'],\n ]);\n\n if (screenReader) {\n return (\n <Box flexDirection=\"column\">\n <Text>{message}</Text>\n <Text>\n {CONFIRM_PROMPT_TYPED_LITERAL}\n {typed.buffer.length > 0 ? ` ${typed.buffer}` : ''}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={palette.border.attention}\n paddingX={1}\n >\n <Text color={palette.text.warning}>{message}</Text>\n <Box marginTop={1}>\n {options.map((opt, i) => (\n <Box key={opt} marginRight={2}>\n <Text\n color={i === state.selectedIndex ? palette.text.accent : undefined}\n bold={i === state.selectedIndex}\n >\n {i === state.selectedIndex ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {opt}\n </Text>\n </Box>\n ))}\n </Box>\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","/**\n * Hook: fetch data for PluginTUI screens.\n * Extracted from PluginTUI.tsx for single-responsibility.\n */\n\nimport { useState, useEffect } from 'react';\n\nimport type { IMenuSelectItem } from '../MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\nexport function usePluginScreenData(\n screen: string,\n marketplace: string | undefined,\n callbacks: ICommandPluginAdapter,\n refreshCounter: number,\n stackLength: number,\n): { items: IMenuSelectItem[]; loading: boolean; error: string | undefined } {\n const [items, setItems] = useState<IMenuSelectItem[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n setItems([]);\n setError(undefined);\n\n if (screen === 'marketplace-list') {\n setLoading(true);\n callbacks\n .marketplaceList()\n .then((sources) => {\n const baseItems: IMenuSelectItem[] = [{ label: 'Add Marketplace', value: '__add__' }];\n const sourceItems: IMenuSelectItem[] = sources.map((s) => ({\n label: s.name,\n value: s.name,\n hint: s.type,\n }));\n setItems([...baseItems, ...sourceItems]);\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n } else if (screen === 'marketplace-browse') {\n const mp = marketplace ?? '';\n setLoading(true);\n callbacks\n .listAvailablePlugins(mp)\n .then((plugins) => {\n setItems(\n plugins.map((p) => ({\n label: p.name,\n value: p.name,\n hint: p.installed ? 'installed' : p.description,\n })),\n );\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n } else if (screen === 'installed-list') {\n setLoading(true);\n callbacks\n .listInstalled()\n .then((plugins) => {\n setItems(\n plugins.map((p) => ({\n label: p.name,\n value: p.name,\n hint: p.description,\n })),\n );\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n }\n }, [stackLength, screen, marketplace, callbacks, refreshCounter]);\n\n return { items, loading, error };\n}\n","import { Box } from 'ink';\nimport React, { useState, useCallback, useRef } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedList } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** Footer for the interactive menu state. */\nexport const MENU_SELECT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Back' },\n];\n\n/** Footer for the error state — Esc (cancel) is the only active key. */\nexport const MENU_SELECT_ERROR_FOOTER_HINTS: readonly IKeyHint[] = [{ keys: 'Esc', label: 'Back' }];\n\nexport interface IMenuSelectItem {\n label: string;\n value: string;\n hint?: string;\n}\n\ninterface IProps {\n title: string;\n items: IMenuSelectItem[];\n onSelect: (value: string) => void;\n onBack: () => void;\n loading?: boolean;\n error?: string;\n}\n\nexport default function MenuSelect({\n title,\n items,\n onSelect,\n onBack,\n loading,\n error,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n const isEnabled = !loading && !error;\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && isEnabled,\n itemCount: items.length,\n cancellable: true,\n onSelect: (index) => {\n const item = items[index];\n if (item !== undefined) onSelect(item.value);\n },\n onCancel: onBack,\n });\n const applyAction = useCallback(\n (action: TSelectionInputAction): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: items.length,\n enabled: isEnabled,\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onBack();\n } else if (result.effect.type === 'select') {\n const item = items[result.effect.index];\n if (item !== undefined) {\n onSelect(item.value);\n }\n }\n },\n [isEnabled, items, onBack, onSelect],\n );\n\n useKeybindingActions(\n 'menu-select',\n (actions) => actions.forEach((action) => applyAction(action === 'back' ? 'cancel' : action)),\n { isActive: !screenReader },\n );\n const menuHints = useKeybindingHints('menu-select', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Select'],\n ['back', 'Back'],\n ]);\n const errorHints = useKeybindingHints('menu-select', [['back', 'Back']]);\n\n const normalizedState = normalizeSelectionState(state, { itemCount: items.length });\n if (normalizedState !== state) {\n stateRef.current = normalizedState;\n }\n const selected = normalizedState.selectedIndex;\n\n if (screenReader) {\n return (\n <NumberedList\n title={title}\n options={loading ? [] : error ? [] : items.map((item) => item.label)}\n description={loading ? 'Loading...' : error}\n cancellable\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={palette.border.attention}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n {loading && (\n <Box marginTop={1}>\n <Text dimColor>Loading...</Text>\n </Box>\n )}\n {error && (\n <Box marginTop={1}>\n <Text color={palette.text.error}>{error}</Text>\n </Box>\n )}\n {!loading && !error && (\n <Box flexDirection=\"column\" marginTop={1}>\n {items.map((item, i) => (\n <Box key={item.value}>\n <Text color={i === selected ? palette.text.accent : undefined} bold={i === selected}>\n {i === selected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {item.label}\n </Text>\n {item.hint && <Text dimColor> {item.hint}</Text>}\n </Box>\n ))}\n </Box>\n )}\n <KeyHintFooter hints={loading ? [] : error ? errorHints : menuHints} />\n </Box>\n );\n}\n","/**\n * Screen-specific selection handlers for PluginTUI.\n * Extracted to keep PluginTUI.tsx under 300 lines.\n */\n\nimport type { IMenuSelectItem } from './MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\ninterface IConfirmState {\n message: string;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\ninterface IMenuContext {\n marketplace?: string;\n pluginId?: string;\n}\n\ninterface INavActions {\n push: (state: { screen: string; context?: IMenuContext }) => void;\n pop: () => void;\n popN: (n: number) => void;\n notify: (content: string) => void;\n setConfirm: (state: IConfirmState | undefined) => void;\n refresh: () => void;\n}\n\nexport function handleMainSelect(value: string, nav: Pick<INavActions, 'push'>): void {\n if (value === 'marketplace') {\n nav.push({ screen: 'marketplace-list' });\n } else if (value === 'installed') {\n nav.push({ screen: 'installed-list' });\n }\n}\n\nexport function handleMarketplaceListSelect(value: string, nav: Pick<INavActions, 'push'>): void {\n if (value === '__add__') {\n nav.push({ screen: 'marketplace-add' });\n } else {\n nav.push({ screen: 'marketplace-action', context: { marketplace: value } });\n }\n}\n\nexport function handleMarketplaceActionSelect(\n value: string,\n marketplace: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'push' | 'pop' | 'popN' | 'notify' | 'setConfirm'>,\n): void {\n if (value === 'browse') {\n nav.push({ screen: 'marketplace-browse', context: { marketplace } });\n } else if (value === 'update') {\n callbacks\n .marketplaceUpdate(marketplace)\n .then(() => {\n nav.notify(`Updated marketplace \"${marketplace}\".`);\n nav.pop();\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n } else if (value === 'remove') {\n nav.setConfirm({\n message: `Remove marketplace \"${marketplace}\" and all its plugins?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .marketplaceRemove(marketplace)\n .then(() => {\n nav.notify(`Removed marketplace \"${marketplace}\".`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n }\n}\n\nexport function handleMarketplaceBrowseSelect(\n value: string,\n marketplace: string,\n items: IMenuSelectItem[],\n nav: Pick<INavActions, 'push'>,\n): void {\n const fullId = `${value}@${marketplace}`;\n const item = items.find((i) => i.value === value);\n if (item?.hint === 'installed') {\n nav.push({ screen: 'installed-action', context: { pluginId: fullId } });\n } else {\n nav.push({ screen: 'marketplace-install-scope', context: { marketplace, pluginId: fullId } });\n }\n}\n\nexport function handleInstallScopeSelect(\n value: string,\n pluginId: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'popN' | 'notify'>,\n): void {\n const scope = value as 'user' | 'project';\n callbacks\n .install(pluginId, scope)\n .then(() => {\n nav.notify(`Installed plugin \"${pluginId}\" (${scope} scope).`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n}\n\nexport function handleInstalledListSelect(\n value: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'notify' | 'setConfirm' | 'refresh'>,\n): void {\n nav.setConfirm({\n message: `Uninstall plugin \"${value}\"?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .uninstall(value)\n .then(() => {\n nav.notify(`Uninstalled plugin \"${value}\".`);\n nav.refresh();\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n}\n\nexport function handleInstalledActionSelect(\n value: string,\n pluginId: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'popN' | 'notify' | 'setConfirm'>,\n): void {\n if (value === 'uninstall') {\n nav.setConfirm({\n message: `Uninstall plugin \"${pluginId}\"?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .uninstall(pluginId)\n .then(() => {\n nav.notify(`Uninstalled plugin \"${pluginId}\".`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n }\n}\n","/**\n * PluginTUI — Main orchestrator component for plugin management.\n * Manages a stack of screens: main, marketplace, installed plugins, etc.\n */\n\nimport React, { useState, useCallback } from 'react';\n\nimport ConfirmPrompt from './ConfirmPrompt.js';\nimport { usePluginScreenData } from './hooks/usePluginScreenData.js';\nimport MenuSelect from './MenuSelect.js';\nimport {\n handleMainSelect,\n handleMarketplaceListSelect,\n handleMarketplaceActionSelect,\n handleMarketplaceBrowseSelect,\n handleInstallScopeSelect,\n handleInstalledListSelect,\n handleInstalledActionSelect,\n} from './plugin-tui-handlers.js';\nimport TextPrompt from './TextPrompt.js';\n\nimport type { IMenuSelectItem } from './MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\ntype TScreenId =\n | 'main'\n | 'marketplace-list'\n | 'marketplace-action'\n | 'marketplace-browse'\n | 'marketplace-install-scope'\n | 'marketplace-add'\n | 'installed-list'\n | 'installed-action';\n\ninterface IMenuContext {\n marketplace?: string;\n pluginId?: string;\n}\n\ninterface IMenuState {\n screen: TScreenId;\n context?: IMenuContext;\n}\n\ninterface IConfirmState {\n message: string;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\ninterface IProps {\n callbacks: ICommandPluginAdapter;\n onClose: () => void;\n addMessage?: (msg: { role: string; content: string }) => void;\n}\n\nexport default function PluginTUI({ callbacks, onClose, addMessage }: IProps): React.ReactElement {\n const [stack, setStack] = useState<IMenuState[]>([{ screen: 'main' }]);\n const [confirm, setConfirm] = useState<IConfirmState | undefined>();\n const [refreshCounter, setRefreshCounter] = useState(0);\n\n const current = stack[stack.length - 1] ?? { screen: 'main' };\n\n const push = useCallback((state: IMenuState) => {\n setStack((prev) => [...prev, state]);\n }, []);\n\n const pop = useCallback(() => {\n setStack((prev) => {\n if (prev.length <= 1) {\n onClose();\n return prev;\n }\n return prev.slice(0, -1);\n });\n }, [onClose]);\n\n const popN = useCallback(\n (n: number) => {\n setStack((prev) => {\n const next = prev.slice(0, Math.max(1, prev.length - n));\n if (next.length === 0) {\n onClose();\n return prev;\n }\n return next;\n });\n },\n [onClose],\n );\n\n const notify = useCallback(\n (content: string) => {\n addMessage?.({ role: 'system', content });\n },\n [addMessage],\n );\n\n const refresh = useCallback(() => {\n setRefreshCounter((c) => c + 1);\n }, []);\n\n const setConfirmNav = useCallback(\n (state: IConfirmState | undefined) => setConfirm(state),\n [setConfirm],\n );\n // nav.push accepts a loose { screen: string } shape to satisfy plugin-tui-handlers types;\n // we cast screen to TScreenId which is safe because handlers only push valid screen names.\n const pushNav = useCallback(\n (state: { screen: string; context?: IMenuContext }) =>\n push({ screen: state.screen as TScreenId, context: state.context }),\n [push],\n );\n const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };\n\n const { items, loading, error } = usePluginScreenData(\n current.screen,\n current.context?.marketplace,\n callbacks,\n refreshCounter,\n stack.length,\n );\n\n const handleSelect = useCallback(\n (value: string) => {\n const screen = current.screen;\n const ctx = current.context;\n\n if (screen === 'main') handleMainSelect(value, nav);\n else if (screen === 'marketplace-list') handleMarketplaceListSelect(value, nav);\n else if (screen === 'marketplace-action')\n handleMarketplaceActionSelect(value, ctx?.marketplace ?? '', callbacks, nav);\n else if (screen === 'marketplace-browse')\n handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? '', items, nav);\n else if (screen === 'marketplace-install-scope')\n handleInstallScopeSelect(value, ctx?.pluginId ?? '', callbacks, nav);\n else if (screen === 'installed-list') handleInstalledListSelect(value, callbacks, nav);\n else if (screen === 'installed-action')\n handleInstalledActionSelect(value, ctx?.pluginId ?? '', callbacks, nav);\n },\n [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh],\n );\n\n const handleTextSubmit = useCallback(\n (value: string) => {\n if (current.screen === 'marketplace-add') {\n callbacks\n .marketplaceAdd(value)\n .then((name) => {\n notify(`Added marketplace \"${name}\" from ${value}.`);\n pop();\n })\n .catch((err) => {\n notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n pop();\n });\n }\n },\n [current.screen, callbacks, notify, pop],\n );\n\n // Confirm overlay intercepts everything\n if (confirm) {\n return (\n <ConfirmPrompt\n message={confirm.message}\n onSelect={(index) => {\n if (index === 0) confirm.onConfirm();\n else confirm.onCancel();\n }}\n />\n );\n }\n\n const screen = current.screen;\n\n if (screen === 'marketplace-add') {\n return (\n <TextPrompt\n title=\"Add Marketplace Source\"\n placeholder=\"owner/repo or git URL\"\n onSubmit={handleTextSubmit}\n onCancel={pop}\n validate={(v) => (!v.includes('/') ? 'Must be owner/repo or a git URL' : undefined)}\n />\n );\n }\n\n if (screen === 'marketplace-action') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Marketplace: ${current.context?.marketplace ?? ''}`}\n items={[\n { label: 'Browse plugins', value: 'browse' },\n { label: 'Update', value: 'update' },\n { label: 'Remove', value: 'remove' },\n ]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n if (screen === 'marketplace-install-scope') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Install scope for \"${current.context?.pluginId ?? ''}\"`}\n items={[\n { label: 'User scope', value: 'user' },\n { label: 'Project scope', value: 'project' },\n ]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n if (screen === 'installed-action') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Plugin: ${current.context?.pluginId ?? ''}`}\n items={[{ label: 'Uninstall', value: 'uninstall' }]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n // Screens with async items: main, marketplace-list, marketplace-browse, installed-list\n const titleMap: Record<string, string> = {\n main: 'Plugin Management',\n 'marketplace-list': 'Marketplace',\n 'marketplace-browse': `Browse: ${current.context?.marketplace ?? ''}`,\n 'installed-list': 'Installed Plugins',\n };\n\n const staticItemsMap: Record<string, IMenuSelectItem[]> = {\n main: [\n { label: 'Marketplace', value: 'marketplace' },\n { label: 'Installed Plugins', value: 'installed' },\n ],\n };\n\n return (\n <MenuSelect\n key={`${screen}-${stack.length}-${refreshCounter}`}\n title={titleMap[screen] ?? 'Plugin Management'}\n items={staticItemsMap[screen] ?? items}\n onSelect={handleSelect}\n onBack={pop}\n loading={loading}\n error={error}\n />\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ITuiSessionEventNotice } from './tui-session-events.js';\n\nexport interface ISessionEventNoticesProps {\n notices: readonly ITuiSessionEventNotice[];\n}\n\nexport default function SessionEventNotices({\n notices,\n}: ISessionEventNoticesProps): React.ReactElement | null {\n const palette = usePalette();\n if (notices.length === 0) return null;\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n {notices.map((notice) => (\n <Text key={notice.id} color={palette.text.muted}>\n {notice.message}\n </Text>\n ))}\n </Box>\n );\n}\n","/**\n * Session picker component for /resume command.\n * Shows a list of sessions for the current cwd.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport ListPicker from './ListPicker.js';\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IResumableSessionSummary } from '@robota-sdk/agent-interface-session';\n\nconst SESSION_ID_DISPLAY_LENGTH = 8;\nconst SESSION_PREVIEW_DISPLAY_LENGTH = 60;\n\ninterface IProps {\n sessions: readonly IResumableSessionSummary[];\n onSelect: (sessionId: string) => void;\n onCancel: () => void;\n}\n\nexport default function SessionPicker({\n sessions,\n onSelect,\n onCancel,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box flexDirection=\"column\" paddingX={1} marginBottom={1}>\n <Text bold color={palette.text.accent}>\n Select a session to resume:\n </Text>\n <ListPicker<IResumableSessionSummary>\n items={[...sessions]}\n renderItem={(session: IResumableSessionSummary, isSelected: boolean) => {\n const preview = session.preview\n ? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) +\n (session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? '...' : '')\n : '';\n return (\n <Text>\n {isSelected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n <Text bold>{session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH)}</Text>\n {' '}\n <Text dimColor>\n {new Date(session.updatedAt).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </Text>\n {' '}\n <Text dimColor>msgs: {session.messageCount}</Text>\n {preview ? (\n <>\n {'\\n '}\n <Text dimColor>{preview}</Text>\n </>\n ) : null}\n </Text>\n );\n }}\n onSelect={(session: IResumableSessionSummary) => onSelect(session.id)}\n onCancel={onCancel}\n />\n </Box>\n );\n}\n","/**\n * SCREEN-2002: this module is pure, so it names a colour TOKEN, not a colour. `StatusBar` resolves\n * the token against the live theme — a pure function that returned a chalk name could not follow one.\n */\nexport type TStatusActivityKind = 'tools' | 'thinking' | 'background' | 'queued' | 'idle';\n\nexport interface IStatusActivityInput {\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n}\n\n/** The `IThemeColors['text']` key a status activity is rendered in. */\nexport type TStatusActivityTone = 'accent' | 'warning' | 'muted';\n\nexport interface IStatusActivity {\n kind: TStatusActivityKind;\n label: string;\n tone: TStatusActivityTone;\n segments: string[];\n text: string;\n}\n\nconst NO_ACTIVE_ITEMS = 0;\n\nexport function formatStatusActivity(input: IStatusActivityInput): IStatusActivity {\n const base = getPrimaryActivity(input);\n const segments = input.hasPendingPrompt && base.kind !== 'queued' ? ['queued'] : [];\n const text = [base.label, ...segments].join(' · ');\n return { ...base, segments, text };\n}\n\nfunction getPrimaryActivity(\n input: IStatusActivityInput,\n): Omit<IStatusActivity, 'segments' | 'text'> {\n if (input.activeToolCount > NO_ACTIVE_ITEMS) {\n return {\n kind: 'tools',\n label: `Tools (${input.activeToolCount})`,\n tone: 'accent',\n };\n }\n if (input.isThinking) {\n return {\n kind: 'thinking',\n label: 'Thinking',\n tone: 'warning',\n };\n }\n if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {\n return {\n kind: 'background',\n label: `Background (${input.activeBackgroundTaskCount})`,\n tone: 'accent',\n };\n }\n if (input.hasPendingPrompt) {\n return {\n kind: 'queued',\n label: 'Queued',\n tone: 'warning',\n };\n }\n return {\n kind: 'idle',\n label: 'Idle',\n tone: 'muted',\n };\n}\n","import { formatTokenCount } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { formatStatusActivity } from './status-activity.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type { TModelEffortSelection, TPermissionMode } from '@robota-sdk/agent-core';\n\n/** Threshold boundaries for context percentage color coding */\nconst CONTEXT_YELLOW_THRESHOLD = 70;\nconst CONTEXT_RED_THRESHOLD = 90;\n\n/** Segment separator between status-bar fields (SCREEN-006: single SSOT, was repeated inline 6x). */\nconst SEP = ' | ';\n\ninterface IProps {\n permissionMode: TPermissionMode;\n modelName: string;\n providerDisplayName?: string | undefined;\n sessionId: string;\n isThinking: boolean;\n activeToolCount?: number;\n activeBackgroundTaskCount?: number;\n hasPendingPrompt?: boolean;\n contextPercentage: number;\n contextUsedTokens: number;\n contextMaxTokens: number;\n sessionName?: string;\n gitBranch?: string;\n showGitBranch?: boolean;\n activeAgentLabel?: string;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n}\n\ninterface IStatusLeftProps {\n permissionMode: TPermissionMode;\n modelName: string;\n providerDisplayName?: string | undefined;\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n contextPercentage: number;\n contextUsedTokens: number;\n contextMaxTokens: number;\n sessionName?: string;\n gitBranch?: string;\n showGitBranch: boolean;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n}\n\n/** Return the color for the context percentage indicator */\nfunction getContextColor(percentage: number, palette: IThemeColors): string {\n if (percentage >= CONTEXT_RED_THRESHOLD) return palette.text.error;\n if (percentage >= CONTEXT_YELLOW_THRESHOLD) return palette.text.warning;\n return palette.text.success;\n}\n\nfunction StatusActivityText({\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n}: Pick<\n IStatusLeftProps,\n 'isThinking' | 'activeToolCount' | 'activeBackgroundTaskCount' | 'hasPendingPrompt'\n>): React.ReactElement {\n const palette = usePalette();\n const activity = formatStatusActivity({\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n });\n\n return (\n <Text color={palette.text[activity.tone]} bold={activity.kind !== 'idle'}>\n {activity.text}\n </Text>\n );\n}\n\nfunction ContextText({\n percentage,\n usedTokens,\n maxTokens,\n}: {\n percentage: number;\n usedTokens: number;\n maxTokens: number;\n}): React.ReactElement {\n const palette = usePalette();\n return (\n <Text color={getContextColor(percentage, palette)}>\n Context: {Math.round(percentage)}% ({formatTokenCount(usedTokens)}/\n {formatTokenCount(maxTokens)} tokens)\n </Text>\n );\n}\n\nfunction ModeText({ permissionMode }: { permissionMode: TPermissionMode }): React.ReactElement {\n const palette = usePalette();\n return (\n <>\n <Text color={palette.text.accent} bold>\n Mode:\n </Text>{' '}\n <Text>{permissionMode}</Text>\n </>\n );\n}\n\n/**\n * CLI-2004 verdict (l): outside the mode the `default` mode is hidden as noise. In the mode it is\n * ALWAYS rendered — with no key-based cycling to announce, the status line is the only place a\n * reader can find which permission mode it is in, and a hidden `default` leaves nothing to find.\n */\nfunction shouldShowPermissionMode(permissionMode: TPermissionMode, screenReader: boolean): boolean {\n return screenReader || permissionMode !== 'default';\n}\n\nfunction PresetText({ activePresetId }: { activePresetId: string }): React.ReactElement {\n const palette = usePalette();\n return (\n <>\n <Text color={palette.text.accent} bold>\n Preset:\n </Text>{' '}\n <Text>{activePresetId}</Text>\n </>\n );\n}\n\nfunction shouldShowActivePreset(activePresetId: string | undefined): activePresetId is string {\n return activePresetId !== undefined && activePresetId !== 'default';\n}\n\nfunction ProviderText({\n modelName,\n providerDisplayName,\n effort,\n}: {\n modelName: string;\n providerDisplayName?: string | undefined;\n effort?: TModelEffortSelection;\n}): React.ReactElement {\n const provider =\n providerDisplayName !== undefined ? `${providerDisplayName} ${modelName}` : modelName;\n if (providerDisplayName !== undefined) {\n return (\n <Text dimColor>\n {provider}\n {effort !== undefined ? ` Effort: ${effort}` : ''}\n </Text>\n );\n }\n return (\n <Text dimColor>\n {provider}\n {effort !== undefined ? ` Effort: ${effort}` : ''}\n </Text>\n );\n}\n\nfunction StatusLeft(props: IStatusLeftProps): React.ReactElement {\n const palette = usePalette();\n const shouldShowGitBranch =\n props.showGitBranch && props.gitBranch !== undefined && props.gitBranch.length > 0;\n // CLI-2004 § Solution 13: one rule applied to fields of different VOLATILITY. The activity text\n // and the context percentage re-render on their own cadence and on every token, so a reader\n // reviewing the status line would re-announce them continuously while the operator types — they\n // are suppressed. The permission mode, the preset and the model change only when the operator\n // changes them: those are anchors, and they stay.\n const screenReader = useScreenReader();\n const showPermissionMode = shouldShowPermissionMode(props.permissionMode, screenReader);\n const activePresetId = props.activePresetId;\n const showActivePreset = shouldShowActivePreset(activePresetId);\n return (\n <Text>\n {!screenReader && (\n <StatusActivityText\n isThinking={props.isThinking}\n activeToolCount={props.activeToolCount}\n activeBackgroundTaskCount={props.activeBackgroundTaskCount}\n hasPendingPrompt={props.hasPendingPrompt}\n />\n )}\n {showPermissionMode && (\n <>\n {SEP}\n <ModeText permissionMode={props.permissionMode} />\n </>\n )}\n {showActivePreset && (\n <>\n {SEP}\n <PresetText activePresetId={activePresetId} />\n </>\n )}\n {props.sessionName && (\n <>\n {SEP}\n <Text color={palette.text.session}>{props.sessionName}</Text>\n </>\n )}\n {shouldShowGitBranch && (\n <>\n {SEP}\n <Text dimColor>git: {props.gitBranch}</Text>\n </>\n )}\n {SEP}\n <ProviderText\n modelName={props.modelName}\n providerDisplayName={props.providerDisplayName}\n effort={props.effort}\n />\n {!screenReader && (\n <>\n {SEP}\n <ContextText\n percentage={props.contextPercentage}\n usedTokens={props.contextUsedTokens}\n maxTokens={props.contextMaxTokens}\n />\n </>\n )}\n </Text>\n );\n}\n\nexport default function StatusBar({\n permissionMode,\n modelName,\n providerDisplayName,\n sessionId: _sessionId,\n isThinking,\n activeToolCount = 0,\n activeBackgroundTaskCount = 0,\n hasPendingPrompt = false,\n contextPercentage,\n contextUsedTokens,\n contextMaxTokens,\n sessionName,\n gitBranch,\n showGitBranch = true,\n activeAgentLabel,\n activePresetId,\n effort,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box paddingLeft={1} paddingRight={1} justifyContent=\"space-between\">\n <StatusLeft\n permissionMode={permissionMode}\n modelName={modelName}\n providerDisplayName={providerDisplayName}\n isThinking={isThinking}\n activeToolCount={activeToolCount}\n activeBackgroundTaskCount={activeBackgroundTaskCount}\n hasPendingPrompt={hasPendingPrompt}\n contextPercentage={contextPercentage}\n contextUsedTokens={contextUsedTokens}\n contextMaxTokens={contextMaxTokens}\n sessionName={sessionName}\n gitBranch={gitBranch}\n showGitBranch={showGitBranch}\n activePresetId={activePresetId}\n effort={effort}\n />\n {activeAgentLabel !== undefined && (\n <Text color={palette.text.warning} bold>\n [{activeAgentLabel}]\n </Text>\n )}\n </Box>\n );\n}\n","import { createContext, useContext } from 'react';\n\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\n\nconst TuiCliAdapterContext = createContext<ITuiCliAdapter | null>(null);\n\nexport const TuiCliAdapterProvider = TuiCliAdapterContext.Provider;\n\nexport function useTuiCliAdapter(): ITuiCliAdapter {\n const adapter = useContext(TuiCliAdapterContext);\n if (!adapter) throw new Error('TuiCliAdapterContext not provided');\n return adapter;\n}\n","import React, { useMemo } from 'react';\n\nimport StatusBar from './StatusBar.js';\nimport { useTuiCliAdapter } from './tui-cli-adapter-context.js';\n\nimport type { TModelEffortSelection, TPermissionMode } from '@robota-sdk/agent-core';\nimport type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-command';\n\ninterface IProps {\n cwd: string;\n permissionMode: TPermissionMode;\n modelId?: string;\n providerType?: string | undefined;\n sessionId: string;\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n contextState: { percentage: number; usedTokens: number; maxTokens: number };\n sessionName?: string;\n settings: IStatusLineCommandSettings;\n activeAgentLabel?: string;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n gitRefreshToken?: number;\n}\n\nexport default function SessionStatusBar({\n cwd,\n permissionMode,\n modelId,\n providerType,\n sessionId,\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n contextState,\n sessionName,\n settings,\n activeAgentLabel,\n activePresetId,\n effort,\n gitRefreshToken,\n}: IProps): React.ReactElement | null {\n const cliAdapter = useTuiCliAdapter();\n const gitBranch = useMemo(() => cliAdapter.getGitBranch(cwd), [cliAdapter, cwd, gitRefreshToken]);\n const providerDisplayName = useMemo(\n () =>\n providerType !== undefined ? cliAdapter.getProviderDisplayName(providerType) : undefined,\n [cliAdapter, providerType],\n );\n if (!settings.enabled) return null;\n\n return (\n <StatusBar\n permissionMode={permissionMode}\n modelName={modelId ?? ''}\n providerDisplayName={providerDisplayName}\n sessionId={sessionId}\n isThinking={isThinking}\n activeToolCount={activeToolCount}\n activeBackgroundTaskCount={activeBackgroundTaskCount}\n hasPendingPrompt={hasPendingPrompt}\n contextPercentage={contextState.percentage}\n contextUsedTokens={contextState.usedTokens}\n contextMaxTokens={contextState.maxTokens}\n sessionName={sessionName}\n gitBranch={gitBranch}\n showGitBranch={settings.gitBranch}\n activeAgentLabel={activeAgentLabel}\n activePresetId={activePresetId}\n effort={effort}\n />\n );\n}\n","/**\n * Streaming indicator — shows real-time tool execution and AI response text.\n * Displayed during session.run() execution.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { humanizeToolArgument, humanizeToolName } from './humanize-tool-name.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport {\n useModelCommandToolPrefix,\n useProductDisplayName,\n} from './product-display-name-context.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS } from './screen-reader-labels.js';\nimport { STATUS_SYMBOL, statusGlyphColor, toolStateStatusKind } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\nimport ToolDiffBlock from './ToolDiffBlock.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nfunction getToolStyle(\n t: IToolState,\n palette: IThemeColors,\n): {\n color: string;\n icon: string;\n strikethrough: boolean;\n} {\n const kind = toolStateStatusKind(t);\n return {\n color: statusGlyphColor(palette, kind),\n icon: STATUS_SYMBOL[kind],\n strikethrough: kind === 'error' || kind === 'denied',\n };\n}\n\ninterface IProps {\n text: string;\n activeTools: readonly IToolState[];\n isThinking?: boolean;\n}\n\nfunction ThinkingFallback({ isThinking }: { isThinking: boolean }): React.ReactElement {\n const palette = usePalette();\n if (!isThinking) return <></>;\n return (\n <Box marginBottom={1}>\n <Text color={palette.text.warning}>Thinking...</Text>\n </Box>\n );\n}\n\nfunction ActiveTools({ activeTools }: { activeTools: readonly IToolState[] }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.emphasis} bold>\n Tools:\n </Text>\n <Text> </Text>\n {activeTools.map((t, i) => {\n const { color, icon, strikethrough } = getToolStyle(t, palette);\n return (\n <Box key={`${t.toolName}-${i}`} flexDirection=\"column\">\n <Text color={color} strikethrough={strikethrough}>\n {' '}\n {icon} {humanizeToolName(t.toolName, modelCommandToolPrefix)}({humanizeToolArgument(t.firstArg)})\n </Text>\n {t.diffLines && t.diffLines.length > 0 && (\n <ToolDiffBlock file={t.diffFile} lines={t.diffLines} />\n )}\n </Box>\n );\n })}\n </Box>\n );\n}\n\n/**\n * CLI-2004: the whole progress indicator collapses to ONE static line. A spinner is a repaint per\n * frame, and a repaint is an announcement — the reader would hear the same line forever. The line\n * still carries what changed (which tools are running, how far the reply has got), just once per\n * actual change instead of once per frame.\n */\nfunction renderScreenReaderStatus(\n text: string,\n activeTools: readonly IToolState[],\n isThinking: boolean,\n modelCommandToolPrefix?: string,\n): React.ReactElement {\n const running = activeTools\n .map((tool) => humanizeToolName(tool.toolName, modelCommandToolPrefix))\n .join(', ');\n const parts = [\n running.length > 0 ? `${SCREEN_READER_LABELS.tool} ${running}` : undefined,\n text.length > 0 ? text : isThinking ? SCREEN_READER_LABELS.thinking : undefined,\n ].filter((part): part is string => part !== undefined);\n if (parts.length === 0) return <></>;\n return <Text>{parts.join(' — ')}</Text>;\n}\n\nexport default function StreamingIndicator({\n text,\n activeTools,\n isThinking = false,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const renderMarkdown = useRenderMarkdown();\n const hasTools = activeTools.length > 0;\n const hasText = text.length > 0;\n const screenReader = useScreenReader();\n const productDisplayName = useProductDisplayName();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n\n if (screenReader) {\n return renderScreenReaderStatus(text, activeTools, isThinking, modelCommandToolPrefix);\n }\n\n if (!hasTools && !hasText) {\n return <ThinkingFallback isThinking={isThinking} />;\n }\n\n return (\n <Box flexDirection=\"column\">\n {hasTools && <ActiveTools activeTools={activeTools} />}\n {hasText && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {productDisplayName}:\n </Text>\n <Text> </Text>\n <Box marginLeft={2}>\n {/* `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours. */}\n <RenderedText wrap=\"wrap\">{renderMarkdown(text)}</RenderedText>\n </Box>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * SCREEN-2002 — the theme picker.\n *\n * Preview-on-move: the highlighted row IS the live region's theme while the picker is open, because\n * a colour scheme cannot be judged from a swatch beside the thing it does not apply to. Escape\n * restores what was persisted; selecting submits `/theme <id>` so the HOST writes the settings\n * document and the picker never becomes a second writer.\n *\n * The transcript above the picker keeps the theme it was written in — Ink's `<Static>` emits an\n * entry once — so a preview moves the input frame, the status bar and this overlay. That is the\n * region a reader is looking at while choosing, and it is the property that makes a preview honest\n * rather than partial.\n */\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { KeyHintFooter, SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IAppThemePickerViewModel, IThemeToggles } from './hooks/useAppThemeState.js';\nimport type { ITuiTheme } from './theme/index.js';\nimport type { IThemeSkip } from './theme/theme-registry.js';\n\nfunction describeTheme(theme: ITuiTheme, isActive: boolean): string {\n const active = isActive ? ' (current)' : '';\n return `${theme.name} — ${theme.appearance}, ${theme.source}${active}`;\n}\n\nfunction ThemeRow({\n theme,\n isFocused,\n isActive,\n rowNumber,\n}: {\n theme: ITuiTheme;\n isFocused: boolean;\n isActive: boolean;\n rowNumber?: number | undefined;\n}): React.ReactElement {\n const palette = usePalette();\n const marker =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isFocused\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n return (\n <Text\n {...(isFocused && rowNumber === undefined ? { color: palette.text.accent, bold: true } : {})}\n >\n {marker}\n {describeTheme(theme, isActive)}\n </Text>\n );\n}\n\n/** The typed-selection prompt, re-printed verbatim after an out-of-range answer. */\nfunction ScreenReaderThemePrompt({\n itemCount,\n buffer,\n invalid,\n}: {\n itemCount: number;\n buffer: string;\n invalid: boolean;\n}): React.ReactElement {\n const prompt = formatNumberedSelectionPrompt(itemCount, true);\n return (\n <Box flexDirection=\"column\">\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {invalid && <Text>{prompt}</Text>}\n </Box>\n );\n}\n\nfunction focusedIndexOf(themes: readonly ITuiTheme[], activeThemeId: string): number {\n const index = themes.findIndex((theme) => theme.id === activeThemeId);\n return index < 0 ? 0 : index;\n}\n\n/**\n * Navigation PREVIEWS and only `select` applies, so the highlight and the live region move together\n * without anything being written until the user says so.\n */\n/**\n * Pending, not applied: the toggles travel with the selection and are submitted WITH it, so escape\n * abandons them exactly as it abandons the previewed theme. Applying them live would make escape\n * mean two different things for two controls in the same overlay.\n */\nfunction usePendingToggles(picker: IAppThemePickerViewModel): {\n toggles: IThemeToggles;\n setToggles: React.Dispatch<React.SetStateAction<IThemeToggles>>;\n} {\n const [toggles, setToggles] = React.useState<IThemeToggles>(() => ({\n syntaxHighlighting: picker.syntaxHighlighting,\n reducedMotion: picker.reducedMotion,\n }));\n return { toggles, setToggles };\n}\n\n/**\n * The highlight, and the preview that follows it. Moving WRAPS at both ends rather than stopping —\n * four themes is a short list, and a dead end at the bottom reads as a broken key.\n */\nfunction useFocusedRow(picker: IAppThemePickerViewModel): {\n focusedIndex: number;\n move: (delta: number) => void;\n} {\n const { themes, activeThemeId, preview } = picker;\n const [focusedIndex, setFocusedIndex] = React.useState(() =>\n focusedIndexOf(themes, activeThemeId),\n );\n const move = React.useCallback(\n (delta: number): void => {\n if (themes.length === 0) return;\n setFocusedIndex((current) => {\n const next = (current + delta + themes.length) % themes.length;\n const theme = themes[next];\n if (theme) preview(theme.id);\n return next;\n });\n },\n [preview, themes],\n );\n return { focusedIndex, move };\n}\n\nfunction useThemePickerSelection(\n picker: IAppThemePickerViewModel,\n screenReader: boolean,\n): {\n focusedIndex: number;\n toggles: IThemeToggles;\n numbered: ReturnType<typeof useNumberedSelection>;\n} {\n const { themes, select, cancel } = picker;\n const { focusedIndex, move } = useFocusedRow(picker);\n const { toggles, setToggles } = usePendingToggles(picker);\n const togglesRef = React.useRef(toggles);\n togglesRef.current = toggles;\n const applyAt = React.useCallback(\n (index: number): void => {\n const theme = themes[index];\n if (theme) select(theme.id, togglesRef.current);\n },\n [select, themes],\n );\n\n // CLI-2004: an arrow-key menu gets numbers and a typed answer in the mode. It applies on SELECT\n // rather than on navigation — there is no highlight to hear, so a preview per keystroke would\n // just be repaints a reader has to sit through.\n const numbered = useNumberedSelection({\n enabled: screenReader && themes.length > 0,\n itemCount: themes.length,\n cancellable: true,\n onSelect: applyAt,\n onCancel: cancel,\n });\n\n const toggle = React.useCallback(\n (key: keyof IThemeToggles): void => {\n setToggles((current) => ({ ...current, [key]: !current[key] }));\n },\n [setToggles],\n );\n\n useKeybindingActions(\n 'theme-picker',\n (actions) => {\n for (const action of actions) {\n if (action === 'previous') move(-1);\n else if (action === 'next') move(1);\n else if (action === 'select') applyAt(focusedIndex);\n else if (action === 'toggle-syntax') toggle('syntaxHighlighting');\n else if (action === 'toggle-motion') toggle('reducedMotion');\n else if (action === 'cancel') cancel();\n }\n },\n { isActive: !screenReader },\n );\n return { focusedIndex, toggles, numbered };\n}\n\nfunction ThemeRows({\n themes,\n activeThemeId,\n focusedIndex,\n screenReader,\n}: {\n themes: readonly ITuiTheme[];\n activeThemeId: string;\n focusedIndex: number;\n screenReader: boolean;\n}): React.ReactElement {\n if (themes.length === 0) return <Text dimColor>No themes installed</Text>;\n return (\n <>\n {themes.map((theme, index) => (\n <ThemeRow\n key={theme.id}\n theme={theme}\n isFocused={index === focusedIndex}\n isActive={theme.id === activeThemeId}\n {...(screenReader ? { rowNumber: index } : {})}\n />\n ))}\n </>\n );\n}\n\n/**\n * Under the colour gate a preview shows nothing, so the picker says so in words rather than letting\n * the user conclude the highlight is broken. The gate is the degradation rule this package has\n * always had; this is the one place that has to ADMIT it.\n */\nfunction ColourGateNotice(): React.ReactElement {\n if (isInteractiveColorTerminal()) return <></>;\n return (\n <Text dimColor>Colour is off for this terminal — themes apply, previews cannot show.</Text>\n );\n}\n\n/**\n * The theme files this run found and refused. They are shown, never offered: a row a user cannot\n * pick is still the only place that says why the theme they wrote is not in the list — without it\n * the file just silently is not there, and the startup line has long since scrolled away.\n */\nfunction SkippedRows({ skipped }: { skipped: readonly IThemeSkip[] }): React.ReactElement {\n if (skipped.length === 0) return <></>;\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/*\n Keyed by POSITION, not by id. `IThemeSkip.id` is a LABEL and its contract says so: a\n scope-level failure carries one id for a whole scope, and two scopes can both fail, so two\n rows can share it. A duplicate React key would put a warning on stderr from inside the live\n frame, corrupting the frame it is complaining about.\n\n Recorded limit: no test pins this. Rendering two same-id rows under ink-testing-library\n produced both rows and no warning on `console.error`, so a test asserting the warning's\n ABSENCE is green either way — an unfalsifiable guard, which is worse than none. The reason\n is written here instead of measured.\n */}\n {skipped.map((skip, index) => (\n <Text key={index} dimColor>\n {`${SELECTION_INDICATOR_NONE}Skipped \"${skip.fileName}\" — ${skip.reason}`}\n </Text>\n ))}\n </Box>\n );\n}\n\nfunction TogglesRow({\n toggles,\n reducedMotionPin,\n screenReader,\n}: {\n toggles: IThemeToggles;\n reducedMotionPin?: IAppThemePickerViewModel['reducedMotionPin'];\n screenReader: boolean;\n}): React.ReactElement {\n const on = (value: boolean): string => (value ? 'on' : 'off');\n // The pin is admitted here as it is by `/theme list` and `/theme motion` — and it names what THIS\n // RUN does, not only who decided it. The tier alone is not enough: `--no-reduced-motion` is an\n // override too and pins the opposite value, so a row carrying the chosen value beside the tier\n // can read `motion on (pinned by flag)` on a visibly still run.\n const pinned =\n reducedMotionPin === undefined\n ? ''\n : ` (this run: motion ${on(!reducedMotionPin.reducedMotion)}, pinned by ${reducedMotionPin.tier})`;\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>\n {`syntax ${on(toggles.syntaxHighlighting)} · motion ${on(!toggles.reducedMotion)}${pinned}`}\n </Text>\n {/*\n The mode takes a typed answer, so `s` and `m` are not bound in it — but the state above is\n still worth reading before choosing. So the state stays and the AFFORDANCE changes: this\n names a route the mode can actually take, instead of leaving two keys silently inert.\n */}\n {screenReader && (\n <Text dimColor>Change with /theme syntax on|off or /theme motion on|off</Text>\n )}\n </Box>\n );\n}\n\nfunction ThemePickerFooter({\n screenReader,\n itemCount,\n numbered,\n}: {\n screenReader: boolean;\n itemCount: number;\n numbered: ReturnType<typeof useNumberedSelection>;\n}): React.ReactElement {\n // Footer order: navigate → primary → dismiss, the same as every other picker in this package.\n const footerHints = useKeybindingHints('theme-picker', [\n [['previous', 'next'], 'Preview'],\n ['select', 'Apply'],\n ['toggle-syntax', 'Syntax'],\n ['toggle-motion', 'Motion'],\n ['cancel', 'Cancel'],\n ]);\n if (!screenReader) return <KeyHintFooter hints={footerHints} />;\n return (\n <ScreenReaderThemePrompt\n itemCount={itemCount}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n}\n\nexport default function ThemePicker({\n picker,\n}: {\n picker: IAppThemePickerViewModel;\n}): React.ReactElement {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const { themes, activeThemeId } = picker;\n const { focusedIndex, toggles, numbered } = useThemePickerSelection(picker, screenReader);\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.focused })}\n paddingX={1}\n >\n <Text color={palette.text.accent} bold>\n Theme\n </Text>\n <ColourGateNotice />\n <TogglesRow\n toggles={toggles}\n reducedMotionPin={picker.reducedMotionPin}\n screenReader={screenReader}\n />\n <Box flexDirection=\"column\" marginTop={1}>\n <ThemeRows\n themes={themes}\n activeThemeId={activeThemeId}\n focusedIndex={focusedIndex}\n screenReader={screenReader}\n />\n </Box>\n <SkippedRows skipped={picker.skipped} />\n <ThemePickerFooter\n screenReader={screenReader}\n itemCount={themes.length}\n numbered={numbered}\n />\n </Box>\n );\n}\n","/**\n * TransportTUI — interactive overlay for transport enable/disable settings.\n *\n * Arrow keys navigate the list, space toggles enabled/disabled, enter/esc closes.\n *\n * TRANS-009: a toggle SAVES a setting. It does not start or stop anything — `registry.setEnabled`\n * writes the settings file and returns, and `startAll` reads `getEnabled()` when a session starts, so\n * the change takes effect at the next start. This component used to render `[enabled]` on the way\n * back from that write, which told the user a transport was running when nothing had been started.\n *\n * The badge therefore names the SAVED setting and the footer says when it applies. And a failed write\n * is rendered rather than swallowed: the previous `.catch(() => setSaving(false))` took the error and\n * discarded it, so a settings file that could not be written looked exactly like a successful save\n * with the row unchanged.\n */\n\nimport { Box } from 'ink';\nimport React, { useState, useCallback } from 'react';\n\nimport { KeyHintFooter } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type {\n ITransportEntry,\n ITransportSettingsRegistryView,\n} from '@robota-sdk/agent-interface-transport';\n\nconst TRANSPORT_NAME_WIDTH = 18;\n\ninterface IEntryRowProps {\n entry: ITransportEntry;\n selected: boolean;\n}\n\nfunction TransportEntryRow({ entry, selected }: IEntryRowProps): React.ReactElement {\n const palette = usePalette();\n const enabled = entry.config.enabled;\n const dot = enabled ? '●' : '○';\n // The saved setting, not a running state — see the file header.\n const badge = enabled ? '[on] ' : '[off]';\n const portOpt = entry.config.options?.port;\n const portHint = typeof portOpt === 'number' ? `port: ${portOpt}` : '';\n return (\n <Box>\n <Text color={selected ? palette.text.accent : undefined} bold={selected}>\n {`${dot} ${entry.transport.name.padEnd(TRANSPORT_NAME_WIDTH)} ${badge} ${portHint}`}\n </Text>\n </Box>\n );\n}\n\nfunction useTransportInput(\n entries: ITransportEntry[],\n cursor: number,\n saving: boolean,\n registry: ITransportSettingsRegistryView,\n setCursor: (fn: (c: number) => number) => void,\n setSaving: (v: boolean) => void,\n setError: (v: string | undefined) => void,\n onClose: () => void,\n refresh: () => void,\n): void {\n useKeybindingActions(\n 'transport-settings',\n useCallback(\n (actions: readonly ('previous' | 'next' | 'toggle' | 'close')[]) => {\n if (saving) return;\n if (actions.includes('previous')) {\n setCursor((c) => Math.max(0, c - 1));\n return;\n }\n if (actions.includes('next')) {\n setCursor((c) => Math.min(entries.length - 1, c + 1));\n return;\n }\n if (actions.includes('close')) {\n onClose();\n return;\n }\n if (actions.includes('toggle')) {\n const entry = entries[cursor];\n if (!entry) return;\n setSaving(true);\n setError(undefined);\n registry\n .setEnabled(entry.transport.name, !entry.config.enabled)\n .then(() => {\n refresh();\n setSaving(false);\n })\n .catch((cause: unknown) => {\n // The reason, not just the fact. A settings file that cannot be written and a\n // transport that refuses configuration are different problems for the user.\n setError(cause instanceof Error ? cause.message : 'could not save the setting');\n setSaving(false);\n });\n }\n },\n [saving, entries, cursor, registry, onClose, refresh, setCursor, setSaving, setError],\n ),\n );\n}\n\ninterface IProps {\n registry: ITransportSettingsRegistryView;\n onClose: () => void;\n}\n\nexport default function TransportTUI({ registry, onClose }: IProps): React.ReactElement {\n const palette = usePalette();\n const productDisplayName = useProductDisplayName();\n const [entries, setEntries] = useState(() => registry.getAll());\n const [cursor, setCursor] = useState(0);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const refresh = useCallback((): void => {\n setEntries(registry.getAll());\n }, [registry]);\n const footerHints = useKeybindingHints('transport-settings', [\n [['previous', 'next'], 'Select'],\n ['toggle', 'Toggle'],\n ['close', 'Close'],\n ]);\n\n useTransportInput(\n entries,\n cursor,\n saving,\n registry,\n setCursor,\n setSaving,\n setError,\n onClose,\n refresh,\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n <Text bold>Settings › Transports</Text>\n <Box marginTop={1} flexDirection=\"column\">\n {entries.map((entry, i) => (\n <TransportEntryRow key={entry.transport.name} entry={entry} selected={i === cursor} />\n ))}\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <KeyHintFooter hints={footerHints} />\n <Text dimColor>\n A toggle is saved now and applies the next time {productDisplayName} starts.\n </Text>\n </Box>\n {saving && (\n <Box marginTop={1}>\n <Text color={palette.text.warning}>Saving…</Text>\n </Box>\n )}\n {error !== undefined && (\n <Box marginTop={1}>\n <Text color={palette.text.error}>{`Not saved — ${error}`}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\ninterface IProps {\n message: string;\n}\n\nexport default function UpdateNotice({ message }: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box paddingX={1} marginBottom={1}>\n <Text color={palette.text.warning}>{message}</Text>\n </Box>\n );\n}\n","import { Box, Static } from 'ink';\nimport React from 'react';\n\nimport { AppBanner } from './app-banner.js';\nimport BackgroundTaskPanel from './BackgroundTaskPanel.js';\nimport { ContextWarningBanner } from './ContextWarningBanner.js';\nimport ExecutionWorkspaceDetailPane from './ExecutionWorkspaceDetailPane.js';\nimport ExecutionWorkspaceSwitcher from './ExecutionWorkspaceSwitcher.js';\nimport InputArea from './InputArea.js';\nimport { useKeybindings } from './keybindings/keybindings-context.js';\nimport { EntryItem } from './MessageList.js';\nimport PendingActionPrompt from './PendingActionPrompt.js';\nimport PermissionPrompt from './PermissionPrompt.js';\nimport PluginTUI from './PluginTUI.js';\nimport { Text } from './SafeText.js';\nimport SessionEventNotices from './SessionEventNotices.js';\nimport SessionPicker from './SessionPicker.js';\nimport SessionStatusBar from './SessionStatusBar.js';\nimport StreamingIndicator from './StreamingIndicator.js';\nimport { usePalette } from './theme/index.js';\nimport ThemePicker from './ThemePicker.js';\nimport TransportTUI from './TransportTUI.js';\nimport UpdateNotice from './UpdateNotice.js';\n\nimport type { IAppViewModel } from './app-view-model.js';\n\nfunction Transcript({ model }: { model: IAppViewModel }): React.ReactElement {\n return (\n <Static items={model.staticItems}>\n {(item) =>\n item.kind === 'banner' ? (\n <AppBanner key=\"logo\" version={item.version} />\n ) : (\n <EntryItem key={item.entry.id} entry={item.entry} />\n )\n }\n </Static>\n );\n}\n\nfunction Activity({ model }: { model: IAppViewModel }): React.ReactElement {\n const palette = usePalette();\n const { background, stream } = model;\n return (\n <Box flexDirection=\"column\" paddingX={1} flexGrow={1}>\n {background.selectedEntry && background.selectedEntry.kind !== 'main_thread' && (\n <ExecutionWorkspaceDetailPane\n entry={background.selectedEntry}\n page={background.detail.page}\n loading={background.detail.loading}\n error={background.detail.error}\n />\n )}\n {model.isShuttingDown && <Text color={palette.text.warning}>Shutting down...</Text>}\n {(stream.isThinking || stream.activeTools.length > 0) && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <StreamingIndicator\n text={stream.text}\n activeTools={stream.activeTools}\n isThinking={stream.isThinking}\n />\n {stream.isStalled && (\n <Text color={palette.text.warning}>\n ⚠ Still waiting on the provider — the network may be stalled. Esc to interrupt.\n </Text>\n )}\n </Box>\n )}\n {!stream.isThinking && stream.lastErrorMessage && (\n <Text color={palette.text.error}>\n ✖ Last turn failed — the session is alive; type your next prompt when ready.\n </Text>\n )}\n <BackgroundTaskPanel entries={background.entries} focusedIndex={background.focusedIndex} />\n </Box>\n );\n}\n\nfunction Overlays({ model }: { model: IAppViewModel }): React.ReactElement {\n if (model.coordinationError !== undefined || model.coordinationPending) return <></>;\n const { background, plugin, sessionPicker, transport } = model;\n const pendingUserAction = model.pendingUserAction;\n return (\n <>\n {background.switcherVisible && (\n <ExecutionWorkspaceSwitcher\n snapshot={background.snapshot}\n selectedEntryId={background.selectedEntryId}\n onSelect={background.select}\n onClose={background.closeSwitcher}\n onAttach={background.attachToFork}\n />\n )}\n {model.permissionRequest && <PermissionPrompt request={model.permissionRequest} />}\n {pendingUserAction && (\n <PendingActionPrompt\n request={pendingUserAction}\n onAnswer={(response) => model.resolveUserAction(pendingUserAction, response)}\n />\n )}\n {plugin.visible && (\n <PluginTUI\n callbacks={plugin.callbacks}\n onClose={plugin.close}\n addMessage={(message) => plugin.addMessage(message.content)}\n />\n )}\n {transport.visible && transport.registry && (\n <TransportTUI registry={transport.registry} onClose={transport.close} />\n )}\n {sessionPicker.visible && (\n <SessionPicker\n sessions={sessionPicker.sessions}\n onSelect={sessionPicker.select}\n onCancel={sessionPicker.cancel}\n />\n )}\n {model.theme.picker.visible && <ThemePicker picker={model.theme.picker} />}\n </>\n );\n}\n\nfunction PromptAndStatus({ model }: { model: IAppViewModel }): React.ReactElement {\n const { input } = model;\n return (\n <>\n <ContextWarningBanner percentage={model.contextPercentage} />\n <InputArea\n onSubmit={input.submit}\n onCancelQueue={input.cancelQueue}\n isDisabled={input.disabled}\n isQueueCancellationDisabled={input.queueCancellationDisabled}\n isAborting={input.isAborting}\n pendingPrompt={input.pendingPrompt}\n pendingCount={input.pendingCount}\n commandQueryPort={input.commandQueryPort}\n sessionName={input.sessionName}\n history={input.history}\n onRequestFocusBackgroundList={input.focusBackgroundList}\n historySearch={input.historySearch}\n initialValue={input.initialValue}\n consumeInitialValue={input.consumeInitialValue}\n externalPromptOrigin={input.externalPromptOrigin}\n />\n <SessionStatusBar {...model.status} />\n </>\n );\n}\n\n/** Pure presentation tree: it receives no channel, session, registry, or mutable state manager. */\nexport default function AppPresentation({\n viewModel,\n}: {\n viewModel: IAppViewModel;\n}): React.ReactElement {\n const palette = usePalette();\n const keybindings = useKeybindings();\n return (\n <Box flexDirection=\"column\">\n <Transcript model={viewModel} />\n {!viewModel.handoffSuspended && (\n <>\n {viewModel.updateNotice && <UpdateNotice message={viewModel.updateNotice} />}\n {viewModel.coordinationError && (\n <Text color={palette.text.error}>\n {viewModel.coordinationError} Press Enter to retry.\n </Text>\n )}\n {keybindings.diagnostic && (\n <Text color={palette.text.error}>\n {`Keybindings ${keybindings.diagnostic.file} ${keybindings.diagnostic.path}: ${keybindings.diagnostic.message} Last valid bindings remain active.`}\n </Text>\n )}\n {keybindings.warnings.map((warning) => (\n <Text key={`${warning.path}:${warning.code}`} color={palette.text.warning}>\n {`Keybindings ${warning.path}: ${warning.message}`}\n </Text>\n ))}\n {viewModel.theme.unknownThemeNotice !== undefined && (\n <Text color={palette.text.warning}>{viewModel.theme.unknownThemeNotice}</Text>\n )}\n <SessionEventNotices notices={viewModel.sessionEventNotices} />\n <Activity model={viewModel} />\n <Overlays model={viewModel} />\n <PromptAndStatus model={viewModel} />\n </>\n )}\n </Box>\n );\n}\n","/**\n * SCREEN-010 / CLI-2004 — what gets committed to the terminal's native scrollback.\n *\n * The App renders exactly this list inside a single Ink `<Static>`, so each item is written once and\n * never repainted. Building the list is a pure function so the ONE decision that varies — whether\n * the startup banner is committed at all — is assertable without mounting the App.\n */\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\n/** A committed item: the startup banner, or one conversation entry. */\nexport type TStaticItem =\n | { readonly kind: 'banner'; readonly version: string }\n | { readonly kind: 'entry'; readonly entry: IHistoryEntry };\n\nexport interface IBuildStaticItemsInputs {\n history: readonly IHistoryEntry[];\n version: string | undefined;\n /**\n * Screen-reader mode. The banner is ASCII art with no spoken meaning — a reader announces it\n * character by character before the session has said anything — so the mode omits the item\n * entirely rather than styling it differently.\n */\n screenReader: boolean;\n}\n\nconst FALLBACK_VERSION = '0.0.0';\n\n/** Build the `<Static>` item list. */\nexport function buildStaticItems(inputs: IBuildStaticItemsInputs): TStaticItem[] {\n const banner: TStaticItem[] = inputs.screenReader\n ? []\n : [{ kind: 'banner', version: inputs.version ?? FALLBACK_VERSION }];\n return [...banner, ...inputs.history.map((entry): TStaticItem => ({ kind: 'entry', entry }))];\n}\n","/**\n * Interrupt-signal policy for the interactive TUI (CLI-075 / RUNTIME-33).\n *\n * The first Ctrl+C or termination signal runs the graceful shutdown (persist session, cancel\n * background work, drain queues). A second interrupt arriving while a shutdown is already in flight\n * is a force-quit: the user is telling us the graceful path is wedged, so we exit immediately rather\n * than no-op. Exit code 130 = 128 + SIGINT(2), the conventional \"terminated by Ctrl+C\" code.\n */\n\n/** Conventional \"terminated by SIGINT\" exit code (128 + 2). */\nexport const FORCE_QUIT_EXIT_CODE = 130;\n\nexport interface IHandleInterruptParams {\n /** Whether a graceful shutdown is already in progress. */\n isShuttingDown: boolean;\n /** Runs the graceful shutdown (only on the first interrupt). */\n graceful: () => void;\n /** Force-quits the process (defaults to `process.exit`); injectable for tests. */\n forceExit?: (code: number) => void;\n}\n\n/**\n * Route an interrupt (Ctrl+C / SIGINT / SIGTERM) to either the graceful path or a force-quit.\n * Keeping this pure and side-effect-injectable makes the second-signal contract unit-testable\n * without spawning a process.\n */\nexport function handleInterrupt(params: IHandleInterruptParams): void {\n const forceExit = params.forceExit ?? ((code: number): void => process.exit(code));\n if (params.isShuttingDown) {\n forceExit(FORCE_QUIT_EXIT_CODE);\n return;\n }\n params.graceful();\n}\n","/**\n * CLI-2004 — the audible attention signal.\n *\n * Three triggers, all of them \"the session now needs you and you cannot see that it does\": a reply\n * finished, a prompt or dialog wants an answer, and a tool that ran long enough that you had stopped\n * watching has finished.\n *\n * The long-tool threshold is a NAMED CONSTANT rather than a copied literal. It comes from a single\n * reference, so it is the kind of number that has to be tunable and attributable — not folded into\n * an inline `> 5000`.\n *\n * Written outside Ink for the same reason as `terminal-marks.ts`: BEL is terminal framing, not\n * rendered content.\n */\n\n/** A tool that took longer than this rings on completion. Single-reference; tunable by design. */\nexport const LONG_TOOL_BELL_MS = 5000;\n\n/** The bell byte. */\nexport const BELL = '\\x07';\n\nexport interface IAttentionBellOptions {\n /** Screen-reader mode. Off ⇒ nothing is ever written. */\n enabled: boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n /** Injected clock so the threshold is testable without real time. */\n now?: () => number;\n /** Override the long-tool threshold. */\n longToolBellMs?: number;\n}\n\nexport interface IAttentionBell {\n /** The assistant finished a reply. */\n replyCompleted(): void;\n /** A prompt or dialog mounted and is waiting for an answer. */\n promptMounted(): void;\n /** Record a tool's start so its duration can be judged at completion. */\n toolStarted(toolId: string): void;\n /** A tool finished — rings only if its recorded start is older than the threshold. */\n toolCompleted(toolId: string): void;\n}\n\n/**\n * Build the bell. A tool whose start was never recorded never rings: the signal is \"the thing you\n * stopped waiting for is done\", and without a start there is no duration to judge, so silence is the\n * correct answer rather than a guessed one.\n */\nexport function createAttentionBell(options: IAttentionBellOptions): IAttentionBell {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n const now = options.now ?? ((): number => Date.now());\n const threshold = options.longToolBellMs ?? LONG_TOOL_BELL_MS;\n const startedAt = new Map<string, number>();\n\n const ring = (): void => {\n if (!options.enabled) return;\n write(BELL);\n };\n\n return {\n replyCompleted: ring,\n promptMounted: ring,\n toolStarted(toolId: string): void {\n if (!options.enabled) return;\n startedAt.set(toolId, now());\n },\n toolCompleted(toolId: string): void {\n const started = startedAt.get(toolId);\n startedAt.delete(toolId);\n if (started === undefined) return;\n if (now() - started <= threshold) return;\n ring();\n },\n };\n}\n","/**\n * CLI-2004 — OSC 133 shell-integration marks at agent-turn boundaries.\n *\n * `OSC 133 ; A` marks prompt start, `; B` prompt end, `; C` pre-execution and `; D` execution\n * finished. Terminals that implement the protocol turn those into jump-to-previous-prompt\n * navigation, which is how a reader reviews a long session without scrolling line by line.\n *\n * Written OUTSIDE Ink, following the `use-terminal-title.ts` carve-out: `sanitize-terminal-text.ts`\n * strips OSC from untrusted text and the `tui-safe-text-boundary` scan permits only `SafeText` to\n * import Ink's `Text`, so an escape sequence this package writes deliberately cannot go through a\n * component. Unlike the title writer there is no interpolated payload here at all — the four\n * sequences are constants — so nothing needs sanitizing.\n *\n * A terminal that does not implement OSC 133 discards the unknown sequence silently, so emission is\n * safe wherever it is on; `supportsTurnMarks()` (terminal-capabilities.ts) carries the documented\n * negatives rather than a capability probe.\n */\n\n/** The four boundary marks, in the order one turn emits them. */\nexport type TTurnMark = 'promptStart' | 'promptEnd' | 'turnStart' | 'turnEnd';\n\nconst MARK_CODES: Readonly<Record<TTurnMark, string>> = {\n promptStart: 'A',\n promptEnd: 'B',\n turnStart: 'C',\n turnEnd: 'D',\n};\n\n/** The exact byte sequence for one mark. */\nexport function turnMarkSequence(mark: TTurnMark): string {\n return `\\x1b]133;${MARK_CODES[mark]}\\x07`;\n}\n\nexport interface ITurnMarkWriterOptions {\n /** Screen-reader mode. Off ⇒ nothing is ever written. */\n enabled: boolean;\n /** Documented per-terminal support gate. */\n supported: () => boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n}\n\nexport interface ITurnMarkWriter {\n emit(mark: TTurnMark): void;\n}\n\n/**\n * Build the mark writer. Both gates are read at emit time, not construction time, so a test can flip\n * the support gate between emissions without rebuilding the writer.\n */\nexport function createTurnMarkWriter(options: ITurnMarkWriterOptions): ITurnMarkWriter {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n return {\n emit(mark: TTurnMark): void {\n if (!options.enabled || !options.supported()) return;\n write(turnMarkSequence(mark));\n },\n };\n}\n","/**\n * CLI-2004 — the non-visual half of a turn: the bell and the OSC 133 boundary marks.\n *\n * Both are byte-stream events, not rendering, so they live in a hook rather than a component: the\n * App says WHEN a turn starts, ends, or wants an answer, and this decides what reaches the terminal.\n * When the mode is off both writers are inert, so the App has exactly one code path.\n *\n * One turn emits `A` (prompt start) → `B` (prompt end) → `C` (turn start) → `D` (turn end): the\n * prompt is marked while the user is being asked for input, and the execution pair brackets the\n * agent's work. That is the boundary a reader jumps between.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { createAttentionBell } from '../attention-bell.js';\nimport { useScreenReaderPacing } from '../screen-reader-pacing-context.js';\nimport { supportsTurnMarks } from '../terminal-capabilities.js';\nimport { useTerminalCapabilityOverrides } from '../terminal-capabilities-context.js';\nimport { createTurnMarkWriter } from '../terminal-marks.js';\n\nimport type { IAttentionBell } from '../attention-bell.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseScreenReaderTurnSignalsInputs {\n enabled: boolean;\n /** The agent is working (a turn is in flight). */\n isThinking: boolean;\n /** Tools currently reported as active by the session. */\n activeTools: readonly IToolState[];\n /** A permission ask or user-action dialog is mounted and waiting for an answer. */\n awaitingAnswer: boolean;\n}\n\n/**\n * Stable key for one tool run within a turn.\n *\n * `executionId` where the session supplies one; otherwise the position, which `onToolEnd` preserves\n * (it replaces the entry at its index rather than removing it).\n */\nfunction toolKey(tool: IToolState, index: number): string {\n return tool.executionId ?? `${tool.toolName}#${index}`;\n}\n\n/** The keys of the tools currently RUNNING — not merely present in the array. */\nfunction runningKeys(activeTools: readonly IToolState[]): Set<string> {\n const running = new Set<string>();\n activeTools.forEach((tool, index) => {\n if (tool.isRunning) running.add(toolKey(tool, index));\n });\n return running;\n}\n\n/** Wire the bell and the turn marks to the App's turn lifecycle. */\nexport function useScreenReaderTurnSignals(inputs: IUseScreenReaderTurnSignalsInputs): void {\n const { enabled, isThinking, activeTools, awaitingAnswer } = inputs;\n const bell = useMemo<IAttentionBell>(() => createAttentionBell({ enabled }), [enabled]);\n // SCREEN-2670: the marks are POSITIONAL — the terminal records the line they arrive on — so they\n // travel through the owned write path, ordered with the commit they belong to. Outside a provider\n // the port writes to the process's own stdout, which is exactly today's path.\n const pacing = useScreenReaderPacing();\n const terminalCapabilities = useTerminalCapabilityOverrides();\n const marks = useMemo(\n () =>\n createTurnMarkWriter({\n enabled,\n supported: () => supportsTurnMarks({ override: terminalCapabilities.turnMarks }),\n write: (text) => pacing.write(text),\n }),\n [enabled, pacing, terminalCapabilities.turnMarks],\n );\n const wasThinking = useRef(false);\n const runningTools = useRef<ReadonlySet<string>>(new Set());\n\n // Turn boundaries: B/C when work starts, D/A when it ends and the prompt comes back.\n useEffect(() => {\n if (isThinking && !wasThinking.current) {\n marks.emit('promptEnd');\n marks.emit('turnStart');\n } else if (!isThinking && wasThinking.current) {\n marks.emit('turnEnd');\n marks.emit('promptStart');\n bell.replyCompleted();\n }\n wasThinking.current = isThinking;\n }, [isThinking, bell, marks]);\n\n // The very first prompt of the session is marked once, before anything has been asked.\n useEffect(() => {\n marks.emit('promptStart');\n }, [marks]);\n\n // A prompt or dialog that wants an answer rings once on mount.\n useEffect(() => {\n if (awaitingAnswer) bell.promptMounted();\n }, [awaitingAnswer, bell]);\n\n // Long-running tools: record each start, ring on the completions that outlived the threshold.\n //\n // Completion is the `isRunning` true→false transition, NOT the entry leaving the array. The\n // session's `onToolEnd` replaces the finished entry in place (same index, same name, so the same\n // key) and only `onThinking(false)` empties the array — so a membership test would report every\n // tool as completing at turn end, judging its duration against the whole turn.\n useEffect(() => {\n const running = runningKeys(activeTools);\n for (const key of running) {\n if (!runningTools.current.has(key)) bell.toolStarted(key);\n }\n for (const key of runningTools.current) {\n if (!running.has(key)) bell.toolCompleted(key);\n }\n runningTools.current = running;\n }, [activeTools, bell]);\n}\n","import { useApp, useInput } from 'ink';\nimport { useEffect } from 'react';\n\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { handleInterrupt } from '../shutdown-signal.js';\nimport { useScreenReaderTurnSignals } from './useScreenReaderTurnSignals.js';\n\nimport type { IPendingPermissionRequest } from '../types.js';\nimport type { IActionRequest, TSessionEndReason } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\ninterface IOptions {\n readonly isThinking: boolean;\n readonly isShuttingDown: boolean;\n readonly permissionRequest: IPendingPermissionRequest | null;\n readonly pendingUserAction: IActionRequest | null;\n readonly pluginVisible: boolean;\n readonly transportVisible: boolean;\n readonly sessionPickerVisible: boolean;\n readonly workspaceSwitcherVisible: boolean;\n /** SCREEN-1993: the history-search overlay owns Esc and the switcher key while open. */\n readonly historySearchOpen: boolean;\n readonly selectedEntry: IExecutionWorkspaceEntry | undefined;\n readonly backgroundListFocused: boolean;\n readonly mainThreadEntryId: string | undefined;\n readonly activeTools: readonly IToolState[];\n readonly abort: () => void;\n readonly stopWaitingLoop: () => Promise<void>;\n readonly toggleWorkspaceSwitcher: () => void;\n readonly selectWorkspaceEntry: (entryId: string) => void;\n readonly shutdown: (reason?: TSessionEndReason) => Promise<void>;\n readonly recoveryError: string | undefined;\n readonly recoveryPending: boolean;\n readonly coordinationBlocked: boolean;\n /** SCREEN-2002: the theme picker owns its keys while it is open, like every sibling overlay. */\n readonly themePickerVisible: boolean;\n readonly retryRecovery: () => void;\n}\n\nfunction overlaysBlockKeys(options: IOptions): boolean {\n return Boolean(\n options.permissionRequest ||\n options.pendingUserAction ||\n options.pluginVisible ||\n options.transportVisible ||\n options.sessionPickerVisible ||\n options.workspaceSwitcherVisible ||\n options.historySearchOpen ||\n options.themePickerVisible ||\n options.coordinationBlocked,\n );\n}\n\nfunction useEscapeBindings(options: IOptions): void {\n const context = options.isThinking ? 'thinking' : 'background-detail';\n useKeybindingActions(context, (actions) => {\n if (overlaysBlockKeys(options)) return;\n if (actions.includes('abort') && options.isThinking) {\n options.abort();\n return;\n }\n const selected = options.selectedEntry;\n if (\n actions.includes('return-to-main') &&\n selected &&\n selected.kind !== 'main_thread' &&\n options.mainThreadEntryId\n ) {\n options.selectWorkspaceEntry(options.mainThreadEntryId);\n return;\n }\n if (actions.includes('return-to-main') && !options.backgroundListFocused) {\n void options.stopWaitingLoop();\n }\n });\n}\n\nfunction useWorkspaceSwitcherBinding(options: IOptions): void {\n useKeybindingActions('app', (actions) => {\n if (!actions.includes('open-workspace-switcher')) return;\n if (\n options.permissionRequest ||\n options.pendingUserAction ||\n options.pluginVisible ||\n options.sessionPickerVisible ||\n options.historySearchOpen ||\n options.themePickerVisible ||\n options.coordinationBlocked ||\n options.isShuttingDown\n )\n return;\n options.toggleWorkspaceSwitcher();\n });\n}\n\nfunction useRecoveryBinding(options: IOptions): void {\n useKeybindingActions('recovery', (actions) => {\n if (\n options.recoveryError !== undefined &&\n !options.recoveryPending &&\n actions.includes('retry')\n ) {\n options.retryRecovery();\n }\n });\n}\n\nfunction useShutdownBindings(options: IOptions): void {\n const { exit } = useApp();\n const requestShutdown = (reason: TSessionEndReason): void => {\n handleInterrupt({\n isShuttingDown: options.isShuttingDown,\n graceful: () => void options.shutdown(reason).finally(() => exit()),\n });\n };\n useInput((input, key) => {\n if (key.ctrl && input === 'c') requestShutdown('prompt_input_exit');\n });\n useEffect(() => {\n const onSignal = (): void => requestShutdown('other');\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n return () => {\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n };\n });\n}\n\nexport function useAppInputBindings(options: IOptions): boolean {\n useEscapeBindings(options);\n useRecoveryBinding(options);\n useWorkspaceSwitcherBinding(options);\n useShutdownBindings(options);\n const screenReader = useScreenReader();\n useScreenReaderTurnSignals({\n enabled: screenReader,\n isThinking: options.isThinking,\n activeTools: options.activeTools,\n awaitingAnswer: options.permissionRequest !== null || options.pendingUserAction !== null,\n });\n return screenReader;\n}\n","/**\n * Hook: uses the host plugin adapter, or an explicit unsupported adapter when none is provided.\n *\n * A missing host capability must fail visibly. Returning successful no-ops would make the plugin\n * screen claim mutations succeeded while changing nothing.\n */\n\nimport { useMemo } from 'react';\n\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\nfunction unsupported(): Promise<never> {\n return Promise.reject(new Error('Plugin management is unavailable in this host.'));\n}\n\nfunction createUnsupportedPluginAdapter(): ICommandPluginAdapter {\n return {\n listInstalled: unsupported,\n listAvailablePlugins: unsupported,\n install: unsupported,\n uninstall: unsupported,\n enable: unsupported,\n disable: unsupported,\n marketplaceAdd: unsupported,\n marketplaceRemove: unsupported,\n marketplaceUpdate: unsupported,\n marketplaceList: unsupported,\n reloadPlugins: unsupported,\n };\n}\n\nexport function resolvePluginCallbacks(adapter?: ICommandPluginAdapter): ICommandPluginAdapter {\n return adapter ?? createUnsupportedPluginAdapter();\n}\n\nexport function usePluginCallbacks(adapter?: ICommandPluginAdapter): ICommandPluginAdapter {\n return useMemo(() => resolvePluginCallbacks(adapter), [adapter]);\n}\n","import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport { listResumableSessionSummaries } from '@robota-sdk/agent-framework';\nimport { useCallback } from 'react';\n\nimport { usePluginCallbacks } from './usePluginCallbacks.js';\n\nimport type {\n IAppPluginViewModel,\n IAppSessionPickerViewModel,\n IAppTransportViewModel,\n} from '../app-view-model.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IOptions {\n readonly cwd: string;\n readonly pluginAdapter: ICommandPluginAdapter | undefined;\n readonly pluginVisible: boolean;\n readonly setPluginVisible: (visible: boolean) => void;\n readonly transportRegistry: ITransportRegistryView | undefined;\n readonly transportVisible: boolean;\n readonly setTransportVisible: (visible: boolean) => void;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly sessionPickerVisible: boolean;\n readonly setSessionPickerVisible: (visible: boolean) => void;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\nexport interface IAppOverlays {\n readonly plugin: IAppPluginViewModel;\n readonly transport: IAppTransportViewModel;\n readonly sessionPicker: IAppSessionPickerViewModel;\n}\n\nexport function useAppOverlays(options: IOptions): IAppOverlays {\n const pluginCallbacks = usePluginCallbacks(options.pluginAdapter);\n const addPluginMessage = useCallback(\n (content: string) => options.addEntry(messageToHistoryEntry(createSystemMessage(content))),\n [options.addEntry],\n );\n const selectSession = useCallback(\n (sessionId: string) => {\n options.setSessionPickerVisible(false);\n void options.onSessionSwitch(sessionId);\n },\n [options.onSessionSwitch, options.setSessionPickerVisible],\n );\n const cancelSessionPicker = useCallback(() => {\n options.setSessionPickerVisible(false);\n options.addEntry(messageToHistoryEntry(createSystemMessage('Session resume cancelled.')));\n }, [options.addEntry, options.setSessionPickerVisible]);\n return {\n plugin: {\n visible: options.pluginVisible,\n callbacks: pluginCallbacks,\n close: () => options.setPluginVisible(false),\n addMessage: addPluginMessage,\n },\n transport: {\n visible: options.transportVisible,\n registry: options.transportRegistry,\n close: () => options.setTransportVisible(false),\n },\n sessionPicker: {\n visible: options.sessionPickerVisible,\n sessions: listResumableSessionSummaries(options.sessionStore, options.cwd),\n select: selectSession,\n cancel: cancelSessionPicker,\n },\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\ninterface IOptions {\n readonly selectedEntry: IExecutionWorkspaceEntry | undefined;\n readonly submit: (input: string) => Promise<void>;\n readonly sendAgentJob: (taskId: string, input: string) => Promise<void>;\n readonly isThinking: boolean;\n}\n\nexport interface IAppSubmissionState {\n readonly submit: (input: string) => Promise<void>;\n readonly gitRefreshToken: number;\n}\n\nexport function useAppSubmissionState(options: IOptions): IAppSubmissionState {\n const [gitRefreshToken, setGitRefreshToken] = useState(0);\n const routedSubmit = useCallback(\n async (input: string): Promise<void> => {\n const entry = options.selectedEntry;\n if (entry && entry.kind !== 'main_thread' && entry.controls.includes('send')) {\n await options.sendAgentJob(entry.sourceId, input);\n return;\n }\n await options.submit(input);\n },\n [options.selectedEntry, options.sendAgentJob, options.submit],\n );\n const submit = useCallback(\n async (input: string): Promise<void> => {\n setGitRefreshToken((token) => token + 1);\n await routedSubmit(input);\n },\n [routedSubmit],\n );\n const wasThinkingRef = useRef(false);\n useEffect(() => {\n if (wasThinkingRef.current && !options.isThinking) {\n setGitRefreshToken((token) => token + 1);\n }\n wasThinkingRef.current = options.isThinking;\n }, [options.isThinking]);\n return { submit, gitRefreshToken };\n}\n","import { useAppInputBindings } from './useAppInputBindings.js';\nimport { useAppOverlays } from './useAppOverlays.js';\nimport { useAppSubmissionState } from './useAppSubmissionState.js';\n\nimport type { IAppOverlays } from './useAppOverlays.js';\nimport type { IAppScreenState } from './useAppScreenState.js';\nimport type { IAppSubmissionState } from './useAppSubmissionState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IOptions {\n readonly cwd: string;\n readonly state: ITuiChannelState;\n readonly shell: IAppScreenState;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly pluginAdapter: ICommandPluginAdapter | undefined;\n readonly transportRegistry: ITransportRegistryView | undefined;\n readonly recoveryError: string | undefined;\n readonly recoveryPending: boolean;\n readonly coordinationBlocked: boolean;\n readonly retryRecovery: () => void;\n}\n\nexport interface IAppInteractionState {\n readonly submission: IAppSubmissionState;\n readonly overlays: IAppOverlays;\n readonly screenReader: boolean;\n}\n\nexport function useAppInteractionState(options: IOptions): IAppInteractionState {\n const { screens, workspace } = options.shell;\n const submission = useAppSubmissionState({\n selectedEntry: workspace.background.selectedEntry,\n submit: screens.handleSubmit,\n sendAgentJob: options.state.sendAgentJob,\n isThinking: options.state.isThinking,\n });\n const screenReader = useAppInputBindings({\n isThinking: options.state.isThinking,\n isShuttingDown: options.state.isShuttingDown,\n permissionRequest: options.state.permissionRequest,\n pendingUserAction: options.state.pendingUserAction,\n pluginVisible: screens.showPluginTUI,\n transportVisible: screens.showTransportTUI,\n sessionPickerVisible: screens.showSessionPicker,\n workspaceSwitcherVisible: workspace.background.switcherVisible,\n historySearchOpen: options.shell.historySearchOpen,\n themePickerVisible: screens.showThemePicker,\n selectedEntry: workspace.background.selectedEntry,\n backgroundListFocused: workspace.isBackgroundListFocused,\n mainThreadEntryId: workspace.mainThreadEntryId,\n activeTools: options.state.activeTools,\n abort: options.state.handleAbort,\n stopWaitingLoop: options.state.handleStopWaitingLoop,\n toggleWorkspaceSwitcher: workspace.toggleSwitcher,\n selectWorkspaceEntry: options.state.selectExecutionWorkspaceEntry,\n shutdown: options.state.handleShutdown,\n recoveryError: options.recoveryError,\n recoveryPending: options.recoveryPending,\n coordinationBlocked: options.coordinationBlocked,\n retryRecovery: options.retryRecovery,\n });\n const overlays = useAppOverlays({\n cwd: options.cwd,\n pluginAdapter: options.pluginAdapter,\n pluginVisible: screens.showPluginTUI,\n setPluginVisible: screens.setShowPluginTUI,\n transportRegistry: options.transportRegistry,\n transportVisible: screens.showTransportTUI,\n setTransportVisible: screens.setShowTransportTUI,\n sessionStore: options.sessionStore,\n sessionPickerVisible: screens.showSessionPicker,\n setSessionPickerVisible: screens.setShowSessionPicker,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.state.addEntry,\n });\n return { submission, overlays, screenReader };\n}\n","import { useEffect } from 'react';\n\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\n/**\n * Set the terminal's window title from the session name.\n *\n * Split out of `App.tsx` under SEC-019 (issue #2022) by responsibility: this is the one place the TUI\n * writes an escape sequence to stdout directly rather than through Ink, which makes it the one place\n * where a value is interpolated INTO terminal syntax rather than rendered as content. Keeping it\n * inside a 600-line component is what let it sit unexamined next to code that never touches an\n * escape.\n *\n * `sessionName` is untrusted — a session can be named from a prompt, and a name containing BEL or ESC\n * terminates the OSC early, so everything after it is read by the terminal as its own command.\n *\n * Both the host-selected product name and the session name are sanitized separately from the\n * `\\x1b]0;` … `\\x07` framing this module deliberately writes.\n */\nexport function useTerminalTitle(sessionName: string | undefined): void {\n const productDisplayName = useProductDisplayName();\n useEffect(() => {\n const safeName = sanitizeTerminalText(sessionName ?? '');\n const safeProductName = sanitizeTerminalText(productDisplayName);\n const title = safeName ? `${safeProductName} — ${safeName}` : safeProductName;\n process.stdout.write(`\\x1b]0;${title}\\x07`);\n }, [sessionName, productDisplayName]);\n}\n","/**\n * TERM-002: React glue for the terminal-handoff controller.\n *\n * Registers the App's suspend/resume into the controller. On suspend the App renders nothing (see\n * App.tsx) so Ink unmounts its input hooks and releases raw mode, handing the real terminal to the\n * child process; on resume the App re-renders.\n */\nimport { useEffect, useState } from 'react';\n\nimport type { TerminalHandoffController } from '../terminal-handoff-controller.js';\n\n/** Time for React/Ink to commit the empty render (unmount input, release raw mode) before handoff. */\nconst SUSPEND_COMMIT_DELAY_MS = 50;\n\nexport function useTerminalHandoffSuspension(\n controller: TerminalHandoffController | undefined,\n): boolean {\n const [suspended, setSuspended] = useState(false);\n useEffect(() => {\n if (!controller) return;\n return controller.registerSuspendHooks({\n suspend: async () => {\n setSuspended(true);\n await new Promise<void>((resolve) => setTimeout(resolve, SUSPEND_COMMIT_DELAY_MS));\n },\n resume: () => setSuspended(false),\n });\n }, [controller]);\n return suspended;\n}\n","import { useCallback, useEffect, useState } from 'react';\n\nimport { useTerminalTitle } from '../use-terminal-title.js';\nimport { useTerminalHandoffSuspension } from './useTerminalHandoffSuspension.js';\n\nimport type { ITuiAppChannelPort } from '../tui-app-channel-port.js';\n\nexport interface IAppLifecycleState {\n readonly sessionName: string | undefined;\n readonly setSessionName: (name: string) => void;\n readonly handoffSuspended: boolean;\n readonly updateNotice: string | undefined;\n readonly startError: string | undefined;\n readonly startPending: boolean;\n readonly retryStart: () => void;\n}\n\nfunction useChannelLifecycle(channel: ITuiAppChannelPort): {\n startError: string | undefined;\n startPending: boolean;\n retryStart: () => void;\n} {\n const [startError, setStartError] = useState<string | undefined>();\n const [startPending, setStartPending] = useState(true);\n const retryStart = useCallback((): void => {\n setStartPending(true);\n void channel\n .start()\n .then(() => setStartError(undefined))\n .catch((cause) => {\n const message = cause instanceof Error ? cause.message : String(cause);\n setStartError(`TUI start failed: ${message}`);\n })\n .finally(() => setStartPending(false));\n }, [channel]);\n useEffect(() => {\n retryStart();\n }, [channel, retryStart]);\n return { startError, startPending, retryStart };\n}\n\nfunction useStartupUpdateNotice(source?: Promise<string | undefined>): string | undefined {\n const [notice, setNotice] = useState<string | undefined>();\n useEffect(() => {\n let mounted = true;\n source\n ?.then((value) => {\n if (mounted && value !== undefined) setNotice(value);\n })\n .catch(() => {\n // Startup update checks are best-effort and must not disrupt the TUI.\n });\n return () => {\n mounted = false;\n };\n }, [source]);\n return notice;\n}\n\nexport function useAppLifecycleState(\n channel: ITuiAppChannelPort,\n startupUpdateNotice?: Promise<string | undefined>,\n): IAppLifecycleState {\n const channelLifecycle = useChannelLifecycle(channel);\n const [sessionName, setSessionName] = useState<string | undefined>(channel.sessionName);\n useEffect(() => {\n if (channel.sessionName !== undefined && channel.sessionName !== sessionName) {\n setSessionName(channel.sessionName);\n }\n }, [channel, channel.sessionName, sessionName]);\n useTerminalTitle(sessionName);\n const handoffSuspended = useTerminalHandoffSuspension(channel.terminalHandoffController);\n const updateNotice = useStartupUpdateNotice(startupUpdateNotice);\n return {\n sessionName,\n setSessionName,\n handoffSuspended,\n updateNotice,\n ...channelLifecycle,\n };\n}\n","import { readAppearanceSettings } from '@robota-sdk/agent-framework';\nimport { useCallback, useState } from 'react';\n\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { ITuiCliAdapter } from '../tui-cli-adapter.js';\nimport type { IAppearanceSettings } from '@robota-sdk/agent-interface-command';\n\nfunction readFromAdapter(cliAdapter: ITuiCliAdapter): IAppearanceSettings {\n return readAppearanceSettings(cliAdapter.readSettings(cliAdapter.getUserSettingsPath()));\n}\n\n/**\n * SCREEN-2002 — the persisted appearance, plus a from-disk `refresh()`.\n *\n * Refresh-on-result, the shape `useStatusLineSettings` already uses: the HOST applies\n * `appearance-settings-patch` through the settings adapter, so the renderer learns about a change by\n * RE-READING the document when a command result arrives. The TUI never writes these keys itself,\n * which is what keeps one writer for one file.\n */\nexport function useAppearanceSettings(): [IAppearanceSettings, () => void] {\n const cliAdapter = useTuiCliAdapter();\n const [appearance, setAppearance] = useState<IAppearanceSettings>(() =>\n readFromAdapter(cliAdapter),\n );\n const refresh = useCallback((): void => {\n setAppearance(readFromAdapter(cliAdapter));\n }, [cliAdapter]);\n return [appearance, refresh];\n}\n","/**\n * Loads the inline detail page for the selected background-execution entry.\n *\n * Extracted from `App.tsx` (CLI-2004): three pieces of state and one async effect that exist only\n * for the detail pane, sitting in the middle of the component's input handlers. One responsibility,\n * one module — and the App keeps room for the concerns that actually belong to it.\n */\n\nimport { useEffect, useState } from 'react';\n\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\n\nexport interface IExecutionDetailState {\n page: IExecutionDetailPage | null;\n error: string | undefined;\n loading: boolean;\n}\n\nexport interface IUseExecutionDetailPageInputs {\n /** The selected entry, or undefined / the main thread when no detail is shown. */\n entry: IExecutionWorkspaceEntry | undefined;\n /** Re-read trigger: the snapshot the entry came from. */\n snapshot: IExecutionWorkspaceSnapshot | null;\n read: (entryId: string) => Promise<IExecutionDetailPage>;\n}\n\n/** Load (and cancel-on-change) the detail page for the selected entry. */\nexport function useExecutionDetailPage(\n inputs: IUseExecutionDetailPageInputs,\n): IExecutionDetailState {\n const [page, setPage] = useState<IExecutionDetailPage | null>(null);\n const [error, setError] = useState<string | undefined>();\n const [loading, setLoading] = useState(false);\n const { entry, snapshot, read } = inputs;\n\n useEffect(() => {\n if (!entry || entry.kind === 'main_thread') {\n setPage(null);\n setError(undefined);\n setLoading(false);\n return;\n }\n\n let isCurrent = true;\n setLoading(true);\n setError(undefined);\n read(entry.id)\n .then((loaded) => {\n if (!isCurrent) return;\n setPage(loaded);\n setLoading(false);\n })\n .catch((cause: Error) => {\n if (!isCurrent) return;\n setError(cause.message);\n setLoading(false);\n });\n\n return () => {\n isCurrent = false;\n };\n }, [snapshot, read, entry]);\n\n return { page, error, loading };\n}\n","/**\n * CLI-1994: the TUI half of `attach` — turning the workspace entry the operator picked into either\n * a session switch or a stated refusal.\n *\n * The DECISION is not made here. `resolveExecutionAttach` is the framework's, beside the projection\n * that decides when the control is offered, so the surface cannot invent a second answer to \"may\n * this be attached to\". What this module owns is the surface's part: the refusal reaches the\n * operator as a message, and the switch goes through the SAME session-switch path the session\n * picker uses — a new channel from the factory, the previous one stopped first.\n *\n * A switch is a VIEW change. Nothing is merged: the parent session's record is not read or written,\n * and the two records stay separate exactly as `/fork` left them.\n */\n\nimport { resolveExecutionAttach } from '@robota-sdk/agent-framework';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\n/** The `switch-session` member of the command layer's UI-intent union, narrowed to itself. */\n/**\n * What an attach did: the session this terminal was pointed at.\n *\n * Deliberately NOT a `TCommandUiIntent` member. Attach starts at a keypress in this surface's own\n * background panel, not at a command, so routing it through the requester-routed intent bus would\n * add a hop with no second consumer at the end of it — and the intent's `useSideEffects` case had no\n * emitter, which is a reader with no writer.\n */\nexport interface IForkAttachOutcome {\n readonly sessionId: string;\n}\n\nexport interface IForkAttachDeps {\n /** Whether the forked record is still in the store this surface would switch onto. */\n readonly hasSessionRecord: (sessionId: string) => boolean;\n /** The surface's existing session-switch path — the one the session picker calls. */\n readonly switchSession: (sessionId: string) => void;\n /** How a refusal reaches the operator (a system entry in the transcript). */\n readonly notify: (message: string) => void;\n /** Optional host-owned command for reopening a completed fork outside the attach view. */\n readonly formatResumeCommand?: (sessionId: string) => string;\n}\n\n/**\n * Attach to `entry`'s forked session, or tell the operator why not.\n *\n * Returns the UI intent that was emitted, so a caller — and a test — can see the switch as the\n * command layer's own vocabulary rather than as a side effect. `undefined` means refused, and the\n * refusal has already been delivered through `notify`.\n */\nexport function attachToForkedSession(\n entry: IExecutionWorkspaceEntry,\n deps: IForkAttachDeps,\n): IForkAttachOutcome | undefined {\n const outcome = resolveExecutionAttach(entry, { hasSessionRecord: deps.hasSessionRecord });\n if (outcome.type === 'refused') {\n const resumeCommand = outcome.resumeSessionId && deps.formatResumeCommand?.(outcome.resumeSessionId);\n deps.notify(resumeCommand ? `${outcome.reason} Open it with: ${resumeCommand}` : outcome.reason);\n return undefined;\n }\n deps.switchSession(outcome.sessionId);\n return { sessionId: outcome.sessionId };\n}\n","/**\n * CLI-1994: the App's `attach` handler, as a hook rather than another closure inside `App.tsx`.\n *\n * `App.tsx` is a file the size floor has already frozen as debt, where the rule is \"split instead of\n * extending\" — so the handler lives beside the other App hooks. The decision itself is neither here\n * nor there: `attachToForkedSession` asks the framework, which owns when `attach` is offered and\n * when it is honoured.\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport { useCallback } from 'react';\n\nimport { attachToForkedSession } from '../flows/fork-attach-flow.js';\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseForkAttachOptions {\n /** Absent when the surface persists nothing; attach then refuses, saying so. */\n readonly sessionStore: IInteractiveSessionStore | undefined;\n /** The surface's session-switch path — the same one the session picker takes. */\n readonly onSessionSwitch: (sessionId: string) => void;\n /** Where a refusal is written, so it reaches the operator instead of disappearing. */\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\n/**\n * Attach to a forked conversation: point this terminal at the fork's own session record. A VIEW\n * switch, never a merge — the parent's record is not read, written or joined either way.\n */\nexport function useForkAttach({\n sessionStore,\n onSessionSwitch,\n addEntry,\n}: IUseForkAttachOptions): (entry: IExecutionWorkspaceEntry) => void {\n const cliAdapter = useTuiCliAdapter();\n return useCallback(\n (entry: IExecutionWorkspaceEntry): void => {\n const notify = (message: string): void =>\n addEntry(messageToHistoryEntry(createSystemMessage(message)));\n // A surface composed without a session store cannot open another session's record. Said\n // plainly rather than reported as \"the record is missing\", which is a different fact.\n if (sessionStore === undefined) {\n notify('This surface has no session store, so it cannot attach to a forked session.');\n return;\n }\n attachToForkedSession(entry, {\n hasSessionRecord: (sessionId) => sessionStore.load(sessionId).status === 'valid',\n switchSession: onSessionSwitch,\n notify,\n ...(cliAdapter.formatResumeCommand === undefined\n ? {}\n : { formatResumeCommand: cliAdapter.formatResumeCommand }),\n });\n },\n [addEntry, cliAdapter, onSessionSwitch, sessionStore],\n );\n}\n","/**\n * The background panel's two data concerns as one call: what the selected entry's detail page says,\n * and what happens when the operator attaches to a forked one.\n *\n * Both belong to the same surface and both are read by the same component, so `App` asks for them\n * once rather than wiring two hooks whose inputs it has to keep in step. Split out because CLI-2004\n * had already brought `App.tsx` to its size budget by extracting the detail read, and CLI-1994 adds\n * the attach handler beside it — growing the file was the alternative the size rule forbids.\n */\n\nimport { useMemo } from 'react';\n\nimport { useExecutionDetailPage } from './useExecutionDetailPage.js';\nimport { useForkAttach } from './useForkAttach.js';\n\nimport type { IExecutionDetailState } from './useExecutionDetailPage.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseBackgroundPanelOptions {\n /** Which entry the switcher has selected; resolved against the snapshot below. */\n readonly selectedEntryId: string | undefined;\n /** The panel's data, and the re-read trigger — the detail follows a running task's output. */\n readonly snapshot: IExecutionWorkspaceSnapshot | null;\n readonly read: (entryId: string) => Promise<IExecutionDetailPage>;\n /** Absent ⇒ this surface cannot open another session's record, and attach says so. */\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => void;\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\nexport interface IBackgroundPanel {\n /** The selected entry itself, which the switcher and the detail view both render. */\n readonly entry: IExecutionWorkspaceEntry | undefined;\n readonly detail: IExecutionDetailState;\n readonly attachToFork: (entry: IExecutionWorkspaceEntry) => void;\n}\n\n/** Wire the background panel's detail read and its attach handler together. */\nexport function useBackgroundPanel(inputs: IUseBackgroundPanelOptions): IBackgroundPanel {\n const { snapshot, selectedEntryId } = inputs;\n const entry = useMemo(\n () => snapshot?.entries.find((candidate) => candidate.id === selectedEntryId),\n [snapshot, selectedEntryId],\n );\n const detail = useExecutionDetailPage({ entry, snapshot, read: inputs.read });\n const attachToFork = useForkAttach({\n sessionStore: inputs.sessionStore,\n onSessionSwitch: inputs.onSessionSwitch,\n addEntry: inputs.addEntry,\n });\n return { entry, detail, attachToFork };\n}\n","/**\n * SCREEN-014: pure reducer for keyboard navigation of the inline background-work list.\n *\n * Kept out of the App component so the navigation rules are exhaustively unit-testable without\n * rendering the whole TUI. The App's `useInput` translates the resulting action into focus/selection\n * state changes.\n */\n\n/** A key event relevant to background-list navigation (subset of Ink's key object). */\nexport interface IBackgroundFocusKey {\n upArrow: boolean;\n downArrow: boolean;\n return: boolean;\n escape: boolean;\n}\n\nexport type TBackgroundFocusAction =\n | { type: 'move'; index: number | null } // null = focus returns to the prompt input\n | { type: 'open'; index: number } // open the highlighted task's inline detail\n | { type: 'exit' } // leave the list, focus the prompt input\n | { type: 'none' }; // unhandled key — ignore\n\n/**\n * Resolve a keypress while the background list holds focus.\n *\n * - ↑ moves up; ↑ past the first item returns focus to the input (`move` to `null`).\n * - ↓ moves down, clamped at the last item.\n * - Enter opens the highlighted task.\n * - Esc (or an emptied list) exits to the input.\n */\nexport function resolveBackgroundFocusKey(\n current: number,\n entryCount: number,\n key: IBackgroundFocusKey,\n): TBackgroundFocusAction {\n if (entryCount <= 0) return { type: 'exit' };\n if (key.upArrow) return { type: 'move', index: current > 0 ? current - 1 : null };\n if (key.downArrow) {\n return { type: 'move', index: current < entryCount - 1 ? current + 1 : current };\n }\n if (key.return) return { type: 'open', index: Math.min(Math.max(current, 0), entryCount - 1) };\n if (key.escape) return { type: 'exit' };\n return { type: 'none' };\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport {\n countActiveBackgroundWorkspaceEntries,\n getDefaultBackgroundWorkspaceEntries,\n} from '../execution-workspace-view-model.js';\nimport { useBackgroundPanel } from './useBackgroundPanel.js';\nimport { resolveBackgroundFocusKey } from '../flows/background-focus-flow.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { IAppBackgroundViewModel } from '../app-view-model.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IAppWorkspaceState {\n readonly background: IAppBackgroundViewModel;\n readonly activeBackgroundTaskCount: number;\n readonly activeAgentLabel: string | undefined;\n readonly mainThreadEntryId: string | undefined;\n readonly isSelectedEntryInteractive: boolean;\n readonly isBackgroundListFocused: boolean;\n readonly openSwitcher: () => void;\n readonly toggleSwitcher: () => void;\n readonly focusBackgroundList: () => void;\n}\n\ninterface IOptions {\n readonly snapshot: IExecutionWorkspaceSnapshot | null;\n readonly selectedEntryId: string | undefined;\n readonly select: (entryId: string) => void;\n readonly read: (entryId: string) => Promise<IExecutionDetailPage>;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly addEntry: (entry: IHistoryEntry) => void;\n readonly navigationEnabled: boolean;\n readonly switcherVisible: boolean;\n readonly setSwitcherVisible: (visible: boolean | ((current: boolean) => boolean)) => void;\n}\n\ninterface ISwitcherActions {\n readonly open: () => void;\n readonly toggle: () => void;\n readonly close: () => void;\n}\n\nfunction useBackgroundListKeys(\n focusedIndex: number | null,\n entryIds: readonly string[],\n setFocusedIndex: (index: number | null) => void,\n select: (entryId: string) => void,\n enabled: boolean,\n): void {\n useKeybindingActions(\n 'background-list',\n (actions) => {\n if (focusedIndex === null) return;\n const action = resolveBackgroundFocusKey(focusedIndex, entryIds.length, {\n upArrow: actions.includes('previous'),\n downArrow: actions.includes('next'),\n return: actions.includes('open'),\n escape: actions.includes('close'),\n });\n if (action.type === 'move') setFocusedIndex(action.index);\n if (action.type === 'open') {\n const entryId = entryIds[action.index];\n if (entryId) select(entryId);\n setFocusedIndex(null);\n }\n if (action.type === 'exit') setFocusedIndex(null);\n },\n { isActive: focusedIndex !== null && enabled },\n );\n}\n\nfunction useBackgroundFocus(\n entryIds: readonly string[],\n select: (entryId: string) => void,\n enabled: boolean,\n): { focusedIndex: number | null; focusList: () => void } {\n const [focusedIndex, setFocusedIndex] = useState<number | null>(null);\n useEffect(() => {\n setFocusedIndex((index) => {\n if (index === null || entryIds.length === 0) return null;\n return Math.min(index, entryIds.length - 1);\n });\n }, [entryIds.length]);\n useBackgroundListKeys(focusedIndex, entryIds, setFocusedIndex, select, enabled);\n const focusList = useCallback(() => {\n if (entryIds.length > 0) setFocusedIndex(0);\n }, [entryIds.length]);\n return { focusedIndex, focusList };\n}\n\nfunction useSwitcherActions(setVisible: IOptions['setSwitcherVisible']): ISwitcherActions {\n const open = useCallback(() => setVisible(true), [setVisible]);\n const toggle = useCallback(() => setVisible((visible) => !visible), [setVisible]);\n const close = useCallback(() => setVisible(false), [setVisible]);\n return { open, toggle, close };\n}\n\nexport function useAppWorkspaceState(options: IOptions): IAppWorkspaceState {\n const entries = useMemo(\n () => getDefaultBackgroundWorkspaceEntries(options.snapshot),\n [options.snapshot],\n );\n const { focusedIndex, focusList } = useBackgroundFocus(\n entries.map((entry) => entry.id),\n options.select,\n options.navigationEnabled,\n );\n\n const panel = useBackgroundPanel({\n selectedEntryId: options.selectedEntryId,\n snapshot: options.snapshot,\n read: options.read,\n sessionStore: options.sessionStore,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.addEntry,\n });\n const switcher = useSwitcherActions(options.setSwitcherVisible);\n const selected = panel.entry;\n return {\n background: {\n entries,\n focusedIndex,\n selectedEntry: selected,\n detail: panel.detail,\n switcherVisible: options.switcherVisible,\n snapshot: options.snapshot,\n selectedEntryId: options.selectedEntryId,\n select: options.select,\n closeSwitcher: switcher.close,\n attachToFork: panel.attachToFork,\n },\n activeBackgroundTaskCount: countActiveBackgroundWorkspaceEntries(options.snapshot),\n activeAgentLabel: selected?.kind === 'main_thread' ? undefined : selected?.title,\n mainThreadEntryId: options.snapshot?.entries.find((entry) => entry.kind === 'main_thread')?.id,\n isSelectedEntryInteractive:\n !selected || selected.kind === 'main_thread' || selected.controls.includes('send'),\n isBackgroundListFocused: focusedIndex !== null,\n openSwitcher: switcher.open,\n toggleSwitcher: switcher.toggle,\n focusBackgroundList: focusList,\n };\n}\n","/**\n * CMD-004 Phase 2 Stage C — the TUI as a pure renderer.\n *\n * The session layer (the host) executes every command host action via `ICommandHostAdapters`\n * BEFORE the command result returns (language change, settings reset, exit/restart, rename,\n * statusline patch, remote control — see `interactive-session-host-actions.ts`). This hook only:\n *\n * 1. renders the four UI screens from the requester-routed `ui_intent` session event\n * (`show-plugin-manager` / `show-settings` / `show-session-picker` / `show-agent-switcher`) —\n * the surface that issued the command renders the intent; intents stamped with another\n * surface's driver id (or unattributed) are ignored;\n * 2. reflects the broadcast `session_renamed` event into the rendered title (the host performed\n * the rename — the TUI never mutates the session);\n * 3. refreshes the statusline display after a slash-command result arrives by RE-READING the\n * persisted settings (refresh-on-result — the host applied any patch via the settings adapter).\n */\n\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\n\nimport type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';\nimport type { ITuiSessionUiEventPort } from '../tui-app-channel-port.js';\nimport type { ISessionRenamedEvent, IUiIntentEvent } from '@robota-sdk/agent-interface-session';\n\ninterface IUiEventHandlers {\n setSessionName: (name: string) => void;\n refreshStatusLineSettings: () => void;\n /** SCREEN-2002: re-read the persisted appearance after a slash command may have patched it. */\n refreshAppearanceSettings: () => void;\n openAgentSwitcher?: (() => void) | undefined;\n}\n\ninterface IScreenSetters {\n setShowPluginTUI: (show: boolean) => void;\n setShowTransportTUI: (show: boolean) => void;\n setShowSessionPicker: (show: boolean) => void;\n setShowThemePicker: (show: boolean) => void;\n}\n\n/** Subscribe to `ui_intent` (requester-routed) + `session_renamed` (broadcast); returns cleanup. */\nfunction subscribeToSessionUiEvents(\n uiEventPort: ITuiSessionUiEventPort,\n screens: IScreenSetters,\n handlersRef: { current: IUiEventHandlers },\n): () => void {\n const onUiIntent = (event: IUiIntentEvent): void => {\n // Requester-routed: this surface is the local operator (OWNER_DRIVER_ID). Intents issued by\n // other surfaces — or unattributed ones — are theirs to render, not ours.\n if (event.requesterDriverId !== OWNER_DRIVER_ID) return;\n switch (event.intent.type) {\n case 'show-plugin-manager':\n screens.setShowPluginTUI(true);\n return;\n case 'show-settings':\n screens.setShowTransportTUI(true);\n return;\n case 'show-session-picker':\n screens.setShowSessionPicker(true);\n return;\n case 'show-agent-switcher':\n handlersRef.current.openAgentSwitcher?.();\n return;\n case 'show-theme-picker':\n screens.setShowThemePicker(true);\n return;\n }\n };\n const onSessionRenamed = (event: ISessionRenamedEvent): void => {\n handlersRef.current.setSessionName(event.name);\n };\n uiEventPort.on('ui_intent', onUiIntent);\n uiEventPort.on('session_renamed', onSessionRenamed);\n return () => {\n uiEventPort.off('ui_intent', onUiIntent);\n uiEventPort.off('session_renamed', onSessionRenamed);\n };\n}\n\n/**\n * Latest-callback refs so the session subscription binds exactly once per session: App recreates\n * some callbacks every render, and re-subscribing on each identity change would churn listeners.\n */\nfunction useLatestHandlers(handlers: IUiEventHandlers): { current: IUiEventHandlers } {\n const ref = useRef(handlers);\n ref.current = handlers;\n return ref;\n}\n\nexport function useSideEffects({\n uiEventPort,\n baseHandleSubmit,\n setSessionName,\n refreshStatusLineSettings,\n refreshAppearanceSettings,\n showSessionPickerOnStart,\n openAgentSwitcher,\n}: IUseSideEffectsOptions): IUseSideEffectsResult {\n const [showPluginTUI, setShowPluginTUI] = useState(false);\n const [showSessionPicker, setShowSessionPicker] = useState(showSessionPickerOnStart ?? false);\n const [showTransportTUI, setShowTransportTUI] = useState(false);\n const [showThemePicker, setShowThemePicker] = useState(false);\n\n const handlersRef = useLatestHandlers({\n setSessionName,\n refreshStatusLineSettings,\n refreshAppearanceSettings,\n openAgentSwitcher,\n });\n\n useLayoutEffect(\n () =>\n subscribeToSessionUiEvents(\n uiEventPort,\n { setShowPluginTUI, setShowTransportTUI, setShowSessionPicker, setShowThemePicker },\n handlersRef,\n ),\n [uiEventPort],\n );\n\n const handleSubmit = useCallback(\n async (input: string): Promise<void> => {\n await baseHandleSubmit(input);\n // Refresh-on-result: a slash command's result has arrived; the host may have patched the\n // persisted statusline settings — re-read them for the status bar.\n if (input.trimStart().startsWith('/')) {\n handlersRef.current.refreshStatusLineSettings();\n handlersRef.current.refreshAppearanceSettings();\n }\n },\n [baseHandleSubmit],\n );\n\n return {\n handleSubmit,\n showPluginTUI,\n showSessionPicker,\n showTransportTUI,\n showThemePicker,\n setShowPluginTUI,\n setShowSessionPicker,\n setShowTransportTUI,\n setShowThemePicker,\n };\n}\n","import { DEFAULT_STATUS_LINE_COMMAND_SETTINGS } from '@robota-sdk/agent-framework';\nimport { useCallback, useState } from 'react';\n\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { ITuiCliAdapter } from '../tui-cli-adapter.js';\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\nimport type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-command';\n\nfunction readStatusLineSettings(\n settings: Record<string, TUniversalValue>,\n): IStatusLineCommandSettings {\n const defaults = { ...DEFAULT_STATUS_LINE_COMMAND_SETTINGS };\n const raw = settings.statusline;\n if (!isRecord(raw)) {\n return defaults;\n }\n return {\n enabled: typeof raw.enabled === 'boolean' ? raw.enabled : defaults.enabled,\n gitBranch: typeof raw.gitBranch === 'boolean' ? raw.gitBranch : defaults.gitBranch,\n };\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return (\n value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction readFromAdapter(cliAdapter: ITuiCliAdapter): IStatusLineCommandSettings {\n return readStatusLineSettings(cliAdapter.readSettings(cliAdapter.getUserSettingsPath()));\n}\n\n/**\n * Statusline settings as persisted on disk, plus a from-disk `refresh()`.\n *\n * CMD-004 Phase 2 Stage C: the HOST applies `statusline-settings-patch` via the settings adapter\n * (the TUI no longer writes settings), so the renderer refreshes by RE-READING the persisted\n * document when a command result arrives (refresh-on-result).\n */\nexport function useStatusLineSettings(): [IStatusLineCommandSettings, () => void] {\n const cliAdapter = useTuiCliAdapter();\n const [settings, setSettings] = useState<IStatusLineCommandSettings>(() =>\n readFromAdapter(cliAdapter),\n );\n const refresh = useCallback((): void => {\n setSettings(readFromAdapter(cliAdapter));\n }, [cliAdapter]);\n return [settings, refresh];\n}\n","import { useState } from 'react';\n\nimport { useAppearanceSettings } from './useAppearanceSettings.js';\nimport { useAppWorkspaceState } from './useAppWorkspaceState.js';\nimport { useSideEffects } from './useSideEffects.js';\nimport { useStatusLineSettings } from './useStatusLineSettings.js';\n\nimport type { IUseSideEffectsResult } from './side-effects-types.js';\nimport type { IAppWorkspaceState } from './useAppWorkspaceState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type {\n IAppearanceSettings,\n IStatusLineCommandSettings,\n} from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\ninterface IOptions {\n readonly state: ITuiChannelState;\n readonly setSessionName: (name: string) => void;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly showSessionPickerOnStart: boolean | undefined;\n readonly coordinationBlocked: boolean;\n}\n\nexport interface IAppScreenState {\n readonly screens: IUseSideEffectsResult;\n readonly workspace: IAppWorkspaceState;\n readonly statusSettings: IStatusLineCommandSettings;\n /** SCREEN-2002: the persisted appearance, re-read after every slash-command result. */\n readonly appearance: IAppearanceSettings;\n /** SCREEN-1993: the input area's history-search overlay is open and owns the keys. */\n readonly historySearchOpen: boolean;\n readonly setHistorySearchOpen: (open: boolean) => void;\n}\n\nexport function useAppScreenState(options: IOptions): IAppScreenState {\n const [workspaceSwitcherVisible, setWorkspaceSwitcherVisible] = useState(false);\n const [historySearchOpen, setHistorySearchOpen] = useState(false);\n const [statusSettings, refreshStatusSettings] = useStatusLineSettings();\n const [appearance, refreshAppearance] = useAppearanceSettings();\n const screens = useSideEffects({\n uiEventPort: options.state.uiEventPort,\n baseHandleSubmit: options.state.handleSubmit,\n setSessionName: options.setSessionName,\n refreshStatusLineSettings: refreshStatusSettings,\n refreshAppearanceSettings: refreshAppearance,\n showSessionPickerOnStart: options.showSessionPickerOnStart,\n openAgentSwitcher: () => setWorkspaceSwitcherVisible(true),\n });\n const navigationEnabled =\n options.state.permissionRequest === null &&\n options.state.pendingUserAction === null &&\n !screens.showPluginTUI &&\n !screens.showTransportTUI &&\n !screens.showSessionPicker &&\n !screens.showThemePicker &&\n !workspaceSwitcherVisible &&\n !historySearchOpen &&\n !options.coordinationBlocked;\n const workspace = useAppWorkspaceState({\n snapshot: options.state.executionWorkspaceSnapshot,\n selectedEntryId: options.state.selectedExecutionEntryId,\n select: options.state.selectExecutionWorkspaceEntry,\n read: options.state.readExecutionWorkspaceDetail,\n sessionStore: options.sessionStore,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.state.addEntry,\n navigationEnabled,\n switcherVisible: workspaceSwitcherVisible,\n setSwitcherVisible: setWorkspaceSwitcherVisible,\n });\n return {\n screens,\n workspace,\n statusSettings,\n appearance,\n historySearchOpen,\n setHistorySearchOpen,\n };\n}\n","/**\n * SCREEN-2002 — the theme catalogue, and the port the command layer asks through.\n *\n * The registry is the only thing that turns an ID into a theme. Work unit 1 put the built-ins\n * behind `listBuiltInThemes()`; work unit 3 adds user and plugin themes to the same list, and\n * nothing downstream has to learn where a theme came from — a row carries its `source` so the\n * picker and `/theme list` can SAY where it came from, which is a different question.\n *\n * An unknown id resolves to the default and reports the id it could not find. Resolving silently\n * would make a removed plugin look like a theme that stopped working, and the id is the only thing\n * that can name the cause.\n */\nimport { DEFAULT_THEME_ID, listBuiltInThemes, resolveTheme } from './built-in-themes.js';\n\n// The registry's default source, re-exported from the module that USES it. The package's public\n// surface takes this route rather than the theme barrel: the barrel re-exports `theme-context.tsx`,\n// and a public re-export chain that reaches a `.tsx` is what `sdk-public-surface` refuses.\nexport { listBuiltInThemes } from './built-in-themes.js';\n\nimport type { ITuiTheme } from './theme-contracts.js';\nimport type {\n IThemeAppearanceState,\n IThemeCatalogueEntry,\n IThemeCataloguePort,\n TReducedMotionOverride,\n} from '@robota-sdk/agent-interface-command';\n\n/**\n * A theme file that was found and refused. It is carried BESIDE the themes rather than among them,\n * because a surface must be able to show the file without ever resolving to it — an id that can be\n * listed and cannot be applied is the shape of the bug this avoids.\n */\nexport interface IThemeSkip {\n /**\n * The id the file WOULD have had, so a `/theme <id>` that fails has a visible reason — or, when\n * the file's own NAME is what made an id impossible, the quoted name in its place. It is a label,\n * not a key: two files can carry the same one.\n */\n readonly id: string;\n readonly fileName: string;\n /** The path-named diagnostic, verbatim from `parseThemeDocument`. */\n readonly reason: string;\n}\n\nexport interface IThemeResolution {\n readonly theme: ITuiTheme;\n /** The id that was asked for and not found. Absent ⇒ the request was satisfied. */\n readonly unknownId?: string;\n}\n\nexport interface IThemeRegistry {\n list(): readonly ITuiTheme[];\n get(id: string): ITuiTheme | undefined;\n /** The theme for an id, falling back to the default and NAMING what it could not find. */\n resolve(id: string | undefined): IThemeResolution;\n /** The theme files that were found and refused, with the reason each was refused for. */\n skipped(): readonly IThemeSkip[];\n}\n\nexport function createThemeRegistry(\n themes: readonly ITuiTheme[] = listBuiltInThemes(),\n skipped: readonly IThemeSkip[] = [],\n): IThemeRegistry {\n const byId = new Map(themes.map((theme) => [theme.id, theme]));\n return {\n list: () => themes,\n skipped: () => skipped,\n get: (id) => byId.get(id),\n resolve: (id) => {\n if (id === undefined || id === DEFAULT_THEME_ID) {\n return { theme: resolveTheme(byId.get(DEFAULT_THEME_ID)) };\n }\n const found = byId.get(id);\n if (found) return { theme: found };\n return { theme: resolveTheme(byId.get(DEFAULT_THEME_ID)), unknownId: id };\n },\n };\n}\n\n/** The line a run prints once when its persisted theme is not installed. */\nexport function formatUnknownThemeNotice(id: string): string {\n return `Theme \"${id}\" is not installed — using \"${DEFAULT_THEME_ID}\".`;\n}\n\nfunction toEntry(theme: ITuiTheme): IThemeCatalogueEntry {\n return {\n id: theme.id,\n name: theme.name,\n appearance: theme.appearance,\n source: theme.source,\n };\n}\n\nexport interface IThemeCataloguePortOptions {\n readonly registry: IThemeRegistry;\n /**\n * Re-read on every call, not captured once: `/theme` reports what is PERSISTED, and the host\n * applies a patch by writing the settings document. A snapshot would answer with the appearance\n * the process started with, so the command would report the change it just made as not having\n * happened.\n */\n readonly readAppearance: () => IThemeAppearanceState['settings'];\n /**\n * The pin on reduced motion for this run, when something above the settings applied one — WHICH\n * tier and WHAT it pinned, as one value. Two optional fields would let a caller supply the tier\n * alone, and a default for the missing half is exactly the wrong answer this pair exists to stop:\n * `--no-reduced-motion` is an override too and pins the opposite value.\n */\n readonly reducedMotionPin?:\n { readonly tier: TReducedMotionOverride; readonly reducedMotion: boolean } | undefined;\n}\n\nexport function createThemeCataloguePort(options: IThemeCataloguePortOptions): IThemeCataloguePort {\n return {\n listThemes: () => options.registry.list().map(toEntry),\n getTheme: (id) => {\n const theme = options.registry.get(id);\n return theme ? toEntry(theme) : undefined;\n },\n getAppearance: () => ({\n settings: options.readAppearance(),\n ...(options.reducedMotionPin === undefined\n ? {}\n : { reducedMotionPin: options.reducedMotionPin }),\n }),\n };\n}\n","import { useCallback, useMemo, useState } from 'react';\n\nimport { createThemeRegistry, formatUnknownThemeNotice } from '../theme/theme-registry.js';\n\nimport type { ITuiTheme } from '../theme/theme-contracts.js';\nimport type { IThemeRegistry, IThemeSkip } from '../theme/theme-registry.js';\nimport type {\n IAppearanceSettings,\n IThemeAppearanceState,\n TReducedMotionOverride,\n} from '@robota-sdk/agent-interface-command';\n\n/** The tier that pinned motion for this run and what it pinned — the contract's own pair. */\ntype IReducedMotionPin = NonNullable<IThemeAppearanceState['reducedMotionPin']>;\n\nexport interface IThemeToggles {\n readonly syntaxHighlighting: boolean;\n readonly reducedMotion: boolean;\n}\n\nexport interface IAppThemePickerViewModel {\n readonly visible: boolean;\n readonly themes: readonly ITuiTheme[];\n /** Theme files that were found and refused — shown as rows that cannot be chosen. */\n readonly skipped: readonly IThemeSkip[];\n /** The id that is PERSISTED — the row the picker returns to when it is cancelled. */\n readonly activeThemeId: string;\n /** The PERSISTED toggles the picker seeds itself from and submits alongside the theme. */\n readonly syntaxHighlighting: boolean;\n readonly reducedMotion: boolean;\n /**\n * Set when a tier above the settings pinned motion for this run — the tier AND what it pinned,\n * as one value, for the reason `IThemeAppearanceState` states: the tier alone cannot say which\n * way a run went.\n */\n readonly reducedMotionPin?: IReducedMotionPin | undefined;\n /** Move the highlight: the live region re-renders in that theme, nothing is written. */\n readonly preview: (id: string) => void;\n /**\n * Commit: submits `/theme <id> syntax <on|off> motion <on|off>` — ONE patch through the command\n * path, because the HOST owns writing the settings document. Three separate submissions would be\n * three chances to half-apply what the user saw as one decision.\n */\n readonly select: (id: string, toggles: IThemeToggles) => void;\n /** Leave: the preview is dropped and the persisted theme is what remains. */\n readonly cancel: () => void;\n}\n\nexport interface IAppThemeViewModel {\n /** The theme every component renders with right now — the preview when one is open. */\n readonly resolved: ITuiTheme;\n /** Whether motion is suppressed for this run, after the flag/env/settings chain. */\n readonly reducedMotion: boolean;\n /** Whether fenced code blocks are highlighted. Every markdown render site reads this. */\n readonly syntaxHighlighting: boolean;\n /** Present once, when the persisted id names a theme this build does not have. */\n readonly unknownThemeNotice?: string;\n readonly picker: IAppThemePickerViewModel;\n}\n\ninterface IOptions {\n readonly appearance: IAppearanceSettings;\n readonly registry?: IThemeRegistry | undefined;\n readonly reducedMotion?: boolean | undefined;\n /** Which tier decided it, when that was not the settings. */\n readonly reducedMotionOverride?: TReducedMotionOverride | undefined;\n readonly visible: boolean;\n readonly setVisible: (visible: boolean) => void;\n readonly submit: (input: string) => void;\n}\n\n/**\n * SCREEN-2002 — which theme the live region renders with, and the picker over it.\n *\n * Two inputs, one answer. The PERSISTED appearance is the baseline; a preview is a local overlay\n * that exists only while the picker is open. Selecting does not write — it submits `/theme <id>`,\n * so the host applies the patch and the refresh-on-result path re-reads it. That keeps ONE writer\n * for the settings document and means the picker cannot persist something `/theme` would refuse.\n *\n * `reducedMotion` arrives already resolved from the CLI (settings ← env ← flag). It is not\n * recomputed here, because a second place deciding the same question is how the two start\n * disagreeing — and it is still not the final say: `useMotion()` gates on the colour gate and\n * screen-reader mode above it.\n */\nexport function useAppThemeState(options: IOptions): IAppThemeViewModel {\n const registry = useMemo(() => options.registry ?? createThemeRegistry(), [options.registry]);\n const [previewId, setPreviewId] = useState<string | undefined>(undefined);\n const resolution = useMemo(\n () => registry.resolve(previewId ?? options.appearance.theme),\n [registry, previewId, options.appearance.theme],\n );\n\n const { setVisible, submit } = options;\n const cancel = useCallback((): void => {\n setPreviewId(undefined);\n setVisible(false);\n }, [setVisible]);\n const { syntaxHighlighting, reducedMotion } = options.appearance;\n const select = useCallback(\n (id: string, toggles: IThemeToggles): void => {\n setPreviewId(undefined);\n setVisible(false);\n const parts = [`/theme ${id}`];\n // Only what the user actually CHANGED. Sending all three would make choosing a theme report\n // \"syntax highlighting on, motion on\" as applied to someone who touched neither — and on a\n // run with a reduced-motion flag it would also drag out the pinned-for-this-run notice for a\n // setting they never asked about.\n if (toggles.syntaxHighlighting !== syntaxHighlighting) {\n parts.push(`syntax ${toggles.syntaxHighlighting ? 'on' : 'off'}`);\n }\n if (toggles.reducedMotion !== reducedMotion) {\n // `motion on` means ANIMATE, which is `reducedMotion: false` — the inversion lives in the\n // command's parser, and this is the one place that has to speak its vocabulary.\n parts.push(`motion ${toggles.reducedMotion ? 'off' : 'on'}`);\n }\n submit(parts.join(' '));\n },\n [reducedMotion, setVisible, submit, syntaxHighlighting],\n );\n\n // A preview of an unknown id is impossible (the rows come from the registry), so the notice can\n // only be about what is PERSISTED — and it is suppressed while previewing so it does not read as\n // a complaint about the row the user is looking at.\n const unknownId = previewId === undefined ? resolution.unknownId : undefined;\n // One expression for \"what motion IS for this run\", read by both the view model and the picker:\n // two copies of the same `??` chain are two things to keep in step.\n const reducedMotionForRun = options.reducedMotion ?? options.appearance.reducedMotion;\n return {\n resolved: resolution.theme,\n reducedMotion: reducedMotionForRun,\n syntaxHighlighting: options.appearance.syntaxHighlighting,\n ...(unknownId === undefined ? {} : { unknownThemeNotice: formatUnknownThemeNotice(unknownId) }),\n picker: {\n visible: options.visible,\n themes: registry.list(),\n skipped: registry.skipped(),\n activeThemeId: options.appearance.theme,\n syntaxHighlighting: options.appearance.syntaxHighlighting,\n reducedMotion: options.appearance.reducedMotion,\n // Both halves or neither. A tier with no resolved value would have to be paired with the\n // PERSISTED one, which is the contradiction this pair exists to stop — so a caller that\n // supplies only the tier gets no pin rather than a wrong one, and the row simply says\n // nothing about a pin instead of naming the wrong direction.\n ...(options.reducedMotionOverride === undefined || options.reducedMotion === undefined\n ? {}\n : {\n reducedMotionPin: {\n tier: options.reducedMotionOverride,\n reducedMotion: options.reducedMotion,\n },\n }),\n preview: setPreviewId,\n select,\n cancel,\n },\n };\n}\n","/**\n * useTuiChannel — React hook that subscribes to the bounded TUI channel port.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\n\nimport type {\n ITuiAppChannelPort,\n ITuiCommandQueryPort,\n ITuiRuntimeStatusSnapshot,\n ITuiSessionUiEventPort,\n} from '../tui-app-channel-port.js';\nimport type { ITuiSessionEventNotice } from '../tui-session-events.js';\nimport type { IPendingPermissionRequest } from '../types.js';\nimport type {\n IActionRequest,\n IHistoryEntry,\n TActionResponse,\n TPermissionMode,\n TSessionEndReason,\n} from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiChannelState {\n uiEventPort: ITuiSessionUiEventPort;\n commandQueryPort: ITuiCommandQueryPort;\n history: readonly IHistoryEntry[];\n addEntry: (entry: IHistoryEntry) => void;\n streamingText: string;\n activeTools: readonly IToolState[];\n isThinking: boolean;\n isAborting: boolean;\n /** ERR-001 G2: humanized message of the last failed turn (null once the next turn starts). */\n lastErrorMessage: string | null;\n /** ERR-001 G3: no provider activity for a while during a turn — connection may be stalled. */\n isStalled: boolean;\n sessionEventNotices: readonly ITuiSessionEventNotice[];\n isShuttingDown: boolean;\n pendingPrompt: string | null;\n /** REMOTE-014 E5: total queued turns across all drivers (owner + co-drivers); >1 means a co-driver is queued. */\n pendingCount: number;\n executionWorkspaceSnapshot: IExecutionWorkspaceSnapshot | null;\n selectedExecutionEntryId?: string;\n permissionRequest: IPendingPermissionRequest | null;\n /** CMD-004: the unified action awaiting a user answer, or null. */\n pendingUserAction: IActionRequest | null;\n contextState: { percentage: number; usedTokens: number; maxTokens: number };\n handleSubmit: (input: string) => Promise<void>;\n handleAbort: () => void;\n handleCancelQueue: () => void;\n handleStopWaitingLoop: () => Promise<void>;\n handleShutdown: (reason?: TSessionEndReason) => Promise<void>;\n sendAgentJob: (taskId: string, input: string) => Promise<void>;\n resolveUserAction: (request: IActionRequest, response: TActionResponse) => void;\n getRuntimeStatusSnapshot: (fallback: TPermissionMode) => ITuiRuntimeStatusSnapshot;\n selectExecutionWorkspaceEntry: (entryId: string) => void;\n readExecutionWorkspaceDetail: (entryId: string) => Promise<IExecutionDetailPage>;\n}\n\ninterface IHistoryReadableSession {\n getFullHistory(): IHistoryEntry[];\n}\n\ninterface IHistorySyncManager {\n syncHistory(entries: IHistoryEntry[]): void;\n}\n\nexport function applyCompactEventToManager(\n interactiveSession: IHistoryReadableSession,\n manager: IHistorySyncManager,\n): void {\n manager.syncHistory(interactiveSession.getFullHistory());\n}\n\nexport function useTuiChannel(channel: ITuiAppChannelPort): ITuiChannelState {\n const [, forceRender] = useState(0);\n\n useEffect(() => channel.subscribe(() => forceRender((n) => n + 1)), [channel]);\n const snapshot = channel.getSnapshot();\n\n // SCREEN-014 fix: these are consumed in `useEffect` dependency arrays in App. `channel` is stable\n // (created once), so memoize them — a fresh closure each render made the detail-loading effect\n // re-run every render and `setState`-loop (\"Maximum update depth exceeded\") whenever a background\n // entry was selected.\n const selectExecutionWorkspaceEntry = useCallback(\n (id: string) => channel.selectExecutionWorkspaceEntry(id),\n [channel],\n );\n const readExecutionWorkspaceDetail = useCallback(\n (id: string) => channel.readExecutionWorkspaceDetail(id),\n [channel],\n );\n\n return {\n uiEventPort: channel.getSessionUiEventPort(),\n commandQueryPort: channel.getCommandQueryPort(),\n history: snapshot.history,\n addEntry: (entry) => channel.addEntry(entry),\n streamingText: snapshot.streamingText,\n activeTools: snapshot.activeTools,\n isThinking: snapshot.isThinking,\n isAborting: snapshot.isAborting,\n lastErrorMessage: snapshot.lastErrorMessage,\n isStalled: snapshot.isStalled,\n sessionEventNotices: snapshot.sessionEventNotices,\n isShuttingDown: snapshot.isShuttingDown,\n pendingPrompt: snapshot.pendingPrompt,\n pendingCount: snapshot.pendingCount,\n executionWorkspaceSnapshot: snapshot.executionWorkspaceSnapshot,\n selectedExecutionEntryId: snapshot.selectedExecutionEntryId,\n permissionRequest: snapshot.permissionRequest,\n pendingUserAction: snapshot.pendingUserAction,\n contextState: snapshot.contextState,\n handleSubmit: (input) => channel.handleInput(input),\n handleAbort: () => channel.abort(),\n handleCancelQueue: () => channel.cancelQueue(),\n handleStopWaitingLoop: () => channel.stopWaitingSelfPacedLoop(),\n handleShutdown: (reason) => channel.shutdown({ reason }),\n sendAgentJob: (taskId, input) => channel.sendAgentJob(taskId, input),\n resolveUserAction: (request, response) => channel.resolveUserAction(request, response),\n getRuntimeStatusSnapshot: (fallback) => channel.getRuntimeStatusSnapshot(fallback),\n selectExecutionWorkspaceEntry,\n readExecutionWorkspaceDetail,\n };\n}\n","import { useCallback, useMemo, useState } from 'react';\n\nimport { buildStaticItems } from '../app-static-items.js';\nimport { useAppInteractionState } from './useAppInteractionState.js';\nimport { useAppLifecycleState } from './useAppLifecycleState.js';\nimport { useAppScreenState } from './useAppScreenState.js';\nimport { useAppThemeState } from './useAppThemeState.js';\nimport { useTuiChannel } from './useTuiChannel.js';\n\nimport type { IBuildStaticItemsInputs } from '../app-static-items.js';\nimport type { IAppInputViewModel, IAppStatusViewModel, IAppViewModel } from '../app-view-model.js';\nimport type { ITuiAppChannelPort, ITuiRuntimeStatusSnapshot } from '../tui-app-channel-port.js';\nimport type { IAppInteractionState } from './useAppInteractionState.js';\nimport type { IAppLifecycleState } from './useAppLifecycleState.js';\nimport type { IAppScreenState } from './useAppScreenState.js';\nimport type { IAppThemeViewModel } from './useAppThemeState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type { IThemeRegistry } from '../theme/theme-registry.js';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { IPromptHistorySource } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\nexport interface IUseAppControllerOptions {\n cwd: string;\n providerType?: string | undefined;\n modelId?: string;\n permissionMode?: TPermissionMode;\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: a deep link's prefill, handed to the composer exactly once. */\n initialInput?: string;\n initialInputOrigin?: 'external-link';\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n pluginAdapter?: ICommandPluginAdapter;\n sessionSwitchError?: string;\n sessionSwitchPending?: boolean;\n channel: ITuiAppChannelPort;\n onSessionSwitch: (sessionId: string) => Promise<void>;\n onRetrySessionSwitch: () => void;\n /** SCREEN-1993: the stored-prompt source and this run's project key; both or neither. */\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n /** SCREEN-2002: the catalogue this surface renders from; absent ⇒ the built-ins. */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n}\n\ninterface ICoordinationState {\n readonly error: string | undefined;\n readonly pending: boolean;\n readonly blocked: boolean;\n}\n\ninterface IComposition {\n readonly props: IUseAppControllerOptions;\n readonly state: ITuiChannelState;\n readonly lifecycle: IAppLifecycleState;\n readonly shell: IAppScreenState;\n readonly interaction: IAppInteractionState;\n readonly staticItems: IAppViewModel['staticItems'];\n readonly runtime: ITuiRuntimeStatusSnapshot;\n readonly coordination: ICoordinationState;\n readonly theme: IAppThemeViewModel;\n}\n\nfunction getCoordinationState(\n props: IUseAppControllerOptions,\n lifecycle: IAppLifecycleState,\n): ICoordinationState {\n const error = props.sessionSwitchError ?? lifecycle.startError;\n const pending = Boolean(props.sessionSwitchPending || lifecycle.startPending);\n return { error, pending, blocked: error !== undefined || pending };\n}\n\nfunction buildHistorySearch(composition: IComposition): IAppInputViewModel['historySearch'] {\n const { promptHistorySource, promptHistoryProject } = composition.props;\n if (promptHistorySource === undefined || promptHistoryProject === undefined) return undefined;\n return {\n source: promptHistorySource,\n project: promptHistoryProject,\n sessionId: composition.runtime.sessionId,\n onOpenChange: composition.shell.setHistorySearchOpen,\n };\n}\n\nfunction buildInput(composition: IComposition): IAppInputViewModel {\n const { screens, workspace } = composition.shell;\n const state = composition.state;\n const overlayOpen = Boolean(\n state.permissionRequest ||\n state.pendingUserAction ||\n screens.showPluginTUI ||\n screens.showTransportTUI ||\n screens.showSessionPicker ||\n screens.showThemePicker ||\n workspace.background.switcherVisible,\n );\n const interactionBlocked =\n composition.coordination.blocked ||\n overlayOpen ||\n state.isShuttingDown ||\n !workspace.isSelectedEntryInteractive ||\n workspace.isBackgroundListFocused;\n return {\n submit: composition.interaction.submission.submit,\n cancelQueue: state.handleCancelQueue,\n disabled: interactionBlocked || (state.isThinking && state.pendingPrompt !== null),\n queueCancellationDisabled: interactionBlocked,\n isAborting: state.isAborting,\n pendingPrompt: state.pendingPrompt,\n pendingCount: state.pendingCount,\n commandQueryPort: state.commandQueryPort,\n sessionName: composition.lifecycle.sessionName,\n history: state.history,\n focusBackgroundList: workspace.focusBackgroundList,\n historySearch: buildHistorySearch(composition),\n };\n}\n\nfunction buildStatus(composition: IComposition): IAppStatusViewModel {\n const { props, runtime, state } = composition;\n const workspace = composition.shell.workspace;\n return {\n cwd: props.cwd,\n permissionMode: runtime.permissionMode,\n modelId: props.modelId,\n providerType: props.providerType,\n sessionId: runtime.sessionId,\n isThinking: state.isThinking,\n activeToolCount: state.activeTools.length,\n activeBackgroundTaskCount: workspace.activeBackgroundTaskCount,\n hasPendingPrompt: state.pendingPrompt !== null,\n contextState: state.contextState,\n sessionName: composition.lifecycle.sessionName,\n settings: composition.shell.statusSettings,\n activeAgentLabel: workspace.activeAgentLabel,\n activePresetId: runtime.activePresetId,\n effort: runtime.effort,\n gitRefreshToken: composition.interaction.submission.gitRefreshToken,\n };\n}\n\nfunction buildViewModel(composition: IComposition): IAppViewModel {\n const { interaction, lifecycle, shell, state } = composition;\n return {\n staticItems: composition.staticItems,\n handoffSuspended: lifecycle.handoffSuspended,\n updateNotice: lifecycle.updateNotice,\n coordinationError: composition.coordination.error,\n coordinationPending: composition.coordination.pending,\n sessionEventNotices: state.sessionEventNotices,\n isShuttingDown: state.isShuttingDown,\n stream: {\n text: state.streamingText,\n activeTools: state.activeTools,\n isThinking: state.isThinking,\n isStalled: state.isStalled,\n lastErrorMessage: state.lastErrorMessage,\n },\n background: shell.workspace.background,\n permissionRequest: state.permissionRequest,\n pendingUserAction: state.pendingUserAction,\n resolveUserAction: state.resolveUserAction,\n ...interaction.overlays,\n theme: composition.theme,\n contextPercentage: state.contextState.percentage,\n input: buildInput(composition),\n status: buildStatus(composition),\n };\n}\n\n/** The live theme and its picker, composed from the shell's persisted appearance and the shell's props. */\nfunction useControllerTheme(\n props: IUseAppControllerOptions,\n shell: IAppScreenState,\n): IAppThemeViewModel {\n return useAppThemeState({\n appearance: shell.appearance,\n registry: props.themeRegistry,\n reducedMotion: props.reducedMotion,\n reducedMotionOverride: props.reducedMotionOverride,\n visible: shell.screens.showThemePicker,\n setVisible: shell.screens.setShowThemePicker,\n submit: (input) => void shell.screens.handleSubmit(input),\n });\n}\n\n/**\n * FLOW-2006: the deep link's prefill is owned HERE, not by the composer's `useState` initializer.\n * `AppPresentation` unmounts the prompt subtree while a terminal handoff is suspended, so a\n * component-local initializer would re-seed a prompt the user had already cleared or submitted.\n */\nfunction useExternalPrefill(\n initialInput: string | undefined,\n origin: IUseAppControllerOptions['initialInputOrigin'],\n): Pick<IAppInputViewModel, 'initialValue' | 'consumeInitialValue' | 'externalPromptOrigin'> {\n const [initialValue, setInitialValue] = useState(initialInput);\n const consumeInitialValue = useCallback(() => setInitialValue(undefined), []);\n return { initialValue, consumeInitialValue, externalPromptOrigin: origin === 'external-link' };\n}\n\n/** The scrollback Ink commits once: the banner (unless suppressed) and the settled history. */\nfunction useStaticItems(\n history: IBuildStaticItemsInputs['history'],\n version: IBuildStaticItemsInputs['version'],\n screenReader: IBuildStaticItemsInputs['screenReader'],\n): IAppViewModel['staticItems'] {\n return useMemo(\n () => buildStaticItems({ history, version, screenReader }),\n [history, screenReader, version],\n );\n}\n\n/** Compose responsibility-specific hooks into the only view model accepted by presentation. */\nexport function useAppController(props: IUseAppControllerOptions): IAppViewModel {\n const prefill = useExternalPrefill(props.initialInput, props.initialInputOrigin);\n const state = useTuiChannel(props.channel);\n const lifecycle = useAppLifecycleState(props.channel, props.startupUpdateNotice);\n const coordination = getCoordinationState(props, lifecycle);\n const shell = useAppScreenState({\n state,\n setSessionName: lifecycle.setSessionName,\n sessionStore: props.sessionStore,\n onSessionSwitch: props.onSessionSwitch,\n showSessionPickerOnStart: props.showSessionPickerOnStart,\n coordinationBlocked: coordination.blocked,\n });\n const interaction = useAppInteractionState({\n cwd: props.cwd,\n state,\n shell,\n sessionStore: props.sessionStore,\n onSessionSwitch: props.onSessionSwitch,\n pluginAdapter: props.pluginAdapter,\n transportRegistry: props.transportRegistry,\n recoveryError: coordination.error,\n recoveryPending: coordination.pending,\n coordinationBlocked: coordination.blocked,\n retryRecovery:\n props.sessionSwitchError !== undefined ? props.onRetrySessionSwitch : lifecycle.retryStart,\n });\n const staticItems = useStaticItems(state.history, props.version, interaction.screenReader);\n const runtime = state.getRuntimeStatusSnapshot(props.permissionMode ?? 'default');\n const theme = useControllerTheme(props, shell);\n const viewModel = buildViewModel({\n props,\n state,\n lifecycle,\n shell,\n interaction,\n staticItems,\n runtime,\n coordination,\n theme,\n });\n return { ...viewModel, input: { ...viewModel.input, ...prefill } };\n}\n\nexport type { IAppViewModel } from '../app-view-model.js';\n","import React from 'react';\n\nimport AppPresentation from './AppPresentation.js';\nimport { useAppController } from './hooks/useAppController.js';\nimport { ThemeProvider } from './theme/index.js';\n\nimport type { IUseAppControllerOptions } from './hooks/useAppController.js';\n\nexport type IAppViewProps = IUseAppControllerOptions;\n\n/** Controller boundary: the channel terminates here and only a view model reaches presentation. */\nexport default function AppView(props: IAppViewProps): React.ReactElement {\n const viewModel = useAppController(props);\n // SCREEN-2002: the provider sits HERE rather than above the controller, because the resolved\n // theme is part of the view model — a provider higher up would have to be fed a theme nobody had\n // resolved yet, and the controller's own hooks read no colours.\n return (\n <ThemeProvider\n theme={viewModel.theme.resolved}\n reducedMotion={viewModel.theme.reducedMotion}\n syntaxHighlighting={viewModel.theme.syntaxHighlighting}\n >\n <AppPresentation viewModel={viewModel} />\n </ThemeProvider>\n );\n}\n","import React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport AppView from './AppView.js';\nimport { TuiCliAdapterProvider } from './tui-cli-adapter-context.js';\n\nimport type { IThemeRegistry } from './theme/theme-registry.js';\nimport type { ITuiAppChannelPort } from './tui-app-channel-port.js';\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { IPromptHistorySource } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IProps {\n cwd: string;\n /** The composition root narrows every concrete channel to this port before React receives it. */\n createChannel: (resumeSessionId?: string) => ITuiAppChannelPort;\n providerOverride?: string | undefined;\n providerType?: string | undefined;\n modelId?: string;\n permissionMode?: TPermissionMode;\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n resumeSessionId?: string;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: a deep link's prefill, consumed once by the composer. */\n initialInput?: string;\n initialInputOrigin?: 'external-link';\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n pluginAdapter?: ICommandPluginAdapter;\n cliAdapter: ITuiCliAdapter;\n /** SCREEN-1993: the stored-prompt source and project key for the input area's search. */\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n /** SCREEN-2002: the theme catalogue this run renders from; absent ⇒ the built-ins. */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n}\n\ninterface IActiveChannel {\n readonly state: { channel: ITuiAppChannelPort; sessionId: string | undefined };\n readonly showPicker: boolean;\n readonly error: string | undefined;\n readonly switching: boolean;\n readonly switchSession: (sessionId: string) => Promise<void>;\n readonly retrySwitch: () => void;\n}\n\nfunction useActiveChannel(props: IProps): IActiveChannel {\n const [state, setState] = useState(() => ({\n channel: props.createChannel(props.resumeSessionId),\n sessionId: props.resumeSessionId,\n }));\n const [showPicker, setShowPicker] = useState(props.showSessionPickerOnStart ?? false);\n const [error, setError] = useState<string | undefined>();\n const [isSwitching, setIsSwitching] = useState(false);\n const [failedTarget, setFailedTarget] = useState<string | undefined>();\n const switching = useRef(false);\n const mounted = useRef(true);\n useEffect(\n () => () => {\n mounted.current = false;\n },\n [],\n );\n const switchSession = useCallback(\n async (sessionId: string): Promise<void> => {\n if (switching.current) return;\n switching.current = true;\n setIsSwitching(true);\n try {\n setShowPicker(false);\n await state.channel.stop();\n if (!mounted.current) return;\n setState({ channel: props.createChannel(sessionId), sessionId });\n setFailedTarget(undefined);\n setError(undefined);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n setError(`Session switch failed: ${message}`);\n setFailedTarget(sessionId);\n } finally {\n switching.current = false;\n setIsSwitching(false);\n }\n },\n [props.createChannel, state.channel],\n );\n const retrySwitch = useCallback((): void => {\n if (failedTarget !== undefined) void switchSession(failedTarget);\n }, [failedTarget, switchSession]);\n return { state, showPicker, error, switching: isSwitching, switchSession, retrySwitch };\n}\n\n/** React composition shell. Concrete framework objects stay outside the React tree. */\nexport default function App(props: IProps): React.ReactElement {\n const active = useActiveChannel(props);\n\n return (\n <TuiCliAdapterProvider value={props.cliAdapter}>\n <AppView\n key={active.state.sessionId ?? '__new__'}\n cwd={props.cwd}\n channel={active.state.channel}\n onSessionSwitch={active.switchSession}\n onRetrySessionSwitch={active.retrySwitch}\n sessionSwitchError={active.error}\n sessionSwitchPending={active.switching}\n showSessionPickerOnStart={active.showPicker}\n {...(props.initialInput !== undefined ? { initialInput: props.initialInput } : {})}\n {...(props.initialInputOrigin !== undefined\n ? { initialInputOrigin: props.initialInputOrigin }\n : {})}\n {...(props.providerType !== undefined ? { providerType: props.providerType } : {})}\n {...(props.modelId !== undefined ? { modelId: props.modelId } : {})}\n {...(props.permissionMode !== undefined ? { permissionMode: props.permissionMode } : {})}\n {...(props.version !== undefined ? { version: props.version } : {})}\n {...(props.sessionStore !== undefined ? { sessionStore: props.sessionStore } : {})}\n {...(props.startupUpdateNotice !== undefined\n ? { startupUpdateNotice: props.startupUpdateNotice }\n : {})}\n {...(props.transportRegistry !== undefined\n ? { transportRegistry: props.transportRegistry }\n : {})}\n {...(props.pluginAdapter !== undefined ? { pluginAdapter: props.pluginAdapter } : {})}\n {...(props.promptHistorySource !== undefined\n ? { promptHistorySource: props.promptHistorySource }\n : {})}\n {...(props.promptHistoryProject !== undefined\n ? { promptHistoryProject: props.promptHistoryProject }\n : {})}\n {...(props.themeRegistry !== undefined ? { themeRegistry: props.themeRegistry } : {})}\n {...(props.reducedMotion !== undefined ? { reducedMotion: props.reducedMotion } : {})}\n {...(props.reducedMotionOverride !== undefined\n ? { reducedMotionOverride: props.reducedMotionOverride }\n : {})}\n />\n </TuiCliAdapterProvider>\n );\n}\n","/**\n * SCREEN-1992: whether the user is at the terminal.\n *\n * Two sources feed one boolean. Focus reporting (DECSET 1004) was REQUESTED, not confirmed — a\n * terminal that does not implement the mode discards the request and never answers — so focus\n * becomes authoritative only once the first `CSI I`/`CSI O` has actually arrived; from then on\n * keystrokes carry no attention meaning. Until then, and wherever the mode is off, the only signal\n * is input: silence past the idle threshold means \"away\", the next keystroke means \"back\".\n */\nimport { MS_PER_MINUTE } from './time-units.js';\n\nconst DEFAULT_IDLE_THRESHOLD_MINUTES = 5;\nexport const DEFAULT_IDLE_THRESHOLD_MS = DEFAULT_IDLE_THRESHOLD_MINUTES * MS_PER_MINUTE;\n\nexport type TAttentionChangeKind = 'lost' | 'returned';\n\nexport interface IAttentionChange {\n readonly kind: TAttentionChangeKind;\n /** ISO timestamp of the transition, from the injected clock. */\n readonly at: string;\n}\n\nexport type TAttentionListener = (change: IAttentionChange) => void;\n\n/** What a consumer of attention sees: the level, and every transition. */\nexport interface IAttentionSource {\n readonly attended: boolean;\n /** Which source decides the level right now — recorded so a degraded run is never a silent default. */\n readonly source: 'focus' | 'idle';\n subscribe(listener: TAttentionListener): () => void;\n}\n\nexport interface IAttentionTrackerOptions {\n /** Whether DECSET 1004 was requested; the first focus event is what makes it authoritative. */\n readonly focusReporting: boolean;\n readonly now: () => number;\n readonly idleThresholdMs?: number;\n}\n\nexport class AttentionTracker implements IAttentionSource {\n private attendedState = true;\n private focusObserved = false;\n private readonly listeners = new Set<TAttentionListener>();\n private idleTimer: ReturnType<typeof setTimeout> | undefined;\n private readonly idleThresholdMs: number;\n\n constructor(private readonly options: IAttentionTrackerOptions) {\n this.idleThresholdMs = options.idleThresholdMs ?? DEFAULT_IDLE_THRESHOLD_MS;\n }\n\n get attended(): boolean {\n return this.attendedState;\n }\n\n get source(): 'focus' | 'idle' {\n return this.focusObserved ? 'focus' : 'idle';\n }\n\n subscribe(listener: TAttentionListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Arms the idle fallback; the first focus event disarms it for good. */\n start(): void {\n if (this.focusObserved) return;\n this.armIdleTimer();\n }\n\n focusIn(): void {\n this.observeFocus();\n this.transition(true);\n }\n\n focusOut(): void {\n this.observeFocus();\n this.transition(false);\n }\n\n /** Any key the user pressed. Only the idle source reads it. */\n keystroke(): void {\n if (this.focusObserved) return;\n this.transition(true);\n this.armIdleTimer();\n }\n\n private observeFocus(): void {\n if (!this.options.focusReporting) {\n throw new Error('AttentionTracker: a focus event arrived although focus reporting is off.');\n }\n if (this.focusObserved) return;\n this.focusObserved = true;\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n }\n\n dispose(): void {\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n this.listeners.clear();\n }\n\n private armIdleTimer(): void {\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = setTimeout(() => {\n this.idleTimer = undefined;\n this.transition(false);\n }, this.idleThresholdMs);\n // Never keep the process alive for an attention timer.\n if (typeof this.idleTimer === 'object' && 'unref' in this.idleTimer) this.idleTimer.unref();\n }\n\n private transition(attended: boolean): void {\n if (this.attendedState === attended) return;\n this.attendedState = attended;\n const change: IAttentionChange = {\n kind: attended ? 'returned' : 'lost',\n at: new Date(this.options.now()).toISOString(),\n };\n for (const listener of this.listeners) listener(change);\n }\n}\n","import { Readable } from 'node:stream';\n\n/**\n * SCREEN-1992: the stdin proxy handed to Ink's `render({ stdin })`.\n *\n * Ink tokenizes `ESC [ I` as a keypress and every `useInput` listener would receive `[I`, so the\n * composer would type it. The proxy sits in front of the real stdin, strips the two focus sequences\n * while mode 1004 is negotiated, reports focus and keystrokes, and forwards every other byte\n * unchanged — including a split escape sequence and a bracketed paste, which are Ink's to parse.\n */\nconst ESC = 0x1b;\nconst FOCUS_IN = '\\x1b[I';\nconst FOCUS_OUT = '\\x1b[O';\n\nexport interface IFocusInputFilterResult {\n /** The bytes Ink receives, byte-identical to the input apart from the stripped focus sequences. */\n readonly forwarded: Buffer;\n readonly focusIn: number;\n readonly focusOut: number;\n}\n\n/** Pure: split one chunk into focus events and the bytes that remain. */\nexport function filterFocusSequences(chunk: Buffer, negotiated: boolean): IFocusInputFilterResult {\n if (!negotiated || !chunk.includes(ESC)) return { forwarded: chunk, focusIn: 0, focusOut: 0 };\n const text = chunk.toString('latin1');\n let focusIn = 0;\n let focusOut = 0;\n let forwarded = '';\n let index = 0;\n while (index < text.length) {\n if (text.startsWith(FOCUS_IN, index)) {\n focusIn += 1;\n index += FOCUS_IN.length;\n } else if (text.startsWith(FOCUS_OUT, index)) {\n focusOut += 1;\n index += FOCUS_OUT.length;\n } else {\n forwarded += text[index];\n index += 1;\n }\n }\n return { forwarded: Buffer.from(forwarded, 'latin1'), focusIn, focusOut };\n}\n\nexport interface IFocusReportingStdinHooks {\n /** Whether mode 1004 is currently negotiated; read per chunk so a handoff can flip it. */\n readonly negotiated: () => boolean;\n readonly onFocusIn: () => void;\n readonly onFocusOut: () => void;\n /** Any byte that was not a focus sequence. */\n readonly onKeystroke: () => void;\n}\n\ntype TStdinDataListener = (chunk: Buffer | string) => void;\n\n/** What ink 7.1.1 reads from the stream it is given (`App.js` input handling, `ink.js` kitty probe). */\ntype TInkStdinSurface = Pick<\n Readable,\n 'read' | 'setEncoding' | 'unshift' | 'on' | 'addListener' | 'removeListener'\n> & {\n readonly isTTY: boolean;\n // `tty.ReadStream` types these as returning itself; Ink discards the return value.\n setRawMode(mode: boolean): TInkStdinSurface;\n ref(): TInkStdinSurface;\n unref(): TInkStdinSurface;\n};\n\n/** The subset of the real stdin the proxy touches (what `process.stdin` provides). */\nexport interface IFocusReportingStdinSource {\n readonly isTTY?: boolean;\n on(event: 'data', listener: TStdinDataListener): void;\n off(event: 'data', listener: TStdinDataListener): void;\n setRawMode?: (mode: boolean) => void;\n ref?: () => void;\n unref?: () => void;\n}\n\nexport class FocusReportingStdin extends Readable {\n private readonly onData = (data: Buffer | string): void => {\n const chunk = typeof data === 'string' ? Buffer.from(data) : data;\n const result = filterFocusSequences(chunk, this.hooks.negotiated());\n for (let i = 0; i < result.focusOut; i += 1) this.hooks.onFocusOut();\n for (let i = 0; i < result.focusIn; i += 1) this.hooks.onFocusIn();\n if (result.forwarded.length === 0) return;\n this.hooks.onKeystroke();\n this.push(result.forwarded);\n };\n\n constructor(\n private readonly source: IFocusReportingStdinSource,\n private readonly hooks: IFocusReportingStdinHooks,\n ) {\n super({ read() {} });\n source.on('data', this.onData);\n }\n\n get isTTY(): boolean {\n return this.source.isTTY === true;\n }\n\n setRawMode(mode: boolean): this {\n this.source.setRawMode?.(mode);\n return this;\n }\n\n ref(): this {\n this.source.ref?.();\n return this;\n }\n\n unref(): this {\n this.source.unref?.();\n return this;\n }\n\n /**\n * Ink declares `stdin?: NodeJS.ReadStream` (a `tty.ReadStream`, which cannot be constructed\n * without an fd) but touches only the members named in `TInkStdinSurface` — verified against ink\n * 7.1.1's App and Ink classes. Assigning `this` to that surface is a typed check that every one of\n * them exists here; the final narrowing is from the surface Ink reads to the type it declares.\n */\n asInkStdin(): NodeJS.ReadStream {\n const surface: TInkStdinSurface = this;\n return surface as NodeJS.ReadStream;\n }\n\n /** Detach from the underlying stdin; the proxy emits nothing afterwards. */\n detach(): void {\n this.source.off('data', this.onData);\n }\n}\n","/**\n * CLI-2004 — the first line the process prints.\n *\n * Two mutually exclusive lines, and never more than one:\n * - the mode is ON → `[Screen reader mode: on via flag|env|settings]`. It is the only\n * self-diagnostic in the design: when someone reports \"I passed the flag and nothing changed\",\n * this line says whether the mode is on and which input turned it on.\n * - the mode is OFF and the environment looks like a reader is present → one advisory line naming\n * the flag. That is the whole of this project's answer to the auto-detection question: opt-in\n * keeps determinism, and a false positive costs one line of text instead of reshaping a sighted\n * user's interface.\n *\n * Printed before Ink's first frame and never during a turn, so it cannot interleave with output a\n * reader is consuming.\n */\n\n/** Which input turned the mode on. Mirrors the CLI resolver's channel. */\nexport type TScreenReaderChannel = 'flag' | 'env' | 'settings';\n\nexport interface IScreenReaderAnnouncementInputs {\n enabled: boolean;\n channel?: TScreenReaderChannel | undefined;\n /** True when the mode is off but the environment suggests a reader is running. */\n hint?: boolean | undefined;\n}\n\n/** The confirmation / advisory line, or `undefined` when neither applies. Pure. */\nexport function screenReaderAnnouncement(\n inputs: IScreenReaderAnnouncementInputs,\n): string | undefined {\n if (inputs.enabled) {\n // A mode that is on always names a channel; `settings` is the lowest tier and the safe read\n // when a caller enables the mode without saying how.\n return `[Screen reader mode: on via ${inputs.channel ?? 'settings'}]`;\n }\n if (inputs.hint === true) {\n return '[Screen reader mode: off — run with --screen-reader]';\n }\n return undefined;\n}\n\n/** Write the line (with its newline) when there is one. Silent otherwise. */\nexport function writeScreenReaderAnnouncement(\n inputs: IScreenReaderAnnouncementInputs,\n write: (text: string) => void = (text) => {\n process.stdout.write(text);\n },\n): void {\n const line = screenReaderAnnouncement(inputs);\n if (line === undefined) return;\n write(`${line}\\n`);\n}\n","/**\n * CLI-2004 — the startup quiet period.\n *\n * A terminal has no `aria-live`. The ONLY way to influence what a reader announces is which bytes\n * land in the buffer and WHEN, which is why this wait exists at all: it lets the reader finish\n * speaking the confirmation line before the first prompt frame overwrites the region it is reading.\n * A keypress ends it early.\n *\n * THE PRE-WRITE PARK (SCREEN-2670) is the second wait § Decision verdict (i) adopted. It lives in\n * `screen-reader-stdout.ts`: an owned write path handed to Ink as `stdout`, which separates COMMITS\n * in time so a diff-based reader's snapshot can fall between two of them. This module only resolves\n * its interval. The default is PROVISIONAL: screen-reader mode runs Ink unthrottled, so no frame\n * interval derives it, and the governing timescale — the reader's own sampling cadence — is not\n * measurable from this harness. `50` matches the sole product precedent until it is measured\n * against a real reader (recorded as an open item in this package's SPEC).\n *\n * The DEFAULT IS MEASURED AGAINST THIS RENDER LOOP, not copied: `DEFAULT_STARTUP_QUIET_MS` is the\n * observed boot-to-first-prompt interval of this binary (~450 ms in the PTY harness) doubled, so the\n * confirmation line is spoken before the prompt lands without the reference's 3 s stall, which is\n * tuned to a heavier boot.\n *\n * The cap is a sanity bound, not policy: a value above it is clamped AND REPORTED. Nothing here\n * silently substitutes a number — an unparseable value is refused with a note on stderr and the\n * default stands (No-Fallback: the refusal is visible).\n */\n\n/** Sanity bound on the startup quiet period (10 minutes). */\nexport const STARTUP_QUIET_MS_MAX = 600_000;\n/** Boot-to-first-prompt of this binary, doubled. */\nexport const DEFAULT_STARTUP_QUIET_MS = 900;\n\n/** Sanity bound on the pre-write park (5 seconds), matching the product precedent verdict (i) adopted. */\nexport const PREPARK_MS_MAX = 5000;\n/** PROVISIONAL — taken to match the sole product precedent, pending measurement against a reader. */\nexport const DEFAULT_PREPARK_MS = 50;\n\n/** The resolved pacing. `0` whenever the mode is off. */\nexport interface IScreenReaderPacing {\n startupQuietMs: number;\n /** Interval parked in front of each printable commit after the first. `0` disables the park. */\n preparkMs: number;\n}\n\n/** Raw timing choices supplied by the embedding host, with names for visible diagnostics. */\nexport interface IScreenReaderPacingOverrides {\n readonly startupQuiet?: { readonly raw: string | undefined; readonly label: string };\n readonly prepark?: { readonly raw: string | undefined; readonly label: string };\n}\n\nexport interface IResolvePacingInputs {\n /** Screen-reader mode. Off ⇒ the wait is 0 regardless of host overrides. */\n enabled: boolean;\n /** Absent values use the renderer's numeric defaults; no environment is read here. */\n overrides?: IScreenReaderPacingOverrides;\n /** Where a clamp or a refusal is reported. Defaults to stderr. */\n warn?: (message: string) => void;\n}\n\nfunction reportTo(warn: IResolvePacingInputs['warn']): (message: string) => void {\n return (\n warn ??\n ((message: string): void => {\n process.stderr.write(`${message}\\n`);\n })\n );\n}\n\n/**\n * Read the duration variable. Absent ⇒ the default. Non-numeric or negative ⇒ the default WITH a\n * note (never a silent 0). Above the bound ⇒ the bound WITH a note. `0` is honoured exactly — it is\n * the documented way to ask for no wait at all.\n */\nfunction resolveDuration(\n raw: string | undefined,\n name: string,\n fallback: number,\n bound: number,\n warn: (message: string) => void,\n): number {\n if (raw === undefined || raw.trim() === '') return fallback;\n const parsed = Number(raw.trim());\n if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 0) {\n warn(\n `${name}: ignoring \"${raw}\" — expected a whole number of milliseconds. Using ${fallback}.`,\n );\n return fallback;\n }\n if (parsed > bound) {\n warn(`${name}: clamping ${parsed} to the ${bound} ms sanity bound.`);\n return bound;\n }\n return parsed;\n}\n\n/** Resolve the wait once, at startup. */\nexport function resolvePacing(inputs: IResolvePacingInputs): IScreenReaderPacing {\n if (!inputs.enabled) return { startupQuietMs: 0, preparkMs: 0 };\n const warn = reportTo(inputs.warn);\n return {\n startupQuietMs: resolveDuration(\n inputs.overrides?.startupQuiet?.raw,\n inputs.overrides?.startupQuiet?.label ?? 'startup quiet period',\n DEFAULT_STARTUP_QUIET_MS,\n STARTUP_QUIET_MS_MAX,\n warn,\n ),\n preparkMs: resolveDuration(\n inputs.overrides?.prepark?.raw,\n inputs.overrides?.prepark?.label ?? 'pre-write park',\n DEFAULT_PREPARK_MS,\n PREPARK_MS_MAX,\n warn,\n ),\n };\n}\n\n/** The one-shot keypress source that can settle the startup wait early. */\nexport interface IKeypressSource {\n once(event: 'data', listener: () => void): void;\n off(event: 'data', listener: () => void): void;\n resume?(): void;\n pause?(): void;\n /** Present on a TTY stdin. Without raw mode a lone key produces no `data` event. */\n isTTY?: boolean;\n setRawMode?(mode: boolean): void;\n}\n\n/**\n * Put a TTY stdin into raw mode for the duration of the wait, returning the undo.\n *\n * Without this the promise below only settles on Enter: in canonical mode the terminal buffers a\n * line, so \"any keypress ends the wait\" — which this package's SPEC states — would be false on a\n * real terminal and true only against a synthetic source. Ink sets raw mode itself once it renders;\n * this window closes before that, and restores whatever was set before.\n */\nfunction withRawMode(keys: IKeypressSource | undefined): () => void {\n if (keys?.isTTY !== true || typeof keys.setRawMode !== 'function') return (): void => {};\n keys.setRawMode(true);\n return (): void => {\n keys.setRawMode?.(false);\n };\n}\n\n/**\n * Wait out the startup quiet period. Resolves at the deadline, or as soon as a key is pressed —\n * whichever comes first. A `0` wait resolves without touching the keypress source at all.\n *\n * The keystroke that ends the wait is CONSUMED, which is the documented behaviour: the wait exists\n * so the reader is not interrupted, and the key that says \"I am ready\" is the answer to it.\n */\nexport async function awaitStartupQuietPeriod(\n ms: number,\n keys?: IKeypressSource,\n): Promise<'elapsed' | 'keypress'> {\n if (ms <= 0) return 'elapsed';\n const restoreRawMode = withRawMode(keys);\n return new Promise<'elapsed' | 'keypress'>((resolve) => {\n const settle = (outcome: 'elapsed' | 'keypress'): void => {\n restoreRawMode();\n keys?.pause?.();\n resolve(outcome);\n };\n const onKey = (): void => {\n clearTimeout(timer);\n settle('keypress');\n };\n const timer = setTimeout(() => {\n if (keys !== undefined) keys.off('data', onKey);\n settle('elapsed');\n }, ms);\n // Do not hold the process open on the pacing timer alone.\n timer.unref?.();\n if (keys !== undefined) {\n keys.once('data', onKey);\n keys.resume?.();\n }\n });\n}\n","/**\n * SCREEN-2670 — the pre-write park: an owned write path between Ink and the terminal.\n *\n * A diff-based screen reader computes what to speak from successive terminal snapshots. In\n * screen-reader mode Ink runs UNTHROTTLED (`ink.js:193`), so commits land back to back and a reader\n * that samples between two of them sees a torn or already-overwritten region. The park separates\n * commits IN TIME so a snapshot can fall between them. It does not move the cursor: every frame Ink\n * writes already begins with `eraseLines`, which ends in `cursorLeft`, so column zero is where the\n * frame starts anyway (CLI-062 invariant I3 — no cursor sequence is written here).\n *\n * THE UNIT IS A COMMIT, NOT A CHUNK. One commit in the mode is up to four `write()` calls in one\n * synchronous run (`ink.js:371-412`): synchronized-output begin, `erase + staticOutput`, the frame,\n * synchronized-output end. Parking each would pay the interval three times and leave the erased\n * region on screen between the erase and the frame — the torn state the park exists to prevent. So\n * the chunks of one synchronous run form ONE batch (closed on `process.nextTick`), parked once in\n * front and released contiguously.\n *\n * WHAT IS NEVER PARKED:\n * - the first batch of a session (there is no earlier commit to separate it from);\n * - a batch with no printable content — Ink's empty-string barrier (`ink.js:588`, `:647`), an OSC\n * 133 mark, a bare control sequence — which still queues BEHIND anything pending so order holds;\n * - a batch stamped with the echo flag: the composer's own key handler arms it for text-mutating\n * keys only, it is read when the batch OPENS and carried on it (so a keystroke queued behind a\n * parked reply keeps its exemption), and it expires on `setImmediate` whether or not a batch took\n * it, so a keystroke that commits nothing cannot un-park a later transcript batch.\n *\n * NOTHING IS EVER DROPPED. Ink keeps its own erase bookkeeping (`lastOutputHeight`) from the frame\n * it believes it wrote, and writes each `<Static>` item exactly once — so a batch that never reaches\n * the terminal corrupts the next erase and loses a transcript line for good. When a newer printable\n * batch closes while an older one is still parked, the OLDER one is released at once, contiguously\n * and without its park, so at most one batch ever waits and memory stays bounded on a fast stream;\n * the park then applies to the newest commit only.\n */\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { IScreenReaderPacingPort } from './screen-reader-pacing-context.js';\n\n/** Ink's own fallbacks when the stream reports no size. */\nconst DEFAULT_COLUMNS = 80;\nconst DEFAULT_ROWS = 24;\n\ntype TWriteCallback = (error?: Error | null) => void;\n\n/**\n * What Ink 7.1.1 touches on the stream it is given (`ink.js`, `log-update.js`): the write, the\n * dimensions, the TTY flag, the resize listener pair, and the writable-state probes.\n */\nexport type TParkedStdoutSource = Pick<\n NodeJS.WriteStream,\n | 'write'\n | 'columns'\n | 'rows'\n | 'isTTY'\n | 'destroyed'\n | 'writable'\n | 'writableEnded'\n | 'writableLength'\n | 'writableNeedDrain'\n | 'on'\n | 'off'\n>;\n\nexport interface ICreateParkedStdoutOptions {\n stdout: TParkedStdoutSource;\n /** Milliseconds between the previous printable release and the next parked one. */\n preparkMs: number;\n /** Injected clock for tests. Defaults to `Date.now`. */\n now?: () => number;\n}\n\ninterface IChunk {\n text: string;\n callback: TWriteCallback | undefined;\n}\n\ninterface IBatch {\n chunks: IChunk[];\n /** Read at formation, never at release. */\n echo: boolean;\n printable: boolean;\n /** Ink's `write('', cb)` — the exit barrier, which must reach the stream in order. */\n barrier: boolean;\n}\n\nexport interface IParkedStdout {\n /** The stream to hand Ink as `stdout`. One stable object: Ink keys its instance map by it. */\n asInkStdout(): NodeJS.WriteStream;\n /** The composer's key handler arms this for a text-mutating key. Expires on `setImmediate`. */\n armEchoRelease(): void;\n /** Write through the same ordered path — for the OSC 133 marks, which are positional. */\n write(chunk: string | Uint8Array, callback?: TWriteCallback): boolean;\n /**\n * Release everything queued NOW, in order and without waiting out a park, and keep parking what\n * comes later — for a terminal handoff, so nothing lands on top of the child's output.\n */\n drain(): Promise<void>;\n /** Release everything still queued, in order and without further parking; resolves when written. */\n flush(): Promise<void>;\n}\n\n/** The members Ink reads from `options.stdout`, narrowed as `TInkStdinSurface` narrows stdin. */\ntype TInkStdoutSurface = TParkedStdoutSource;\n\nfunction toText(chunk: string | Uint8Array): string {\n return typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');\n}\n\n/** Printable = something a reader could speak: visible text after every escape is stripped. */\nfunction isPrintable(text: string): boolean {\n return sanitizeTerminalText(text).trim().length > 0;\n}\n\nclass ParkedStdout implements TInkStdoutSurface {\n private readonly source: TParkedStdoutSource;\n private readonly preparkMs: number;\n private readonly now: () => number;\n private open: IBatch | undefined;\n private readonly queue: IBatch[] = [];\n private timer: NodeJS.Timeout | undefined;\n private lastPrintableReleaseAt: number | undefined;\n private echoArmed = false;\n private echoExpiryScheduled = false;\n private flushing = false;\n private drainWaiters: Array<() => void> = [];\n\n constructor(options: ICreateParkedStdoutOptions) {\n this.source = options.stdout;\n this.preparkMs = options.preparkMs;\n this.now = options.now ?? Date.now;\n this.on = ((...args: Parameters<NodeJS.WriteStream['on']>) =>\n this.source.on(...args)) as NodeJS.WriteStream['on'];\n this.off = ((...args: Parameters<NodeJS.WriteStream['off']>) =>\n this.source.off(...args)) as NodeJS.WriteStream['off'];\n }\n\n // ── the surface Ink reads ──────────────────────────────────────────────────────────────────\n\n get columns(): number {\n return this.source.columns ?? DEFAULT_COLUMNS;\n }\n\n get rows(): number {\n return this.source.rows ?? DEFAULT_ROWS;\n }\n\n get isTTY(): boolean {\n return this.source.isTTY ?? false;\n }\n\n get destroyed(): boolean {\n return this.source.destroyed ?? false;\n }\n\n get writable(): boolean {\n return this.source.writable ?? true;\n }\n\n get writableEnded(): boolean {\n return this.source.writableEnded ?? false;\n }\n\n get writableLength(): number {\n return this.source.writableLength ?? 0;\n }\n\n get writableNeedDrain(): boolean {\n return this.source.writableNeedDrain ?? false;\n }\n\n // Ink registers and removes its `resize` listener through these and discards the return value.\n readonly on: NodeJS.WriteStream['on'];\n readonly off: NodeJS.WriteStream['off'];\n\n /**\n * The write is deferred, so the boolean is the source's CURRENT backpressure state — what the\n * same call would have returned a moment ago. Ink does not read it; a caller that does is told\n * the truth about the stream it is ultimately writing to.\n */\n write(\n chunk: string | Uint8Array,\n encodingOrCallback?: BufferEncoding | TWriteCallback,\n maybeCallback?: TWriteCallback,\n ): boolean {\n const callback = typeof encodingOrCallback === 'function' ? encodingOrCallback : maybeCallback;\n const text = toText(chunk);\n let batch = this.open;\n if (batch === undefined) {\n const opened: IBatch = {\n chunks: [],\n echo: this.echoArmed,\n printable: false,\n barrier: false,\n };\n batch = opened;\n this.open = opened;\n // One synchronous run = one batch. nextTick drains before the check phase, so a batch opened\n // in the turn that armed the echo flag always closes before the flag's setImmediate expiry.\n process.nextTick(() => this.close(opened));\n }\n batch.chunks.push({ text, callback });\n if (text.length === 0) batch.barrier = true;\n else if (!batch.printable && isPrintable(text)) batch.printable = true;\n return !this.writableNeedDrain;\n }\n\n // ── the owned-writer surface ──────────────────────────────────────────────────────────────\n\n asInkStdout(): NodeJS.WriteStream {\n const surface: TInkStdoutSurface = this;\n return surface as NodeJS.WriteStream;\n }\n\n armEchoRelease(): void {\n this.echoArmed = true;\n if (this.echoExpiryScheduled) return;\n this.echoExpiryScheduled = true;\n setImmediate(() => {\n this.echoArmed = false;\n this.echoExpiryScheduled = false;\n });\n }\n\n drain(): Promise<void> {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n for (const waiting of this.queue.splice(0)) this.release(waiting);\n return this.settled();\n }\n\n flush(): Promise<void> {\n this.flushing = true;\n return this.drain();\n }\n\n /** Resolves once no batch is open or queued. */\n private settled(): Promise<void> {\n if (this.open === undefined && this.queue.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n this.drainWaiters.push(resolve);\n });\n }\n\n // ── batching and release ──────────────────────────────────────────────────────────────────\n\n private close(batch: IBatch): void {\n if (this.open !== batch) return;\n this.open = undefined;\n // A newer printable commit closing behind a parked one: release the older one NOW rather than\n // drop it — Ink's erase bookkeeping and its once-only Static writes both assume it landed.\n if (this.isParked(batch)) {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n for (const waiting of this.queue.splice(0)) this.release(waiting);\n }\n this.queue.push(batch);\n this.pump();\n }\n\n /** The first printable release of a session is never parked: nothing precedes it to separate. */\n private isParked(batch: IBatch): boolean {\n return (\n this.preparkMs > 0 &&\n this.lastPrintableReleaseAt !== undefined &&\n batch.printable &&\n !batch.echo &&\n !this.flushing\n );\n }\n\n private pump(): void {\n // Always recompute from the head: a superseded head leaves a stale timer behind, and the batch\n // now at the head may be a control-only one that must not wait on it.\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n while (this.queue.length > 0) {\n const head = this.queue[0] as IBatch;\n if (this.isParked(head)) {\n const due = (this.lastPrintableReleaseAt ?? 0) + this.preparkMs;\n const wait = due - this.now();\n if (wait > 0) {\n // Kept referenced: this timer guards queued output and Ink's exit barrier, whose callback\n // resolves `waitUntilExit()`; the process must not exit with a frame still parked.\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.pump();\n }, wait);\n return;\n }\n }\n this.queue.shift();\n this.release(head);\n }\n if (this.open === undefined) {\n const waiters = this.drainWaiters;\n this.drainWaiters = [];\n for (const resolve of waiters) resolve();\n }\n }\n\n private release(batch: IBatch): void {\n for (const chunk of batch.chunks) {\n this.source.write(chunk.text, chunk.callback);\n }\n if (batch.printable) this.lastPrintableReleaseAt = this.now();\n }\n}\n\n/** Build the owned write path. Constructed only when the mode is on and `preparkMs > 0`. */\nexport function createParkedStdout(options: ICreateParkedStdoutOptions): IParkedStdout {\n return new ParkedStdout(options);\n}\n\n/** The port the render tree publishes: arming and the positional write, bound to one proxy. */\nexport function toPacingPort(parked: IParkedStdout): IScreenReaderPacingPort {\n return {\n armEchoRelease: () => parked.armEchoRelease(),\n write: (text) => {\n parked.write(text);\n },\n };\n}\n","/**\n * SCREEN-1992 — DECSET/DECRST 1004: ask the terminal to report focus changes (`CSI I` / `CSI O`).\n *\n * Written OUTSIDE Ink, following the `terminal-marks.ts` carve-out: the two sequences are constants,\n * so nothing needs sanitizing, and no component may emit an escape sequence. A terminal that does not\n * implement the mode discards the request; `supportsFocusReporting()` (terminal-capabilities.ts)\n * carries the gate, and `negotiated` is what the stdin filter and the attention tracker consult.\n */\nconst FOCUS_REPORTING_ENABLE = '\\x1b[?1004h';\nconst FOCUS_REPORTING_DISABLE = '\\x1b[?1004l';\n\nexport interface IFocusReportingWriterOptions {\n /** The capability gate, read at enable time. */\n supported: () => boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n}\n\nexport interface IFocusReportingWriter {\n /** True between a successful `enable()` and the next `disable()`. */\n readonly negotiated: boolean;\n enable(): void;\n disable(): void;\n}\n\nexport function createFocusReportingWriter(\n options: IFocusReportingWriterOptions,\n): IFocusReportingWriter {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n let negotiated = false;\n return {\n get negotiated(): boolean {\n return negotiated;\n },\n enable(): void {\n if (negotiated || !options.supported()) return;\n negotiated = true;\n write(FOCUS_REPORTING_ENABLE);\n },\n disable(): void {\n if (!negotiated) return;\n negotiated = false;\n write(FOCUS_REPORTING_DISABLE);\n },\n };\n}\n","/**\n * TERM-002: TUI implementation of the `ITerminalHandoff` transport contract.\n *\n * Manual suspend/resume (NOT Ink 7.1.0 `suspendTerminal`): while a child process owns the real\n * terminal, the App renders nothing so Ink unmounts its input hooks and releases raw mode; the Ink\n * frame is cleared; the caller's `fn` runs the child with inherited stdio; then the App re-renders.\n *\n * The framework (`InteractiveSession`) owns the orchestration (exclusivity, fast-fail) — this class\n * only performs the actual screen release/reclaim. It spawns nothing itself (platform-neutral).\n */\nimport type { ITerminalHandoff } from '@robota-sdk/agent-interface-session';\n\n/** How the App suspends/resumes its rendering, registered into the controller on mount. */\nexport interface ITuiSuspendHooks {\n /** Render nothing (release Ink input/raw mode); resolves once that has committed. */\n suspend(): Promise<void>;\n /** Resume rendering and force a redraw. */\n resume(): void;\n}\n\n/**\n * SCREEN-1992: terminal modes the TUI negotiated (focus reporting) are released before a child owns\n * the terminal and re-negotiated once it is reclaimed, so the child never receives `CSI I`/`CSI O`\n * and a child that reset the mode does not leave the TUI blind afterwards.\n */\nexport interface ITerminalModeHooks {\n /** May be async: SCREEN-2670 drains the parked screen-reader output before the child gets the TTY. */\n preSuspend(): void | Promise<void>;\n postResume(): void;\n}\n\n/** Minimal slice of the Ink render instance the controller needs. */\nexport interface IInkClearable {\n clear(): void;\n}\n\nexport class TerminalHandoffController implements ITerminalHandoff {\n private hooks?: ITuiSuspendHooks;\n private instance?: IInkClearable;\n private modeHooks?: ITerminalModeHooks;\n\n /**\n * The App registers how to suspend/resume Ink. Returns an unregister function for cleanup on\n * unmount. Only a mounted App provides these hooks, which gates `canHandoffTerminal`.\n */\n registerSuspendHooks(hooks: ITuiSuspendHooks): () => void {\n this.hooks = hooks;\n return () => {\n if (this.hooks === hooks) this.hooks = undefined;\n };\n }\n\n /** render.tsx registers the terminal-mode bracket (SCREEN-1992) before `render()`. */\n setTerminalModeHooks(hooks: ITerminalModeHooks | undefined): void {\n this.modeHooks = hooks;\n }\n\n /** render.tsx supplies the Ink instance (for `clear()`) after `render()` returns. */\n setInkInstance(instance: IInkClearable): void {\n this.instance = instance;\n }\n\n get canHandoffTerminal(): boolean {\n return (\n process.stdin.isTTY === true && process.stdout.isTTY === true && this.hooks !== undefined\n );\n }\n\n async runWithTerminal<T>(fn: () => Promise<T>): Promise<T> {\n const hooks = this.hooks;\n if (!this.canHandoffTerminal || hooks === undefined) {\n throw new Error(\n 'TUI terminal handoff unavailable: no interactive TTY, or the App is not mounted.',\n );\n }\n await hooks.suspend();\n await this.modeHooks?.preSuspend();\n this.instance?.clear();\n // Releasing Ink's React input hooks (empty render) is not enough: the parent process still holds\n // a raw-mode TTY read on stdin, which (a) steals input from the inherited child and (b) starves\n // the parent event loop so the child's exit is never observed — the handoff would hang forever.\n // Explicitly hand stdin to the child by dropping raw mode and pausing the parent's reader; Ink\n // re-grabs stdin (raw mode + resume) when its input hooks re-mount on resume.\n const stdin = process.stdin;\n if (stdin.isTTY && typeof stdin.setRawMode === 'function') stdin.setRawMode(false);\n stdin.pause();\n try {\n return await fn();\n } finally {\n // Always reclaim the screen, even when the child failed. Re-rendering re-mounts the input\n // hooks, which is what restores raw mode and resumes the parent's stdin reader.\n if (stdin.isTTY && typeof stdin.setRawMode === 'function') stdin.setRawMode(true);\n stdin.resume();\n hooks.resume();\n this.modeHooks?.postResume();\n }\n }\n}\n","import { MINUTES_PER_HOUR, MS_PER_MINUTE } from './time-units.js';\n\nimport type { TExecutionNormalizedState } from '@robota-sdk/agent-interface-execution';\n\n/**\n * SCREEN-1992: one line for what happened while the user was away.\n *\n * Counts only while unattended; `onReturned` renders the interval and resets it. Nothing here\n * inspects the session — the coordinator feeds it the channel events it already receives.\n */\nexport const RECAP_MAX_WIDTH = 120;\n\n/** The framework's `TTurnSource` plus whatever a future source adds; only `agent-wakeup` is special here. */\ntype TTurnSource = string;\n\nconst TERMINAL_STATES: ReadonlySet<TExecutionNormalizedState> = new Set([\n 'completed',\n 'failed',\n 'stopped',\n]);\n\nexport class IntervalRecap {\n private lostAt: number | undefined;\n private lastTurnSource: TTurnSource = 'user';\n private turnsFinished = 0;\n private wakeTurnsFinished = 0;\n private needsInputCount = 0;\n private errorCount = 0;\n /** Every entry's latest state, attended or not — the baseline an interval starts from. */\n private readonly currentStates = new Map<string, TExecutionNormalizedState>();\n /** Entries that were not terminal when attention was lost and are terminal now. */\n private readonly reachedTerminal = new Map<string, TExecutionNormalizedState>();\n private baseline = new Map<string, TExecutionNormalizedState>();\n private baselinePending = false;\n\n get away(): boolean {\n return this.lostAt !== undefined;\n }\n\n onLost(atIso: string): void {\n this.lostAt = Date.parse(atIso);\n this.reset();\n this.baseline = new Map(this.currentStates);\n // Lost before any snapshot arrived (a channel wired while the user is already away): the first\n // snapshot is the baseline, so a resumed session's restored terminal tasks are not \"news\".\n this.baselinePending = this.currentStates.size === 0;\n }\n\n /** The recap line for the interval just ended, or undefined when there is nothing to say. */\n onReturned(atIso: string): string | undefined {\n if (this.lostAt === undefined) return undefined;\n const elapsedMs = Math.max(0, Date.parse(atIso) - this.lostAt);\n this.lostAt = undefined;\n const parts = this.parts();\n this.reset();\n if (parts.length === 0) return undefined;\n return bound(`While away ${formatElapsed(elapsedMs)}: ${parts.join(' · ')}`);\n }\n\n turnSource(source: TTurnSource): void {\n this.lastTurnSource = source;\n }\n\n turnCompleted(): void {\n if (!this.away) return;\n this.turnsFinished += 1;\n if (this.lastTurnSource === 'agent-wakeup') this.wakeTurnsFinished += 1;\n }\n\n needsInput(): void {\n if (this.away) this.needsInputCount += 1;\n }\n\n error(): void {\n if (this.away) this.errorCount += 1;\n }\n\n /**\n * A workspace entry's current state. Only an entry that REACHED a terminal state during the\n * interval is counted: one already terminal when attention was lost is old news, and one that\n * left the terminal state again (a resumed schedule) is dropped.\n */\n entryState(entryId: string, state: TExecutionNormalizedState): void {\n this.currentStates.set(entryId, state);\n if (!this.away) return;\n if (this.baselinePending) {\n this.baseline.set(entryId, state);\n return;\n }\n const before = this.baseline.get(entryId);\n const wasTerminal = before !== undefined && TERMINAL_STATES.has(before);\n if (!TERMINAL_STATES.has(state) || wasTerminal) {\n this.reachedTerminal.delete(entryId);\n return;\n }\n this.reachedTerminal.set(entryId, state);\n }\n\n /** The end of one snapshot: a pending baseline is now complete. */\n snapshotComplete(): void {\n this.baselinePending = false;\n }\n\n private parts(): string[] {\n const parts: string[] = [];\n if (this.turnsFinished > 0) {\n const wake = this.wakeTurnsFinished > 0 ? ` (${this.wakeTurnsFinished} wake)` : '';\n parts.push(`${this.turnsFinished} ${plural(this.turnsFinished, 'turn')} finished${wake}`);\n }\n if (this.needsInputCount > 0) parts.push(`${this.needsInputCount} needs input`);\n const byState = { completed: 0, failed: 0, stopped: 0 };\n for (const state of this.reachedTerminal.values()) {\n if (state in byState) byState[state as keyof typeof byState] += 1;\n }\n for (const state of ['completed', 'failed', 'stopped'] as const) {\n if (byState[state] > 0) parts.push(`${byState[state]} ${state}`);\n }\n if (this.errorCount > 0) {\n parts.push(`${this.errorCount} ${plural(this.errorCount, 'error')}`);\n }\n return parts;\n }\n\n private reset(): void {\n this.turnsFinished = 0;\n this.wakeTurnsFinished = 0;\n this.needsInputCount = 0;\n this.errorCount = 0;\n this.reachedTerminal.clear();\n }\n}\n\nfunction plural(count: number, noun: string): string {\n return count === 1 ? noun : `${noun}s`;\n}\n\nfunction formatElapsed(elapsedMs: number): string {\n const totalMinutes = Math.floor(elapsedMs / MS_PER_MINUTE);\n if (totalMinutes < 1) return '<1m';\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n if (hours === 0) return `${minutes}m`;\n return minutes === 0 ? `${hours}h` : `${hours}h ${minutes}m`;\n}\n\nfunction bound(line: string): string {\n return line.length <= RECAP_MAX_WIDTH ? line : `${line.slice(0, RECAP_MAX_WIDTH - 1)}…`;\n}\n","import { IntervalRecap } from './interval-recap.js';\n\nimport type { IAttentionSource } from './attention-tracker.js';\nimport type { IExecutionWorkspaceSnapshot } from '@robota-sdk/agent-interface-execution';\n\n/**\n * SCREEN-1992: joins the attention level to the recap accumulator for one channel.\n *\n * The tracker outlives channels (it is the terminal's), the recap belongs to the session being\n * shown; the coordinator subscribes for the channel's lifetime and pushes each recap line into the\n * channel's notice store. It lives here rather than in `tui-state-manager.ts` (at its line ceiling).\n */\nexport interface IAttentionCoordinatorOptions {\n readonly source: IAttentionSource;\n readonly onRecap: (line: string) => void;\n readonly now?: () => number;\n}\n\nexport class AttentionCoordinator {\n private readonly recap = new IntervalRecap();\n private unsubscribe: (() => void) | undefined;\n private readonly now: () => number;\n\n constructor(private readonly options: IAttentionCoordinatorOptions) {\n this.now = options.now ?? Date.now;\n }\n\n wire(): void {\n if (this.unsubscribe !== undefined) return;\n // A channel created while the user is already away starts its interval now.\n if (!this.options.source.attended) this.recap.onLost(new Date(this.now()).toISOString());\n this.unsubscribe = this.options.source.subscribe((change) => {\n if (change.kind === 'lost') {\n this.recap.onLost(change.at);\n return;\n }\n const line = this.recap.onReturned(change.at);\n if (line !== undefined) this.options.onRecap(line);\n });\n }\n\n unwire(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n }\n\n onTurnSource = (source: string): void => {\n this.recap.turnSource(source);\n };\n\n onComplete = (): void => {\n this.recap.turnCompleted();\n };\n\n onError = (): void => {\n this.recap.error();\n };\n\n onNeedsInput = (): void => {\n this.recap.needsInput();\n };\n\n onWorkspaceSnapshot = (snapshot: IExecutionWorkspaceSnapshot): void => {\n for (const entry of snapshot.entries) {\n // The main thread's turns are counted from `complete`; its idle `completed` is not an event.\n if (entry.kind === 'main_thread') continue;\n this.recap.entryState(entry.id, entry.state);\n }\n this.recap.snapshotComplete();\n };\n}\n","/**\n * Bounded polling helper for asynchronous session initialization.\n *\n * Distinguishes benign \"not yet initialized\" errors (retried until a timeout)\n * from real failures (surfaced immediately), so the TUI can show an error\n * instead of spinning forever.\n */\n\nexport type TSessionInitFailure =\n { kind: 'timeout'; lastError: Error | undefined } | { kind: 'error'; error: Error };\n\nexport interface ISessionInitPollerOptions {\n /** Throws while the session is not ready; returns normally when ready. */\n check: () => void;\n intervalMs: number;\n timeoutMs: number;\n /** Returns true when an error means \"keep polling\". Defaults to /not initialized/i. */\n isBenignError?: (error: Error) => boolean;\n onReady: () => void;\n onFailure: (failure: TSessionInitFailure) => void;\n}\n\nexport interface ISessionInitPoller {\n start(): void;\n stop(): void;\n}\n\nfunction defaultIsBenignError(error: Error): boolean {\n return /not initialized/i.test(error.message);\n}\n\nexport function createSessionInitPoller(options: ISessionInitPollerOptions): ISessionInitPoller {\n const isBenign = options.isBenignError ?? defaultIsBenignError;\n let timer: ReturnType<typeof setInterval> | null = null;\n let elapsedMs = 0;\n let lastBenignError: Error | undefined;\n\n function stop(): void {\n if (timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n }\n\n function tick(): void {\n elapsedMs += options.intervalMs;\n try {\n options.check();\n } catch (raw) {\n // allow-fallback: poller's purpose is to classify and route this error, never to swallow it\n const error = raw instanceof Error ? raw : new Error(String(raw));\n if (!isBenign(error)) {\n stop();\n options.onFailure({ kind: 'error', error });\n return;\n }\n lastBenignError = error;\n if (elapsedMs >= options.timeoutMs) {\n stop();\n options.onFailure({ kind: 'timeout', lastError: lastBenignError });\n }\n return;\n }\n stop();\n options.onReady();\n }\n\n return {\n start(): void {\n if (timer !== null) return;\n elapsedMs = 0;\n timer = setInterval(tick, options.intervalMs);\n },\n stop,\n };\n}\n","/**\n * Renders a system-command result into the TUI state (CMD-004 Phase 2).\n *\n * Pure rendering only: host actions were already executed (and consumed) by the session layer,\n * screen navigation arrives via the requester-routed `ui_intent` session event (`useSideEffects`),\n * and state-change notifications arrive as broadcast session events (`session_renamed`,\n * `history_cleared` — bound in `TuiInteractionChannel`). The only result-carried hint consumed\n * here is `data.pluginRegistryReloaded` — the requester-local command-registry/autocomplete\n * refresh (the semantic plugin reload already ran host-side inside the command).\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { TuiStateManager } from '../tui-state-manager.js';\nimport type { CommandRegistry } from '@robota-sdk/agent-framework';\nimport type { ICommandResult } from '@robota-sdk/agent-interface-command';\nimport type {\n ISessionConversationRead,\n ISessionLifecycle,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ICommandResultSession extends ISessionLifecycle, ISessionConversationRead {}\n\nexport function applySystemCommandResult(\n result: ICommandResult,\n interactiveSession: ICommandResultSession,\n registry: CommandRegistry,\n manager: TuiStateManager,\n reloadPluginCommandSource?: (registry: CommandRegistry) => void,\n): void {\n if (result.data?.['pluginRegistryReloaded'] === true) {\n reloadPluginCommandSource?.(registry);\n }\n manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));\n\n if (interactiveSession.isInitialized) {\n const ctx = interactiveSession.getContextState();\n manager.setContextState({\n percentage: ctx.usedPercentage,\n usedTokens: ctx.usedTokens,\n maxTokens: ctx.maxTokens,\n });\n }\n}\n","import type { TSessionEndReason } from '@robota-sdk/agent-core';\n\nexport interface ITuiChannelShutdownRequest {\n reason: TSessionEndReason;\n message: string;\n}\n\nexport interface ITuiChannelLifecycleOperations {\n start(): Promise<void>;\n stop(): Promise<void>;\n beginShutdown(): void;\n shutdownSession(request: ITuiChannelShutdownRequest): Promise<void>;\n}\n\n/** Marks a startup failure whose rollback also failed, so retrying cannot duplicate live resources. */\nexport class TuiChannelStartRollbackError extends AggregateError {}\n\n/** Owns channel lifecycle transitions and bounds SDK shutdown without owning presentation state. */\nexport class TuiChannelLifecycleCoordinator {\n private started = false;\n private startPromise: Promise<void> | undefined;\n private teardownBegun = false;\n private stopped = false;\n private shuttingDown = false;\n private shutdownPromise: Promise<void> | undefined;\n private stopPromise: Promise<void> | undefined;\n\n constructor(\n private readonly operations: ITuiChannelLifecycleOperations,\n private readonly defaultShutdownTimeoutMs: number,\n ) {}\n\n get isShuttingDown(): boolean {\n return this.shuttingDown;\n }\n\n /** False before startup and immediately when teardown begins, even if teardown later fails. */\n get isActiveForPeerStatus(): boolean {\n return this.started && !this.teardownBegun && !this.shuttingDown;\n }\n\n async start(): Promise<void> {\n if (this.teardownBegun) {\n throw new Error('Cannot start a channel after teardown has begun.');\n }\n if (this.started) return;\n const pendingStart = (this.startPromise ??= this.operations.start());\n try {\n await pendingStart;\n this.started = true;\n } catch (error) {\n if (error instanceof TuiChannelStartRollbackError) this.teardownBegun = true;\n throw error;\n } finally {\n if (this.startPromise === pendingStart) this.startPromise = undefined;\n }\n }\n\n async stop(): Promise<void> {\n this.teardownBegun = true;\n if (this.stopped) return;\n this.stopPromise ??= this.performStop();\n try {\n await this.stopPromise;\n } catch (error) {\n // A failed teardown is retryable; claiming stopped here would strand owned resources.\n this.stopPromise = undefined;\n throw error;\n }\n }\n\n private async performStop(): Promise<void> {\n const pendingStart = this.startPromise;\n let startError: Error | undefined;\n if (pendingStart !== undefined) {\n try {\n await pendingStart;\n } catch (cause) {\n startError = cause instanceof Error ? cause : new Error(String(cause));\n }\n }\n let stopError: Error | undefined;\n try {\n await this.operations.stop();\n } catch (error) {\n stopError = error instanceof Error ? error : new Error(String(error));\n }\n this.shutdownPromise ??= this.shutdownSessionBounded(\n { reason: 'other', message: 'channel stopped' },\n this.defaultShutdownTimeoutMs,\n );\n await this.shutdownPromise;\n if (stopError !== undefined && startError !== undefined) {\n throw new AggregateError(\n [startError, stopError],\n 'TUI channel start and teardown both failed.',\n );\n }\n if (stopError !== undefined) throw stopError;\n this.started = false;\n this.stopped = true;\n if (startError !== undefined) throw startError;\n }\n\n async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {\n this.teardownBegun = true;\n if (!this.shuttingDown) {\n this.shuttingDown = true;\n this.operations.beginShutdown();\n }\n const pendingStop = this.stopPromise;\n if (pendingStop !== undefined) {\n await pendingStop.catch(() => undefined); // allow-fallback: the stop caller owns teardown failure\n return;\n }\n this.shutdownPromise ??= this.performShutdown(options);\n await this.shutdownPromise;\n }\n\n private async performShutdown(options?: {\n reason?: TSessionEndReason;\n timeoutMs?: number;\n }): Promise<void> {\n const pendingStart = this.startPromise;\n if (pendingStart !== undefined) {\n await pendingStart.catch(() => undefined); // allow-fallback: the start caller owns startup failure\n }\n await this.shutdownSessionBounded(\n { reason: options?.reason ?? 'prompt_input_exit', message: 'CLI shutdown' },\n options?.timeoutMs ?? this.defaultShutdownTimeoutMs,\n );\n }\n\n private async shutdownSessionBounded(\n request: ITuiChannelShutdownRequest,\n timeoutMs: number,\n ): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n timer.unref?.();\n });\n await Promise.race([\n this.operations.shutdownSession(request).catch(() => undefined), // allow-fallback: shutdown is best-effort during process exit and remains timeout-bounded\n timeout,\n ]);\n if (timer) clearTimeout(timer);\n }\n}\n","import type { IPendingPermissionRequest } from './types.js';\nimport type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';\nimport type { TPermissionResultValue } from '@robota-sdk/agent-interface-session';\n\ninterface IUserActionQueueEntry {\n presentedRequest: IActionRequest;\n resolve: (response: TActionResponse) => void;\n id?: string;\n}\n\ninterface IPermissionQueueEntry {\n toolName: string;\n toolArgs: TToolArgs;\n canPersistProjectPermission?: boolean;\n requestedByPeer?: string;\n resolve: (result: TPermissionResultValue) => void;\n id?: string;\n}\n\n/** Serializes action prompts and guarantees every pending promise is settled during teardown. */\nexport class TuiUserActionQueue {\n private entries: IUserActionQueueEntry[] = [];\n private processing = false;\n\n constructor(private readonly onChange: () => void) {}\n\n get current(): IActionRequest | null {\n return this.processing ? (this.entries[0]?.presentedRequest ?? null) : null;\n }\n\n enqueue(request: IActionRequest, id?: string): Promise<TActionResponse> {\n return new Promise<TActionResponse>((resolve) => {\n this.entries.push({\n presentedRequest: { ...request },\n resolve,\n ...(id !== undefined ? { id } : {}),\n });\n this.processNext();\n });\n }\n\n resolveCurrent(expectedRequest: IActionRequest, response: TActionResponse): void {\n if (this.entries[0]?.presentedRequest !== expectedRequest) return;\n const pending = this.entries.shift();\n if (!pending) return;\n this.processing = false;\n this.onChange();\n pending.resolve(response);\n this.processNext();\n }\n\n dismissById(id: string): boolean {\n const dismissed = this.entries.filter((entry) => entry.id === id);\n if (dismissed.length === 0) return false;\n this.entries = this.entries.filter((entry) => entry.id !== id);\n this.processing = false;\n for (const entry of dismissed) entry.resolve({ type: 'cancelled' });\n this.processNext();\n return true;\n }\n\n cancelAll(): void {\n const queued = this.entries;\n this.entries = [];\n this.processing = false;\n for (const pending of queued) pending.resolve({ type: 'cancelled' });\n this.onChange();\n }\n\n private processNext(): void {\n if (this.processing) return;\n if (!this.entries[0]) {\n this.onChange();\n return;\n }\n this.processing = true;\n this.onChange();\n }\n}\n\n/** Serializes permission prompts and denies every pending request during teardown. */\nexport class TuiPermissionQueue {\n private entries: IPermissionQueueEntry[] = [];\n private processing = false;\n private currentRequest: IPendingPermissionRequest | null = null;\n\n constructor(private readonly onChange: () => void) {}\n\n get current(): IPendingPermissionRequest | null {\n return this.currentRequest;\n }\n\n enqueue(\n toolName: string,\n toolArgs: TToolArgs,\n id?: string,\n canPersistProjectPermission?: boolean,\n requestedByPeer?: string,\n ): Promise<TPermissionResultValue> {\n return new Promise<TPermissionResultValue>((resolve) => {\n this.entries.push({\n toolName,\n toolArgs,\n resolve,\n ...(id !== undefined ? { id } : {}),\n ...(canPersistProjectPermission !== undefined ? { canPersistProjectPermission } : {}),\n ...(requestedByPeer !== undefined ? { requestedByPeer } : {}),\n });\n this.processNext();\n });\n }\n\n dismissById(id: string): boolean {\n const dismissed = this.entries.filter((entry) => entry.id === id);\n if (dismissed.length === 0) return false;\n const dismissedCurrent = this.entries[0]?.id === id;\n this.entries = this.entries.filter((entry) => entry.id !== id);\n for (const entry of dismissed) entry.resolve(false);\n if (dismissedCurrent) {\n this.processing = false;\n this.currentRequest = null;\n this.processNext();\n }\n return true;\n }\n\n cancelAll(): void {\n const queued = this.entries;\n this.entries = [];\n this.processing = false;\n this.currentRequest = null;\n for (const pending of queued) pending.resolve(false);\n this.onChange();\n }\n\n private processNext(): void {\n if (this.processing) return;\n const next = this.entries[0];\n if (!next) {\n this.currentRequest = null;\n this.onChange();\n return;\n }\n this.processing = true;\n this.currentRequest = {\n toolName: next.toolName,\n toolArgs: next.toolArgs,\n ...(next.canPersistProjectPermission !== undefined\n ? { canPersistProjectPermission: next.canPersistProjectPermission }\n : {}),\n ...(next.requestedByPeer !== undefined ? { requestedByPeer: next.requestedByPeer } : {}),\n resolve: (result) => {\n if (this.entries[0] !== next) return;\n this.entries.shift();\n this.processing = false;\n this.currentRequest = null;\n next.resolve(result);\n setTimeout(() => this.processNext(), 0);\n },\n };\n this.onChange();\n }\n}\n","import { createUserMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\n/** The part of a session this needs: who is driving the turn now, if it can say. */\nexport interface IActiveDriverSource {\n getActiveDriverId?: () => string | null;\n}\n\n/**\n * PEER-007 (issue #1915): the transcript echo of a user message, attributed to the ACTIVE turn's\n * driver so a peer's message is labelled as theirs the moment it appears rather than reading as the\n * operator's own for the whole turn. The id is the framework's DERIVED one (`peer:<session-id>`),\n * never peer-supplied text.\n *\n * The lookup is guarded because attribution is an ADDITION to the message, never a precondition for\n * it: a session that cannot say who drove the turn must still get the message on screen. Unguarded,\n * a throw here loses the message entirely and the transcript shows that nothing happened.\n */\nexport function attributedUserEcho(content: string, session: IActiveDriverSource): IHistoryEntry {\n let driverId: string | null = null;\n try {\n driverId = session.getActiveDriverId?.() ?? null;\n } catch {\n driverId = null;\n }\n return messageToHistoryEntry(\n createUserMessage(content, driverId ? { metadata: { driverId } } : {}),\n );\n}\n","import type { TuiStateManager } from './tui-state-manager.js';\nimport type {\n IInteractiveSession,\n IInteractiveSessionEvents,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiSessionEventBinding {\n event: TInteractiveEventName;\n handler: (...args: never[]) => void;\n}\n\nexport interface ITuiSessionBinder {\n <E extends TInteractiveEventName>(event: E, handler: IInteractiveSessionEvents[E]): void;\n}\n\nexport function bindTuiSessionNoticeEvents(\n bind: ITuiSessionBinder,\n manager: TuiStateManager,\n): void {\n bind('plan_event', (payload) => manager.addSessionEventNotice({ event: 'plan_event', payload }));\n bind('context_file_refreshed', (payload) =>\n manager.addSessionEventNotice({ event: 'context_file_refreshed', payload }),\n );\n bind('branch_event', (payload) =>\n manager.addSessionEventNotice({ event: 'branch_event', payload }),\n );\n}\n\n/** Isolate a TUI-owned projection failure from the session operation that already committed. */\nexport function bindTuiSessionEvent<E extends TInteractiveEventName>(\n session: Pick<IInteractiveSession, 'on'>,\n event: E,\n handler: IInteractiveSessionEvents[E],\n onDeliveryError: (error: Error, event: E) => void,\n bindings: ITuiSessionEventBinding[],\n): void {\n const ownedHandler = ((...args: never[]): void => {\n try {\n (handler as (...values: never[]) => void)(...args);\n } catch (error) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n try {\n onDeliveryError(normalized, event);\n } catch {\n // Diagnostics cannot make a committed session operation fail.\n }\n }\n }) as IInteractiveSessionEvents[E];\n session.on(event, ownedHandler);\n bindings.push({ event, handler: ownedHandler as (...args: never[]) => void });\n}\n","import { printablePeerDriver } from '@robota-sdk/agent-core';\n\nimport { attributedUserEcho } from './attributed-user-echo.js';\nimport { bindTuiSessionEvent, bindTuiSessionNoticeEvents } from './tui-session-binding.js';\n\nimport type { AttentionCoordinator } from './attention/attention-coordinator.js';\nimport type { ITuiSessionEventBinding } from './tui-session-binding.js';\nimport type { TuiStateManager } from './tui-state-manager.js';\nimport type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';\nimport type { InteractiveSession } from '@robota-sdk/agent-framework';\nimport type { IExecutionWorkspaceEvent } from '@robota-sdk/agent-interface-execution';\nimport type {\n IExecutionResult,\n IInteractiveSessionEvents,\n TInteractiveEventName,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiSessionEventProjectorOptions {\n session: InteractiveSession;\n manager: TuiStateManager;\n onUserMessage: (content: string) => void;\n requestPermission: (\n toolName: string,\n toolArgs: TToolArgs,\n id: string,\n canPersistProjectPermission?: boolean,\n requestedByPeer?: string,\n ) => Promise<TPermissionResultValue>;\n askUser: (request: IActionRequest, id: string) => Promise<TActionResponse>;\n dismissPrompt: (id: string) => void;\n onDeliveryError?: (error: Error, event: TInteractiveEventName) => void;\n /** SCREEN-1992: fed the events the interval recap counts; absent ⇒ nothing is counted. */\n attention?: AttentionCoordinator;\n}\n\n/** Projects runtime session events into TUI state and owns the complete listener lifecycle. */\nexport class TuiSessionEventProjector {\n private readonly bindings: ITuiSessionEventBinding[] = [];\n\n constructor(private readonly options: ITuiSessionEventProjectorOptions) {}\n\n wire(): void {\n if (this.bindings.length > 0) return;\n const { session, manager, attention } = this.options;\n\n const onUserMessage = (content: string): void => {\n this.options.onUserMessage(content);\n manager.addUserEcho(attributedUserEcho(content, session));\n };\n const syncHistory = (): void => manager.syncHistory(session.getFullHistory());\n const onComplete = (result: IExecutionResult): void => {\n manager.onComplete(result);\n attention?.onComplete();\n syncHistory();\n };\n const onError = (error: Error): void => {\n manager.onError(error);\n attention?.onError();\n syncHistory();\n };\n const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void => {\n manager.syncExecutionWorkspaceSnapshot(event.snapshot);\n attention?.onWorkspaceSnapshot(event.snapshot);\n };\n\n this.bind('user_message', onUserMessage);\n this.bind('text_delta', manager.onTextDelta);\n this.bind('tool_start', manager.onToolStart);\n this.bind('tool_end', manager.onToolEnd);\n this.bind('thinking', manager.onThinking);\n this.bind('complete', onComplete);\n this.bind('interrupted', manager.onInterrupted);\n this.bind('error', onError);\n this.bind('context_update', manager.onContextUpdate);\n this.bind('compact', syncHistory);\n this.bind('skill_activation', syncHistory);\n this.bind('memory_event', syncHistory);\n this.bind('execution_workspace_event', onExecutionWorkspaceEvent);\n this.bind('history_cleared', () => manager.clearHistory());\n // SCREEN-1992: subscribed regardless, so the channel's subscribed set equals the classification.\n this.bind('turn_source', (source) => attention?.onTurnSource(source));\n bindTuiSessionNoticeEvents(this.bind.bind(this), manager);\n\n const onPermissionRequest: IInteractiveSessionEvents['permission_request'] = ({\n id,\n toolName,\n toolArgs,\n canPersistProjectPermission,\n requesterDriverId,\n }) => {\n attention?.onNeedsInput();\n // A peer turn's ask names the peer, printed only as a plain identifier.\n const requestedByPeer = requesterDriverId?.startsWith('peer:')\n ? printablePeerDriver(requesterDriverId)\n : undefined;\n void this.options\n .requestPermission(toolName, toolArgs, id, canPersistProjectPermission, requestedByPeer)\n .then((result) => session.resolvePermission(id, result))\n .catch(() => session.resolvePermission(id, false));\n };\n const onAskRequest: IInteractiveSessionEvents['ask_request'] = ({ id, request }) => {\n attention?.onNeedsInput();\n void this.options\n .askUser(request, id)\n .then((response) => session.resolveAsk(id, response))\n .catch(() => session.resolveAsk(id, { type: 'cancelled' }));\n };\n this.bind('permission_request', onPermissionRequest);\n this.bind('ask_request', onAskRequest);\n this.bind('prompt_resolved', ({ id }) => this.options.dismissPrompt(id));\n attention?.wire();\n }\n\n unwire(): void {\n const { session } = this.options;\n this.options.attention?.unwire();\n for (const { event, handler } of this.bindings) {\n session.off(event, handler as IInteractiveSessionEvents[typeof event]);\n }\n this.bindings.length = 0;\n }\n\n private bind<E extends TInteractiveEventName>(\n event: E,\n handler: IInteractiveSessionEvents[E],\n ): void {\n bindTuiSessionEvent(\n this.options.session,\n event,\n handler,\n (error) => {\n const report = this.options.onDeliveryError;\n if (report) report(error, event);\n else this.options.manager.addSessionEventDeliveryError(error, event);\n },\n this.bindings,\n );\n }\n}\n","/**\n * ARCH-005 — the TUI's session-option mapping, split out of `TuiInteractionChannel`.\n *\n * Pure: it maps the channel's already-resolved options onto the framework's `TInteractiveSessionOptions`.\n * RUNTIME-001 — the channel then builds through the shared `buildRuntimeSession` seam (agent-framework),\n * never a private `new InteractiveSession`: one session-construction SSOT across the TUI, print, and\n * `--serve`.\n */\n\nimport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\nimport type { TInteractiveSessionOptions } from '@robota-sdk/agent-framework';\n\n/** Map the channel's resolved options onto the framework session-construction options. */\nexport function buildTuiSessionOptions(\n opts: ITuiInteractionChannelOptions,\n): TInteractiveSessionOptions {\n return {\n cwd: opts.cwd,\n ...(opts.livePromptTrace ? { livePromptTrace: opts.livePromptTrace } : {}),\n provider: opts.provider,\n ...(opts.providerErrorGuidance !== undefined\n ? { providerErrorGuidance: opts.providerErrorGuidance }\n : {}),\n ...(opts.promptFileReferenceTag !== undefined\n ? { promptFileReferenceTag: opts.promptFileReferenceTag }\n : {}),\n ...(opts.modelCommandToolPrefix !== undefined\n ? { modelCommandToolPrefix: opts.modelCommandToolPrefix }\n : {}),\n ...(opts.subagentHookEnvironmentNames !== undefined\n ? { subagentHookEnvironmentNames: opts.subagentHookEnvironmentNames }\n : {}),\n ...(opts.observerFailureWarningCode !== undefined\n ? { observerFailureWarningCode: opts.observerFailureWarningCode }\n : {}),\n ...(opts.commandHookShell !== undefined ? { commandHookShell: opts.commandHookShell } : {}),\n ...(opts.bare === true ? { bare: true } : {}),\n ...(opts.skipConfiguredHooks === true ? { skipConfiguredHooks: true } : {}),\n ...(opts.orgPolicy !== undefined ? { orgPolicy: opts.orgPolicy } : {}),\n ...(opts.projectAccess !== undefined ? { projectAccess: opts.projectAccess } : {}),\n ...(opts.projectSettingsPaths !== undefined\n ? { projectSettingsPaths: opts.projectSettingsPaths }\n : {}),\n ...(opts.baselinePermissionAllow !== undefined\n ? { baselinePermissionAllow: opts.baselinePermissionAllow }\n : {}),\n ...(opts.taskContext !== undefined ? { taskContext: opts.taskContext } : {}),\n ...(opts.contributionSources !== undefined\n ? { contributionSources: opts.contributionSources }\n : {}),\n ...(opts.skillRoots !== undefined ? { skillRoots: opts.skillRoots } : {}),\n ...(opts.userSettingsSources !== undefined\n ? { userSettingsSources: opts.userSettingsSources }\n : {}),\n ...(opts.editCheckpointStore !== undefined\n ? { editCheckpointStore: opts.editCheckpointStore }\n : {}),\n // CLI-076: forward the resolved model so `--model` takes effect rather than falling through to the\n // session's config/default model.\n ...(opts.model !== undefined ? { model: opts.model } : {}),\n ...(opts.effort !== undefined ? { effort: opts.effort } : {}),\n ...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),\n ...(opts.maxOutputTokens !== undefined ? { maxOutputTokens: opts.maxOutputTokens } : {}),\n ...(opts.presetSystemPrompt !== undefined\n ? { presetSystemPrompt: opts.presetSystemPrompt }\n : {}),\n ...(opts.responseFormat !== undefined ? { responseFormat: opts.responseFormat } : {}),\n ...(opts.outputStyle !== undefined ? { outputStyle: opts.outputStyle } : {}),\n permissionMode: opts.permissionMode,\n maxTurns: opts.maxTurns,\n // REMOTE-007: no injected permission/ask handlers — the TUI subscribes to the session's\n // transport-neutral `permission_request`/`ask_request` events (wireSessionEvents) and answers via\n // `resolvePermission`/`resolveAsk`. The local Ink queues + rendering are unchanged.\n sessionStore: opts.sessionStore,\n disableSessionLoops: opts.disableSessionLoops,\n resolveDefaultLoopPrompt: opts.resolveDefaultLoopPrompt,\n resumeSessionId: opts.resumeSessionId,\n forkSession: opts.forkSession,\n ...(opts.workspaceMovedFrom !== undefined\n ? { workspaceMovedFrom: opts.workspaceMovedFrom }\n : {}),\n sessionName: opts.sessionName,\n backgroundTaskRunners: opts.backgroundTaskRunners,\n ...(opts.toolCallHandoff !== undefined ? { toolCallHandoff: opts.toolCallHandoff } : {}),\n subagentRunnerFactory: opts.subagentRunnerFactory,\n ...(opts.agentDefinitions !== undefined ? { agentDefinitions: opts.agentDefinitions } : {}),\n ...(opts.agentDefinitionRoots !== undefined\n ? { agentDefinitionRoots: opts.agentDefinitionRoots }\n : {}),\n ...(opts.pluginDirectories !== undefined ? { pluginDirectories: opts.pluginDirectories } : {}),\n ...(opts.additionalTools !== undefined ? { additionalTools: opts.additionalTools } : {}),\n ...(opts.defaultTools !== undefined ? { defaultTools: opts.defaultTools } : {}),\n commandModules: opts.commandModules,\n commandHostAdapters: opts.commandHostAdapters,\n shellExec: opts.shellExec,\n ...(opts.remoteCommandPolicy ? { remoteCommandPolicy: opts.remoteCommandPolicy } : {}),\n language: opts.language,\n agentName: opts.agentName,\n activePresetId: opts.activePresetId,\n persona: opts.persona,\n systemPrompt: opts.systemPrompt,\n appendSystemPrompt: opts.appendSystemPrompt,\n allowedTools: opts.allowedTools,\n deniedTools: opts.deniedTools,\n enableParallelSubagents: opts.enableParallelSubagents,\n selfVerification: opts.selfVerification,\n terminalHandoff: opts.terminalHandoff,\n // #1844: the session reads these when `/provider switch` hot-swaps. Absent, the switch throws\n // \"Unknown provider: <name>. Currently supported: \" with an EMPTY list — measured, not inferred.\n ...(opts.providerDefinitions ? { providerDefinitions: opts.providerDefinitions } : {}),\n // SELFHOST-008 P6: forward the surface-resolved memory fields only when present (absent ⇒ OFF).\n ...(opts.memoryStore ? { memoryStore: opts.memoryStore } : {}),\n ...(opts.automaticMemory ? { automaticMemory: opts.automaticMemory } : {}),\n ...(opts.recallMemory ? { recallMemory: opts.recallMemory } : {}),\n // SCREEN-1993: the owner-prompt append (absent ⇒ the controller records nothing).\n ...(opts.promptHistory ? { promptHistory: opts.promptHistory } : {}),\n };\n}\n","import type {\n IBranchEvent,\n IContextFileRefreshedEvent,\n IPlanApprovalEvent,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\nexport type TTuiSessionEventClassification = 'channel' | 'react-side-effect' | 'non-surface';\n\n/** Exhaustive TUI-owner policy over the shared session-event vocabulary. */\nexport const TUI_SESSION_EVENT_CLASSIFICATION = {\n text_delta: 'channel',\n tool_start: 'channel',\n tool_end: 'channel',\n thinking: 'channel',\n complete: 'channel',\n error: 'channel',\n context_update: 'channel',\n compact: 'channel',\n interrupted: 'channel',\n skill_activation: 'channel',\n background_task_event: 'non-surface',\n background_job_group_event: 'non-surface',\n execution_workspace_event: 'channel',\n user_message: 'channel',\n turn_source: 'channel',\n context_file_refreshed: 'channel',\n memory_event: 'channel',\n goal_event: 'non-surface',\n plan_event: 'channel',\n branch_event: 'channel',\n permission_request: 'channel',\n ask_request: 'channel',\n prompt_resolved: 'channel',\n ui_intent: 'react-side-effect',\n session_renamed: 'react-side-effect',\n history_cleared: 'channel',\n} as const satisfies Record<TInteractiveEventName, TTuiSessionEventClassification>;\n\nexport interface ITuiSessionEventNotice {\n id: string;\n event:\n | 'plan_event'\n | 'context_file_refreshed'\n | 'branch_event'\n | 'delivery-error'\n /** SCREEN-1992: TUI-originated — the interval recap on return, not a session event. */\n | 'attention-recap';\n message: string;\n}\n\nexport type TTuiNoticeInput =\n | { event: 'plan_event'; payload: IPlanApprovalEvent }\n | { event: 'context_file_refreshed'; payload: IContextFileRefreshedEvent }\n | { event: 'branch_event'; payload: IBranchEvent };\n\n/** Pure deterministic projection from domain event to TUI render state. */\nexport function createTuiSessionEventNotice(\n id: string,\n input: TTuiNoticeInput,\n): ITuiSessionEventNotice {\n switch (input.event) {\n case 'plan_event':\n return {\n id,\n event: input.event,\n message: `Plan ${input.payload.type.replaceAll('_', ' ')}`,\n };\n case 'context_file_refreshed':\n return {\n id,\n event: input.event,\n message: `Context refreshed: ${input.payload.filePath}`,\n };\n case 'branch_event':\n return {\n id,\n event: input.event,\n message: `Branch ${input.payload.kind.replaceAll('_', ' ')}: ${input.payload.branchId} @ ${input.payload.checkpointId}`,\n };\n }\n}\n","import {\n createTuiSessionEventNotice,\n type ITuiSessionEventNotice,\n type TTuiNoticeInput,\n} from './tui-session-events.js';\n\nimport type { TInteractiveEventName } from '@robota-sdk/agent-interface-session';\n\nconst NOTICE_LIMIT = 50;\n\n/** Bounded append-only render projection, deliberately separate from canonical session history. */\nexport class TuiSessionNoticeStore {\n private sequence = 0;\n readonly notices: ITuiSessionEventNotice[] = [];\n\n add(input: TTuiNoticeInput): void {\n this.append(createTuiSessionEventNotice(this.nextId(), input));\n }\n\n addDeliveryError(error: Error, event: TInteractiveEventName): void {\n this.append({\n id: this.nextId(),\n event: 'delivery-error',\n message: `Session event delivery failed (${event}): ${error.message}`,\n });\n }\n\n /** SCREEN-1992: one line for the unattended interval that just ended. */\n addAttentionRecap(line: string): void {\n this.append({ id: this.nextId(), event: 'attention-recap', message: line });\n }\n\n private nextId(): string {\n this.sequence += 1;\n return `session-event-${this.sequence}`;\n }\n\n private append(notice: ITuiSessionEventNotice): void {\n this.notices.push(notice);\n const overflow = this.notices.length - NOTICE_LIMIT;\n if (overflow > 0) this.notices.splice(0, overflow);\n }\n}\n","/**\n * TuiStateManager — pure TypeScript rendering state manager.\n *\n * Converts InteractiveSession events into rendering state without a React dependency.\n * The React hook subscribes to onChange and reads state for rendering.\n */\n\nimport { TuiSessionNoticeStore } from './tui-session-notice-store.js';\n\nimport type { TTuiNoticeInput } from './tui-session-events.js';\nimport type { IContextWindowState, IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceSnapshot } from '@robota-sdk/agent-interface-execution';\nimport type {\n IExecutionResult,\n IToolState,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\n/** Debounce interval for streaming text notify (limits renderMarkdown frequency) */\nconst STREAMING_DEBOUNCE_MS = 300;\n/** ERR-001 G3: with no provider activity for this long while thinking, hint that the\n * connection may be stalled (well under the 120s provider idle timeout). */\nconst STALL_HINT_MS = 15_000;\n/**\n * TUI view of the core context-window state. The token fields are derived from the agent-core\n * SSOT (`IContextWindowState`) via `Pick` so they stay structurally tied to it; `percentage` is an\n * explicit display-facing mirror of `IContextWindowState.usedPercentage`.\n */\nexport type TContextState = Pick<IContextWindowState, 'usedTokens' | 'maxTokens'> & {\n /** Mirror of `IContextWindowState.usedPercentage`, named for the TUI display layer. */\n percentage: number;\n};\n\n/** Create a debounced notify — schedules at most one call per interval. */\nfunction createDebouncedNotify(\n notify: () => void,\n ms: number,\n): { schedule: () => void; flush: () => void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n return {\n schedule() {\n if (!timer) {\n timer = setTimeout(() => {\n timer = null;\n notify();\n }, ms);\n }\n },\n flush() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n };\n}\n\nexport class TuiStateManager {\n // ── Rendering state ───────────────────────────────────────────\n history: IHistoryEntry[] = [];\n /**\n * Notices this terminal added itself (command results, init errors). They are not in the session's\n * history, so each remembers how many session entries preceded it and is re-inserted there on sync.\n */\n private localNotices: { readonly entry: IHistoryEntry; readonly afterSessionEntries: number }[] =\n [];\n streamingText = '';\n activeTools: IToolState[] = [];\n isThinking = false;\n isAborting = false;\n pendingPrompt: string | null = null;\n contextState: TContextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };\n executionWorkspaceSnapshot: IExecutionWorkspaceSnapshot | null = null;\n selectedExecutionEntryId: string | undefined;\n /** ERR-001 G2: humanized message of the last failed turn; cleared when the next turn starts. */\n lastErrorMessage: string | null = null;\n /** ERR-001 G3: no stream/tool activity for STALL_HINT_MS while thinking. */\n isStalled = false;\n private readonly sessionNoticeStore = new TuiSessionNoticeStore();\n /** Bounded render-only projection; never merged into canonical session history. */\n readonly sessionEventNotices = this.sessionNoticeStore.notices;\n\n /** Called after any state change. React hook sets this to trigger re-render. */\n onChange: (() => void) | null = null;\n\n // ── Internal ──────────────────────────────────────────────────\n private streamBuf = '';\n private debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);\n private stallTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** (Re)start the dead-air timer — any provider activity proves the connection is alive. */\n private armStallTimer(): void {\n this.clearStallTimer();\n this.stallTimer = setTimeout(() => {\n this.stallTimer = null;\n this.isStalled = true;\n this.notify();\n }, STALL_HINT_MS);\n }\n\n private clearStallTimer(): void {\n if (this.stallTimer) {\n clearTimeout(this.stallTimer);\n this.stallTimer = null;\n }\n if (this.isStalled) {\n this.isStalled = false;\n }\n }\n\n private notify(): void {\n this.onChange?.();\n }\n\n // ── Event handlers (InteractiveSession → state) ───────────────\n\n onTextDelta = (delta: string): void => {\n if (this.isThinking) this.armStallTimer();\n this.streamBuf += delta;\n this.streamingText = this.streamBuf;\n this.debouncedStreamNotify.schedule();\n };\n\n onToolStart = (state: IToolState): void => {\n // RUNTIME-39: a running tool is legitimate activity, not a stalled provider connection —\n // suppress the dead-air hint for the duration of the tool run (re-armed in onToolEnd).\n this.clearStallTimer();\n this.activeTools = [...this.activeTools, state];\n this.notify();\n };\n\n onToolEnd = (state: IToolState): void => {\n // findLastIndex: when same tool runs concurrently, match the most recently started instance\n const idx = this.activeTools.findLastIndex((t) => t.toolName === state.toolName && t.isRunning);\n if (idx !== -1) {\n const updated = [...this.activeTools];\n updated[idx] = state;\n this.activeTools = updated;\n }\n // RUNTIME-39: re-arm the dead-air watch only once no tool is still running and the turn is\n // still in progress — otherwise the provider is again the thing we're waiting on.\n if (this.isThinking && !this.activeTools.some((t) => t.isRunning)) {\n this.armStallTimer();\n }\n this.notify();\n };\n\n onThinking = (thinking: boolean): void => {\n this.isThinking = thinking;\n if (thinking) {\n // Clear at START of new execution (preserves previous result until next)\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.lastErrorMessage = null;\n this.armStallTimer();\n } else {\n this.isAborting = false;\n this.activeTools = [];\n this.clearStallTimer();\n }\n this.notify();\n };\n\n onComplete = (result: IExecutionResult): void => {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.contextState = {\n percentage: result.contextState.usedPercentage,\n usedTokens: result.contextState.usedTokens,\n maxTokens: result.contextState.maxTokens,\n };\n this.notify();\n };\n\n onInterrupted = (): void => {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n };\n\n onError = (error?: Error): void => {\n // The partial answer is preserved as an interrupted history entry by the framework\n // (ERR-001); clearing the local stream buffer here no longer loses it.\n this.clearStallTimer();\n this.lastErrorMessage = error?.message ?? 'Unknown error';\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n };\n\n onContextUpdate = (state: IContextWindowState): void => {\n this.setContextState({\n percentage: state.usedPercentage,\n usedTokens: state.usedTokens,\n maxTokens: state.maxTokens,\n });\n };\n\n // ── State updates from external sources ───────────────────────\n\n /**\n * Sync history from InteractiveSession — authoritative SSOT replace (SCREEN-010: no front-windowing).\n * The session history grows monotonically during a session, so Ink `<Static>` (count-based) renders\n * only the newly-appended tail; committed lines already in terminal scrollback are never re-printed.\n */\n syncHistory(entries: IHistoryEntry[]): void {\n if (entries.length === 0) return;\n // `<Static>` counts what it already printed, so a local notice dropped here would shift every\n // later index by one and the next committed entry (the assistant's answer) would never print.\n const merged: IHistoryEntry[] = [];\n let next = 0;\n for (const notice of this.localNotices) {\n const upTo = Math.min(notice.afterSessionEntries, entries.length);\n while (next < upTo) merged.push(entries[next++]!);\n merged.push(notice.entry);\n }\n while (next < entries.length) merged.push(entries[next++]!);\n this.history = merged;\n this.notify();\n }\n\n /** Add a notice this terminal owns; it survives the next session-history sync in place. */\n addEntry(entry: IHistoryEntry): void {\n this.localNotices.push({\n entry,\n afterSessionEntries: this.history.length - this.localNotices.length,\n });\n this.history = [...this.history, entry];\n this.notify();\n }\n\n /** Echo the user's prompt at once; the session's own user entry replaces it on the next sync. */\n addUserEcho(entry: IHistoryEntry): void {\n this.history = [...this.history, entry];\n this.notify();\n }\n\n clearHistory(): void {\n this.history = [];\n this.localNotices = [];\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n }\n\n /** Update pending prompt state */\n setPendingPrompt(prompt: string | null): void {\n this.pendingPrompt = prompt;\n this.notify();\n }\n\n /** Set aborting flag */\n setAborting(aborting: boolean): void {\n this.isAborting = aborting;\n this.notify();\n }\n\n /** Update context state */\n setContextState(state: TContextState): void {\n this.contextState = state;\n this.notify();\n }\n\n addSessionEventNotice(input: TTuiNoticeInput): void {\n this.sessionNoticeStore.add(input);\n this.notify();\n }\n\n addAttentionRecap(line: string): void {\n this.sessionNoticeStore.addAttentionRecap(line);\n this.notify();\n }\n\n addSessionEventDeliveryError(error: Error, event: TInteractiveEventName): void {\n this.sessionNoticeStore.addDeliveryError(error, event);\n this.notify();\n }\n\n syncExecutionWorkspaceSnapshot(snapshot: IExecutionWorkspaceSnapshot): void {\n const currentSelection = this.selectedExecutionEntryId;\n const hasCurrentSelection =\n currentSelection !== undefined &&\n snapshot.entries.some((entry) => entry.id === currentSelection);\n const selectedExecutionEntryId = hasCurrentSelection\n ? currentSelection\n : (snapshot.selectedEntryId ?? snapshot.entries[0]?.id);\n this.executionWorkspaceSnapshot = {\n ...snapshot,\n ...(selectedExecutionEntryId ? { selectedEntryId: selectedExecutionEntryId } : {}),\n };\n this.selectedExecutionEntryId = selectedExecutionEntryId;\n this.notify();\n }\n\n selectExecutionWorkspaceEntry(entryId: string): void {\n if (!this.executionWorkspaceSnapshot?.entries.some((entry) => entry.id === entryId)) return;\n this.selectedExecutionEntryId = entryId;\n this.executionWorkspaceSnapshot = {\n ...this.executionWorkspaceSnapshot,\n selectedEntryId: entryId,\n };\n this.notify();\n }\n\n /**\n * RUNTIME-52: release every timer this manager owns and detach the render callback. Called by the\n * channel's teardown (`stop()`); after dispose the manager must never fire `onChange` or trip the\n * stall hint on a discarded session.\n */\n dispose(): void {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.onChange = null;\n }\n}\n","/**\n * TuiInteractionChannel — session-owning presentation surface for the Ink TUI.\n *\n * Moves session lifecycle (InteractiveSession, CommandRegistry, TuiStateManager)\n * out of React hooks and into a plain TypeScript class.\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport {\n CommandRegistry,\n buildRuntimeSession,\n generateSessionName,\n} from '@robota-sdk/agent-framework';\n\nimport { AttentionCoordinator } from './attention/attention-coordinator.js';\nimport { MS_PER_SECOND } from './attention/time-units.js';\nimport { createSessionInitPoller } from './flows/session-init-poller.js';\nimport { applySystemCommandResult } from './hooks/command-result-handler.js';\nimport {\n TuiChannelLifecycleCoordinator,\n TuiChannelStartRollbackError,\n} from './tui-channel-lifecycle-coordinator.js';\nimport { TuiPermissionQueue, TuiUserActionQueue } from './tui-interaction-queues.js';\nimport { TuiSessionEventProjector } from './tui-session-event-projector.js';\nimport { buildTuiSessionOptions } from './tui-session-options.js';\nimport { TuiStateManager } from './tui-state-manager.js';\n\nimport type { IAttentionSource } from './attention/attention-tracker.js';\nimport type { ISessionInitPoller, TSessionInitFailure } from './flows/session-init-poller.js';\nimport type { TerminalHandoffController } from './terminal-handoff-controller.js';\nimport type {\n ITuiAppChannelPort,\n ITuiChannelSnapshot,\n ITuiCommandQueryPort,\n ITuiRuntimeStatusSnapshot,\n ITuiSessionUiEventPort,\n} from './tui-app-channel-port.js';\nimport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\nimport type { IPendingPermissionRequest } from './types.js';\nimport type {\n IHistoryEntry,\n TPermissionMode,\n TSessionEndReason,\n TToolArgs,\n} from '@robota-sdk/agent-core';\n// CMD-004 unified action contract (SSOT in agent-core).\nimport type {\n IActionRequest,\n TActionResponse as TUserActionResponse,\n} from '@robota-sdk/agent-core';\nimport type { InteractiveSession } from '@robota-sdk/agent-framework';\nimport type { IExecutionDetailPage } from '@robota-sdk/agent-interface-execution';\nimport type { ICommandInfo, TPermissionResultValue } from '@robota-sdk/agent-interface-session';\n\nconst SESSION_INIT_POLL_MS = 200;\nconst SESSION_INIT_TIMEOUT_MS = 15000;\n/** Upper bound on the graceful session shutdown so a wedged subsystem cannot block process exit\n * (CLI-075 / RUNTIME-33; api-boundary \"safely cancelled within a configurable timeout\"). */\nconst SHUTDOWN_TIMEOUT_MS = 5000;\n\nexport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\n\nexport class TuiInteractionChannel implements ITuiAppChannelPort {\n readonly stateManager: TuiStateManager;\n\n private readonly interactiveSession: InteractiveSession;\n private readonly registry: CommandRegistry;\n private readonly opts: ITuiInteractionChannelOptions;\n\n private submitHandler: ((text: string) => Promise<void>) | null = null;\n\n private readonly userActions: TuiUserActionQueue;\n private readonly permissions: TuiPermissionQueue;\n private readonly eventProjector: TuiSessionEventProjector;\n /** SCREEN-1992: joins the terminal's attention to this channel's recap; absent without a source. */\n private readonly attention: AttentionCoordinator | undefined;\n private readonly lifecycle: TuiChannelLifecycleCoordinator;\n availableCommands: ICommandInfo[] = [];\n sessionName: string | undefined;\n\n private autoNameTriggered = false;\n private initPoller: ISessionInitPoller | null = null;\n\n /** TERM-002: the App registers its Ink suspend/resume hooks into this controller. */\n get terminalHandoffController(): TerminalHandoffController | undefined {\n return this.opts.terminalHandoff;\n }\n /** Set by React hook to trigger re-render on state change */\n onChange: (() => void) | null = null;\n\n constructor(opts: ITuiInteractionChannelOptions) {\n this.opts = opts;\n this.sessionName = opts.sessionName;\n this.stateManager = new TuiStateManager();\n this.stateManager.onChange = () => this.onChange?.();\n this.userActions = new TuiUserActionQueue(() => this.onChange?.());\n this.permissions = new TuiPermissionQueue(() => this.onChange?.());\n\n this.interactiveSession = this.createSession();\n this.registry = this.createRegistry();\n this.attention = this.createAttention(opts.attention);\n this.eventProjector = new TuiSessionEventProjector({\n session: this.interactiveSession,\n manager: this.stateManager,\n ...(this.attention ? { attention: this.attention } : {}),\n onUserMessage: (content) => this.handleAutoNaming(content),\n requestPermission: (toolName, toolArgs, id, canPersistProjectPermission, requestedByPeer) =>\n this.permissions.enqueue(\n toolName,\n toolArgs,\n id,\n canPersistProjectPermission,\n requestedByPeer,\n ),\n askUser: (request, id) => this.askUser(request, id),\n dismissPrompt: (id) => this.dismissPromptById(id),\n ...(opts.onSessionEventDeliveryError\n ? { onDeliveryError: opts.onSessionEventDeliveryError }\n : {}),\n });\n this.lifecycle = new TuiChannelLifecycleCoordinator(\n {\n start: () => this.startRuntime(),\n stop: async () => {\n this.eventProjector.unwire();\n this.cancelAllPermissions();\n this.cancelAllUserActions();\n this.stopInitCheck();\n this.onChange = null;\n this.stateManager.dispose();\n await this.stopTransports();\n },\n beginShutdown: () => {\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.stateManager.addEntry(\n messageToHistoryEntry(createSystemMessage('Shutting down...')),\n );\n this.onChange?.();\n },\n shutdownSession: async ({ reason, message }) => {\n await this.interactiveSession.shutdown({ reason, message });\n },\n },\n SHUTDOWN_TIMEOUT_MS,\n );\n }\n\n private async startRuntime(): Promise<void> {\n let transportStartAttempted = false;\n try {\n this.eventProjector.wire();\n this.syncRestoredHistory();\n this.startInitCheck();\n if (this.opts.transportRegistry) {\n transportStartAttempted = true;\n await this.opts.bindTransports?.(this.interactiveSession);\n await this.opts.transportRegistry.startAll();\n }\n } catch (startError) {\n this.eventProjector.unwire();\n this.stopInitCheck();\n if (transportStartAttempted && this.opts.transportRegistry) {\n try {\n await this.stopTransports();\n } catch (rollbackError) {\n const rollbackErrors =\n rollbackError instanceof AggregateError ? rollbackError.errors : [rollbackError];\n throw new TuiChannelStartRollbackError(\n [startError, ...rollbackErrors],\n 'TUI channel start failed and transport rollback also failed.',\n );\n }\n }\n throw startError;\n }\n }\n\n private async stopTransports(): Promise<void> {\n if (!this.opts.transportRegistry) return;\n const result = await this.opts.transportRegistry.stopAll();\n if (result.errors.length > 0) {\n throw new AggregateError(result.errors, 'TUI transport teardown failed.');\n }\n }\n\n get permissionRequest(): IPendingPermissionRequest | null {\n return this.permissions.current;\n }\n\n /** CMD-004: the action currently awaiting a user answer, or null. */\n get pendingUserAction(): IActionRequest | null {\n return this.userActions.current;\n }\n\n get isShuttingDown(): boolean {\n return this.lifecycle.isShuttingDown;\n }\n\n get isActiveForPeerStatus(): boolean {\n return this.lifecycle.isActiveForPeerStatus;\n }\n\n private createSession(): InteractiveSession {\n return buildRuntimeSession(buildTuiSessionOptions(this.opts));\n }\n\n private createRegistry(): CommandRegistry {\n const registry = new CommandRegistry();\n for (const module of this.opts.commandModules ?? []) {\n registry.addModule(module);\n }\n this.opts.reloadPluginCommandSource?.(registry);\n return registry;\n }\n\n onSubmit(handler: (text: string) => Promise<void>): void {\n this.submitHandler = handler;\n }\n\n setAvailableCommands(commands: ICommandInfo[]): void {\n this.availableCommands = commands;\n this.onChange?.();\n }\n\n setBusy(busy: boolean): void {\n this.stateManager.onThinking(busy);\n }\n\n async start(): Promise<void> {\n await this.lifecycle.start();\n }\n\n /**\n * Full, idempotent channel teardown (CLI-075). Unwires every session listener, drains both\n * request queues, stops the init poller, disposes the render-state manager, stops transports, and\n * — unless a graceful `shutdown()` already ran — shuts the underlying session down so a discarded\n * or switched-away channel releases its background tasks, subagent processes, and timers.\n */\n async stop(): Promise<void> {\n await this.lifecycle.stop();\n }\n\n // ── Additional methods for App.tsx ───────────────────────────\n\n getSession(): InteractiveSession {\n return this.interactiveSession;\n }\n\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n subscribe(onChange: () => void): () => void {\n this.onChange = onChange;\n return () => {\n if (this.onChange === onChange) this.onChange = null;\n };\n }\n\n getSnapshot(): ITuiChannelSnapshot {\n const manager = this.stateManager;\n return {\n history: manager.history,\n streamingText: manager.streamingText,\n activeTools: manager.activeTools,\n isThinking: manager.isThinking,\n isAborting: manager.isAborting,\n lastErrorMessage: manager.lastErrorMessage,\n isStalled: manager.isStalled,\n sessionEventNotices: manager.sessionEventNotices,\n isShuttingDown: this.isShuttingDown,\n pendingPrompt: manager.pendingPrompt,\n pendingCount: this.interactiveSession.getPendingCount(),\n executionWorkspaceSnapshot: manager.executionWorkspaceSnapshot,\n ...(manager.selectedExecutionEntryId !== undefined\n ? { selectedExecutionEntryId: manager.selectedExecutionEntryId }\n : {}),\n permissionRequest: this.permissionRequest,\n pendingUserAction: this.pendingUserAction,\n contextState: manager.contextState,\n };\n }\n\n getCommandQueryPort(): ITuiCommandQueryPort {\n return this.registry;\n }\n\n getSessionUiEventPort(): ITuiSessionUiEventPort {\n return this.interactiveSession;\n }\n\n getRuntimeStatusSnapshot(fallbackPermissionMode: TPermissionMode): ITuiRuntimeStatusSnapshot {\n try {\n const session = this.interactiveSession.getSession();\n const activePresetId = session.getActivePresetId?.();\n const effort = session.getModelEffort();\n return {\n permissionMode: session.getPermissionMode(),\n sessionId: session.getSessionId(),\n ...(activePresetId !== undefined ? { activePresetId } : {}),\n ...(effort !== undefined ? { effort } : {}),\n };\n } catch {\n return { permissionMode: fallbackPermissionMode, sessionId: '' };\n }\n }\n\n addEntry(entry: IHistoryEntry): void {\n this.stateManager.addEntry(entry);\n }\n\n abort(): void {\n this.stateManager.setAborting(true);\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.interactiveSession.abort();\n }\n\n cancelQueue(): void {\n this.interactiveSession.cancelQueue();\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.stateManager.setPendingPrompt(null);\n }\n\n async stopWaitingSelfPacedLoop(): Promise<void> {\n const waiting = this.interactiveSession\n .listSelfPacedLoops()\n .filter((loop) => loop.phase === 'waiting');\n if (waiting.length === 0) return;\n if (waiting.length > 1) {\n this.addEntry(\n messageToHistoryEntry(\n createSystemMessage(\n 'Several self-paced loops are waiting. Use /loop list and /loop stop <id> to choose one.',\n ),\n ),\n );\n return;\n }\n const loopId = waiting[0]!.loopId;\n try {\n await this.interactiveSession.stopSelfPacedLoop(loopId, 'Loop stopped by Esc');\n this.addEntry(messageToHistoryEntry(createSystemMessage(`Loop ${loopId} stopped by Esc.`)));\n } catch (error) {\n this.addEntry(\n messageToHistoryEntry(\n createSystemMessage(\n `Could not stop loop ${loopId}: ${error instanceof Error ? error.message : String(error)}`,\n ),\n ),\n );\n }\n }\n\n async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {\n await this.lifecycle.shutdown(options);\n }\n\n selectExecutionWorkspaceEntry(entryId: string): void {\n this.stateManager.selectExecutionWorkspaceEntry(entryId);\n }\n\n async readExecutionWorkspaceDetail(entryId: string): Promise<IExecutionDetailPage> {\n return this.interactiveSession.readExecutionWorkspaceDetail(entryId);\n }\n\n async sendAgentJob(taskId: string, input: string): Promise<void> {\n await this.interactiveSession.sendAgentJob(taskId, input);\n }\n\n setSessionName(name: string): void {\n this.sessionName = name;\n this.interactiveSession.setName(name);\n this.onChange?.();\n }\n\n // ── CMD-004 unified ask path ─────────────────────────────────\n\n /**\n * Queue an ask and resolve when the user answers. Reached via the `ask_request` event handler\n * (which passes the framework prompt `id` for co-drive dismissal) and by any direct\n * `IInteractionChannel.askUser` caller (no id).\n */\n async askUser(request: IActionRequest, id?: string): Promise<TUserActionResponse> {\n return this.userActions.enqueue(request, id);\n }\n\n /** Called by App's PendingActionPrompt when the user answers (or cancels) the pending action. */\n resolveUserAction(request: IActionRequest, response: TUserActionResponse): void {\n this.userActions.resolveCurrent(request, response);\n }\n\n /** Resolve every queued/in-flight ask as cancelled (abort, shutdown). */\n private cancelAllUserActions(): void {\n this.userActions.cancelAll();\n }\n\n async handleInput(input: string): Promise<void> {\n if (!input.startsWith('/')) {\n await this.interactiveSession.submit(input);\n this.stateManager.setPendingPrompt(this.interactiveSession.getPendingPrompt());\n return;\n }\n await this.handleSlashCommand(input);\n }\n\n private async handleSlashCommand(input: string): Promise<void> {\n const parts = input.slice(1).split(/\\s+/);\n const cmd = parts[0]?.toLowerCase() ?? '';\n const args = parts.slice(1).join(' ');\n\n const result = await this.interactiveSession.executeCommand(cmd, args);\n if (result) {\n // CMD-004 Stage E: `data.sessionExecution` is the requester-local \"a session turn is now\n // running\" hint (formerly the `session-execution-started` effect).\n if (result.data?.['sessionExecution'] === true) {\n this.stateManager.setPendingPrompt(this.interactiveSession.getPendingPrompt());\n return;\n }\n applySystemCommandResult(\n result,\n this.interactiveSession,\n this.registry,\n this.stateManager,\n this.opts.reloadPluginCommandSource,\n );\n return;\n }\n\n this.stateManager.addEntry(\n messageToHistoryEntry(createSystemMessage(`Unknown command \"/${cmd}\". Type /help for help.`)),\n );\n }\n\n // ── Private helpers ──────────────────────────────────────────\n\n private handlePermissionRequest(\n toolName: string,\n toolArgs: TToolArgs,\n id?: string,\n ): Promise<TPermissionResultValue> {\n return this.permissions.enqueue(toolName, toolArgs, id);\n }\n\n /**\n * REMOTE-007 co-drive: another surface answered the prompt with this framework `id`, so dismiss the\n * still-showing local dialog. Resolving the local entry (cancelled) is a no-op on the framework side\n * — that id is already settled — but it clears the TUI's queue + render state.\n */\n private dismissPromptById(id: string): void {\n this.userActions.dismissById(id);\n this.permissions.dismissById(id);\n }\n\n /**\n * Resolve every queued/in-flight permission request as `false` (deny) — abort, cancelQueue,\n * shutdown, stop. Symmetric to `cancelAllUserActions()`: tearing down must neither leave a tool's\n * permission promise dangling (the tool would hang) nor silently grant it (CLI-075 RUNTIME-32).\n */\n private cancelAllPermissions(): void {\n this.permissions.cancelAll();\n }\n\n private handleAutoNaming(content: string): void {\n if (this.autoNameTriggered) return;\n if (this.opts.sessionName || this.interactiveSession.getName()) return;\n this.autoNameTriggered = true;\n generateSessionName(this.opts.provider, content)\n .then((name) => {\n this.interactiveSession.setName(name);\n this.sessionName = name;\n this.opts.onAutoNamed?.(name);\n this.onChange?.();\n })\n .catch(() => {\n this.autoNameTriggered = false;\n });\n }\n\n private syncRestoredHistory(): void {\n if (this.stateManager.history.length === 0) {\n const restored = this.interactiveSession.getFullHistory();\n if (restored.length > 0) {\n this.stateManager.syncHistory(restored);\n }\n }\n }\n\n private startInitCheck(): void {\n this.stopInitCheck();\n this.initPoller = createSessionInitPoller({\n check: () => this.runInitCheck(),\n intervalMs: SESSION_INIT_POLL_MS,\n timeoutMs: SESSION_INIT_TIMEOUT_MS,\n onReady: () => undefined,\n onFailure: (failure) => this.onInitFailure(failure),\n });\n this.initPoller.start();\n }\n\n /** Throws while the session is not ready; the init poller classifies the error. */\n private runInitCheck(): void {\n const ctx = this.interactiveSession.getContextState();\n const restoredSessionName = this.interactiveSession.getName();\n if (restoredSessionName && restoredSessionName !== this.sessionName) {\n this.sessionName = restoredSessionName;\n this.onChange?.();\n }\n this.stateManager.setContextState({\n percentage: ctx.usedPercentage,\n usedTokens: ctx.usedTokens,\n maxTokens: ctx.maxTokens,\n });\n const restored = this.interactiveSession.getFullHistory();\n if (restored.length > 0) {\n this.stateManager.syncHistory(restored);\n }\n this.syncExecutionWorkspace();\n }\n\n /** SCREEN-1992: the recap lands in this channel's notice store; no source ⇒ no coordinator. */\n private createAttention(source: IAttentionSource | undefined): AttentionCoordinator | undefined {\n if (!source) return undefined;\n return new AttentionCoordinator({\n source,\n onRecap: (line) => this.stateManager.addAttentionRecap(line),\n });\n }\n\n private onInitFailure(failure: TSessionInitFailure): void {\n const message =\n failure.kind === 'timeout'\n ? `Session initialization timed out after ${SESSION_INIT_TIMEOUT_MS / MS_PER_SECOND}s${\n failure.lastError ? ` (last error: ${failure.lastError.message})` : ''\n }`\n : `Session initialization failed: ${failure.error.message}`;\n this.stateManager.onError();\n this.stateManager.addEntry({\n id: `session-init-error-${Date.now()}`,\n timestamp: new Date(),\n category: 'event',\n type: 'session-init-error',\n data: { message },\n });\n }\n\n private stopInitCheck(): void {\n this.initPoller?.stop();\n this.initPoller = null;\n }\n\n private syncExecutionWorkspace(): void {\n try {\n // allow-fallback: session may not be initialized yet; swallow until ready\n this.stateManager.syncExecutionWorkspaceSnapshot(\n this.interactiveSession.getExecutionWorkspaceSnapshot({\n selectedEntryId: this.stateManager.selectedExecutionEntryId,\n }),\n );\n } catch {\n // allow-fallback: session may not be initialized yet; swallow until ready\n /* Session not initialized yet */\n }\n }\n}\n","/**\n * Ink render entry point.\n */\n\nimport chalk from 'chalk';\nimport { render } from 'ink';\nimport React from 'react';\n\nimport App from './App.js';\nimport { AttentionTracker } from './attention/attention-tracker.js';\nimport { FocusReportingStdin } from './attention/focus-input-filter.js';\nimport { KeybindingsProvider } from './keybindings/keybindings-context.js';\nimport { ProductDisplayNameProvider } from './product-display-name-context.js';\nimport { writeScreenReaderAnnouncement } from './screen-reader-announcement.js';\nimport { ScreenReaderProvider } from './screen-reader-context.js';\nimport { ScreenReaderPacingProvider } from './screen-reader-pacing-context.js';\nimport { awaitStartupQuietPeriod, resolvePacing } from './screen-reader-pacing.js';\nimport type { IScreenReaderPacingOverrides } from './screen-reader-pacing.js';\nimport { createParkedStdout, toPacingPort } from './screen-reader-stdout.js';\nimport { isInteractiveColorTerminal, supportsFocusReporting } from './terminal-capabilities.js';\nimport { TerminalCapabilitiesProvider } from './terminal-capabilities-context.js';\nimport type { ITerminalCapabilityOverrides } from './terminal-capabilities-context.js';\nimport { createFocusReportingWriter } from './terminal-focus-reporting.js';\nimport { TerminalHandoffController } from './terminal-handoff-controller.js';\nimport { TuiInteractionChannel } from './TuiInteractionChannel.js';\n\nimport type { IKeybindingsSource } from './keybindings/node-keybindings-source.js';\nimport type { TScreenReaderChannel } from './screen-reader-announcement.js';\nimport type { IThemeRegistry } from './theme/theme-registry.js';\nimport type { ITuiAppChannelPort } from './tui-app-channel-port.js';\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { ITuiInteractionChannelOptions } from './TuiInteractionChannel.js';\nimport type {\n IAIProvider,\n IToolWithEventService,\n IProviderDefinition,\n} from '@robota-sdk/agent-core';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type {\n IBackgroundTaskRunner,\n ICommandHostAdapters,\n IOutputStylePrompt,\n ICommandModule,\n IRemoteCommandPolicy,\n TSubagentRunnerFactory,\n IAgentDefinition,\n TShellExecFn,\n CommandRegistry,\n IMemoryStore,\n IPromptHistoryOptions,\n IAutomaticMemoryConfig,\n IPerTurnRecallConfig,\n TWorkspaceProjectAccess,\n EditCheckpointStore,\n IOrgPolicy,\n IProviderErrorGuidance,\n IProjectSettingsPath,\n IContributionSource,\n ISkillRootDescriptor,\n INodeHostSettingsSource,\n IToolCallHandoffPolicy,\n ICreateSessionOptions,\n ILivePromptTracePort,\n} from '@robota-sdk/agent-framework';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type {\n IInteractiveSessionStore,\n IPromptHistorySource,\n} from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\nexport interface IRenderOptions {\n cwd: string;\n livePromptTrace?: ILivePromptTracePort;\n /** Product identity for terminal labels, title, and host-facing copy. */\n productDisplayName?: string;\n modelCommandToolPrefix?: string;\n subagentHookEnvironmentNames?: ICreateSessionOptions['subagentHookEnvironmentNames'];\n observerFailureWarningCode?: ICreateSessionOptions['observerFailureWarningCode'];\n commandHookShell?: string;\n /** Skip instruction files and plugin discovery (`--safe-mode`). */\n bare?: boolean;\n /** Run no hook the settings layers declare (`--safe-mode`). */\n skipConfiguredHooks?: boolean;\n promptFileReferenceTag?: string;\n provider: IAIProvider;\n providerErrorGuidance?: IProviderErrorGuidance;\n projectAccess?: TWorkspaceProjectAccess;\n projectSettingsPaths?: readonly IProjectSettingsPath[];\n baselinePermissionAllow?: readonly string[];\n /** Host-selected task-context root; absent means no framework task-directory scan. */\n taskContext?: { readonly enabled?: boolean; readonly dir?: string };\n contributionSources?: readonly IContributionSource[];\n skillRoots?: readonly ISkillRootDescriptor[];\n userSettingsSources?: readonly INodeHostSettingsSource[];\n /**\n * CLI-083 (issue #2287) — the org policy, forwarded to the session so `blockedCommands` is\n * enforced on the plain `robota` path as well as under `--serve`.\n *\n * DECLARED, not merely spread through. The shell forwards this with\n * `...(orgPolicy === null ? {} : { orgPolicy })`, and a spread bypasses TypeScript's\n * excess-property check — so before this field existed the value compiled, arrived, and was\n * dropped by `toChannelOptions` below, which copies field by field. The idiom that looked safest\n * is what disabled the one check that would have caught the missing declaration.\n */\n orgPolicy?: IOrgPolicy | undefined;\n /** Explicit authority- and permission-backed edit checkpoint capability. */\n editCheckpointStore?: EditCheckpointStore;\n providerOverride?: string | undefined;\n /**\n * #1844: forwarded to the session so `/provider switch` can construct the provider it switches TO.\n *\n * The session cannot discover these — they are assembled at the composition root from the provider\n * packages. Without them the hot-swap throws with an empty supported-list, which is the failure\n * this option exists to prevent rather than a nicety.\n */\n providerDefinitions?: readonly IProviderDefinition[];\n providerType?: string | undefined;\n modelId?: string;\n /** CLI-1988: resolved provider-neutral response style. */\n outputStyle?: IOutputStylePrompt;\n /** ARCH-013: resolved preset effort, forwarded to the session's `effort` seam. */\n effort?: ITuiInteractionChannelOptions['effort'];\n temperature?: number;\n maxOutputTokens?: number;\n /** Preset prompt seed, distinct from the replacing `systemPrompt` option. */\n presetSystemPrompt?: string;\n /** CLI-sourced additive prompt text, composed before the TUI is rendered. */\n appendSystemPrompt?: string;\n responseFormat?: ITuiInteractionChannelOptions['responseFormat'];\n language?: string;\n permissionMode?: TPermissionMode;\n maxTurns?: number;\n allowedTools?: readonly string[];\n deniedTools?: readonly string[];\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n disableSessionLoops?: boolean;\n resolveDefaultLoopPrompt?: () => string;\n resumeSessionId?: string;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: text a deep link prefilled into the composer. Never submitted on its own. */\n initialInput?: string;\n /** Where that text came from; `external-link` renders the provenance notice. */\n initialInputOrigin?: 'external-link';\n forkSession?: boolean;\n /** Issue #3081: the startup session is the target of a `/cd` from this directory. */\n workspaceMovedFrom?: string;\n sessionName?: string;\n backgroundTaskRunners?: IBackgroundTaskRunner[];\n /** MCP-004: the tool-call handoff policy the composition root computed for this runtime. */\n toolCallHandoff?: IToolCallHandoffPolicy;\n subagentRunnerFactory?: TSubagentRunnerFactory;\n /**\n * ARCH-005: subagent definitions contributed by the composition root (the capability packs\n * `assembleProduct` merged). Forwarded to the session's `agentDefinitions` seam; absent ⇒ unchanged.\n */\n agentDefinitions?: readonly IAgentDefinition[];\n agentDefinitionRoots?: readonly string[];\n pluginDirectories?: { readonly user?: string; readonly project?: string };\n /**\n * ARCH-006: tools contributed by the composition root (the capability packs `assembleProduct` merged)\n * and, when the profile hands the packs the whole tool surface, the suppressed framework default tier\n * (`defaultTools: []`). Forwarded to the session's tool-composition seam; absent ⇒ unchanged.\n */\n additionalTools?: IToolWithEventService[];\n defaultTools?: readonly IToolWithEventService[];\n commandModules?: readonly ICommandModule[];\n commandHostAdapters?: ICommandHostAdapters;\n shellExec?: TShellExecFn;\n /** REMOTE-006: optional remote-command policy (allow-by-default; local == remote). */\n remoteCommandPolicy?: IRemoteCommandPolicy;\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n bindTransports?: ITuiInteractionChannelOptions['bindTransports'];\n cliAdapter: ITuiCliAdapter;\n reloadPluginCommandSource?: (registry: CommandRegistry) => void;\n agentName?: string;\n /** Active preset id selected at startup (PRESET-011 runtime state). Defaults to 'default'. */\n activePresetId?: string;\n /** Preset persona block composed as a `source: 'persona'` system-prompt section (priority 5). */\n persona?: string;\n /** Preset execution capability: activate agent runtime + subagent/background dispatch. */\n enableParallelSubagents?: boolean;\n /** Preset execution capability: run a post-task self-verification step. */\n selfVerification?: boolean;\n /**\n * Called with each live channel (including session-switch re-creations). Lets the embedding\n * product wire process-level concerns (ERR-001 G1: error routing into the live session).\n */\n onChannelReady?: (channel: TuiInteractionChannel) => void;\n /**\n * SELFHOST-008 P6: optional durable-memory store injected by the surface (agent-cli). Forwarded to the\n * channel → `buildRuntimeSession`; absent ⇒ memory OFF (today's behavior). Enablement is surface-owned.\n */\n memoryStore?: IMemoryStore;\n /** SELFHOST-008 P6: optional automatic post-turn capture policy (absent ⇒ capture OFF). */\n automaticMemory?: IAutomaticMemoryConfig;\n /** SELFHOST-008 P6: optional per-turn recall policy (absent ⇒ recall OFF, startup-only injection). */\n recallMemory?: IPerTurnRecallConfig;\n /**\n * CLI-2004: the resolved screen-reader mode. DECLARED, not spread through — `toChannelOptions`\n * below copies field by field, so an undeclared field compiles, arrives and is silently dropped\n * (the ARCH-110 containment hazard the `orgPolicy` comment above records). Absent ⇒ mode OFF and\n * today's byte stream is unchanged.\n */\n screenReader?: boolean | undefined;\n /** Host-selected raw timing overrides; absent values use the renderer's defaults. */\n screenReaderPacing?: IScreenReaderPacingOverrides;\n /** Host-selected cursor and turn-mark overrides; absent values use terminal detection. */\n terminalCapabilities?: ITerminalCapabilityOverrides;\n /** CLI-2004: which input turned the mode on — printed in the confirmation line. */\n screenReaderChannel?: TScreenReaderChannel | undefined;\n /** CLI-2004: mode off, but the environment suggests a reader is running ⇒ one advisory line. */\n screenReaderHint?: boolean | undefined;\n /** BEHAVIOR-2003: one watched source shared with the optional `/keybindings` command. */\n keybindingsSource?: IKeybindingsSource;\n /**\n * SCREEN-2002: the theme catalogue this run renders from, shared with the optional `/theme`\n * command. DECLARED, not spread — see the `screenReader` note above. Absent ⇒ the built-ins.\n */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n /**\n * SCREEN-1992: the focus-reporting override the product shell resolved from its own environment.\n * `true` forces DECSET 1004 on, `false` is the kill switch, absent ⇒ on for an interactive TTY.\n */\n focusReporting?: boolean | undefined;\n /**\n * SCREEN-1993: prompt history, resolved by the product shell. `promptHistory` is the session-side\n * writer (forwarded to the channel like `memoryStore`); `promptHistorySource` and\n * `promptHistoryProject` feed the input area's Ctrl+R search. DECLARED, not spread through — the\n * projection below copies field by field. Absent ⇒ nothing is written and `ctrl+r` is inert.\n */\n promptHistory?: IPromptHistoryOptions;\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n}\n\n/** Map render options to TuiInteractionChannel constructor options. */\nexport function toChannelOptions(\n options: IRenderOptions,\n resumeSessionId?: string,\n): ConstructorParameters<typeof TuiInteractionChannel>[0] {\n return {\n cwd: options.cwd,\n ...(options.livePromptTrace ? { livePromptTrace: options.livePromptTrace } : {}),\n provider: options.provider,\n ...(options.providerErrorGuidance !== undefined\n ? { providerErrorGuidance: options.providerErrorGuidance }\n : {}),\n ...(options.promptFileReferenceTag !== undefined\n ? { promptFileReferenceTag: options.promptFileReferenceTag }\n : {}),\n ...(options.modelCommandToolPrefix !== undefined\n ? { modelCommandToolPrefix: options.modelCommandToolPrefix }\n : {}),\n ...(options.subagentHookEnvironmentNames !== undefined\n ? { subagentHookEnvironmentNames: options.subagentHookEnvironmentNames }\n : {}),\n ...(options.observerFailureWarningCode !== undefined\n ? { observerFailureWarningCode: options.observerFailureWarningCode }\n : {}),\n ...(options.commandHookShell !== undefined\n ? { commandHookShell: options.commandHookShell }\n : {}),\n ...(options.bare === true ? { bare: true } : {}),\n ...(options.skipConfiguredHooks === true ? { skipConfiguredHooks: true } : {}),\n ...(options.projectAccess !== undefined ? { projectAccess: options.projectAccess } : {}),\n ...(options.projectSettingsPaths !== undefined\n ? { projectSettingsPaths: options.projectSettingsPaths }\n : {}),\n ...(options.baselinePermissionAllow !== undefined\n ? { baselinePermissionAllow: options.baselinePermissionAllow }\n : {}),\n ...(options.taskContext !== undefined ? { taskContext: options.taskContext } : {}),\n ...(options.contributionSources !== undefined\n ? { contributionSources: options.contributionSources }\n : {}),\n ...(options.skillRoots !== undefined ? { skillRoots: options.skillRoots } : {}),\n ...(options.userSettingsSources !== undefined\n ? { userSettingsSources: options.userSettingsSources }\n : {}),\n ...(options.orgPolicy !== undefined ? { orgPolicy: options.orgPolicy } : {}),\n ...(options.editCheckpointStore !== undefined\n ? { editCheckpointStore: options.editCheckpointStore }\n : {}),\n ...(options.providerDefinitions ? { providerDefinitions: options.providerDefinitions } : {}),\n // CLI-076: the display model id doubles as the session's model override so `--model` actually reaches\n // the provider chat call (header/status line == the model actually called).\n ...(options.modelId !== undefined ? { model: options.modelId } : {}),\n ...(options.effort !== undefined ? { effort: options.effort } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {}),\n ...(options.presetSystemPrompt !== undefined\n ? { presetSystemPrompt: options.presetSystemPrompt }\n : {}),\n ...(options.appendSystemPrompt !== undefined\n ? { appendSystemPrompt: options.appendSystemPrompt }\n : {}),\n ...(options.responseFormat !== undefined ? { responseFormat: options.responseFormat } : {}),\n ...(options.outputStyle !== undefined ? { outputStyle: options.outputStyle } : {}),\n permissionMode: options.permissionMode,\n maxTurns: options.maxTurns,\n allowedTools: options.allowedTools,\n deniedTools: options.deniedTools,\n sessionStore: options.sessionStore,\n disableSessionLoops: options.disableSessionLoops,\n resolveDefaultLoopPrompt: options.resolveDefaultLoopPrompt,\n resumeSessionId,\n forkSession: options.forkSession,\n sessionName: options.sessionName,\n backgroundTaskRunners: options.backgroundTaskRunners,\n ...(options.toolCallHandoff !== undefined ? { toolCallHandoff: options.toolCallHandoff } : {}),\n subagentRunnerFactory: options.subagentRunnerFactory,\n ...(options.agentDefinitions !== undefined\n ? { agentDefinitions: options.agentDefinitions }\n : {}),\n ...(options.agentDefinitionRoots !== undefined\n ? { agentDefinitionRoots: options.agentDefinitionRoots }\n : {}),\n ...(options.pluginDirectories !== undefined\n ? { pluginDirectories: options.pluginDirectories }\n : {}),\n ...(options.additionalTools !== undefined ? { additionalTools: options.additionalTools } : {}),\n ...(options.defaultTools !== undefined ? { defaultTools: options.defaultTools } : {}),\n commandModules: options.commandModules,\n commandHostAdapters: options.commandHostAdapters,\n shellExec: options.shellExec,\n remoteCommandPolicy: options.remoteCommandPolicy,\n transportRegistry: options.transportRegistry,\n bindTransports: options.bindTransports,\n language: options.language,\n reloadPluginCommandSource: options.reloadPluginCommandSource,\n agentName: options.agentName,\n activePresetId: options.activePresetId,\n persona: options.persona,\n enableParallelSubagents: options.enableParallelSubagents,\n selfVerification: options.selfVerification,\n // SELFHOST-008 P6: forward the surface-resolved memory fields (absent ⇒ OFF).\n ...(options.memoryStore ? { memoryStore: options.memoryStore } : {}),\n ...(options.automaticMemory ? { automaticMemory: options.automaticMemory } : {}),\n ...(options.recallMemory ? { recallMemory: options.recallMemory } : {}),\n // CLI-2004: declared above AND projected here — the hand-maintained copy is the exact place an\n // undeclared option disappears, so both halves are asserted by one test.\n ...(options.screenReader !== undefined ? { screenReader: options.screenReader } : {}),\n // SCREEN-1993: the session-side writer (absent ⇒ nothing is recorded).\n promptHistory: options.promptHistory,\n };\n}\n\nexport async function waitForRenderAndStop<TResult>(\n waitUntilExit: () => Promise<TResult>,\n getActiveChannel: () => Pick<ITuiAppChannelPort, 'stop'> | undefined,\n): Promise<void> {\n let renderFailure: Error | undefined;\n try {\n await waitUntilExit();\n } catch (cause) {\n renderFailure = cause instanceof Error ? cause : new Error(String(cause));\n }\n let stopFailure: Error | undefined;\n try {\n await getActiveChannel()?.stop();\n } catch (cause) {\n stopFailure = cause instanceof Error ? cause : new Error(String(cause));\n }\n if (renderFailure !== undefined && stopFailure !== undefined) {\n throw new AggregateError([renderFailure, stopFailure], 'TUI render and teardown both failed.');\n }\n if (renderFailure !== undefined) throw renderFailure;\n if (stopFailure !== undefined) throw stopFailure;\n}\n\nexport async function renderApp(options: IRenderOptions): Promise<void> {\n // ERR-001 / Library Neutrality Rule: NO process-level error policy here — process survival\n // is the product assembly's boundary (agent-cli installs the guards via onChannelReady).\n try {\n await options.keybindingsSource?.start();\n await renderStartedApp(options);\n } finally {\n options.keybindingsSource?.dispose();\n }\n}\n\nasync function renderStartedApp(options: IRenderOptions): Promise<void> {\n // CLI-2004: one resolved boolean drives Ink's own screen-reader support AND the React context\n // every component reads. Both are set here so they can never disagree.\n const screenReader = options.screenReader === true;\n writeScreenReaderAnnouncement({\n enabled: screenReader,\n channel: options.screenReaderChannel,\n hint: options.screenReaderHint,\n });\n const pacing = resolvePacing({ enabled: screenReader, overrides: options.screenReaderPacing });\n // SCREEN-2670: the pre-write park. Constructed only when the mode is on AND the interval is\n // non-zero, so with the mode off `stdout` is not passed at all and Ink defaults to\n // `process.stdout` — the object it keys its instance map by — exactly as today.\n const parked =\n screenReader && pacing.preparkMs > 0\n ? createParkedStdout({ stdout: process.stdout, preparkMs: pacing.preparkMs })\n : undefined;\n\n // SCREEN-006: chalk (ink's styling engine) does not implement the NO_COLOR convention itself\n // (verified: chalk 5's vendored supports-color reads only FORCE_COLOR/TTY/TERM), so on a real\n // TTY `NO_COLOR=1` would still color every component. Sync chalk once with the package's single\n // color gate (`terminal-capabilities.ts` — the SSOT that DOES honor NO_COLOR) so gate-off means\n // zero SGR color output. Gate-on changes nothing: chalk's own level detection stays authoritative.\n if (!isInteractiveColorTerminal()) {\n chalk.level = 0;\n }\n\n // TERM-002: one terminal-handoff controller per process (one Ink instance / App). Shared across\n // channel re-creations (session switch) so the handoff capability survives a session swap.\n const handoffController = new TerminalHandoffController();\n\n // SCREEN-1992: focus reporting is negotiated once for the process; the tracker outlives channels.\n // Screen-reader mode keeps the recap (a plain notice line) — only the row countdown tick is off.\n const focusReportingSupported = supportsFocusReporting({ override: options.focusReporting });\n const focusReporting = createFocusReportingWriter({ supported: () => focusReportingSupported });\n const attention = new AttentionTracker({\n focusReporting: focusReportingSupported,\n now: Date.now,\n });\n handoffController.setTerminalModeHooks({\n preSuspend: async () => {\n focusReporting.disable();\n // SCREEN-2670: nothing parked may land on top of the child's output.\n await parked?.drain();\n },\n postResume: () => focusReporting.enable(),\n });\n\n // Concrete framework creation has one composition boundary. React receives only the bounded port;\n // App owns which narrowed channel is active, while each channel owns its own lifecycle.\n let activeChannel: ITuiAppChannelPort | undefined;\n // Issue #3081: the move is announced by the FIRST session only — a later switch to another\n // session (a `/fork` attach) did not move anywhere.\n let pendingWorkspaceMovedFrom = options.workspaceMovedFrom;\n const createChannel = (resumeSessionId?: string): ITuiAppChannelPort => {\n const workspaceMovedFrom = pendingWorkspaceMovedFrom;\n pendingWorkspaceMovedFrom = undefined;\n const channel = new TuiInteractionChannel({\n ...toChannelOptions(options, resumeSessionId),\n ...(workspaceMovedFrom !== undefined ? { workspaceMovedFrom } : {}),\n terminalHandoff: handoffController,\n attention,\n });\n // Expose each live channel (incl. session-switch re-creations) to the embedding product,\n // e.g. for process-level error routing (ERR-001 G1).\n options.onChannelReady?.(channel);\n activeChannel = channel;\n return channel;\n };\n\n // The startup quiet period sits between the confirmation line and the first frame, so a reader\n // finishes announcing the mode before the prompt lands. A keypress ends it early; `0` skips it.\n await awaitStartupQuietPeriod(pacing.startupQuietMs, process.stdin);\n\n // The filtering proxy is attached only now: the quiet period consumed its own keystroke above, and\n // from here on every byte reaches Ink through the proxy, minus the focus sequences.\n const stdin = new FocusReportingStdin(process.stdin, {\n negotiated: () => focusReporting.negotiated,\n onFocusIn: () => attention.focusIn(),\n onFocusOut: () => attention.focusOut(),\n onKeystroke: () => attention.keystroke(),\n });\n process.stdin.resume();\n // Requested only now, so a focus event the terminal answers with lands in the proxy, never in\n // the quiet period's keypress wait; the tracker's idle source runs until the first one arrives.\n focusReporting.enable();\n attention.start();\n\n const pacingPort = parked === undefined ? undefined : toPacingPort(parked);\n const tree = (\n <ProductDisplayNameProvider\n name={options.productDisplayName}\n modelCommandToolPrefix={options.modelCommandToolPrefix}\n >\n <KeybindingsProvider source={options.keybindingsSource}>\n <ScreenReaderProvider enabled={screenReader}>\n <TerminalCapabilitiesProvider overrides={options.terminalCapabilities}>\n <App\n cwd={options.cwd}\n createChannel={createChannel}\n providerOverride={options.providerOverride}\n providerType={options.providerType}\n modelId={options.modelId}\n permissionMode={options.permissionMode}\n version={options.version}\n sessionStore={options.sessionStore}\n resumeSessionId={options.resumeSessionId}\n showSessionPickerOnStart={options.showSessionPickerOnStart}\n initialInput={options.initialInput}\n initialInputOrigin={options.initialInputOrigin}\n startupUpdateNotice={options.startupUpdateNotice}\n transportRegistry={options.transportRegistry}\n pluginAdapter={options.commandHostAdapters?.plugin}\n cliAdapter={options.cliAdapter}\n promptHistorySource={options.promptHistorySource}\n promptHistoryProject={options.promptHistoryProject}\n themeRegistry={options.themeRegistry}\n reducedMotion={options.reducedMotion}\n reducedMotionOverride={options.reducedMotionOverride}\n />\n </TerminalCapabilitiesProvider>\n </ScreenReaderProvider>\n </KeybindingsProvider>\n </ProductDisplayNameProvider>\n );\n const instance = render(\n pacingPort === undefined ? (\n tree\n ) : (\n <ScreenReaderPacingProvider port={pacingPort}>{tree}</ScreenReaderPacingProvider>\n ),\n {\n exitOnCtrlC: false,\n isScreenReaderEnabled: screenReader,\n stdin: stdin.asInkStdin(),\n ...(parked === undefined ? {} : { stdout: parked.asInkStdout() }),\n },\n );\n // The controller needs the Ink instance to clear the frame before a handoff.\n handoffController.setInkInstance(instance);\n try {\n await waitForRenderAndStop(\n () => instance.waitUntilExit(),\n () => activeChannel,\n );\n } finally {\n // The last frame of a session must not be the one the park loses: drain before restoring.\n await parked?.flush();\n // Leave the terminal as it was found: no focus sequences after exit, no reader on stdin.\n handoffController.setTerminalModeHooks(undefined);\n focusReporting.disable();\n attention.dispose();\n stdin.detach();\n process.stdin.pause();\n }\n}\n","import { Box, render, useApp, useInput, useStdout } from 'ink';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport stringWidth from 'string-width';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { writeScreenReaderAnnouncement, type TScreenReaderChannel } from './screen-reader-announcement.js';\nimport { ScreenReaderProvider, useScreenReader } from './screen-reader-context.js';\n\nexport interface ISupervisedViewRow {\n readonly id: string;\n readonly liveness: 'alive' | 'dead' | 'unknown';\n readonly control: 'available' | 'unavailable';\n readonly activity: 'working' | 'needs-input' | 'idle' | 'unknown';\n readonly nextLoopAt?: string;\n readonly name?: string;\n readonly cwd?: string;\n readonly pr?: { readonly url: string; readonly host: string; readonly number: number;\n readonly kind: 'pull' | 'merge-request' };\n readonly problem?: 'invalid-registration';\n}\n\nexport interface ISupervisedSessionViewProps {\n readonly loadRows: (signal: AbortSignal) => Promise<readonly ISupervisedViewRow[]>;\n readonly onStop?: (id: string) => Promise<void>;\n readonly onStart?: () => Promise<string>;\n readonly onOpenPr?: (id: string, url: string) => Promise<void>;\n readonly filteredByCwd?: boolean;\n readonly filteredByName?: boolean;\n readonly filteredByPr?: boolean;\n readonly stateFilter?: TGroup;\n readonly refreshMs?: number;\n}\n\nconst GROUP_ORDER = ['needs-input', 'working', 'idle', 'unknown', 'unverified', 'dead'] as const;\ntype TGroup = typeof GROUP_ORDER[number];\ntype TDisplayLine =\n | { readonly kind: 'group'; readonly label: string }\n | { readonly kind: 'row'; readonly row: ISupervisedViewRow; readonly index: number };\n\nfunction groupOf(row: ISupervisedViewRow): TGroup {\n if (row.liveness === 'dead') return 'dead';\n if (row.liveness !== 'alive' || row.control !== 'available') return 'unverified';\n return row.activity;\n}\n\nfunction sortedRows(rows: readonly ISupervisedViewRow[]): readonly ISupervisedViewRow[] {\n return [...rows].sort((a, b) =>\n GROUP_ORDER.indexOf(groupOf(a)) - GROUP_ORDER.indexOf(groupOf(b)) || a.id.localeCompare(b.id));\n}\n\nfunction sortedDirectoryRows(rows: readonly ISupervisedViewRow[]): readonly ISupervisedViewRow[] {\n return [...rows].sort((a, b) =>\n (a.cwd === undefined ? 1 : 0) - (b.cwd === undefined ? 1 : 0) ||\n (a.cwd ?? '').localeCompare(b.cwd ?? '') ||\n GROUP_ORDER.indexOf(groupOf(a)) - GROUP_ORDER.indexOf(groupOf(b)) || a.id.localeCompare(b.id));\n}\n\nfunction uniqueDirectorySuffix(cwd: string, directories: readonly string[], budget: number): string {\n if (cwd === '/') return cwd;\n let longestSharedSuffix = 0;\n for (const other of directories) {\n if (other === cwd) continue;\n let shared = 0;\n while (shared < cwd.length && shared < other.length &&\n cwd[cwd.length - shared - 1] === other[other.length - shared - 1]) shared++;\n longestSharedSuffix = Math.max(longestSharedSuffix, shared);\n }\n const basenameLength = cwd.split(/[\\\\/]/u).filter(Boolean).at(-1)?.length ?? cwd.length;\n const uniqueLength = Math.min(cwd.length, Math.max(basenameLength, longestSharedSuffix + 3));\n const suffixStart = cwd.length - uniqueLength;\n const boundary = Math.max(cwd.lastIndexOf('/', suffixStart - 1), cwd.lastIndexOf('\\\\', suffixStart - 1));\n const componentSuffix = cwd.slice(boundary + 1);\n return stringWidth(displayPath(componentSuffix)) <= budget ? componentSuffix : cwd.slice(suffixStart);\n}\n\nfunction pathTokens(path: string): readonly string[] {\n return Array.from(path, (character) => /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/u.test(character)\n ? `\\\\u{${character.codePointAt(0)!.toString(16)}}` : character);\n}\n\nfunction displayPath(path: string): string {\n return pathTokens(path).join('');\n}\n\nfunction compactPath(path: string, budget: number): string {\n const safe = pathTokens(path);\n if (stringWidth(safe.join('')) <= budget) return safe.join('');\n const headBudget = Math.ceil((budget - 1) / 2);\n const tailBudget = budget - headBudget - 1;\n let head = '';\n for (const token of safe) {\n if (stringWidth(head) + stringWidth(token) > headBudget) break;\n head += token;\n }\n let tail = '';\n for (const token of [...safe].reverse()) {\n if (stringWidth(tail) + stringWidth(token) > tailBudget) break;\n tail = token + tail;\n }\n return `${head}…${tail}`;\n}\n\nfunction sameRows(a: readonly ISupervisedViewRow[], b: readonly ISupervisedViewRow[]): boolean {\n return a.length === b.length && a.every((row, index) => {\n const other = b[index];\n return other !== undefined && row.id === other.id && row.liveness === other.liveness &&\n row.control === other.control && row.activity === other.activity && row.problem === other.problem &&\n row.nextLoopAt === other.nextLoopAt && row.name === other.name && row.cwd === other.cwd &&\n row.pr?.url === other.pr?.url;\n });\n}\n\nfunction loopWaitLabel(nextLoopAt: string, observedAtMs: number): string {\n const remaining = Date.parse(nextLoopAt) - observedAtMs;\n if (!Number.isFinite(remaining)) return '';\n if (remaining <= 0) return 'loop eligible now';\n return remaining < 60_000 ? 'loop eligible in <1m' : `loop eligible in ${Math.ceil(remaining / 60_000)}m`;\n}\n\nexport default function SupervisedSessionView({\n loadRows,\n onStop,\n onStart,\n onOpenPr,\n filteredByCwd = false,\n filteredByName = false,\n filteredByPr = false,\n stateFilter,\n refreshMs = 2_000,\n}: ISupervisedSessionViewProps): React.ReactElement {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const columns = Math.max(12, stdout.columns ?? 80);\n const screenReader = useScreenReader();\n const [rows, setRows] = useState<readonly ISupervisedViewRow[]>([]);\n const [observedAtMs, setObservedAtMs] = useState(Date.now);\n const [status, setStatus] = useState<'loading' | 'ready' | 'unavailable'>('loading');\n const [selectedId, setSelectedId] = useState<string | undefined>();\n const [showHelp, setShowHelp] = useState(false);\n const [groupByDirectory, setGroupByDirectory] = useState(false);\n const [confirmStopId, setConfirmStopId] = useState<string | undefined>();\n const [stopStatus, setStopStatus] = useState<'idle' | 'unavailable' | 'stopping' | 'stopped' | 'failed'>('idle');\n const [lastStoppedId, setLastStoppedId] = useState<string | undefined>();\n const stoppingRef = useRef(false);\n const [startStatus, setStartStatus] = useState<'idle' | 'starting' | 'started' | 'failed'>('idle');\n const [lastStartedId, setLastStartedId] = useState<string | undefined>();\n const startingRef = useRef(false);\n const [prOpenStatus, setPrOpenStatus] = useState<'idle' | 'opening' | 'opened' | 'failed' | 'unavailable'>('idle');\n const openingPrRef = useRef(false);\n const mountedRef = useRef(true);\n const ordered = useMemo(() => {\n const filtered = rows.filter((row) => stateFilter === undefined || groupOf(row) === stateFilter);\n return groupByDirectory ? sortedDirectoryRows(filtered) : sortedRows(filtered);\n }, [rows, stateFilter, groupByDirectory]);\n const displayLines = useMemo((): readonly TDisplayLine[] => {\n const lines: TDisplayLine[] = [];\n const directories = [...new Set(ordered.map((row) => row.cwd).filter((cwd): cwd is string => cwd !== undefined))];\n let previousGroup: string | undefined;\n let directoryNumber = 0;\n ordered.forEach((row, index) => {\n const group = groupByDirectory ? row.cwd ?? 'unverified' : groupOf(row);\n if (group !== previousGroup) {\n const label = groupByDirectory\n ? row.cwd === undefined ? 'Directory: unverified'\n : (() => {\n const prefix = `Dir ${++directoryNumber}: `;\n const budget = Math.max(4, columns - prefix.length);\n const suffix = compactPath(uniqueDirectorySuffix(row.cwd, directories, budget), budget);\n const full = `${prefix}${suffix} — ${displayPath(row.cwd)}`;\n return screenReader || stringWidth(full) <= columns ? full : `${prefix}${suffix}`;\n })()\n : `${group}:`;\n lines.push({ kind: 'group', label });\n }\n lines.push({ kind: 'row', row, index });\n previousGroup = group;\n });\n return lines;\n }, [ordered, groupByDirectory, columns, screenReader]);\n\n useEffect(() => {\n const controller = new AbortController();\n let mounted = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const poll = async (): Promise<void> => {\n try {\n const next = sortedRows(await loadRows(controller.signal));\n if (!mounted || controller.signal.aborted) return;\n const nowMs = Date.now();\n setRows((previous) => sameRows(previous, next) ? previous : next);\n setObservedAtMs((previous) => next.some((row) => row.nextLoopAt !== undefined &&\n loopWaitLabel(row.nextLoopAt, previous) !== loopWaitLabel(row.nextLoopAt, nowMs)) ? nowMs : previous);\n setStatus('ready');\n } catch {\n if (mounted && !controller.signal.aborted) setStatus('unavailable');\n } finally {\n if (mounted && !controller.signal.aborted) timer = setTimeout(() => { void poll(); }, refreshMs);\n }\n };\n void poll();\n return () => {\n mounted = false;\n if (timer !== undefined) clearTimeout(timer);\n controller.abort();\n };\n }, [loadRows, refreshMs]);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => { mountedRef.current = false; };\n }, []);\n\n useEffect(() => {\n setSelectedId((current) => current !== undefined && ordered.some((row) => row.id === current)\n ? current : ordered[0]?.id);\n }, [ordered]);\n\n useInput((input, key) => {\n if (stoppingRef.current) return;\n if (openingPrRef.current && input !== 'q' && !(!screenReader && key.escape) && !(key.ctrl && input === 'c')) return;\n if (startingRef.current && input !== 'q' && !(!screenReader && key.escape) && !(key.ctrl && input === 'c')) return;\n if (confirmStopId !== undefined) {\n if (input === 'n' || key.escape) {\n setConfirmStopId(undefined);\n return;\n }\n if (input === 'y') {\n const row = ordered.find((candidate) => candidate.id === confirmStopId);\n setConfirmStopId(undefined);\n if (status !== 'ready' || row?.liveness !== 'alive' || row.control !== 'available' || onStop === undefined) {\n setStopStatus('unavailable');\n return;\n }\n stoppingRef.current = true;\n setStopStatus('stopping');\n void onStop(confirmStopId).then(() => {\n if (!mountedRef.current) return;\n setLastStoppedId(confirmStopId);\n setStopStatus('stopped');\n }).catch(() => {\n if (mountedRef.current) setStopStatus('failed');\n }).finally(() => { stoppingRef.current = false; });\n }\n return;\n }\n if (input === 'q' || (!screenReader && key.escape) || (key.ctrl && input === 'c')) {\n exit();\n return;\n }\n if (input === '?') {\n setShowHelp((value) => !value);\n return;\n }\n if (input === 'g') {\n numbered.clear();\n setGroupByDirectory((value) => !value);\n return;\n }\n if (input === 'n' && onStart !== undefined) {\n startingRef.current = true;\n setStartStatus('starting');\n void Promise.resolve().then(onStart).then((id) => {\n if (!mountedRef.current) return;\n setLastStartedId(id);\n setStartStatus('started');\n }).catch(() => {\n if (mountedRef.current) setStartStatus('failed');\n }).finally(() => { startingRef.current = false; });\n return;\n }\n if (input === 'p' && onOpenPr !== undefined) {\n const row = ordered.find((candidate) => candidate.id === selectedId);\n if (status !== 'ready' || row?.liveness !== 'alive' || row.control !== 'available' || !row.pr) {\n setPrOpenStatus('unavailable');\n return;\n }\n openingPrRef.current = true;\n setPrOpenStatus('opening');\n void onOpenPr(row.id, row.pr.url).then(() => {\n if (mountedRef.current) setPrOpenStatus('opened');\n }).catch(() => {\n if (mountedRef.current) setPrOpenStatus('failed');\n }).finally(() => { openingPrRef.current = false; });\n return;\n }\n if (input === 's' && onStop !== undefined) {\n const row = ordered.find((candidate) => candidate.id === selectedId);\n if (status !== 'ready' || row?.liveness !== 'alive' || row.control !== 'available') {\n setStopStatus('unavailable');\n } else {\n setStopStatus('idle');\n setConfirmStopId(row.id);\n }\n return;\n }\n if (screenReader || ordered.length === 0) return;\n if (key.upArrow || key.downArrow) {\n const index = Math.max(0, ordered.findIndex((row) => row.id === selectedId));\n const next = Math.max(0, Math.min(ordered.length - 1, index + (key.downArrow ? 1 : -1)));\n setSelectedId(ordered[next]?.id);\n }\n });\n\n const numbered = useNumberedSelection({\n enabled: screenReader && confirmStopId === undefined && stopStatus !== 'stopping',\n itemCount: ordered.length,\n cancellable: true,\n repeatable: true,\n onSelect: (index) => {\n setSelectedId(ordered[index]?.id);\n setStopStatus('idle');\n },\n onCancel: () => { if (!stoppingRef.current) exit(); },\n });\n\n const selectedRow = ordered.find((row) => row.id === selectedId);\n const selectedName = status === 'ready' && selectedRow?.liveness === 'alive' &&\n selectedRow.control === 'available' ? selectedRow.name : undefined;\n const selectedLoopStatus = status === 'ready' && selectedRow?.liveness === 'alive' &&\n selectedRow.control === 'available' && selectedRow.activity === 'idle' && selectedRow.nextLoopAt\n ? loopWaitLabel(selectedRow.nextLoopAt, observedAtMs) : '';\n const selectedPr = status === 'ready' && selectedRow?.liveness === 'alive' &&\n selectedRow.control === 'available' ? selectedRow.pr : undefined;\n\n const height = Math.max(8, stdout.rows ?? 24);\n const helpLines = [\n 'Keys:',\n screenReader ? 'Number+Enter Select' : '↑/↓ Select',\n 's Request stop',\n 'g Group state/dir',\n ...(onStart === undefined ? [] : ['n New session']),\n ...(onOpenPr === undefined ? [] : ['p Open linked PR']),\n 'y Confirm stop',\n 'n/Esc Cancel stop',\n 'q/Esc/Ctrl+C Close',\n '? Toggle help',\n 'Activity ≠ liveness',\n 'Idle ≠ attach-ready',\n 'Close keeps sessions',\n ];\n // Reserve all fixed chrome plus both possible overflow indicators before choosing row lines.\n const helpVisible = showHelp && confirmStopId === undefined && stopStatus !== 'stopping';\n const fixedLines = 2 + (stateFilter === undefined ? 0 : 1)\n + (status === 'loading' || status === 'unavailable' || (status === 'ready' && ordered.length === 0) ? 1 : 0)\n + (selectedId === undefined ? 0 : 1)\n + (selectedName === undefined ? 0 : 1)\n + (selectedPr === undefined ? 0 : 2)\n + (selectedLoopStatus ? 1 : 0)\n + (prOpenStatus === 'idle' ? 0 : 1)\n + (startStatus === 'idle' ? 0 : 1)\n + (confirmStopId !== undefined ? (screenReader ? 1 : 2) : stopStatus !== 'idle' ? 1 : 0)\n + 1 + (helpVisible ? helpLines.length : 0) + 2;\n const viewport = Math.max(1, height - fixedLines);\n const selectedLine = Math.max(0, displayLines.findIndex((line) => line.kind === 'row' && line.row.id === selectedId));\n const start = Math.min(Math.max(0, selectedLine - Math.floor(viewport / 2)), Math.max(0, displayLines.length - viewport));\n const visible = screenReader ? displayLines : displayLines.slice(start, start + viewport);\n const chromeWrap = screenReader ? {} : { wrap: 'truncate-end' as const };\n const footer = stopStatus === 'stopping' ? 'Stop in progress; wait for result.'\n : confirmStopId !== undefined ? 'Confirm stop or cancel before closing.'\n : screenReader ? `Type a number and Enter to select; s Stop;${onStart ? ' n New;' : ''}${onOpenPr ? ' p Open PR;' : ''} g Group; Escape to close; ? Help.`\n : `↑↓ Navigate s Stop${onStart ? ' n New' : ''}${onOpenPr ? ' p Open PR' : ''} g Group ? Help q/Esc Close`;\n return (\n <Box flexDirection=\"column\" {...(screenReader ? {} : { height })}>\n <Text {...chromeWrap}>\n {filteredByCwd ? 'Background sessions in selected directory' : 'Background sessions across projects'}\n {filteredByName ? ' · name filter active' : ''}\n {filteredByPr ? ' · PR filter active' : ''}\n </Text>\n <Text {...chromeWrap}>{ordered.length} supervised session(s). Foreground peers and saved records are separate.</Text>\n {stateFilter !== undefined && <Text {...chromeWrap}>State: {stateFilter}</Text>}\n {status === 'loading' && <Text {...chromeWrap}>Loading supervised sessions...</Text>}\n {status === 'unavailable' && <Text {...chromeWrap}>Supervised session discovery unavailable; last verified rows remain below.</Text>}\n {status === 'ready' && ordered.length === 0 && <Text {...chromeWrap}>No supervised sessions.</Text>}\n {start > 0 && !screenReader && <Text>{start} more above</Text>}\n {visible.map((line) => line.kind === 'group'\n ? <Text key={`group-${line.label}`} {...chromeWrap}>{line.label}</Text>\n : <Text key={`row-${line.row.id}`} {...(screenReader ? {} : { wrap: 'truncate-end' as const })}>\n {screenReader ? numberedRowPrefix(line.index) : line.row.id === selectedId ? '> ' : ' '}\n {line.row.name && line.row.liveness === 'alive' && line.row.control === 'available'\n ? screenReader\n ? `${line.row.name} (${line.row.id})`\n : `${Array.from(line.row.name).slice(0, 24).join('')}${Array.from(line.row.name).length > 24 ? '…' : ''} [${line.row.id.slice(-8)}]`\n : line.row.id} activity {line.row.activity} liveness {line.row.liveness} control {line.row.control}\n {status === 'ready' && line.row.liveness === 'alive' && line.row.control === 'available' &&\n line.row.activity === 'idle' && line.row.nextLoopAt\n ? ` ${loopWaitLabel(line.row.nextLoopAt, observedAtMs)}` : ''}\n {line.row.problem ? ` ${line.row.problem}` : ''}\n {status === 'ready' && line.row.liveness === 'alive' && line.row.control === 'available' && line.row.pr\n ? ` ${line.row.pr.host} ${line.row.pr.kind === 'pull' ? '#' : '!'}${line.row.pr.number}` : ''}\n </Text>)}\n {!screenReader && start + visible.length < displayLines.length &&\n <Text>{displayLines.length - start - visible.length} more below</Text>}\n {selectedId !== undefined && <Text {...chromeWrap}>Selected {selectedId}</Text>}\n {selectedName !== undefined && <Text {...chromeWrap}>Name: {selectedName}</Text>}\n {selectedPr !== undefined && <Text {...chromeWrap}>PR: {selectedPr.host} {selectedPr.kind === 'pull' ? '#' : '!'}{selectedPr.number}</Text>}\n {selectedPr !== undefined && <Text {...chromeWrap}>URL: {selectedPr.url}</Text>}\n {selectedLoopStatus && <Text {...chromeWrap}>{selectedLoopStatus}</Text>}\n {prOpenStatus === 'opening' && <Text {...chromeWrap}>Opening linked PR...</Text>}\n {prOpenStatus === 'opened' && <Text {...chromeWrap}>Opened linked PR.</Text>}\n {prOpenStatus === 'failed' && <Text {...chromeWrap}>PR link changed or could not be opened.</Text>}\n {prOpenStatus === 'unavailable' && <Text {...chromeWrap}>No verified PR link is available.</Text>}\n {startStatus === 'starting' && <Text {...chromeWrap}>Starting a background session...</Text>}\n {startStatus === 'started' && <Text {...chromeWrap}>Started {lastStartedId}{filteredByName || stateFilter !== undefined ? ' (may be hidden by filter)' : ''}</Text>}\n {startStatus === 'failed' && <Text {...chromeWrap}>Start failed; check workspace trust or run session start --background for details.</Text>}\n {confirmStopId !== undefined && (screenReader\n ? <Text>Stop {confirmStopId}? y Yes / n No</Text>\n : <>\n <Text {...chromeWrap}>Stop …{confirmStopId.slice(-8)}?</Text>\n <Text {...chromeWrap}>y Yes / n No</Text>\n </>)}\n {confirmStopId === undefined && stopStatus === 'unavailable' &&\n <Text {...chromeWrap}>This session cannot be stopped from the view.</Text>}\n {stopStatus === 'stopping' && <Text {...chromeWrap}>Stopping selected session; wait for confirmation.</Text>}\n {stopStatus === 'stopped' && <Text {...chromeWrap}>Stopped {lastStoppedId}</Text>}\n {stopStatus === 'failed' && <Text {...chromeWrap}>Stop failed; session remains listed until verified otherwise.</Text>}\n {screenReader && ordered.length > 0 && confirmStopId === undefined && stopStatus !== 'stopping' &&\n <Text>{formatNumberedSelectionPrompt(ordered.length, true)}{numbered.buffer ? ` ${numbered.buffer}` : ''}</Text>}\n {screenReader && numbered.invalid && <Text>Selection out of range.</Text>}\n <Text {...chromeWrap}>{footer}</Text>\n {helpVisible && helpLines.map((line) => <Text key={line} {...chromeWrap}>{line}</Text>)}\n </Box>\n );\n}\n\nexport async function renderSupervisedSessionView(\n options: ISupervisedSessionViewProps & {\n readonly screenReader: boolean;\n readonly screenReaderChannel?: TScreenReaderChannel;\n readonly screenReaderHint?: boolean;\n },\n): Promise<void> {\n writeScreenReaderAnnouncement({\n enabled: options.screenReader,\n channel: options.screenReaderChannel,\n hint: options.screenReaderHint,\n });\n const instance = render(\n <ScreenReaderProvider enabled={options.screenReader}>\n <SupervisedSessionView loadRows={options.loadRows} onStop={options.onStop}\n onStart={options.onStart}\n onOpenPr={options.onOpenPr}\n filteredByCwd={options.filteredByCwd} filteredByName={options.filteredByName}\n filteredByPr={options.filteredByPr}\n stateFilter={options.stateFilter} refreshMs={options.refreshMs} />\n </ScreenReaderProvider>,\n { isScreenReaderEnabled: options.screenReader, exitOnCtrlC: false },\n );\n try {\n await instance.waitUntilExit();\n } finally {\n instance.unmount();\n }\n}\n","import { findProviderDefinition } from '@robota-sdk/agent-core';\nimport {\n applyActiveModelChange,\n createNodeHostSettingsStore,\n readSettings,\n resolveGitBranchFromNodeHost,\n} from '@robota-sdk/agent-framework';\n\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { IProviderDefinition } from '@robota-sdk/agent-core';\nimport type { CommandRegistry } from '@robota-sdk/agent-framework';\nimport type { TSettingsSource } from '@robota-sdk/agent-framework';\n\nexport interface IDefaultTuiCliAdapterOptions {\n providerDefinitions: readonly IProviderDefinition[];\n reloadPluginCommandSource: (registry: CommandRegistry) => void;\n userSettingsPath: string;\n settingsSources: readonly TSettingsSource[];\n formatResumeCommand?: (sessionId: string) => string;\n}\n\nexport function createDefaultTuiCliAdapter({\n providerDefinitions,\n reloadPluginCommandSource,\n userSettingsPath,\n settingsSources,\n formatResumeCommand,\n}: IDefaultTuiCliAdapterOptions): ITuiCliAdapter {\n return {\n getUserSettingsPath: () => userSettingsPath,\n readSettings: (path) => readSettings(path),\n reloadPluginCommandSource: (registry) => {\n reloadPluginCommandSource(registry);\n },\n applyActiveModelChange: (_cwd, modelId, options) => {\n const userStore = createNodeHostSettingsStore('user', userSettingsPath);\n applyActiveModelChange(settingsSources, [userStore], modelId, options);\n return { applied: true };\n },\n getGitBranch: (cwd) => resolveGitBranchFromNodeHost(cwd),\n getProviderDisplayName: (type) =>\n findProviderDefinition(providerDefinitions, type)?.displayName ?? type,\n ...(formatResumeCommand === undefined ? {} : { formatResumeCommand }),\n };\n}\n","import { watch, type FSWatcher } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\n\nimport {\n parseKeybindingsDocument,\n type IKeybindingDiagnostic,\n type IKeybindingSnapshot,\n} from './keybinding-registry.js';\n\nconst RELOAD_DEBOUNCE_MS = 10;\n\nexport interface IKeybindingsFilePort {\n ensureFile(): Promise<string>;\n}\n\nexport interface IKeybindingsSource extends IKeybindingsFilePort {\n readonly filePath: string;\n start(): Promise<void>;\n isStarted(): boolean;\n getSnapshot(): IKeybindingSnapshot;\n subscribe(listener: (snapshot: IKeybindingSnapshot) => void): () => void;\n dispose(): void;\n}\n\nexport interface INodeKeybindingsSourceOptions {\n readonly filePath: string;\n readonly schemaUrl: string;\n readonly onDiagnostic?: (diagnostic: IKeybindingDiagnostic) => void;\n}\n\nfunction defaultDocument(schemaUrl: string) {\n return { $schema: schemaUrl, version: 1 as const, bindings: {} };\n}\n\nfunction defaultSnapshot(filePath: string, schemaUrl: string): IKeybindingSnapshot {\n const parsed = parseKeybindingsDocument(JSON.stringify(defaultDocument(schemaUrl)), filePath);\n if (!parsed.ok) throw new Error(parsed.diagnostic.message);\n return parsed.snapshot;\n}\n\nclass NodeKeybindingsSource implements IKeybindingsSource {\n readonly filePath: string;\n readonly #schemaUrl: string;\n readonly #onDiagnostic: ((diagnostic: IKeybindingDiagnostic) => void) | undefined;\n readonly #listeners = new Set<(snapshot: IKeybindingSnapshot) => void>();\n #snapshot: IKeybindingSnapshot;\n #watcher: FSWatcher | undefined;\n #reloadTimer: ReturnType<typeof setTimeout> | undefined;\n #startPromise: Promise<void> | undefined;\n #disposed = false;\n\n constructor(options: INodeKeybindingsSourceOptions) {\n if (options.filePath.trim() === '') throw new Error('Keybindings file path must not be empty.');\n if (options.schemaUrl.trim() === '') throw new Error('Keybindings schema URL must not be empty.');\n this.filePath = options.filePath;\n this.#schemaUrl = options.schemaUrl;\n this.#onDiagnostic = options.onDiagnostic;\n this.#snapshot = defaultSnapshot(this.filePath, this.#schemaUrl);\n }\n\n start(): Promise<void> {\n if (this.#disposed) return Promise.reject(new Error('Keybindings source is disposed.'));\n if (this.#startPromise !== undefined) return this.#startPromise;\n this.#startPromise = this.#start().catch((cause) => {\n this.#watcher?.close();\n this.#watcher = undefined;\n this.#startPromise = undefined;\n throw cause;\n });\n return this.#startPromise;\n }\n\n async #start(): Promise<void> {\n const directory = dirname(this.filePath);\n await mkdir(directory, { recursive: true });\n await this.#load();\n if (this.#disposed) throw new Error('Keybindings source is disposed.');\n this.#watcher = watch(directory, { persistent: false }, (_event, filename) => {\n if (filename !== null && filename.toString() !== basename(this.filePath)) return;\n this.#scheduleReload();\n });\n }\n\n isStarted(): boolean {\n return this.#watcher !== undefined && !this.#disposed;\n }\n\n getSnapshot(): IKeybindingSnapshot {\n return this.#snapshot;\n }\n\n subscribe(listener: (snapshot: IKeybindingSnapshot) => void): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n async ensureFile(): Promise<string> {\n await mkdir(dirname(this.filePath), { recursive: true });\n try {\n await writeFile(this.filePath, `${JSON.stringify(defaultDocument(this.#schemaUrl), null, 2)}\\n`, {\n encoding: 'utf8',\n flag: 'wx',\n mode: 0o600,\n });\n } catch (cause) {\n if (!isAlreadyExists(cause as object)) throw cause;\n }\n return this.filePath;\n }\n\n dispose(): void {\n this.#disposed = true;\n if (this.#reloadTimer !== undefined) clearTimeout(this.#reloadTimer);\n this.#reloadTimer = undefined;\n this.#watcher?.close();\n this.#watcher = undefined;\n this.#listeners.clear();\n }\n\n #scheduleReload(): void {\n if (this.#disposed) return;\n if (this.#reloadTimer !== undefined) clearTimeout(this.#reloadTimer);\n this.#reloadTimer = setTimeout(() => {\n this.#reloadTimer = undefined;\n void this.#load();\n }, RELOAD_DEBOUNCE_MS);\n }\n\n async #load(): Promise<void> {\n let text: string;\n try {\n text = await readFile(this.filePath, 'utf8');\n } catch (cause) {\n if (isNotFound(cause as object)) return;\n this.#publishInvalid({\n file: this.filePath,\n path: '$',\n message: errorMessage(cause as object),\n });\n return;\n }\n const parsed = parseKeybindingsDocument(text, this.filePath);\n if (!parsed.ok) {\n this.#publishInvalid(parsed.diagnostic);\n return;\n }\n this.#publish(Object.freeze({ ...parsed.snapshot, generation: this.#snapshot.generation + 1 }));\n }\n\n #publishInvalid(diagnostic: IKeybindingDiagnostic): void {\n this.#onDiagnostic?.(diagnostic);\n this.#publish(\n Object.freeze({\n ...this.#snapshot,\n generation: this.#snapshot.generation + 1,\n diagnostic,\n }),\n );\n }\n\n #publish(snapshot: IKeybindingSnapshot): void {\n if (this.#disposed) return;\n this.#snapshot = snapshot;\n for (const listener of this.#listeners) listener(snapshot);\n }\n}\n\nfunction isNotFound(cause: object): boolean {\n return isNodeError(cause) && cause.code === 'ENOENT';\n}\n\nfunction isAlreadyExists(cause: object): boolean {\n return isNodeError(cause) && cause.code === 'EEXIST';\n}\n\nfunction isNodeError(cause: object): cause is NodeJS.ErrnoException {\n return cause instanceof Error && 'code' in cause;\n}\n\nfunction errorMessage(cause: object): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n\nexport function createNodeKeybindingsSource(\n options: INodeKeybindingsSourceOptions,\n): IKeybindingsSource {\n return new NodeKeybindingsSource(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["InkText","Text","formatStatusLabel","Text","Text","Text","Text","isPrintable","Text","Text","Text","MAX_VISIBLE","Text","isPrintable","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","Text","readFromAdapter","isRecord","errorMessage","errorMessage","Text","#schemaUrl","#onDiagnostic","#listeners","#snapshot","#disposed","#startPromise","#start","#watcher","#load","#scheduleReload","#reloadTimer","#publishInvalid","#publish"],"sources":["../../src/sanitize-terminal-text.ts","../../src/SafeText.tsx","../../src/theme/built-in-themes.ts","../../src/screen-reader-context.tsx","../../src/terminal-capabilities.ts","../../src/theme/theme-context.tsx","../../src/theme/theme-contracts.ts","../../src/theme/theme-styles.ts","../../src/theme/color-vision.ts","../../src/json-value.ts","../../src/theme/theme-document.ts","../../src/app-banner.tsx","../../src/attention/time-units.ts","../../src/attention/countdown.ts","../../src/status-glyph.ts","../../src/execution-workspace-view-model.ts","../../src/background-task-row-format.ts","../../src/hooks/useCountdownTick.ts","../../src/BackgroundTaskPanel.tsx","../../src/ContextWarningBanner.tsx","../../src/ExecutionWorkspaceDetailPane.tsx","../../src/flows/selection-flow.ts","../../src/execution-workspace-switcher-selection.ts","../../src/hooks/useNumberedSelection.ts","../../src/key-hint-footer.tsx","../../src/keybindings/keybinding-catalogue.ts","../../src/keybindings/keybinding-syntax.ts","../../src/keybindings/keybinding-registry.ts","../../src/keybindings/keybindings-context.tsx","../../src/numbered-list.tsx","../../src/ExecutionWorkspaceSwitcher.tsx","../../src/flows/cjk-cursor-motion.ts","../../src/flows/cjk-text-deletion.ts","../../src/flows/cjk-text-input-flow.ts","../../src/flows/defer-submit.ts","../../src/flows/real-cursor-flow.ts","../../src/hooks/useRealCursorPosition.ts","../../src/screen-reader-pacing-context.tsx","../../src/terminal-capabilities-context.tsx","../../src/CjkTextInput.tsx","../../src/external-prompt-notice.tsx","../../src/flows/input-area-flow.ts","../../src/history-search/history-search-flow.ts","../../src/HistorySearchOverlay.tsx","../../src/hooks/useAutocomplete.ts","../../src/history-search/useHistorySearch.ts","../../src/hooks/useInputAreaHistorySearch.ts","../../src/hooks/useInputAreaKeys.ts","../../src/utils/input-top-border.ts","../../src/input-area-rules.tsx","../../src/SlashAutocomplete.tsx","../../src/utils/paste-labels.ts","../../src/WaveText.tsx","../../src/InputArea.tsx","../../src/command-output-summary.ts","../../src/render-markdown.ts","../../src/hooks/useRenderMarkdown.ts","../../src/humanize-tool-name.ts","../../src/product-display-name-context.tsx","../../src/screen-reader-labels.ts","../../src/RoleLabel.tsx","../../src/tool-summary-status.ts","../../src/ToolCommandOutput.tsx","../../src/utils/tool-diff-summary.ts","../../src/ToolDiffBlock.tsx","../../src/UsageSummaryEntry.tsx","../../src/MessageList.tsx","../../src/ListPicker.tsx","../../src/MultiSelectList.tsx","../../src/flows/text-prompt-flow.ts","../../src/TextPrompt.tsx","../../src/PendingActionPrompt.tsx","../../src/flows/permission-prompt-flow.ts","../../src/PermissionPrompt.tsx","../../src/flows/confirm-prompt-flow.ts","../../src/ConfirmPrompt.tsx","../../src/hooks/usePluginScreenData.ts","../../src/MenuSelect.tsx","../../src/plugin-tui-handlers.ts","../../src/PluginTUI.tsx","../../src/SessionEventNotices.tsx","../../src/short-session-id.ts","../../src/SessionPicker.tsx","../../src/status-activity.ts","../../src/StatusBar.tsx","../../src/tui-cli-adapter-context.tsx","../../src/SessionStatusBar.tsx","../../src/StreamingIndicator.tsx","../../src/ThemePicker.tsx","../../src/TransportTUI.tsx","../../src/UpdateNotice.tsx","../../src/AppPresentation.tsx","../../src/app-static-items.ts","../../src/shutdown-signal.ts","../../src/attention-bell.ts","../../src/terminal-marks.ts","../../src/hooks/useScreenReaderTurnSignals.ts","../../src/hooks/useAppInputBindings.ts","../../src/hooks/usePluginCallbacks.ts","../../src/hooks/useAppOverlays.ts","../../src/hooks/useAppSubmissionState.ts","../../src/hooks/useAppInteractionState.ts","../../src/use-terminal-title.ts","../../src/hooks/useTerminalHandoffSuspension.ts","../../src/hooks/useAppLifecycleState.ts","../../src/hooks/useAppearanceSettings.ts","../../src/hooks/useExecutionDetailPage.ts","../../src/flows/fork-attach-flow.ts","../../src/hooks/useForkAttach.ts","../../src/hooks/useBackgroundPanel.ts","../../src/flows/background-focus-flow.ts","../../src/hooks/useAppWorkspaceState.ts","../../src/hooks/useSideEffects.ts","../../src/hooks/useStatusLineSettings.ts","../../src/hooks/useAppScreenState.ts","../../src/theme/theme-registry.ts","../../src/hooks/useAppThemeState.ts","../../src/hooks/useTuiChannel.ts","../../src/hooks/useAppController.ts","../../src/AppView.tsx","../../src/App.tsx","../../src/attention/attention-tracker.ts","../../src/attention/focus-input-filter.ts","../../src/screen-reader-announcement.ts","../../src/screen-reader-pacing.ts","../../src/screen-reader-stdout.ts","../../src/terminal-focus-reporting.ts","../../src/terminal-handoff-controller.ts","../../src/attention/interval-recap.ts","../../src/attention/attention-coordinator.ts","../../src/flows/session-init-poller.ts","../../src/hooks/command-result-handler.ts","../../src/slash-command-input.ts","../../src/tui-channel-lifecycle-coordinator.ts","../../src/tui-interaction-queues.ts","../../src/attributed-user-echo.ts","../../src/tui-session-binding.ts","../../src/tui-session-event-projector.ts","../../src/tui-session-options.ts","../../src/tui-session-events.ts","../../src/tui-session-notice-store.ts","../../src/tui-state-manager.ts","../../src/waiting-loop-stop-notice.ts","../../src/TuiInteractionChannel.ts","../../src/wire-history-sync.ts","../../src/wire-tui-client-commands.ts","../../src/wire-tui-projection.ts","../../src/wire-tui-channel.ts","../../src/render.tsx","../../src/SupervisedSessionView.tsx","../../src/create-default-tui-cli-adapter.ts","../../src/keybindings/node-keybindings-source.ts"],"sourcesContent":["/**\n * SEC-019 (issue #2022) - the one place untrusted text is made safe to put on a terminal.\n *\n * Model output, tool output, file contents and plugin text all reach Ink `<Text>`, and from there a\n * terminal. What that terminal actually receives was MEASURED against a real stream rather than\n * assumed, because the first version of this module assumed Ink passed everything through and that\n * is not true:\n *\n * stripped by Ink OSC 52 (clipboard), OSC 0 (title), CSI erase/cursor/alt-screen, DCS, APC,\n * 8-bit CSI\n * REACHES the tty SGR colour, OSC 8 hyperlink, a bare carriage return\n *\n * So through `<Text>` the live attack is a link whose visible text and target differ, and a `\\r`\n * that overwrites the line the transcript just printed. `useTerminalTitle` writes to stdout directly\n * and is subject to none of Ink's filtering, so everything reaches the terminal there.\n *\n * ## Why sanitize the whole class when Ink removes most of it\n *\n * Because Ink's removal is INCIDENTAL. It falls out of slicing text for layout; it is in no\n * contract, no test of Ink's asserts it, and a dependency upgrade can return the entire class\n * without a line changing here. A boundary that rests on another project's implementation detail is\n * a boundary in name. The two sequences that reach the terminal today prove the exposure is real;\n * the ones Ink happens to remove are the reason this must not be scoped to them.\n *\n * ## Allowlist, not denylist\n *\n * Everything is passed through EXCEPT the control range, and inside that range exactly two\n * characters survive - tab and newline. A denylist of \"known dangerous sequences\" is\n * wrong for the same reason it is wrong for shell metacharacters: the set belongs to the terminal\n * emulator, and a list written today is incomplete the next time one adds an escape.\n *\n * ## Ordering: sanitize BEFORE rendering, never after\n *\n * The renderer ADDS ANSI - colours, bold, code-block framing. Sanitizing its output would strip the\n * repository's own presentation along with the attacker's. Sanitizing its input removes the escapes\n * that arrived from outside and leaves the ones generated after. That ordering is the whole design,\n * and it is why this is a function on the untrusted string rather than a filter on the rendered one.\n *\n * ## Streaming\n *\n * A sequence split across two deltas - `\\x1b]5` then `2;c;...\\x07` - is invisible to a sanitizer that\n * sees each chunk alone. {@link createStreamingTerminalSanitizer} holds the incomplete tail back until\n * the chunk that completes it arrives. The TUI's own stream path accumulates before rendering\n * (`tui-state-manager` appends each delta to a buffer and renders the buffer), so the stateless\n * function is sufficient THERE - but a caller that sanitizes per chunk needs the stateful one, and\n * having only the stateless function available is how that caller would get it wrong.\n */\n\n/**\n * Tab and newline are content. Carriage return is NOT, and that is a correction.\n *\n * A bare `\\r` returns the cursor to column zero, so `safe text\\rEVIL` prints `EVIL` over what the\n * transcript said — the same \"what you read is not what happened\" attack as a deceptive hyperlink,\n * with no escape sequence involved. It was kept here as content in the first version of this module\n * on the assumption that CR is a line ending; measurement against a real terminal stream showed a\n * bare CR reaching it intact.\n *\n * `\\r\\n` IS a line ending, and is normalized to `\\n` before the control filter runs, so a document\n * written on Windows keeps its line structure and loses only the overwrite primitive.\n */\nconst KEPT_C0 = new Set(['\\t', '\\n']);\n\n/** `\\r\\n` is one line break written in two bytes; a `\\r` that is not part of one is a cursor move. */\nconst CRLF = /\\r\\n/g;\n\n/**\n * One escape sequence, or a lone control character.\n *\n * The alternation is ordered longest-context-first so a two-character C1 introducer (`ESC ]`) is\n * consumed as the start of its sequence rather than as a bare `ESC`, and every 8-bit C1 form is\n * matched with its BODY before the lone-C1 alternative can take the introducer on its own.\n *\n * Each introducer appears in both spellings, because a terminal accepts both: `ESC ]` and the single\n * byte `\\x9d` are the same OSC. An alternation that removed only the 7-bit spelling would leave the\n * 8-bit one's parameters standing as visible text.\n *\n * - `\\x1b][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?` OSC, terminated by BEL or ST - or unterminated at the end\n * of the input, which is the streaming case and must still be removed rather than left visible.\n * - `\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x9c|\\x1b\\\\)?` the same, 8-bit introducer, with `\\x9c` as a\n * third accepted terminator because 8-bit ST is one byte.\n * - `\\x1b[P^_X][\\s\\S]*?(?:\\x1b\\\\|\\x07)` DCS, PM, APC, SOS, terminated by ST or BEL. The\n * terminator is MANDATORY here, and that is the difference from the OSC alternative above. OSC's\n * body is `[^\\x07\\x1b]*`, which is greedy and cannot cross its own terminator, so an optional\n * terminator costs nothing. A DCS body is `[\\s\\S]*?`, which is lazy - and a lazy quantifier\n * followed by an OPTIONAL group prefers the empty match at every position, so the alternative\n * would consume the two-character introducer and nothing else, leaving `q#0;2;0;0;0` from a Sixel\n * sequence standing as visible text. Found in review of PR #2212, and the reason every case in\n * the test table now asserts the exact output rather than \"no control byte survives\" - the weaker\n * assertion holds for a sanitizer that strips only introducers.\n * - `\\x1b[P^_X][\\s\\S]*$` the same, unterminated at the end of the input. Written as its own\n * alternative rather than an optional terminator, for the reason directly above.\n * - `[\\x90\\x98\\x9e\\x9f][\\s\\S]*?(?:\\x9c|\\x1b\\\\|\\x07)` and `[\\x90\\x98\\x9e\\x9f][\\s\\S]*$`\n * DCS/SOS/PM/APC again, 8-bit introducers, terminated and unterminated.\n * - `\\x1b\\[[0-?]*[ -/]*[@-~]` CSI. The parameter class is the ECMA-48 range 0x30–0x3F written as\n * a range, not the digits-plus-`;`-plus-`?` an author reaches for from memory. That shorter class\n * omits `:`, `<`, `=` and `>`, and the omission is not academic: `ESC [ 38:2:255:0:0 m` is the\n * colon form of a truecolor SGR and `ESC [ < 0;10;20 M` is an SGR mouse report. Neither matched\n * any alternative, so the lone-control fallback took the ESC and left `38:2:255:0:0m` as visible\n * text. Found in review of PR #2212.\n * - `\\x9b[0-?]*[ -/]*[@-~]` the same, 8-bit introducer.\n * - `\\x1b[@-Z\\\\-_]` any other two-character escape.\n * - `[\\x00-\\x1f\\x7f]` a lone control character, including a bare ESC or BEL.\n * - `[\\x80-\\x9f]` a lone C1 control that started no sequence any alternative above matched.\n */\nconst CONTROL_SEQUENCE =\n // eslint-disable-next-line no-control-regex\n /\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?|\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x9c|\\x1b\\\\)?|\\x1b[P^_X][\\s\\S]*?(?:\\x1b\\\\|\\x07)|\\x1b[P^_X][\\s\\S]*$|[\\x90\\x98\\x9e\\x9f][\\s\\S]*?(?:\\x9c|\\x1b\\\\|\\x07)|[\\x90\\x98\\x9e\\x9f][\\s\\S]*$|\\x1b\\[[0-?]*[ -/]*[@-~]|\\x9b[0-?]*[ -/]*[@-~]|\\x1b[@-Z\\\\-_]|[\\x00-\\x1f\\x7f]|[\\x80-\\x9f]/g;\n\n/**\n * The longest prefix of `text` that could still become a control sequence if more text arrived.\n *\n * \"Could still become\" is the whole contract, and the DCS/PM/APC/SOS bodies are where it is easy to\n * lose. Written as a plain `[\\s\\S]*`, they match a sequence that is ALREADY terminated inside the\n * chunk — `'hello' ESC P … ESC \\ 'world'` anchors from the introducer to the end, so `world` is held\n * back and does not appear until the next `push` or the `flush`. Nothing dangerous escapes, but\n * ordinary text stops arriving, which for a streaming renderer is a stall.\n *\n * So each body excludes its own terminators through a negative lookahead: a sequence that has already\n * ended is not an incomplete tail. The OSC and CSI branches need no such guard — `[^\\x07\\x1b]*`\n * cannot cross BEL or ESC, and a CSI without its final byte is incomplete by construction. Found in\n * review of PR #2212, one round after the same asymmetry was fixed in {@link CONTROL_SEQUENCE}: the\n * two patterns describe the same grammar and have to be changed together.\n */\nconst INCOMPLETE_TAIL =\n // eslint-disable-next-line no-control-regex\n /(?:\\x1b(?:\\][^\\x07\\x1b]*|[P^_X](?:(?!\\x1b\\\\|\\x07)[\\s\\S])*|\\[[0-?]*[ -/]*|)?|\\x9d[^\\x07\\x1b\\x9c]*|[\\x90\\x98\\x9e\\x9f](?:(?!\\x9c|\\x1b\\\\|\\x07)[\\s\\S])*|\\x9b[0-?]*[ -/]*)$/;\n\n/**\n * `text` with every terminal control sequence removed, and tab/newline/carriage return preserved.\n *\n * Ordinary Unicode - including characters far outside ASCII - is untouched: the goal is to remove\n * what the TERMINAL acts on, not to restrict what a document may say.\n */\nexport function sanitizeTerminalText(text: string): string {\n return text\n .replace(CRLF, '\\n')\n .replace(CONTROL_SEQUENCE, (match) => (KEPT_C0.has(match) ? match : ''));\n}\n\n/** A sanitizer that carries an incomplete escape across chunk boundaries. */\nexport interface IStreamingTerminalSanitizer {\n /** Sanitize one chunk, holding back any tail that could still become an escape. */\n push(chunk: string): string;\n /** Emit whatever is held back, sanitized. Call when the stream ends. */\n flush(): string;\n}\n\n/**\n * A stateful sanitizer for callers that sanitize each chunk as it arrives.\n *\n * The held-back tail is bounded by the incomplete-prefix pattern rather than by a byte count: a\n * partial OSC can be arbitrarily long, and truncating the buffer would emit the middle of a sequence\n * as visible text while dropping the part that made it dangerous.\n */\nexport function createStreamingTerminalSanitizer(): IStreamingTerminalSanitizer {\n let pending = '';\n return {\n push(chunk: string): string {\n const combined = pending + chunk;\n const tail = INCOMPLETE_TAIL.exec(combined);\n const holdFrom = tail && tail[0].length > 0 ? tail.index : combined.length;\n pending = combined.slice(holdFrom);\n return sanitizeTerminalText(combined.slice(0, holdFrom));\n },\n flush(): string {\n const remaining = pending;\n pending = '';\n return sanitizeTerminalText(remaining);\n },\n };\n}\n","/**\n * `SafeText` — the ONE way a string reaches the terminal from this package (#2222).\n *\n * SEC-019 fixed every render site that was found to put untrusted text — model output, tool stdout,\n * file contents, plugin-supplied names — on the terminal through Ink's `<Text>`. Six review rounds\n * found three sites nobody had covered, because \"sanitized\" was a property of individual lines of\n * code, and a site added later by someone who never read SEC-019 is covered by nothing.\n *\n * This component makes the boundary structural: every string child is passed through\n * `sanitizeTerminalText` before Ink sees it, and the `tui-safe-text-boundary` scan refuses any other\n * module in this package that imports `Text` from `ink` — plain, aliased, or through a namespace.\n * A render site therefore CANNOT put a string on the terminal without passing through here. For the\n * many constant labels this is a no-op; that is the point — the boundary is not \"the sites judged\n * to carry untrusted text\", because that judgement is what already failed twice.\n *\n * This module is the only permitted importer of Ink's `Text`.\n */\n\nimport { Text as InkText } from 'ink';\nimport React from 'react';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { ReactNode } from 'react';\n\ntype TInkTextProps = React.ComponentProps<typeof InkText>;\n\nfunction sanitizeChildren(children: ReactNode): ReactNode {\n if (typeof children === 'string') return sanitizeTerminalText(children);\n if (typeof children === 'number' || typeof children === 'boolean') return children;\n if (children === null || children === undefined) return children;\n if (Array.isArray(children)) return children.map((child) => sanitizeChildren(child));\n // A nested element (another <Text>, a <Box>) sanitizes its own string children on its own render.\n return children;\n}\n\n/** Ink's `Text`, with every string child sanitized for the terminal. Same props, same output shape. */\nexport function SafeText(props: TInkTextProps): React.JSX.Element {\n const { children, ...rest } = props;\n return <InkText {...rest}>{sanitizeChildren(children)}</InkText>;\n}\n\n/**\n * Ink's `Text` for a string THIS PACKAGE'S OWN RENDERER produced from already-sanitized input.\n *\n * `renderMarkdown` sanitizes the untrusted markdown BEFORE `marked-terminal` styles it, and the\n * SGR colour in its output (the theme's diff pairs, heading emphasis) is the renderer's\n * own, not the model's. Routing that output through `SafeText` would strip the styling the renderer\n * just applied — the SCREEN-006 diff colours vanished exactly that way. The boundary still holds:\n * the only string that reaches this component is one the renderer built from sanitized text, and\n * the `tui-safe-text-boundary` scan keeps every other module off Ink's `Text`.\n *\n * PERMITTED here: a string this package's own renderer built from already-sanitized input — today\n * that is exactly `renderMarkdown(...)`, which sanitizes before it styles. FORBIDDEN here: any\n * string that has not passed a sanitizer — raw model output, tool stdout, file contents, plugin\n * names, a template that interpolates one of those — which goes through `SafeText`/`Text` instead.\n * The mirror rule holds too: `renderMarkdown(...)` must NOT be a child of `SafeText`/`Text`, whose\n * sanitizer would strip the renderer's own SGR. The `tui-safe-text-boundary` scan enforces both\n * directions, so neither is left to a reviewer noticing it.\n */\nexport function RenderedText(props: TInkTextProps): React.JSX.Element {\n return <InkText {...props} />;\n}\n\n/** Call sites keep writing `<Text>`; the name resolves to the boundary rather than to Ink. */\nexport { SafeText as Text };\n","/**\n * SCREEN-2002 — the four built-in themes, as data.\n *\n * `dark` is today's colour, gathered from the four places that used to decide it: `PALETTE`/`MOTION`\n * (Ink), `ANSI` (the markdown diff rows), `marked-terminal`'s own chalk defaults and `cli-highlight`'s\n * `DEFAULT_THEME`. Making those defaults explicit is what lets a theme change them at all; it is also\n * why a user who sets nothing sees the same colours as before.\n *\n * Over the 300-line file ceiling, deliberately. The file is ONE concern — four colour tables of the\n * same shape — and the ceiling exists to break up files that do several things. Splitting per theme\n * would put the dark and light values that must correspond in different files, which is the drift\n * the token model was built to stop.\n *\n * The daltonized pair is specified in hex rather than colour names on purpose: a name is whatever the\n * terminal says it is, so a colour-vision-deficiency simulation cannot compute it, and the guard\n * REFUSES what it cannot simulate. Their status and diff pairs avoid red/green entirely — blue for\n * \"good\", orange for \"bad\" — the distinction protanopes and deuteranopes retain.\n *\n * This module is the only file in the package allowed to hold a colour VALUE; the consistency test\n * ratchets on both that import and on direct `chalk.<colour>(` calls elsewhere.\n */\nimport type { ITuiTheme } from './theme-contracts.js';\n\n/** Today's values, name for name. */\nexport const DARK_THEME: ITuiTheme = {\n id: 'dark',\n name: 'Dark',\n appearance: 'dark',\n source: 'built-in',\n colors: {\n text: {\n accent: 'cyan',\n emphasis: 'white',\n success: 'green',\n warning: 'yellow',\n error: 'red',\n session: 'magenta',\n muted: 'gray',\n onAccent: 'black',\n },\n border: {\n attention: 'yellow',\n focused: 'cyan',\n active: 'green',\n muted: 'gray',\n error: 'red',\n },\n status: {\n running: 'yellow',\n success: 'green',\n error: 'red',\n denied: 'yellowBright',\n waiting: 'yellow',\n cancelled: 'yellow',\n idle: 'gray',\n },\n },\n // marked-terminal's own defaults, written down so a theme can change them.\n markdown: {\n heading: 'green',\n firstHeading: 'magenta',\n code: 'yellow',\n codespan: 'yellow',\n link: 'blue',\n href: 'blue',\n blockquote: 'gray',\n del: 'gray',\n html: 'gray',\n // The former `ANSI.lightGreen` / `lightRed` / `darkGreenBackground` / `darkRedBackground`.\n diffAdded: 'ansi256(120)',\n diffRemoved: 'ansi256(210)',\n diffAddedBackground: 'ansi256(22)',\n diffRemovedBackground: 'ansi256(52)',\n diffHunk: 'cyan',\n },\n // cli-highlight's DEFAULT_THEME, written down for the same reason.\n syntax: {\n keyword: 'blue',\n built_in: 'cyan',\n type: 'cyan',\n literal: 'blue',\n number: 'green',\n regexp: 'red',\n string: 'red',\n class: 'blue',\n function: 'yellow',\n comment: 'green',\n doctag: 'green',\n meta: 'gray',\n tag: 'gray',\n name: 'blue',\n attr: 'cyan',\n addition: 'green',\n deletion: 'red',\n },\n motion: { wave: ['#555555', '#777777', '#999999', '#bbbbbb'] },\n};\n\n/** The same roles against a light background: darker foregrounds, light diff backgrounds. */\nexport const LIGHT_THEME: ITuiTheme = {\n id: 'light',\n name: 'Light',\n appearance: 'light',\n source: 'built-in',\n colors: {\n text: {\n accent: 'ansi256(24)',\n emphasis: 'black',\n success: 'ansi256(28)',\n warning: 'ansi256(130)',\n error: 'ansi256(124)',\n session: 'ansi256(90)',\n muted: 'ansi256(243)',\n onAccent: 'white',\n },\n border: {\n attention: 'ansi256(130)',\n focused: 'ansi256(24)',\n active: 'ansi256(28)',\n muted: 'ansi256(249)',\n error: 'ansi256(124)',\n },\n status: {\n running: 'ansi256(130)',\n success: 'ansi256(28)',\n error: 'ansi256(124)',\n denied: 'ansi256(166)',\n waiting: 'ansi256(130)',\n cancelled: 'ansi256(130)',\n idle: 'ansi256(243)',\n },\n },\n markdown: {\n heading: 'ansi256(28)',\n firstHeading: 'ansi256(90)',\n code: 'ansi256(94)',\n codespan: 'ansi256(94)',\n link: 'ansi256(26)',\n href: 'ansi256(26)',\n blockquote: 'ansi256(243)',\n del: 'ansi256(243)',\n html: 'ansi256(243)',\n diffAdded: 'ansi256(22)',\n diffRemoved: 'ansi256(52)',\n diffAddedBackground: 'ansi256(194)',\n diffRemovedBackground: 'ansi256(224)',\n diffHunk: 'ansi256(24)',\n },\n syntax: {\n keyword: 'ansi256(26)',\n built_in: 'ansi256(24)',\n type: 'ansi256(24)',\n literal: 'ansi256(26)',\n number: 'ansi256(28)',\n regexp: 'ansi256(124)',\n string: 'ansi256(124)',\n class: 'ansi256(26)',\n function: 'ansi256(94)',\n comment: 'ansi256(28)',\n doctag: 'ansi256(28)',\n meta: 'ansi256(243)',\n tag: 'ansi256(243)',\n name: 'ansi256(26)',\n attr: 'ansi256(24)',\n addition: 'ansi256(22)',\n deletion: 'ansi256(52)',\n },\n motion: { wave: ['#bbbbbb', '#999999', '#777777', '#555555'] },\n};\n\n/** Blue \"good\" / orange \"bad\": the pair protanopes and deuteranopes keep apart. */\nexport const DARK_DALTONIZED_THEME: ITuiTheme = {\n id: 'dark-daltonized',\n name: 'Dark (daltonized)',\n appearance: 'dark',\n source: 'built-in',\n colors: {\n text: {\n accent: '#56b4e9',\n emphasis: '#ffffff',\n success: '#56b4e9',\n warning: '#e69f00',\n error: '#d55e00',\n session: '#cc79a7',\n muted: '#999999',\n onAccent: '#000000',\n },\n border: {\n attention: '#e69f00',\n focused: '#56b4e9',\n active: '#56b4e9',\n muted: '#999999',\n error: '#d55e00',\n },\n status: {\n running: '#e69f00',\n success: '#56b4e9',\n error: '#d55e00',\n denied: '#f0e442',\n waiting: '#e69f00',\n cancelled: '#cc79a7',\n idle: '#999999',\n },\n },\n markdown: {\n heading: '#56b4e9',\n firstHeading: '#cc79a7',\n code: '#e69f00',\n codespan: '#e69f00',\n link: '#56b4e9',\n href: '#56b4e9',\n blockquote: '#999999',\n del: '#999999',\n html: '#999999',\n diffAdded: '#8ecdf5',\n diffRemoved: '#ffb066',\n diffAddedBackground: '#00335c',\n diffRemovedBackground: '#5c3000',\n diffHunk: '#56b4e9',\n },\n syntax: {\n keyword: '#56b4e9',\n built_in: '#8ecdf5',\n type: '#8ecdf5',\n literal: '#56b4e9',\n number: '#e69f00',\n regexp: '#d55e00',\n string: '#d55e00',\n class: '#56b4e9',\n function: '#f0e442',\n comment: '#999999',\n doctag: '#999999',\n meta: '#999999',\n tag: '#999999',\n name: '#56b4e9',\n attr: '#8ecdf5',\n addition: '#8ecdf5',\n deletion: '#ffb066',\n },\n motion: { wave: ['#3d3d3d', '#5c5c5c', '#7a7a7a', '#999999'] },\n};\n\nexport const LIGHT_DALTONIZED_THEME: ITuiTheme = {\n id: 'light-daltonized',\n name: 'Light (daltonized)',\n appearance: 'light',\n source: 'built-in',\n colors: {\n text: {\n accent: '#0072b2',\n emphasis: '#000000',\n success: '#0072b2',\n warning: '#b35900',\n error: '#8c3d00',\n session: '#a8477f',\n muted: '#5c5c5c',\n onAccent: '#ffffff',\n },\n border: {\n attention: '#b35900',\n focused: '#0072b2',\n active: '#0072b2',\n muted: '#a3a3a3',\n error: '#8c3d00',\n },\n status: {\n running: '#b35900',\n success: '#0072b2',\n error: '#8c3d00',\n denied: '#8a7500',\n waiting: '#b35900',\n cancelled: '#a8477f',\n idle: '#5c5c5c',\n },\n },\n markdown: {\n heading: '#0072b2',\n firstHeading: '#a8477f',\n code: '#8a5a00',\n codespan: '#8a5a00',\n link: '#0072b2',\n href: '#0072b2',\n blockquote: '#5c5c5c',\n del: '#5c5c5c',\n html: '#5c5c5c',\n diffAdded: '#004c77',\n diffRemoved: '#7a3300',\n diffAddedBackground: '#d6ecf9',\n diffRemovedBackground: '#fbe5cd',\n diffHunk: '#0072b2',\n },\n syntax: {\n keyword: '#0072b2',\n built_in: '#004c77',\n type: '#004c77',\n literal: '#0072b2',\n number: '#b35900',\n regexp: '#8c3d00',\n string: '#8c3d00',\n class: '#0072b2',\n function: '#8a7500',\n comment: '#5c5c5c',\n doctag: '#5c5c5c',\n meta: '#5c5c5c',\n tag: '#5c5c5c',\n name: '#0072b2',\n attr: '#004c77',\n addition: '#004c77',\n deletion: '#7a3300',\n },\n motion: { wave: ['#c7c7c7', '#a3a3a3', '#7a7a7a', '#5c5c5c'] },\n};\n\nexport const BUILT_IN_THEMES: readonly ITuiTheme[] = [\n DARK_THEME,\n LIGHT_THEME,\n DARK_DALTONIZED_THEME,\n LIGHT_DALTONIZED_THEME,\n];\n\n/** The theme a run uses when nothing is configured: today's colours. */\nexport const DEFAULT_THEME_ID = DARK_THEME.id;\n\n/**\n * The catalogue, as a function rather than the array itself. A caller outside `src/theme/` — the\n * theme registry a picker reads — needs to ENUMERATE the built-ins, which is a different act from\n * reaching into one of them for a colour; the floor refuses the second by refusing the DATA's names,\n * so the first gets an accessor of its own instead of an exemption.\n */\nexport function listBuiltInThemes(): readonly ITuiTheme[] {\n return BUILT_IN_THEMES;\n}\n\n/**\n * The one place \"no theme was resolved\" is answered. A caller outside `src/theme/` asks this instead\n * of importing the built-in data, so the anti-drift floor can keep the colour DATA inside this\n * module and still let a non-React entry point — `renderMarkdown`, which is called with a theme or\n * without one — render at all.\n */\nexport function resolveTheme(theme: ITuiTheme | undefined): ITuiTheme {\n return theme ?? DARK_THEME;\n}\n","/**\n * CLI-2004 — the one place a component asks \"is screen-reader mode on?\".\n *\n * The mode is a single resolved boolean threaded from the CLI into `renderApp`, handed to Ink as\n * `isScreenReaderEnabled` AND published here. Two sources of truth for one fact is the hazard this\n * design accepts (Ink's option and this context); both are set from the same resolved value in\n * `render.tsx`, and no component takes the mode as a prop from its parent — it reads it here — so a\n * missed prop cannot silently leave one component in the wrong mode.\n *\n * Default `false`: a component rendered outside the provider (every existing test) behaves exactly\n * as it does today.\n */\n\nimport React, { createContext, useContext } from 'react';\n\nconst ScreenReaderContext = createContext<boolean>(false);\n\n/** Publish the resolved mode to the tree. */\nexport function ScreenReaderProvider({\n enabled,\n children,\n}: {\n enabled: boolean;\n children: React.ReactNode;\n}): React.ReactElement {\n return <ScreenReaderContext.Provider value={enabled}>{children}</ScreenReaderContext.Provider>;\n}\n\n/** Read the resolved mode. `false` outside a provider. */\nexport function useScreenReader(): boolean {\n return useContext(ScreenReaderContext);\n}\n","/**\n * Single source of truth for \"should this process emit color + motion to the\n * terminal?\" — used by both the markdown renderer (color) and WaveText (motion),\n * so the two never disagree (SCREEN-008).\n *\n * Precedence:\n * - `NO_COLOR` present (any value, including empty) → off (per the NO_COLOR convention).\n * - `FORCE_COLOR=0` → off; `FORCE_COLOR=<non-empty, non-0>` → on.\n * - otherwise → on only when stdout is an interactive TTY.\n */\nexport function isInteractiveColorTerminal(): boolean {\n if ('NO_COLOR' in process.env) return false;\n const force = process.env.FORCE_COLOR;\n if (force === '0') return false;\n if (force !== undefined && force !== '') return true;\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * CLI-062 — may this process position the REAL terminal cursor for IME composition?\n *\n * Precedence:\n * - Host override true → on (explicit opt-in for Terminal.app); false → off.\n * - `TERM_PROGRAM=Apple_Terminal` → off by default (invariant I5): the historical Korean-IME\n * SIGSEGV is an Apple-side `attributedSubstringFromRange:` bug; its trigger geometry is gone\n * (measured y + post-<Static> tiny frame), but Terminal.app stays opt-in until the manual\n * matrix passes on real hardware. The CLI-052 startup warning remains in place.\n * - otherwise → on only when stdout is an interactive TTY (ink writes no cursor sequences at\n * all on a non-TTY, so this is defense in depth).\n */\nexport function supportsImeCursorPositioning(\n options: { override?: boolean | undefined } = {},\n): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n if (process.env.TERM_PROGRAM === 'Apple_Terminal') return false; // I5\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * CLI-2004 — may this process write OSC 133 shell-integration marks at agent-turn boundaries?\n *\n * The marks are otherwise harmless: every emulator surveyed discards an unknown OSC sequence, so\n * this gate carries DOCUMENTED negatives, not a capability probe. Robota's support table (the TUI\n * package's docs/SPEC.md) is the user-facing half of the same list.\n *\n * Precedence:\n * - Host override true → on (opt back in on a terminal listed below); false → off.\n * - `TERM_PROGRAM=WezTerm` → off: WezTerm treats OSC 133 as shell-owned and a non-shell emitter\n * corrupts its own prompt tracking, so the marks are withheld rather than emitted uselessly.\n * - otherwise → on only when stdout is an interactive TTY. Nothing consumes the marks in a pipe,\n * and they would land in a captured transcript as noise.\n */\nexport function supportsTurnMarks(options: { override?: boolean | undefined } = {}): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n if (process.env.TERM_PROGRAM === 'WezTerm') return false;\n return Boolean(process.stdout.isTTY);\n}\n\n/**\n * SCREEN-1992 — may this process ask the terminal to report focus changes (DECSET 1004)?\n *\n * The mode is harmless where unimplemented (the request is discarded and no `CSI I`/`CSI O` ever\n * arrives), so the gate is the interactive-TTY check plus an override the product shell injects from\n * its own environment — this package reads no product-named literal for it.\n *\n * Precedence:\n * - `override === true` → on; `override === false` → off (kill switch).\n * - otherwise → on only when BOTH stdin and stdout are interactive TTYs: the sequences arrive on\n * stdin, and nothing sends them in a pipe.\n */\nexport function supportsFocusReporting(options: { override?: boolean | undefined } = {}): boolean {\n if (options.override === true) return true;\n if (options.override === false) return false;\n return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);\n}\n","/**\n * SCREEN-2002 — the resolved theme, published to the tree.\n *\n * The `screen-reader-context` shape: one resolved value in a React context, defaulting to the `dark`\n * built-in outside a provider so a component rendered in a test behaves exactly as it did before the\n * theme existed. Components read `usePalette()` where they used to import `PALETTE`; nothing takes the\n * theme as a prop, so a missed prop cannot leave one component on another theme.\n */\nimport React, { createContext, useContext } from 'react';\n\nimport { DARK_THEME, resolveTheme } from './built-in-themes.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { isInteractiveColorTerminal } from '../terminal-capabilities.js';\n\nimport type { IThemeColors, IThemeMotion, ITuiTheme } from './theme-contracts.js';\n\nconst ThemeContext = createContext<ITuiTheme>(DARK_THEME);\nconst ReducedMotionContext = createContext<boolean>(false);\nconst SyntaxHighlightingContext = createContext<boolean>(true);\n\nexport function ThemeProvider({\n theme,\n reducedMotion = false,\n syntaxHighlighting = true,\n children,\n}: {\n theme?: ITuiTheme;\n reducedMotion?: boolean;\n syntaxHighlighting?: boolean;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ThemeContext.Provider value={resolveTheme(theme)}>\n <ReducedMotionContext.Provider value={reducedMotion}>\n <SyntaxHighlightingContext.Provider value={syntaxHighlighting}>\n {children}\n </SyntaxHighlightingContext.Provider>\n </ReducedMotionContext.Provider>\n </ThemeContext.Provider>\n );\n}\n\n/** The whole resolved theme. Most components want {@link usePalette} instead. */\nexport function useTheme(): ITuiTheme {\n return useContext(ThemeContext);\n}\n\n/** The colour tokens — the direct replacement for the former `PALETTE` import. */\nexport function usePalette(): IThemeColors {\n return useTheme().colors;\n}\n\n/** The animation ramp — the direct replacement for the former `MOTION` import. */\nexport function useMotionTokens(): IThemeMotion {\n return useTheme().motion;\n}\n\n/**\n * May this process ANIMATE? One owner for a three-input rule, with exactly one consumer (`WaveText`,\n * the package's only animation). Deliberately NOT gating the background countdown (a once-a-second\n * number is content — freezing it would show a stale `in 59s`) or the streaming indicator's\n * screen-reader collapse (that drops content, which a sighted reduced-motion user still wants).\n */\nexport function useMotion(): boolean {\n const screenReader = useScreenReader();\n const reducedMotion = useContext(ReducedMotionContext);\n // No `useMemo`: the value is a boolean, so memoizing it buys nothing — and a dependency array\n // would have to omit `isInteractiveColorTerminal()`, which reads process state rather than React\n // state. Hiding a non-reactive read inside a memo is how a stale gate gets written later.\n return isInteractiveColorTerminal() && !screenReader && !reducedMotion;\n}\n\n/**\n * SCREEN-2002: whether a fenced code block is highlighted. It travels with the theme rather than as\n * a prop because every markdown render site already reads the theme from here, and a setting that\n * reaches only some of them is worse than one that reaches none — `/theme syntax off` would report\n * success while a block somewhere kept its colours.\n */\nexport function useSyntaxHighlighting(): boolean {\n return useContext(SyntaxHighlightingContext);\n}\n","/**\n * SCREEN-2002 — the TUI's one theme token model.\n *\n * Before this, five things decided a colour: `PALETTE` (Ink names), `ANSI` (raw SGR for the markdown\n * pipeline), `marked-terminal`'s own chalk defaults, `cli-highlight`'s `DEFAULT_THEME`, and a handful\n * of `chalk.<name>` calls. A theme that rewrote only the first two would leave a daltonized run with\n * red/green code blocks, because `cli-highlight` falls back PER KEY to its own theme. So the model\n * carries every group a renderer reads, and the style builder derives every encoding from it through\n * chalk — which is exactly what Ink's own `colorize` does, so no mapping layer is invented.\n *\n * Values use INK'S colour grammar verbatim (`<chalk name> | #rrggbb | ansi256(n) | rgb(r,g,b)`), so a\n * theme file, a built-in and a component all speak one language and the grammar doubles as the\n * injection floor: no raw escape sequence can enter through a theme.\n */\n\n/** A colour in Ink's grammar. Validated at the boundary, never parsed twice. */\nexport type TThemeColor = string;\n\n/** Foreground and border roles every Ink component reads. */\nexport interface IThemeColors {\n readonly text: {\n readonly accent: TThemeColor;\n readonly emphasis: TThemeColor;\n readonly success: TThemeColor;\n readonly warning: TThemeColor;\n readonly error: TThemeColor;\n readonly session: TThemeColor;\n readonly muted: TThemeColor;\n readonly onAccent: TThemeColor;\n };\n readonly border: {\n readonly attention: TThemeColor;\n readonly focused: TThemeColor;\n readonly active: TThemeColor;\n readonly muted: TThemeColor;\n readonly error: TThemeColor;\n };\n readonly status: {\n readonly running: TThemeColor;\n readonly success: TThemeColor;\n readonly error: TThemeColor;\n readonly denied: TThemeColor;\n readonly waiting: TThemeColor;\n readonly cancelled: TThemeColor;\n readonly idle: TThemeColor;\n };\n}\n\n/**\n * Every key `marked-terminal` colours by default, including `html` — a key left out would keep that\n * dependency's own gray under every theme.\n *\n * Deliberately absent, because they carry no colour today and giving them one would change rendering\n * rather than theme it: `strong` (`chalk.bold`), `em` (`chalk.italic`), `listitem` and `hr`\n * (`chalk.reset`), and a diff's `diff `/`index ` metadata rows, which this package writes itself as\n * dim over the inherited foreground.\n */\nexport interface IThemeMarkdown {\n readonly heading: TThemeColor;\n readonly firstHeading: TThemeColor;\n readonly code: TThemeColor;\n readonly codespan: TThemeColor;\n readonly link: TThemeColor;\n readonly href: TThemeColor;\n readonly blockquote: TThemeColor;\n readonly del: TThemeColor;\n readonly html: TThemeColor;\n readonly diffAdded: TThemeColor;\n readonly diffRemoved: TThemeColor;\n readonly diffAddedBackground: TThemeColor;\n readonly diffRemovedBackground: TThemeColor;\n readonly diffHunk: TThemeColor;\n}\n\n/**\n * `cli-highlight`'s seventeen coloured keys, ALL REQUIRED. Its `colorizeNode` reads\n * `theme[token] || DEFAULT_THEME[token] || plain`, so one missing key restores that dependency's\n * red/green pair — the exact thing a daltonized theme exists to remove.\n */\nexport interface IThemeSyntax {\n readonly keyword: TThemeColor;\n readonly built_in: TThemeColor;\n readonly type: TThemeColor;\n readonly literal: TThemeColor;\n readonly number: TThemeColor;\n readonly regexp: TThemeColor;\n readonly string: TThemeColor;\n readonly class: TThemeColor;\n readonly function: TThemeColor;\n readonly comment: TThemeColor;\n readonly doctag: TThemeColor;\n readonly meta: TThemeColor;\n readonly tag: TThemeColor;\n readonly name: TThemeColor;\n readonly attr: TThemeColor;\n readonly addition: TThemeColor;\n readonly deletion: TThemeColor;\n}\n\n/** The colour ramp of the package's one animation. Cadence is not themed. */\nexport interface IThemeMotion {\n readonly wave: readonly [TThemeColor, TThemeColor, TThemeColor, TThemeColor];\n}\n\nexport type TThemeAppearance = 'dark' | 'light';\nexport type TThemeSource = 'built-in' | 'user' | 'plugin';\n\nexport interface ITuiTheme {\n /** `dark`, `light`, `dark-daltonized`, `light-daltonized`, `custom:<slug>`, `custom:<plugin>:<slug>`. */\n readonly id: string;\n readonly name: string;\n readonly appearance: TThemeAppearance;\n readonly source: TThemeSource;\n readonly colors: IThemeColors;\n readonly markdown: IThemeMarkdown;\n readonly syntax: IThemeSyntax;\n readonly motion: IThemeMotion;\n}\n\n/** The seventeen `syntax` keys, in the order `cli-highlight` declares them. */\nexport const THEME_SYNTAX_KEYS: readonly (keyof IThemeSyntax)[] = [\n 'keyword',\n 'built_in',\n 'type',\n 'literal',\n 'number',\n 'regexp',\n 'string',\n 'class',\n 'function',\n 'comment',\n 'doctag',\n 'meta',\n 'tag',\n 'name',\n 'attr',\n 'addition',\n 'deletion',\n];\n","/**\n * SCREEN-2002 — one theme, every encoding.\n *\n * Ink reads `colors` directly. `marked-terminal` wants chalk STYLE FUNCTIONS and `cli-highlight` wants\n * a token→function map, so both are built here from the same values. Ink's own `colorize` is this\n * mapping (`chalk[name]` / `chalk.hex` / `chalk.ansi256` / `chalk.rgb`), which is why deriving them is\n * not the \"name→SGR layer\" SCREEN-006 refused to invent — chalk is already the layer, and a direct\n * dependency.\n *\n * The STRUCTURE of a markdown or syntax token is fixed here, not themed: a heading is bold, a first\n * heading underlined and bold, a blockquote italic, a deletion dim and struck through, an href\n * underlined, and a syntax `type` dim — exactly what the two dependencies do today. A theme changes\n * the colour and nothing else, which is what keeps the `dark` theme's rendering the one users know.\n */\nimport chalk, { Chalk, backgroundColorNames, foregroundColorNames } from 'chalk';\n\nimport { THEME_SYNTAX_KEYS } from './theme-contracts.js';\n\nimport type { IThemeMarkdown, IThemeSyntax, ITuiTheme, TThemeColor } from './theme-contracts.js';\nimport type { ChalkInstance } from 'chalk';\n\nconst HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/iu;\nconst ANSI256 = /^ansi256\\((\\d{1,3})\\)$/iu;\nconst RGB = /^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/iu;\nconst ANSI256_MAX = 255;\n\n/** The grammar, written once: the validator, the refusal below and the file parser all quote it. */\nexport const THEME_COLOR_GRAMMAR = '<chalk name> | #rgb | #rrggbb | ansi256(n) | rgb(r,g,b)';\n\n/**\n * chalk's own name tables, which it exports; its styles are prototype getters, so a table has to be\n * built from the names rather than from `Object.keys(chalk)`. This is the same set Ink's `colorize`\n * accepts, which is why the grammar needs no list of its own.\n *\n * A `Map`, not an object literal: an object built by `Object.fromEntries` inherits `Object.prototype`,\n * so a lookup of `constructor`, `toString`, `valueOf` or `__proto__` answers TRUTHY and the\n * `undefined` guard below never fires — a value that is not a colour name would be handed back as\n * though it were a style. That value becomes reachable the moment a parsed user theme (work unit 3)\n * reaches this builder.\n */\nconst CHALK_STYLES = new Map<string, ChalkInstance>(\n [...foregroundColorNames, ...backgroundColorNames].map((name) => [name, chalk[name]]),\n);\nconst FOREGROUND_NAMES = new Set<string>(foregroundColorNames);\n\n/** A FOREGROUND chalk name Ink would accept — a `bg…` name is not a token value. */\nfunction isChalkColorName(value: string): boolean {\n return FOREGROUND_NAMES.has(value);\n}\n\n/** Whether a string is a colour in Ink's grammar. The one place the grammar is decided. */\nexport function isThemeColor(value: string): boolean {\n if (HEX.test(value)) return true;\n const ansi = ANSI256.exec(value);\n if (ansi) return Number(ansi[1]) <= ANSI256_MAX;\n const rgb = RGB.exec(value);\n if (rgb) return rgb.slice(1).every((part) => Number(part) <= ANSI256_MAX);\n return isChalkColorName(value);\n}\n\n/**\n * A value that reached the builder without being a colour is a DEFECT IN THIS PACKAGE, not in a\n * user's file: `parseThemeDocument` refuses a theme file whole, and every built-in value is checked\n * by TC-01. Before work unit 3 this case had two different silent answers — `foreground` handed\n * back its base, `background`'s helper handed back `chalk.reset` — so the same bad value rendered\n * as unstyled text in one place and as a reset in another, and neither said anything. Throwing is\n * the one behaviour that cannot render a theme nobody can see is wrong.\n */\nfunction refuseThemeColor(value: string): never {\n throw new Error(\n `Theme colour ${JSON.stringify(value)} is not in Ink's colour grammar (${THEME_COLOR_GRAMMAR}).`,\n );\n}\n\n/**\n * A foreground style for one theme value, optionally continuing a chain. The base matters: chalk\n * emits its SGR in chain order, so `chalk.dim` + a colour is not the same bytes as a colour + `.dim`,\n * and reproducing a dependency's own default means reproducing its order.\n */\nexport function foreground(color: TThemeColor, base: ChalkInstance = chalk): ChalkInstance {\n if (HEX.test(color)) return base.hex(color);\n const ansi = ANSI256.exec(color);\n if (ansi) return base.ansi256(Number(ansi[1]));\n const rgb = RGB.exec(color);\n if (rgb) return base.rgb(Number(rgb[1]), Number(rgb[2]), Number(rgb[3]));\n // Gated on the FOREGROUND names, not on the shared table: the table also holds the `bg…` entries\n // that `background()` needs, so asking it alone would answer `foreground('bgRed')` with a\n // background style — a name `isThemeColor` already refuses as a token value.\n const named = isChalkColorName(color) ? CHALK_STYLES.get(color) : undefined;\n if (named === undefined) refuseThemeColor(color);\n // A named colour continues the chain through chalk's own getter on the base instance.\n const chained = Reflect.get(base, color) as ChalkInstance | undefined;\n return chained ?? named;\n}\n\n/** A background style for one theme value. */\nexport function background(color: TThemeColor, base: ChalkInstance = chalk): ChalkInstance {\n if (HEX.test(color)) return base.bgHex(color);\n const ansi = ANSI256.exec(color);\n if (ansi) return base.bgAnsi256(Number(ansi[1]));\n const rgb = RGB.exec(color);\n if (rgb) return base.bgRgb(Number(rgb[1]), Number(rgb[2]), Number(rgb[3]));\n const capitalised = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;\n const chained = Reflect.get(base, capitalised) as ChalkInstance | undefined;\n return chained ?? refuseThemeColor(color);\n}\n\n/**\n * The diff rows are written by this package, not by a dependency, and their caller decides colour\n * with its own `color` flag — so a caller that asked for colour gets it even where chalk's own\n * detection says level 0. That is the behaviour the renderer had when it wrote the escapes by hand,\n * and the reason `renderMarkdown(md, { color: true })` still emits them off a TTY.\n *\n * What is forced is colour, NOT depth. Overriding a DETECTED level would downsample the rest of the\n * frame while these rows alone emit truecolor — visible on a 256-colour terminal with a hex-valued\n * theme, where `#56b4e9` is `ESC[38;5;117m` at level 2 and `ESC[38;2;86;180;233m` at level 3. So the\n * ambient level is kept whenever there is one, and only the OFF case is overridden.\n */\nconst TRUECOLOR = 3;\nconst FORCED_CHALK = new Chalk({ level: TRUECOLOR });\n\nfunction diffRowBase(): ChalkInstance {\n return chalk.level === 0 ? FORCED_CHALK : chalk;\n}\n\n/** The `marked-terminal` constructor options: theme colour + the structure that is not themed. */\nexport function markdownRendererOptions(markdown: IThemeMarkdown): Record<string, ChalkInstance> {\n return {\n // Each chain reproduces marked-terminal's own default order, so the `dark` theme is byte-identical.\n heading: foreground(markdown.heading).bold,\n firstHeading: foreground(markdown.firstHeading).underline.bold,\n code: foreground(markdown.code),\n codespan: foreground(markdown.codespan),\n link: foreground(markdown.link),\n href: foreground(markdown.href).underline,\n blockquote: foreground(markdown.blockquote).italic,\n del: foreground(markdown.del, chalk.dim).strikethrough,\n html: foreground(markdown.html),\n };\n}\n\n/** The `cli-highlight` theme: every one of its seventeen coloured keys, so none falls back. */\nexport function syntaxHighlightTheme(syntax: IThemeSyntax): Record<string, ChalkInstance> {\n const theme: Record<string, ChalkInstance> = {};\n for (const key of THEME_SYNTAX_KEYS) {\n // `type` is `<colour>.dim` in cli-highlight's own theme: the modifier is structure, the colour is\n // the theme's, and the order is the dependency's, so the `dark` theme is byte-identical.\n theme[key] = key === 'type' ? foreground(syntax[key]).dim : foreground(syntax[key]);\n }\n return theme;\n}\n\n/** The styles the diff renderer writes around a row: background and foreground composed per row. */\nexport interface IDiffRowStyles {\n readonly added: (row: string) => string;\n readonly removed: (row: string) => string;\n readonly hunk: (row: string) => string;\n /** Structure only: dim over the inherited foreground, never a theme colour. */\n readonly meta: (row: string) => string;\n}\n\nexport function diffRowStyles(theme: ITuiTheme): IDiffRowStyles {\n const { markdown } = theme;\n const base = diffRowBase();\n const addedBackground = background(markdown.diffAddedBackground, base);\n const addedForeground = foreground(markdown.diffAdded, base);\n const removedBackground = background(markdown.diffRemovedBackground, base);\n const removedForeground = foreground(markdown.diffRemoved, base);\n return {\n added: (row) => addedBackground(addedForeground(row)),\n removed: (row) => removedBackground(removedForeground(row)),\n hunk: foreground(markdown.diffHunk, base),\n // Dim over the INHERITED foreground, as this renderer always wrote it — colouring it would be a\n // rendering change dressed as a theme.\n meta: (row) => base.dim(row),\n };\n}\n","/**\n * SCREEN-2002 — colour-vision-deficiency simulation, for the guard over the daltonized built-ins.\n *\n * A daltonized theme's claim is that two colours a viewer must tell apart still differ AFTER their\n * protan or deutan confusion. That is measurable, so it is measured: simulate both deficiencies\n * (Viénot–Brettel–Mollon, the linear-RGB projection published for dichromats), convert to CIE Lab,\n * and take the pairwise distance.\n *\n * The guard REFUSES what it cannot simulate. A colour NAME is whatever the terminal says it is, so a\n * named value in a daltonized theme is an error rather than a pass — the one direction a guard may\n * never take silently.\n */\nimport type { TThemeColor } from './theme-contracts.js';\n\nexport type TColorVision = 'protanopia' | 'deuteranopia';\n\ninterface IRgb {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n}\n\nconst HEX_LONG = /^#([0-9a-f]{6})$/iu;\nconst HEX_SHORT = /^#([0-9a-f]{3})$/iu;\nconst ANSI256 = /^ansi256\\((\\d{1,3})\\)$/iu;\nconst RGB = /^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/iu;\nconst BYTE_MAX = 255;\nconst HEX_BASE = 16;\nconst HEX_PAIR = /[0-9a-f]{2}/giu;\nconst ANSI_CUBE_START = 16;\nconst ANSI_GRAY_START = 232;\nconst ANSI_CUBE_SIDE = 6;\n/**\n * Published numeric tables are kept as DATA — parsed from their published text rather than spelled\n * as literals in expressions, so the values stay verbatim and reviewable against their source.\n */\nfunction coefficients(published: string): number[] {\n return published.split(/\\s+/u).map(Number);\n}\n\n/** The xterm 6×6×6 cube's channel levels. */\nconst ANSI_CUBE_STEPS = coefficients('0 95 135 175 215 255');\nconst GRAY_STEP = 10;\nconst GRAY_BASE = 8;\n\n/** An Ink-grammar colour as 0–255 sRGB, or `undefined` when it cannot be simulated. */\nexport function toRgb(color: TThemeColor): IRgb | undefined {\n const long = HEX_LONG.exec(color);\n if (long?.[1]) {\n const [r, g, b] = (long[1].match(HEX_PAIR) ?? []).map((pair) =>\n Number.parseInt(pair, HEX_BASE),\n ) as [number, number, number];\n return { r, g, b };\n }\n const short = HEX_SHORT.exec(color);\n if (short?.[1]) {\n const [r, g, b] = [...short[1]].map((digit) => Number.parseInt(`${digit}${digit}`, HEX_BASE));\n return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n }\n const rgb = RGB.exec(color);\n if (rgb) return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) };\n const ansi = ANSI256.exec(color);\n if (ansi) return fromAnsi256(Number(ansi[1]));\n // A colour NAME resolves in the terminal, not here.\n return undefined;\n}\n\nfunction fromAnsi256(index: number): IRgb | undefined {\n if (index >= ANSI_GRAY_START) {\n const level = GRAY_BASE + (index - ANSI_GRAY_START) * GRAY_STEP;\n return { r: level, g: level, b: level };\n }\n if (index < ANSI_CUBE_START) return undefined; // the 16 system colours are terminal-defined\n const offset = index - ANSI_CUBE_START;\n const r = ANSI_CUBE_STEPS[Math.floor(offset / (ANSI_CUBE_SIDE * ANSI_CUBE_SIDE))];\n const g = ANSI_CUBE_STEPS[Math.floor(offset / ANSI_CUBE_SIDE) % ANSI_CUBE_SIDE];\n const b = ANSI_CUBE_STEPS[offset % ANSI_CUBE_SIDE];\n return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n}\n\nconst GAMMA_THRESHOLD = 0.04045;\nconst GAMMA_OFFSET = 0.055;\nconst GAMMA_SCALE = 1.055;\nconst GAMMA_EXPONENT = 2.4;\nconst GAMMA_LINEAR_DIVISOR = 12.92;\n\nfunction toLinear(channel: number): number {\n const value = channel / BYTE_MAX;\n return value <= GAMMA_THRESHOLD\n ? value / GAMMA_LINEAR_DIVISOR\n : ((value + GAMMA_OFFSET) / GAMMA_SCALE) ** GAMMA_EXPONENT;\n}\n\n/** Viénot–Brettel–Mollon dichromat projections, applied in linear RGB. */\nconst PROJECTION: Record<TColorVision, readonly number[][]> = {\n protanopia: [\n coefficients('0.1121 0.8853 -0.0005'),\n coefficients('0.1127 0.8897 -0.0001'),\n coefficients('0.0045 0.0 1.0019'),\n ],\n deuteranopia: [\n coefficients('0.292 0.7054 -0.0003'),\n coefficients('0.2934 0.7089 0.0'),\n coefficients('-0.0209 0.4614 0.5677'),\n ],\n};\n\n/** sRGB → CIE XYZ (D65), the standard matrix. */\nconst SRGB_TO_XYZ = [\n coefficients('0.4124 0.3576 0.1805'),\n coefficients('0.2126 0.7152 0.0722'),\n coefficients('0.0193 0.1192 0.9505'),\n];\n\nfunction project(matrix: readonly number[][], vector: readonly number[]): number[] {\n return matrix.map((row) =>\n row.reduce((sum, factor, index) => sum + factor * (vector[index] ?? 0), 0),\n );\n}\n\ninterface ILab {\n readonly l: number;\n readonly a: number;\n readonly b: number;\n}\n\nconst D65 = coefficients('0.95047 1 1.08883');\nconst LAB_EPSILON_NUMERATOR = 216;\nconst LAB_EPSILON_DENOMINATOR = 24389;\nconst LAB_KAPPA_DIVISOR = 27;\nconst LAB_EPSILON = LAB_EPSILON_NUMERATOR / LAB_EPSILON_DENOMINATOR;\nconst LAB_KAPPA = LAB_EPSILON_DENOMINATOR / LAB_KAPPA_DIVISOR;\nconst LAB_L_SCALE = 116;\nconst LAB_L_OFFSET = 16;\nconst LAB_A_SCALE = 500;\nconst LAB_B_SCALE = 200;\nconst THREE = 3;\nconst CUBE_ROOT = 1 / THREE;\n\nfunction toLab(linear: readonly number[]): ILab {\n const xyz = project(SRGB_TO_XYZ, linear).map((value, index) => value / (D65[index] ?? 1));\n const f = (value: number): number =>\n value > LAB_EPSILON ? value ** CUBE_ROOT : (LAB_KAPPA * value + LAB_L_OFFSET) / LAB_L_SCALE;\n const [fx, fy, fz] = xyz.map(f) as [number, number, number];\n return {\n l: LAB_L_SCALE * fy - LAB_L_OFFSET,\n a: LAB_A_SCALE * (fx - fy),\n b: LAB_B_SCALE * (fy - fz),\n };\n}\n\n/** One colour as a dichromat sees it, in Lab. `undefined` ⇒ the value cannot be simulated. */\nexport function simulate(color: TThemeColor, vision: TColorVision): ILab | undefined {\n const rgb = toRgb(color);\n if (rgb === undefined) return undefined;\n const linear = [toLinear(rgb.r), toLinear(rgb.g), toLinear(rgb.b)];\n return toLab(project(PROJECTION[vision], linear));\n}\n\n/** CIE76 distance between two simulated colours; `undefined` when either cannot be simulated. */\nexport function simulatedDistance(\n first: TThemeColor,\n second: TThemeColor,\n vision: TColorVision,\n): number | undefined {\n const a = simulate(first, vision);\n const b = simulate(second, vision);\n if (a === undefined || b === undefined) return undefined;\n const SQUARE = 2;\n return Math.sqrt((a.l - b.l) ** SQUARE + (a.a - b.a) ** SQUARE + (a.b - b.b) ** SQUARE);\n}\n","/**\n * The shape of a parsed JSON document, and the one guard that narrows it.\n *\n * Two user-owned files are read as JSON in this package — the keybindings document and, since\n * SCREEN-2002, a theme document — and both walk an arbitrary tree looking for the keys they know.\n * A second copy of this type would be a second answer to \"what can a parsed file hold\", which is\n * how two readers of the same kind of file start disagreeing about what a null or an array means.\n */\nexport type TJsonValue = string | number | boolean | null | TJsonValue[] | IJsonRecord;\n\nexport interface IJsonRecord {\n [key: string]: TJsonValue;\n}\n\n/** An object, not an array and not `null` — the distinction `typeof` alone does not make. */\nexport function isJsonRecord(value: TJsonValue): value is IJsonRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * SCREEN-2002 — the theme file boundary.\n *\n * A theme file is `{ name?, base?, overrides? }`: a name, a built-in to start from, and a SPARSE map\n * over the same token paths the built-ins use. It is refused WHOLE on the first unknown token or\n * invalid value, with a `$.`-path naming what was wrong — the keybindings document's contract, for\n * the same reason. A partially-applied theme is the state where a reader cannot tell which colours\n * are theirs and which the base's, and a file that silently loses its third line is worse than a\n * file that is skipped with its reason printed.\n *\n * The token paths are not listed here. They are WALKED from the base theme, so `ITuiTheme` gaining a\n * key makes it overridable and `ITuiTheme` losing one makes it refused — there is no second\n * declaration of the token model to drift from the first.\n *\n * Values go through `isThemeColor`, which is also the injection floor: a raw escape sequence is not\n * in Ink's grammar, so it cannot enter through a theme.\n */\nimport { isJsonRecord } from '../json-value.js';\nimport { sanitizeTerminalText } from '../sanitize-terminal-text.js';\nimport { BUILT_IN_THEMES, DEFAULT_THEME_ID } from './built-in-themes.js';\nimport { isThemeColor, THEME_COLOR_GRAMMAR } from './theme-styles.js';\n\nimport type { IJsonRecord, TJsonValue } from '../json-value.js';\nimport type { ITuiTheme, TThemeSource } from './theme-contracts.js';\n\nexport interface IThemeDocumentInput {\n /** The namespaced id the caller minted — `custom:<slug>` or `custom:<plugin>:<slug>`. */\n readonly id: string;\n /** The file's own name, for the `Skipped theme \"<file>\": …` line. */\n readonly fileName: string;\n readonly source: TThemeSource;\n readonly text: string;\n}\n\nexport type TThemeDocumentResult =\n { readonly ok: true; readonly theme: ITuiTheme } | { readonly ok: false; readonly error: string };\n\nconst DOCUMENT_KEYS = new Set(['name', 'base', 'overrides']);\nconst MAX_NAME_LENGTH = 60;\n// eslint-disable-next-line no-control-regex -- matching the control range IS the point here\nconst CONTROL_CHARACTER = /[\\u0000-\\u001F\\u007F-\\u009F]/u;\n\nconst CONTROL_CHARACTERS = new RegExp(CONTROL_CHARACTER.source, 'gu');\n\n/**\n * Untrusted text on its way into a line a terminal will print — the one escaping policy, exported\n * so the surfaces that build their OWN diagnostics use it rather than re-deriving it.\n *\n * A diagnostic is the one part of a refused file that reaches the terminal, and it quotes what was\n * wrong — so without this the injection floor the grammar provides is defeated by the message that\n * reports a violation of it. Escaped rather than stripped, because an author has to SEE what their\n * file contains (`\"\\u001b[31m\"`) instead of a value that looks fine.\n *\n * `JSON.stringify` alone is NOT enough, and that is the whole reason this is a function: it escapes\n * U+0000–U+001F and leaves U+007F and the C1 range literal — including U+009B and U+009D, the\n * single-byte spellings of CSI and OSC. A terminal accepts both spellings (`sanitize-terminal-text.ts`\n * says so and handles both), so quoting only the 7-bit one leaves the 8-bit one's parameters\n * standing as a live sequence.\n */\nexport function quoteThemeText(value: string): string {\n return JSON.stringify(value).replace(\n CONTROL_CHARACTERS,\n (character) => `\\\\u${(character.codePointAt(0) ?? 0).toString(16).padStart(4, '0')}`,\n );\n}\n\n/**\n * The same escaping WITHOUT the surrounding quotes, for a field a caller already quotes — so a\n * refused file does not print as `Skipped theme \"\"My Theme.json\"\": …`.\n */\nexport function escapeThemeText(value: string): string {\n return quoteThemeText(value).slice(1, -1);\n}\n\n/**\n * Prose a surface did not write — a dependency's message, a path from the environment. Sanitized\n * rather than quoted, because it is a sentence; `sanitizeTerminalText` removes both spellings of\n * every sequence, and the tab/newline it keeps are flattened so one diagnostic stays one line.\n */\nexport function sanitizeThemeProse(message: string): string {\n return sanitizeTerminalText(message).replaceAll('\\n', ' ').replaceAll('\\t', ' ').trim();\n}\n\n/** A path SEGMENT taken from the file. Plain keys read as themselves; anything else is quoted. */\nfunction pathSegment(key: string): string {\n return CONTROL_CHARACTER.test(key) ? quoteThemeText(key) : key;\n}\n\nfunction refuse(error: string): TThemeDocumentResult {\n return { ok: false, error };\n}\n\ntype TMergeResult =\n | { readonly ok: true; readonly value: TJsonValue }\n | { readonly ok: false; readonly error: string };\n\nfunction mergeColor(override: TJsonValue, path: string): TMergeResult {\n if (typeof override !== 'string')\n return { ok: false, error: `${path}: expected a colour string` };\n if (!isThemeColor(override)) {\n return {\n ok: false,\n error: `${path}: ${quoteThemeText(override)} is not a colour in Ink's grammar (${THEME_COLOR_GRAMMAR})`,\n };\n }\n return { ok: true, value: override };\n}\n\nfunction mergeArray(base: readonly TJsonValue[], override: TJsonValue, path: string): TMergeResult {\n if (!Array.isArray(override) || override.length !== base.length) {\n return { ok: false, error: `${path}: expected exactly ${base.length} colours` };\n }\n const merged: TJsonValue[] = [];\n for (const [index, item] of base.entries()) {\n const result = mergeNode(item, override[index] ?? null, `${path}[${index}]`);\n if (!result.ok) return result;\n merged.push(result.value);\n }\n return { ok: true, value: merged };\n}\n\nfunction mergeRecord(base: IJsonRecord, override: TJsonValue, path: string): TMergeResult {\n if (!isJsonRecord(override)) return { ok: false, error: `${path}: expected an object of tokens` };\n const merged: IJsonRecord = { ...base };\n for (const key of Object.keys(override)) {\n // Own-property only: a JSON document can carry a literal `__proto__` key, and the base theme\n // has no own property by that name, so the same check that catches a typo catches that too.\n if (!Object.prototype.hasOwnProperty.call(base, key)) {\n return { ok: false, error: `${path}.${pathSegment(key)} is not a theme token` };\n }\n const result = mergeNode(\n base[key] ?? null,\n override[key] ?? null,\n `${path}.${pathSegment(key)}`,\n );\n if (!result.ok) return result;\n merged[key] = result.value;\n }\n return { ok: true, value: merged };\n}\n\n/** One node of the token tree, matched against the SHAPE of the base theme's node. */\nfunction mergeNode(base: TJsonValue, override: TJsonValue, path: string): TMergeResult {\n if (typeof base === 'string') return mergeColor(override, path);\n if (Array.isArray(base)) return mergeArray(base, override, path);\n if (isJsonRecord(base)) return mergeRecord(base, override, path);\n return { ok: false, error: `${path} is not a theme token` };\n}\n\ntype TThemeTokens = Pick<ITuiTheme, 'colors' | 'markdown' | 'syntax' | 'motion'>;\n\n/**\n * The merged tree, read back as the token groups it was built from.\n *\n * A JSON round-trip rather than a type assertion, in both directions: the token interfaces have no\n * index signature, so neither shape is assignable to the other and an assertion between them is\n * what TypeScript rightly refuses. The conversion is sound by construction — `mergeNode` copies the\n * BASE theme's shape and replaces only string leaves with strings the grammar accepted, so every key\n * `ITuiTheme` declares is present and every leaf is a `TThemeColor`. An unknown key or a non-colour\n * leaf is a refusal above this line and never reaches it.\n */\nfunction asThemeTokens(value: TJsonValue): TThemeTokens {\n return JSON.parse(JSON.stringify(value)) as TThemeTokens;\n}\n\n/** The four token groups, as JSON — the base for the walk above. */\nfunction themeTokens(theme: ITuiTheme): IJsonRecord {\n return JSON.parse(\n JSON.stringify({\n colors: theme.colors,\n markdown: theme.markdown,\n syntax: theme.syntax,\n motion: theme.motion,\n }),\n ) as IJsonRecord;\n}\n\nfunction readDocument(text: string): TMergeResult {\n let parsed: TJsonValue;\n try {\n parsed = JSON.parse(text) as TJsonValue;\n } catch (cause) {\n return {\n ok: false,\n error: `$: ${cause instanceof Error ? sanitizeThemeProse(cause.message) : 'invalid JSON'}`,\n };\n }\n if (!isJsonRecord(parsed)) return { ok: false, error: '$: expected an object' };\n for (const key of Object.keys(parsed)) {\n if (!DOCUMENT_KEYS.has(key)) {\n return { ok: false, error: `$.${pathSegment(key)} is not a theme token` };\n }\n }\n return { ok: true, value: parsed };\n}\n\nfunction readName(document: IJsonRecord, fallback: string): TMergeResult {\n const name = document.name;\n // The FALLBACK is checked on the same terms. It is the id the loader minted, which is constrained\n // where it is minted — but \"the caller already made it safe\" is the assumption that put an\n // unchecked string on every list row once, and this is the function that renders the answer.\n if (name === undefined) {\n if (CONTROL_CHARACTER.test(fallback)) {\n return { ok: false, error: '$.name: the minted id must not contain control characters' };\n }\n if (fallback.length > MAX_NAME_LENGTH) {\n return {\n ok: false,\n error: `$.name: the minted id must be at most ${MAX_NAME_LENGTH} characters`,\n };\n }\n return { ok: true, value: fallback };\n }\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, error: '$.name: expected a non-empty string' };\n }\n // A name is APPLIED, so unlike a diagnostic it is rendered on every `/theme list` row and every\n // picker row for the whole session. A control character there is an escape the terminal acts on,\n // and a newline breaks the box the row is drawn in — so the file is refused rather than the name\n // repaired, which is this module's one policy.\n if (CONTROL_CHARACTER.test(name)) {\n return { ok: false, error: '$.name: must not contain control characters' };\n }\n if (name.length > MAX_NAME_LENGTH) {\n return { ok: false, error: `$.name: must be at most ${MAX_NAME_LENGTH} characters` };\n }\n return { ok: true, value: name };\n}\n\nfunction readBase(\n document: IJsonRecord,\n):\n | { readonly ok: true; readonly theme: ITuiTheme }\n | { readonly ok: false; readonly error: string } {\n // `=== undefined`, not `??`: an explicit `null` is a value the file states, and \"applied whole or\n // refused whole\" means it is refused rather than silently read as \"unset\".\n const id = document.base === undefined ? DEFAULT_THEME_ID : document.base;\n if (typeof id !== 'string') return { ok: false, error: '$.base: expected a built-in theme id' };\n const base = BUILT_IN_THEMES.find((theme) => theme.id === id);\n if (!base) return { ok: false, error: `$.base: ${quoteThemeText(id)} is not a built-in theme` };\n return { ok: true, theme: base };\n}\n\n/**\n * Parse one theme file. The `id`, the `source` and the `appearance` are NOT the document's to\n * decide: the id is minted from where the file was found so nothing can shadow a built-in, the\n * source is where it was read from, and the appearance follows the base a theme chose to extend.\n */\nexport function parseThemeDocument(input: IThemeDocumentInput): TThemeDocumentResult {\n const document = readDocument(input.text);\n if (!document.ok) return refuse(document.error);\n const fields = document.value as IJsonRecord;\n\n const base = readBase(fields);\n if (!base.ok) return refuse(base.error);\n\n const name = readName(fields, input.id);\n if (!name.ok) return refuse(name.error);\n\n const overrides = fields.overrides === undefined ? {} : fields.overrides;\n const tokens = mergeNode(themeTokens(base.theme), overrides, '$.overrides');\n if (!tokens.ok) return refuse(tokens.error);\n\n const { colors, markdown, syntax, motion } = asThemeTokens(tokens.value);\n return {\n ok: true,\n theme: {\n id: input.id,\n name: name.value as string,\n appearance: base.theme.appearance,\n source: input.source,\n colors,\n markdown,\n syntax,\n motion,\n },\n };\n}\n","/**\n * The startup banner, committed once to the terminal scrollback.\n *\n * Extracted from `App.tsx` under CLI-2004: the art is ASCII, not box-drawing, so a\n * no-box-drawing sweep passes over it unchanged — it needs its own suppression AND its own\n * assertion, and neither is reviewable while the glyphs live inline in a 600-line component.\n * `BANNER_GLYPHS` is the set a test asserts is absent in screen-reader mode.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\n/** The ASCII art itself. */\nexport const BANNER_ART = `\n ____ ___ ____ ___ _____ _\n | _ \\\\ / _ \\\\| __ ) / _ \\\\_ _|/ \\\\\n | |_) | | | | _ \\\\| | | || | / _ \\\\\n | _ <| |_| | |_) | |_| || |/ ___ \\\\\n |_| \\\\_\\\\\\\\___/|____/ \\\\___/ |_/_/ \\\\_\\\\\n`;\n\n/**\n * The characters the art is drawn from. None is a box-drawing character, which is exactly why the\n * banner needs a suppression test of its own.\n */\nexport const BANNER_GLYPHS: readonly string[] = ['_', '|', '\\\\', '/', '<'];\n\n/** The banner as it is committed to scrollback: the art plus the version line. */\nexport function AppBanner({ version }: { version: string }): React.ReactElement {\n const palette = usePalette();\n return (\n <Box flexDirection=\"column\" paddingX={1} marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {BANNER_ART}\n </Text>\n <Text dimColor> v{version}</Text>\n </Box>\n );\n}\n","/** The units the attention modules convert between; named so no arithmetic carries a bare literal. */\nexport const MS_PER_SECOND = 1000;\nexport const SECONDS_PER_MINUTE = 60;\nexport const MINUTES_PER_HOUR = 60;\nexport const MS_PER_MINUTE = MS_PER_SECOND * SECONDS_PER_MINUTE;\nexport const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;\n","import {\n MINUTES_PER_HOUR,\n MS_PER_SECOND,\n SECONDS_PER_HOUR,\n SECONDS_PER_MINUTE,\n} from './time-units.js';\n\n/**\n * SCREEN-1992: the time until a sleeping schedule fires, rendered against a given clock so the\n * row can tick once a second without the projection changing.\n */\nexport function formatCountdown(nextFireAt: string, now: Date): string | undefined {\n const target = Date.parse(nextFireAt);\n if (Number.isNaN(target)) return undefined;\n const remainingMs = target - now.getTime();\n if (remainingMs <= 0) return 'now';\n const totalSeconds = Math.floor(remainingMs / MS_PER_SECOND);\n const hours = Math.floor(totalSeconds / SECONDS_PER_HOUR);\n const minutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n if (hours > 0) return `in ${hours}h ${minutes}m`;\n if (minutes > 0) return `in ${minutes}m ${seconds}s`;\n return `in ${seconds}s`;\n}\n","/**\n * Single source of truth for status symbol + color across the TUI.\n *\n * Before SCREEN-005 the same conceptual status (e.g. \"running\") was rendered\n * three different ways — icons (⟳/✓/✗) in the streaming indicator, markers\n * (□/■) in the background-task panel, and color-only in the workspace detail\n * pane. This module gives every component one semantic status vocabulary and\n * one glyph map, so a \"running\" task looks the same everywhere and every status\n * pairs a SYMBOL with a color (never color alone — required for no-color\n * terminals and colorblind users).\n */\n\nimport type { IThemeColors } from './theme/index.js';\nimport type {\n TExecutionAttention,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nexport type TUiStatusKind =\n 'running' | 'success' | 'error' | 'denied' | 'waiting' | 'cancelled' | 'idle';\n\n/**\n * Canonical status → SYMBOL map. Used by every status-rendering component.\n *\n * SCREEN-2002 split the colour out: a symbol is the same in every theme, a colour is not, and a\n * module-level constant cannot follow a theme resolved at render time. The symbol stays here (this\n * module remains the SSOT for status kinds and their glyphs); the colour comes from the live theme\n * through {@link statusGlyphColor}, which every call site reaches with `usePalette()`, so the pairing\n * survives — no caller gets one without the other.\n */\nexport const STATUS_SYMBOL: Record<TUiStatusKind, string> = {\n running: '⟳',\n success: '✓',\n error: '✗',\n denied: '⊘',\n waiting: '◴',\n cancelled: '⊗',\n idle: '·',\n};\n\n/** The live theme's colour for a status kind. Always rendered beside {@link STATUS_SYMBOL}. */\nexport function statusGlyphColor(colors: IThemeColors, kind: TUiStatusKind): string {\n return colors.status[kind];\n}\n\n/** Map a tool-execution state to a semantic status kind. */\nexport function toolStateStatusKind(tool: IToolState): TUiStatusKind {\n if (tool.isRunning) return 'running';\n if (tool.result === 'error') return 'error';\n if (tool.result === 'denied') return 'denied';\n return 'success';\n}\n\nconst ACTIVE_WORKSPACE_STATUSES: readonly TExecutionWorkspaceStatus[] = [\n 'active',\n 'queued',\n 'running',\n 'sleeping',\n];\n\n/**\n * Map an execution-workspace entry's status (+ optional attention) to a semantic\n * status kind. `attention` takes precedence so an entry flagged failed/permission\n * classifies correctly even when its raw status hasn't caught up — this mirrors the\n * precedence the workspace color logic uses, so colour + glyph come from one rule.\n */\nexport function workspaceStatusKind(\n status: TExecutionWorkspaceStatus,\n attention?: TExecutionAttention,\n): TUiStatusKind {\n if (attention === 'failed' || status === 'failed') return 'error';\n if (attention === 'permission' || status === 'waiting_permission') return 'waiting';\n if (status === 'completed') return 'success';\n if (status === 'cancelled') return 'cancelled';\n if (ACTIVE_WORKSPACE_STATUSES.includes(status)) return 'running';\n return 'idle';\n}\n","import { workspaceStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type {\n IExecutionDetailRecord,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\n\n// \"Active\" for the in-flight COUNT (countActiveBackgroundWorkspaceEntries) —\n// intentionally includes `waiting_permission` (a task awaiting permission is still\n// in flight). This is a different question from the status KIND used for\n// colour/glyph (where `waiting_permission` maps to its own 'waiting' kind via\n// `workspaceStatusKind`), so the two lists differ on purpose.\nconst ACTIVE_STATUSES: readonly TExecutionWorkspaceStatus[] = [\n 'active',\n 'queued',\n 'running',\n 'waiting_permission',\n 'sleeping',\n];\nconst DETAIL_RECORD_TEXT_LIMIT = 160;\nconst PREVIEW_WHITESPACE = /\\s+/g;\nconst PREVIEW_SEPARATOR = ' ';\n\nexport interface IExecutionWorkspaceEntryRow {\n id: string;\n radio: '●' | '○';\n /** SCREEN-1992: the five-word state, rendered beside the radio and carried in the accessible text. */\n state: string;\n title: string;\n subtitle?: string;\n statusLabel: string;\n preview?: string;\n /** SCREEN-2002: the status KIND; the component resolves its colour from the live theme. */\n statusKind: TUiStatusKind;\n isSelected: boolean;\n accessibleText: string;\n}\n\nexport interface IExecutionWorkspaceEntryRowOptions {\n selectedEntryId?: string;\n}\n\nexport function getDefaultBackgroundWorkspaceEntries(\n snapshot: IExecutionWorkspaceSnapshot | null,\n): IExecutionWorkspaceEntry[] {\n return (snapshot?.entries ?? []).filter(\n (entry) => entry.kind === 'background_task' && entry.visibility === 'default',\n );\n}\n\nexport function countActiveBackgroundWorkspaceEntries(\n snapshot: IExecutionWorkspaceSnapshot | null,\n): number {\n return getDefaultBackgroundWorkspaceEntries(snapshot).filter((entry) =>\n ACTIVE_STATUSES.includes(entry.status),\n ).length;\n}\n\nexport function formatExecutionWorkspaceEntryRow(\n entry: IExecutionWorkspaceEntry,\n options: IExecutionWorkspaceEntryRowOptions = {},\n): IExecutionWorkspaceEntryRow {\n const isSelected = entry.id === options.selectedEntryId;\n const row = {\n id: entry.id,\n radio: isSelected ? '●' : '○',\n state: entry.state,\n title: formatEntryTitle(entry),\n subtitle: formatEntrySubtitle(entry),\n statusLabel: formatStatusLabel(entry.status),\n preview: trimPreview(entry.preview ?? entry.currentAction),\n statusKind: getEntryStatusKind(entry),\n isSelected,\n } satisfies Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>;\n return { ...row, accessibleText: formatAccessibleText(row) };\n}\n\nexport function formatExecutionDetailRecord(record: IExecutionDetailRecord): string {\n const text = record.text.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);\n if (!text) return record.kind;\n return text.length > DETAIL_RECORD_TEXT_LIMIT\n ? `${text.slice(0, DETAIL_RECORD_TEXT_LIMIT)}...`\n : text;\n}\n\nfunction formatEntryTitle(entry: IExecutionWorkspaceEntry): string {\n if (entry.kind === 'main_thread') return entry.title;\n if (entry.kind === 'background_group') return `${entry.title} group`;\n if (entry.taskKind === 'agent') return `${entry.title} agent`;\n if (entry.taskKind === 'process') return entry.title || 'Process';\n if (entry.taskKind === 'scheduled') return entry.title || 'Scheduled';\n return entry.title;\n}\n\nfunction formatEntrySubtitle(entry: IExecutionWorkspaceEntry): string | undefined {\n if (entry.kind === 'main_thread') return entry.subtitle;\n const parts = [\n entry.taskKind,\n entry.subtitle,\n entry.attention === 'none' ? undefined : entry.attention,\n ];\n return (\n parts\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · ') || undefined\n );\n}\n\nfunction formatStatusLabel(status: TExecutionWorkspaceStatus): string {\n return status.replace(/_/g, ' ');\n}\n\n// SCREEN-2002: the view model names the status KIND, not a colour — a colour belongs to the theme\n// resolved at render time, and the kind is what keeps an entry's colour matching the glyph shown for\n// the same status everywhere (SCREEN-007). The component resolves it through `statusGlyphColor`.\nfunction getEntryStatusKind(entry: IExecutionWorkspaceEntry): TUiStatusKind {\n return workspaceStatusKind(entry.status, entry.attention);\n}\n\nfunction trimPreview(value: string | undefined): string | undefined {\n const preview = value?.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);\n return preview || undefined;\n}\n\nfunction formatAccessibleText(row: Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>): string {\n const parts = [\n `${row.radio} ${row.state}`,\n row.title,\n row.statusLabel,\n row.subtitle,\n row.preview,\n ];\n return parts\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · ');\n}\n","import { formatCountdown } from './attention/countdown.js';\nimport { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';\nimport { STATUS_SYMBOL, workspaceStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\n/**\n * The row's leading connector. CLI-2004 adds the plain form: `├`/`└` draw a tree a sighted reader\n * follows at a glance and a screen reader announces as two more characters per row.\n */\nexport const BACKGROUND_TASK_CONNECTORS = { branch: '├', last: '└', plain: '-' } as const;\n\nexport type TBackgroundTaskConnector =\n (typeof BACKGROUND_TASK_CONNECTORS)[keyof typeof BACKGROUND_TASK_CONNECTORS];\n\nexport interface IBackgroundTaskRow {\n connector: TBackgroundTaskConnector;\n /** Shared status glyph (⟳ running, ✓ done, ✗ failed, …) — see status-glyph.ts. */\n marker: string;\n /** SCREEN-1992: the five-word state beside the glyph — the word a reader hears, not a colour. */\n state: string;\n /** SCREEN-2002: the status KIND; the component resolves its colour from the live theme. */\n statusKind: TUiStatusKind;\n label: string;\n segments: string[];\n preview?: string;\n /** SCREEN-1992: the entry's headline when it says more than the preview already does. */\n headline?: string;\n /** SCREEN-1992: `in 59s` while a schedule sleeps — rendered against `options.now`. */\n countdown?: string;\n accessibleText: string;\n}\n\nexport interface IBackgroundTaskRowOptions {\n isLast?: boolean;\n /** Screen-reader mode ⇒ the plain connector. */\n screenReader?: boolean;\n /** The clock the countdown is rendered against; the panel's tick advances it. */\n now?: Date;\n}\n\n/** The headline earns its place only when the row does not already say it (preview or subtitle). */\nfunction resolveHeadline(\n entry: IExecutionWorkspaceEntry,\n preview: string | undefined,\n subtitle: string | undefined,\n): string | undefined {\n const text = entry.headline?.text;\n if (text === undefined || text.length === 0 || text === preview) return undefined;\n if (subtitle !== undefined && subtitle.includes(text)) return undefined;\n return entry.headline?.kind === 'question' ? `? ${text}` : text;\n}\n\nfunction resolveConnector(options: IBackgroundTaskRowOptions): TBackgroundTaskConnector {\n if (options.screenReader === true) return BACKGROUND_TASK_CONNECTORS.plain;\n return options.isLast === false\n ? BACKGROUND_TASK_CONNECTORS.branch\n : BACKGROUND_TASK_CONNECTORS.last;\n}\n\nexport function formatBackgroundTaskRow(\n entry: IExecutionWorkspaceEntry,\n options: IBackgroundTaskRowOptions = {},\n): IBackgroundTaskRow {\n const row = formatExecutionWorkspaceEntryRow(entry);\n // Symbol from the same (status, attention) classification that drives row.statusKind,\n // so the marker's glyph and colour always agree (SCREEN-007).\n const marker = STATUS_SYMBOL[workspaceStatusKind(entry.status, entry.attention)];\n const segments = [row.statusLabel, row.subtitle].filter(\n (segment): segment is string => typeof segment === 'string' && segment.length > 0,\n );\n const connector = resolveConnector(options);\n const headline = resolveHeadline(entry, row.preview, row.subtitle);\n const countdown =\n entry.nextFireAt === undefined\n ? undefined\n : formatCountdown(entry.nextFireAt, options.now ?? new Date());\n return {\n connector,\n marker,\n state: entry.state,\n statusKind: row.statusKind,\n label: row.title,\n segments,\n preview: row.preview,\n headline,\n countdown,\n accessibleText: [\n `${connector} ${marker} ${entry.state} ${row.title}`,\n ...segments,\n headline,\n row.preview,\n countdown,\n ]\n .filter((part): part is string => typeof part === 'string' && part.length > 0)\n .join(' · '),\n };\n}\n","import { useEffect, useState } from 'react';\n\nimport { MS_PER_SECOND } from '../attention/time-units.js';\n\n/** SCREEN-1992: one second, the resolution a `in 59s` countdown is read at. */\nconst COUNTDOWN_TICK_MS = MS_PER_SECOND;\n\n/**\n * The clock a sleeping schedule's countdown is rendered against. Re-renders once a second only while\n * `active` (an entry carries `nextFireAt`) and not in screen-reader mode — a reader would otherwise\n * hear the row change every second. When not ticking the clock is still the CURRENT one on every\n * render (never the mount-time seed): a reader gets a correct number, read once.\n */\nexport function useCountdownTick(active: boolean, screenReader: boolean): Date {\n const ticking = active && !screenReader;\n const [tickedAt, setTickedAt] = useState(() => new Date());\n useEffect(() => {\n if (!ticking) return undefined;\n setTickedAt(new Date());\n const timer = setInterval(() => setTickedAt(new Date()), COUNTDOWN_TICK_MS);\n return () => clearInterval(timer);\n }, [ticking]);\n return ticking ? tickedAt : new Date();\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { formatBackgroundTaskRow } from './background-task-row-format.js';\nimport { useCountdownTick } from './hooks/useCountdownTick.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\ninterface IProps {\n entries: IExecutionWorkspaceEntry[];\n /** Index of the keyboard-focused row (SCREEN-014), or null when the prompt input is focused. */\n focusedIndex?: number | null;\n}\n\nexport default function BackgroundTaskPanel({\n entries,\n focusedIndex = null,\n}: IProps): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n // SCREEN-1992: ticks once a second only while a schedule sleeps, never in screen-reader mode.\n const now = useCountdownTick(\n entries.some((entry) => entry.nextFireAt !== undefined),\n screenReader,\n );\n if (entries.length === 0) return null;\n\n // SCREEN-014: the hint is focus-aware — it tells you how to enter the list, then how to move/open.\n const hint =\n focusedIndex !== null ? ' · ↑↓ select · Enter open · Esc back' : ' · ↓ select · Ctrl+B all';\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={palette.text.accent} bold>\n Background work\n </Text>\n <Text dimColor>{hint}</Text>\n </Box>\n {entries.map((entry, index) => {\n const row = formatBackgroundTaskRow(entry, {\n isLast: index === entries.length - 1,\n screenReader,\n now,\n });\n const isFocused = index === focusedIndex;\n return (\n // SCREEN-011: one truncated line so the connector + glyph lead the row.\n // SCREEN-014: the keyboard-focused row is inverse-highlighted.\n <Text key={entry.id} wrap=\"truncate-end\" inverse={isFocused}>\n {`${row.connector} `}\n <Text\n color={statusGlyphColor(palette, row.statusKind)}\n >{`${row.marker} ${row.state}`}</Text>\n {` ${row.label}`}\n {row.segments.map((segment, segmentIndex) => (\n <Text key={`${segment}-${segmentIndex}`} dimColor>{` · ${segment}`}</Text>\n ))}\n {row.headline ? <Text dimColor>{` · ${row.headline}`}</Text> : null}\n {row.preview ? <Text dimColor>{` · ${row.preview}`}</Text> : null}\n {row.countdown ? (\n <Text color={statusGlyphColor(palette, row.statusKind)}>{` · ${row.countdown}`}</Text>\n ) : null}\n </Text>\n );\n })}\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\ninterface IProps {\n percentage: number;\n}\n\nconst COMPACT_SUGGESTION_THRESHOLD = 70;\nconst CRITICAL_THRESHOLD = 90;\n\nexport function ContextWarningBanner({ percentage }: IProps): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n if (percentage >= CRITICAL_THRESHOLD) {\n return (\n <Box\n {...(screenReader\n ? {}\n : { borderStyle: 'single' as const, borderColor: palette.border.error })}\n paddingX={1}\n >\n <Text color={palette.text.error} bold>\n ⚠ Context at {Math.round(percentage)}% — window nearly full. Run /compact to summarize the\n conversation.\n </Text>\n </Box>\n );\n }\n\n if (percentage >= COMPACT_SUGGESTION_THRESHOLD) {\n return (\n <Box paddingX={1}>\n <Text color={palette.text.warning}>\n Context at {Math.round(percentage)}% — consider running /compact to free up space.\n </Text>\n </Box>\n );\n }\n\n return null;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport {\n formatExecutionDetailRecord,\n formatExecutionWorkspaceEntryRow,\n} from './execution-workspace-view-model.js';\nimport { Text } from './SafeText.js';\nimport { STATUS_SYMBOL, statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n TExecutionDetailRecordKind,\n} from '@robota-sdk/agent-interface-execution';\n\nconst MAX_VISIBLE_DETAIL_RECORDS = 12;\n\ninterface IProps {\n entry: IExecutionWorkspaceEntry;\n page: IExecutionDetailPage | null;\n loading?: boolean;\n error?: string;\n}\n\nexport default function ExecutionWorkspaceDetailPane({\n entry,\n page,\n loading,\n error,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId: entry.id });\n const records = page?.records.slice(-MAX_VISIBLE_DETAIL_RECORDS) ?? [];\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {`Viewing ${row.title}`}\n </Text>\n <Text dimColor>\n {row.statusLabel}\n {row.subtitle ? ` · ${row.subtitle}` : ''}\n {row.preview ? ` · ${row.preview}` : ''}\n </Text>\n {loading ? <Text dimColor>Loading workspace detail...</Text> : null}\n {error ? <Text color={palette.text.error}>{error}</Text> : null}\n {!loading && !error && records.length === 0 ? <Text dimColor>No detail yet</Text> : null}\n {!loading &&\n !error &&\n records.map((record) => {\n const { symbol, color } = getDetailRecordGlyph(record.kind, palette);\n return (\n <Text key={record.id} color={color}>\n {symbol ? `${symbol} ` : ''}\n {formatExecutionDetailRecord(record)}\n </Text>\n );\n })}\n {page?.nextCursor ? <Text dimColor>... more detail available</Text> : null}\n </Box>\n );\n}\n\n/**\n * Symbol + color per detail-record kind. A symbol always accompanies the color\n * so status is legible without color (SCREEN-005 — no color-only encoding).\n */\nfunction getDetailRecordGlyph(\n kind: TExecutionDetailRecordKind,\n palette: IThemeColors,\n): {\n symbol: string;\n color: string | undefined;\n} {\n if (kind === 'error')\n return { symbol: STATUS_SYMBOL.error, color: statusGlyphColor(palette, 'error') };\n if (kind === 'result')\n return { symbol: STATUS_SYMBOL.success, color: statusGlyphColor(palette, 'success') };\n if (kind === 'group_summary') return { symbol: '▸', color: palette.text.accent };\n if (kind === 'process_output') return { symbol: '·', color: palette.text.emphasis };\n return { symbol: '', color: undefined };\n}\n","export interface ISelectionFlowState {\n selectedIndex: number;\n scrollOffset: number;\n resolved: boolean;\n}\n\nexport interface ISelectionInputKey {\n escape?: boolean;\n return?: boolean;\n upArrow?: boolean;\n downArrow?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n}\n\nexport type TSelectionInputAction = 'cancel' | 'select' | 'previous' | 'next';\n\nexport type TSelectionEffect =\n | { type: 'none' }\n | { type: 'cancel' }\n | { type: 'select'; index: number }\n // CLI-2004: a checklist toggles many times and then commits; only the numbered branch emits it.\n | { type: 'confirm' };\n\nexport interface ISelectionFlowOptions {\n itemCount: number;\n maxVisible?: number;\n wrap?: boolean;\n enabled?: boolean;\n}\n\n/** `initialIndex` seeds the highlight (a request's declared default); the caller clamps it. */\nexport function createSelectionFlowState(initialIndex = 0): ISelectionFlowState {\n return { selectedIndex: initialIndex, scrollOffset: 0, resolved: false };\n}\n\nexport function getVerticalSelectionInputAction(\n key: ISelectionInputKey,\n): TSelectionInputAction | undefined {\n if (key.escape === true) return 'cancel';\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n if (key.return === true) return 'select';\n return undefined;\n}\n\nexport function getDirectionalSelectionInputAction(\n key: ISelectionInputKey,\n): TSelectionInputAction | undefined {\n if (key.escape === true) return 'cancel';\n if (key.leftArrow === true || key.upArrow === true) return 'previous';\n if (key.rightArrow === true || key.downArrow === true) return 'next';\n if (key.return === true) return 'select';\n return undefined;\n}\n\n/**\n * CLI-2004 — the typed-number branch of a selection.\n *\n * Screen-reader mode replaces arrow-key navigation with a numbered list and a typed answer, but the\n * SELECTION itself must stay owned by this module: two reducers deciding what \"selected\" means is\n * how a menu ends up resolving differently depending on how you drove it. `applyNumericSelection`\n * accumulates digits and resolves on Enter; everything downstream is the same `TSelectionEffect`.\n */\nexport interface INumericSelectionState {\n /** Digits typed so far, before Enter. */\n buffer: string;\n /** The last Enter was outside `1..itemCount` (or empty) — the caller re-prints the prompt. */\n invalid: boolean;\n resolved: boolean;\n}\n\nexport interface INumericSelectionOptions {\n itemCount: number;\n /** Escape resolves as a cancel only when the menu offers one. */\n cancellable?: boolean;\n /**\n * A checklist: a number TOGGLES its row and the menu stays open, so Enter on an empty buffer is\n * the commit rather than an invalid entry. A single-choice menu resolves on the first number.\n */\n multi?: boolean;\n /** A persistent view: selecting one row does not resolve the input. */\n repeatable?: boolean;\n}\n\nexport function createNumericSelectionState(): INumericSelectionState {\n return { buffer: '', invalid: false, resolved: false };\n}\n\n/**\n * Apply one keystroke to a numbered selection. Digits accumulate; Backspace edits; Enter resolves or\n * marks the entry invalid; Escape cancels when the menu is cancellable. A non-digit character is\n * ignored rather than treated as an answer — the invalid signal is reserved for a completed entry.\n */\nexport function applyNumericSelection(\n state: INumericSelectionState,\n input: string,\n key: ISelectionInputKey & { backspace?: boolean; delete?: boolean },\n options: INumericSelectionOptions,\n): { state: INumericSelectionState; effect: TSelectionEffect } {\n if (state.resolved) return { state, effect: { type: 'none' } };\n if (key.escape === true && options.cancellable === true) {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (key.backspace === true || key.delete === true) {\n return { state: { ...state, buffer: state.buffer.slice(0, -1) }, effect: { type: 'none' } };\n }\n if (key.return === true) {\n // A checklist commits on an empty Enter; a single-choice menu has nothing to commit, so the\n // same keystroke there is an entry that named no row.\n if (options.multi === true && state.buffer === '') {\n return { state: { ...state, invalid: false, resolved: true }, effect: { type: 'confirm' } };\n }\n const index = Number.parseInt(state.buffer, 10) - 1;\n if (!Number.isInteger(index) || index < 0 || index >= options.itemCount) {\n return { state: { ...state, buffer: '', invalid: true }, effect: { type: 'none' } };\n }\n return {\n // A toggle leaves the menu open — only a single-choice selection resolves it.\n state: { ...state, buffer: '', invalid: false, resolved: options.multi !== true && options.repeatable !== true },\n effect: { type: 'select', index },\n };\n }\n if (/^[0-9]+$/.test(input)) {\n return {\n state: { ...state, buffer: state.buffer + input, invalid: false },\n effect: { type: 'none' },\n };\n }\n return { state, effect: { type: 'none' } };\n}\n\nexport function applySelectionInput(\n state: ISelectionFlowState,\n action: TSelectionInputAction,\n options: ISelectionFlowOptions,\n): { state: ISelectionFlowState; effect: TSelectionEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (action === 'cancel') {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (options.enabled === false || options.itemCount === 0) {\n return { state, effect: { type: 'none' } };\n }\n if (action === 'select') {\n const index = clampIndex(state.selectedIndex, options.itemCount);\n return {\n state: { ...state, selectedIndex: index, resolved: true },\n effect: { type: 'select', index },\n };\n }\n const selectedIndex = moveSelection(state.selectedIndex, action, options);\n const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);\n return { state: { ...state, selectedIndex, scrollOffset }, effect: { type: 'none' } };\n}\n\nexport function normalizeSelectionState(\n state: ISelectionFlowState,\n options: ISelectionFlowOptions,\n): ISelectionFlowState {\n if (options.itemCount === 0) {\n return { ...state, selectedIndex: 0, scrollOffset: 0 };\n }\n const selectedIndex = clampIndex(state.selectedIndex, options.itemCount);\n const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);\n if (selectedIndex === state.selectedIndex && scrollOffset === state.scrollOffset) {\n return state;\n }\n return {\n ...state,\n selectedIndex,\n scrollOffset,\n };\n}\n\nfunction moveSelection(\n selectedIndex: number,\n action: TSelectionInputAction,\n options: ISelectionFlowOptions,\n): number {\n if (action === 'previous') {\n if (options.wrap === true && selectedIndex === 0) return options.itemCount - 1;\n return Math.max(0, selectedIndex - 1);\n }\n if (options.wrap === true && selectedIndex === options.itemCount - 1) return 0;\n return Math.min(options.itemCount - 1, selectedIndex + 1);\n}\n\nfunction resolveScrollOffset(\n selectedIndex: number,\n scrollOffset: number,\n options: ISelectionFlowOptions,\n): number {\n const maxVisible = options.maxVisible ?? options.itemCount;\n if (maxVisible <= 0) return 0;\n if (selectedIndex < scrollOffset) return selectedIndex;\n if (selectedIndex >= scrollOffset + maxVisible) return selectedIndex - maxVisible + 1;\n return Math.max(0, scrollOffset);\n}\n\nfunction clampIndex(index: number, itemCount: number): number {\n return Math.min(Math.max(index, 0), itemCount - 1);\n}\n","/**\n * The switcher's selection state: where the cursor is, which slice of entries is on screen, and what\n * a keypress does to both.\n *\n * Split out of `ExecutionWorkspaceSwitcher.tsx` (CLI-1994 + CLI-2004 both grew that file past its\n * size budget) along the seam that was already there: this half owns \"what is selected\", the\n * component owns \"what is drawn\".\n */\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type React from 'react';\n\n/** How many entries the list shows at once; the rest scroll. Read only by this module. */\nconst MAX_VISIBLE_WORKSPACE_ENTRIES = 8;\n\nexport interface IUseWorkspaceSwitcherSelectionInput {\n entries: IExecutionWorkspaceEntry[];\n selectedEntryId?: string;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n}\n\nexport function useWorkspaceSwitcherSelection({\n entries,\n selectedEntryId,\n onSelect,\n onClose,\n}: IUseWorkspaceSwitcherSelectionInput): {\n normalized: ISelectionFlowState;\n visibleEntries: IExecutionWorkspaceEntry[];\n applyAction: (action: TSelectionInputAction) => void;\n} {\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n\n useEffect(() => {\n const selectedIndex = Math.max(\n 0,\n entries.findIndex((entry) => entry.id === selectedEntryId),\n );\n const nextState = createNormalizedSelection({ selectedIndex, itemCount: entries.length });\n stateRef.current = nextState;\n setState(nextState);\n }, [entries.length, selectedEntryId]);\n\n const normalized = createNormalizedSelection({\n selectedIndex: state.selectedIndex,\n scrollOffset: state.scrollOffset,\n itemCount: entries.length,\n });\n if (normalized !== state) stateRef.current = normalized;\n return {\n normalized,\n visibleEntries: entries.slice(\n normalized.scrollOffset,\n normalized.scrollOffset + MAX_VISIBLE_WORKSPACE_ENTRIES,\n ),\n applyAction: createApplyAction({ entries, stateRef, setState, onSelect, onClose }),\n };\n}\n\nfunction createApplyAction({\n entries,\n stateRef,\n setState,\n onSelect,\n onClose,\n}: {\n entries: IExecutionWorkspaceEntry[];\n stateRef: React.MutableRefObject<ISelectionFlowState>;\n setState: React.Dispatch<React.SetStateAction<ISelectionFlowState>>;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n}): (action: TSelectionInputAction) => void {\n return (action): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: entries.length,\n maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES,\n });\n const nextState =\n result.effect.type === 'select' || result.effect.type === 'cancel'\n ? { ...result.state, resolved: false }\n : result.state;\n stateRef.current = nextState;\n setState(nextState);\n if (result.effect.type === 'cancel') {\n onClose();\n } else if (result.effect.type === 'select') {\n const entry = entries[result.effect.index];\n if (entry) onSelect(entry.id);\n }\n };\n}\n\nfunction createNormalizedSelection(input: {\n selectedIndex: number;\n scrollOffset?: number;\n itemCount: number;\n}): ISelectionFlowState {\n return normalizeSelectionState(\n {\n selectedIndex: input.selectedIndex,\n scrollOffset: input.scrollOffset ?? 0,\n resolved: false,\n },\n { itemCount: input.itemCount, maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES },\n );\n}\n","/**\n * CLI-2004 — the typed-number input wiring shared by every numbered menu.\n *\n * The reducer lives in `flows/selection-flow.ts` (one owner of \"what is selected\"); this hook is the\n * Ink adapter around it, so six components get the same behaviour without six copies of the same\n * `useInput` block. Inactive when the mode is off, so the arrow-key handlers keep the terminal.\n */\n\nimport { useInput } from 'ink';\nimport { useCallback, useRef, useState } from 'react';\n\nimport {\n applyNumericSelection,\n createNumericSelectionState,\n type INumericSelectionState,\n} from '../flows/selection-flow.js';\n\nexport interface IUseNumberedSelectionInputs {\n /** Screen-reader mode. Off ⇒ the hook reads nothing. */\n enabled: boolean;\n itemCount: number;\n cancellable?: boolean;\n /** A checklist: a number toggles a row, the menu stays open, and an empty Enter commits. */\n multi?: boolean;\n /** Keep a single-row selection open so the user can select another row later. */\n repeatable?: boolean;\n onSelect: (index: number) => void;\n onCancel?: () => void;\n /** Required with `multi` — what an empty Enter commits. */\n onConfirm?: () => void;\n}\n\nexport interface INumberedSelection {\n buffer: string;\n invalid: boolean;\n clear: () => void;\n}\n\n/** Read a typed selection for a numbered list. */\nexport function useNumberedSelection(inputs: IUseNumberedSelectionInputs): INumberedSelection {\n const [state, setState] = useState<INumericSelectionState>(createNumericSelectionState);\n const stateRef = useRef(state);\n const { enabled, itemCount, cancellable, multi, repeatable, onSelect, onCancel, onConfirm } =\n inputs;\n\n const clear = useCallback((): void => {\n const empty = createNumericSelectionState();\n stateRef.current = empty;\n setState(empty);\n }, []);\n\n const handle = useCallback(\n (input: string, key: Parameters<typeof applyNumericSelection>[2]): boolean => {\n const result = applyNumericSelection(stateRef.current, input, key, {\n itemCount,\n ...(cancellable === true ? { cancellable: true } : {}),\n ...(multi === true ? { multi: true } : {}),\n ...(repeatable === true ? { repeatable: true } : {}),\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'select') onSelect(result.effect.index);\n else if (result.effect.type === 'cancel') onCancel?.();\n else if (result.effect.type === 'confirm') onConfirm?.();\n return result.effect.type !== 'none';\n },\n [itemCount, cancellable, multi, repeatable, onSelect, onCancel, onConfirm],\n );\n\n useInput(\n (input, key) => {\n // Pasted or fast-typed input can arrive as one chunk (\"2\\r\"): Ink then reports it as plain\n // text with no return key. Replay it as the keystrokes it contains so the Enter is not lost,\n // but only up to the first keystroke that acts: the menu's callbacks see the state of the\n // current render, so acting twice within one chunk could decide on stale state.\n if (key.return !== true && /[\\r\\n]/u.test(input)) {\n const plainKey = { ...key, return: false };\n for (const part of input.split(/(\\r\\n|\\r|\\n)/u)) {\n if (part === '') continue;\n const acted = /^(\\r\\n|\\r|\\n)$/u.test(part)\n ? handle('', { ...plainKey, return: true })\n : handle(part, plainKey);\n if (acted) break;\n }\n return;\n }\n handle(input, key);\n },\n { isActive: enabled },\n );\n\n return { buffer: state.buffer, invalid: state.invalid, clear };\n}\n","/**\n * Key-hint footer SSOT (SCREEN-005).\n *\n * Single source of truth for prompt-footer key hints and the selection-row indicator across the\n * package's Ink prompt/selection components. Every footer call site declares its hints as\n * `IKeyHint[]` and renders them through `formatKeyHints` / `KeyHintFooter`, so exactly one grammar\n * exists (no per-component footer dialects) and a drift cannot re-appear silently — the\n * `key-hint-consistency` test asserts every declared hint set round-trips through this module.\n *\n * This module ships MECHANICS only: the separator, the pair grammar, the indicator constants.\n * Verb vocabulary (Navigate/Select/Confirm/…) is supplied by callers — the package stays a\n * content-neutral library ingredient. No config surface: per-call-site suppression is passing an\n * empty hint list.\n *\n * Affordance contract (see docs/SPEC.md \"Interaction affordance contract\"): the footer lists\n * exactly the keys that do something — a prompt that suppresses Esc in its flow omits Esc here.\n */\n\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** One key hint: the key(s) and the verb label, e.g. `{ keys: '↑↓', label: 'Navigate' }`. */\nexport interface IKeyHint {\n keys: string;\n label: string;\n}\n\n/** Separator between key-hint pairs. Changing the footer separator is a one-constant edit. */\nexport const KEY_HINT_SEPARATOR = ' · ';\n\n/** Selection cursor rendered in front of the focused row. */\nexport const SELECTION_INDICATOR = '> ';\n\n/** Same-width blank rendered in front of non-focused rows so columns stay aligned. */\nexport const SELECTION_INDICATOR_NONE = ' ';\n\n/** Pure formatter: joins `keys label` pairs with {@link KEY_HINT_SEPARATOR}. Empty list → ''. */\nexport function formatKeyHints(hints: readonly IKeyHint[]): string {\n return hints.map((hint) => `${hint.keys} ${hint.label}`).join(KEY_HINT_SEPARATOR);\n}\n\n/**\n * Dim one-line footer: leading pad + formatted hints. Renders nothing for an empty hint list\n * (per-call-site suppression without a config surface).\n */\nexport function KeyHintFooter({\n hints,\n}: {\n hints: readonly IKeyHint[];\n}): React.ReactElement | null {\n if (hints.length === 0) return null;\n return <Text dimColor> {formatKeyHints(hints)}</Text>;\n}\n","export const KEYBINDING_ACTIONS = {\n app: ['open-workspace-switcher'],\n thinking: ['abort'],\n 'background-detail': ['return-to-main'],\n recovery: ['retry'],\n 'chat-input': [\n 'submit',\n 'history-previous',\n 'history-next',\n 'history-search',\n 'cursor-left',\n 'cursor-right',\n 'delete-backward',\n 'delete-word',\n 'delete-line',\n ],\n 'text-input': [\n 'submit',\n 'cursor-left',\n 'cursor-right',\n 'cursor-up',\n 'cursor-down',\n 'delete-backward',\n 'delete-word',\n 'delete-line',\n ],\n 'autocomplete-menu': ['previous', 'next', 'close', 'accept', 'execute'],\n 'queued-prompt': ['cancel'],\n 'confirm-prompt': ['previous', 'next', 'confirm', 'choose-yes', 'choose-no'],\n 'permission-prompt': [\n 'previous',\n 'next',\n 'confirm',\n 'allow-once',\n 'allow-session',\n 'allow-project',\n 'deny',\n ],\n 'text-prompt': ['submit', 'cancel', 'delete-backward'],\n 'list-picker': ['previous', 'next', 'select', 'cancel'],\n 'menu-select': ['previous', 'next', 'select', 'back'],\n 'multi-select': ['previous', 'next', 'toggle', 'confirm', 'cancel'],\n 'workspace-switcher': ['previous', 'next', 'select', 'close', 'attach'],\n 'background-list': ['previous', 'next', 'open', 'close'],\n 'transport-settings': ['previous', 'next', 'toggle', 'close'],\n /** SCREEN-1993: the reverse prompt-history search overlay. */\n 'history-search': ['previous', 'next', 'cycle-scope', 'insert', 'execute', 'cancel'],\n /**\n * SCREEN-2002: the theme picker. Navigation PREVIEWS; only `select` applies. Printable keys are\n * fine here because the picker takes no text.\n */\n 'theme-picker': ['previous', 'next', 'select', 'toggle-syntax', 'toggle-motion', 'cancel'],\n} as const;\n\nexport type TKeybindingContext = keyof typeof KEYBINDING_ACTIONS;\nexport type TKeybindingAction<TContext extends TKeybindingContext = TKeybindingContext> =\n (typeof KEYBINDING_ACTIONS)[TContext][number];\n\ntype TDefaultKeybindings = {\n readonly [TContext in TKeybindingContext]: Readonly<\n Record<TKeybindingAction<TContext>, readonly string[]>\n >;\n};\n\nexport const DEFAULT_KEYBINDINGS = {\n app: { 'open-workspace-switcher': ['ctrl+b'] },\n thinking: { abort: ['escape'] },\n 'background-detail': { 'return-to-main': ['escape'] },\n recovery: { retry: ['enter'] },\n 'chat-input': {\n submit: ['enter'],\n 'history-previous': ['up'],\n 'history-next': ['down'],\n 'history-search': ['ctrl+r'],\n 'cursor-left': ['left'],\n 'cursor-right': ['right'],\n 'delete-backward': ['backspace', 'delete'],\n 'delete-word': ['ctrl+w'],\n 'delete-line': ['ctrl+u'],\n },\n 'text-input': {\n submit: ['enter'],\n 'cursor-left': ['left'],\n 'cursor-right': ['right'],\n 'cursor-up': ['up'],\n 'cursor-down': ['down'],\n 'delete-backward': ['backspace', 'delete'],\n 'delete-word': ['ctrl+w'],\n 'delete-line': ['ctrl+u'],\n },\n 'autocomplete-menu': {\n previous: ['up'],\n next: ['down'],\n close: ['escape'],\n accept: ['tab'],\n execute: ['enter'],\n },\n 'queued-prompt': { cancel: ['backspace', 'delete'] },\n 'confirm-prompt': {\n previous: ['left', 'up'],\n next: ['right', 'down'],\n confirm: ['enter'],\n 'choose-yes': ['y'],\n 'choose-no': ['n'],\n },\n 'permission-prompt': {\n previous: ['left', 'up'],\n next: ['right', 'down'],\n confirm: ['enter'],\n 'allow-once': ['y'],\n 'allow-session': ['s', 'a'],\n 'allow-project': ['p'],\n deny: ['n', 'd'],\n },\n 'text-prompt': {\n submit: ['enter'],\n cancel: ['escape'],\n 'delete-backward': ['backspace', 'delete'],\n },\n 'list-picker': { previous: ['up'], next: ['down'], select: ['enter'], cancel: ['escape'] },\n 'menu-select': { previous: ['up'], next: ['down'], select: ['enter'], back: ['escape'] },\n 'multi-select': {\n previous: ['up'],\n next: ['down'],\n toggle: ['space'],\n confirm: ['enter'],\n cancel: ['escape'],\n },\n 'workspace-switcher': {\n previous: ['up'],\n next: ['down'],\n select: ['enter'],\n close: ['ctrl+b', 'escape'],\n attach: ['a'],\n },\n 'background-list': {\n previous: ['up'],\n next: ['down'],\n open: ['enter'],\n close: ['escape'],\n },\n 'transport-settings': {\n previous: ['up'],\n next: ['down'],\n toggle: ['space'],\n close: ['enter', 'escape'],\n },\n 'history-search': {\n previous: ['up'],\n next: ['down', 'ctrl+r'],\n 'cycle-scope': ['ctrl+s'],\n insert: ['enter', 'tab'],\n execute: ['ctrl+e'],\n cancel: ['escape'],\n },\n 'theme-picker': {\n previous: ['up'],\n next: ['down'],\n select: ['enter'],\n 'toggle-syntax': ['s'],\n 'toggle-motion': ['m'],\n cancel: ['escape'],\n },\n} as const satisfies TDefaultKeybindings;\n\nexport const TEXT_ENTRY_CONTEXTS: ReadonlySet<TKeybindingContext> = new Set([\n 'chat-input',\n 'text-input',\n 'text-prompt',\n 'history-search',\n]);\n","export interface IKeyInput {\n readonly ctrl?: boolean;\n readonly shift?: boolean;\n readonly meta?: boolean;\n readonly return?: boolean;\n readonly escape?: boolean;\n readonly tab?: boolean;\n readonly upArrow?: boolean;\n readonly downArrow?: boolean;\n readonly leftArrow?: boolean;\n readonly rightArrow?: boolean;\n readonly backspace?: boolean;\n readonly delete?: boolean;\n}\n\nconst MODIFIER_ALIASES: Readonly<Record<string, 'ctrl' | 'alt' | 'meta' | 'shift'>> = {\n ctrl: 'ctrl',\n control: 'ctrl',\n alt: 'alt',\n option: 'alt',\n meta: 'meta',\n cmd: 'meta',\n command: 'meta',\n shift: 'shift',\n};\nconst MODIFIER_ORDER = ['ctrl', 'alt', 'meta', 'shift'] as const;\nconst KEY_ALIASES: Readonly<Record<string, string>> = {\n return: 'enter',\n enter: 'enter',\n esc: 'escape',\n escape: 'escape',\n tab: 'tab',\n space: 'space',\n spacebar: 'space',\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n backspace: 'backspace',\n delete: 'delete',\n};\n\nexport function normalizeBinding(raw: string): string | undefined {\n const strokes = raw.trim().split(/\\s+/u);\n if (strokes.length < 1 || strokes.length > 2) return undefined;\n const normalized = strokes.map(normalizeStroke);\n return normalized.every((stroke): stroke is string => stroke !== undefined)\n ? normalized.join(' ')\n : undefined;\n}\n\nfunction normalizeStroke(raw: string): string | undefined {\n if (!raw || raw.includes(' ')) return undefined;\n const parts = raw.split('+');\n if (parts.some((part) => part.length === 0)) return undefined;\n const rawKey = parts.at(-1);\n if (rawKey === undefined) return undefined;\n const modifiers = new Set<'ctrl' | 'alt' | 'meta' | 'shift'>();\n for (const rawModifier of parts.slice(0, -1)) {\n const modifier = MODIFIER_ALIASES[rawModifier.toLowerCase()];\n if (modifier === undefined || modifiers.has(modifier)) return undefined;\n modifiers.add(modifier);\n }\n let key = KEY_ALIASES[rawKey.toLowerCase()];\n if (key === undefined) {\n if ([...rawKey].length !== 1 || /\\s/u.test(rawKey)) return undefined;\n key = rawKey.toLowerCase();\n if (parts.length === 1 && rawKey !== key) modifiers.add('shift');\n }\n return [...MODIFIER_ORDER.filter((modifier) => modifiers.has(modifier)), key].join('+');\n}\n\nfunction keyStrokeFromInput(input: string, key: IKeyInput): string | undefined {\n // Ink preserves LF as a one-character input string instead of setting `key.return`. In terminal\n // raw mode that byte is Ctrl+J; normalize it before printable-text handling can treat it as paste.\n if (/^[\\r\\n]*\\n[\\r\\n]*$/u.test(input)) return 'ctrl+j';\n const special = key.return\n ? 'enter'\n : key.escape\n ? 'escape'\n : key.tab\n ? 'tab'\n : key.upArrow\n ? 'up'\n : key.downArrow\n ? 'down'\n : key.leftArrow\n ? 'left'\n : key.rightArrow\n ? 'right'\n : key.backspace\n ? 'backspace'\n : key.delete\n ? 'delete'\n : input === ' '\n ? 'space'\n : [...input].length === 1\n ? input.toLowerCase()\n : undefined;\n if (special === undefined) return undefined;\n const modifiers = [\n ...(key.ctrl ? ['ctrl'] : []),\n ...(key.meta ? ['meta'] : []),\n ...(key.shift ? ['shift'] : []),\n ];\n return [...modifiers, special].join('+');\n}\n\n/**\n * Terminal protocols collapse several control characters onto named keys. Keep the configured\n * spelling for hints, but offer every spelling that the same received byte can represent.\n */\nexport function keyStrokesFromInput(input: string, key: IKeyInput): readonly string[] {\n const primary = keyStrokeFromInput(input, key);\n if (primary === undefined) return [];\n if (primary === 'enter') return ['enter', 'ctrl+m'];\n if (primary === 'tab') return ['tab', 'ctrl+i'];\n if (primary === 'escape') return ['escape', 'ctrl+['];\n return [primary];\n}\n\nexport function keybindingDeliverySignature(binding: string): string {\n return binding\n .split(' ')\n .map((stroke) => {\n if (stroke === 'ctrl+m') return 'enter';\n if (stroke === 'ctrl+i') return 'tab';\n if (stroke === 'ctrl+[') return 'escape';\n return stroke;\n })\n .join(' ');\n}\n\nexport function displayBinding(binding: string): string {\n const displayStroke = (stroke: string): string =>\n stroke\n .split('+')\n .map(\n (part) =>\n ({\n ctrl: 'Ctrl',\n alt: 'Alt',\n meta: 'Meta',\n shift: 'Shift',\n escape: 'Esc',\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n })[part] ??\n (part.length === 1 ? part.toUpperCase() : `${part[0]?.toUpperCase()}${part.slice(1)}`),\n )\n .join('+');\n return binding.split(' ').map(displayStroke).join(' ');\n}\n","import { isJsonRecord } from '../json-value.js';\nimport {\n DEFAULT_KEYBINDINGS,\n KEYBINDING_ACTIONS,\n TEXT_ENTRY_CONTEXTS,\n type TKeybindingAction,\n type TKeybindingContext,\n} from './keybinding-catalogue.js';\nimport {\n displayBinding,\n keybindingDeliverySignature,\n keyStrokesFromInput,\n normalizeBinding,\n type IKeyInput,\n} from './keybinding-syntax.js';\n\nimport type { TJsonValue } from '../json-value.js';\n\nexport { DEFAULT_KEYBINDINGS } from './keybinding-catalogue.js';\nexport type { IKeyInput } from './keybinding-syntax.js';\nexport type { TKeybindingAction, TKeybindingContext } from './keybinding-catalogue.js';\n\nexport interface IKeybindingWarning {\n readonly code: 'multiplexer-conflict' | 'modifier-delivery';\n readonly path: string;\n readonly message: string;\n}\n\nexport interface IKeybindingDiagnostic {\n readonly file: string;\n readonly path: string;\n readonly message: string;\n}\n\nexport interface IKeybindingSnapshot {\n readonly generation: number;\n readonly sourcePath: string;\n readonly bindings: Readonly<\n Record<TKeybindingContext, Readonly<Record<string, readonly string[]>>>\n >;\n readonly warnings: readonly IKeybindingWarning[];\n readonly diagnostic?: IKeybindingDiagnostic;\n}\n\nexport type TParseKeybindingsResult =\n | { readonly ok: true; readonly snapshot: IKeybindingSnapshot }\n | { readonly ok: false; readonly diagnostic: IKeybindingDiagnostic };\n\nconst CHORD_TIMEOUT_MS = 1_000;\n\nfunction diagnostic(file: string, path: string, message: string): TParseKeybindingsResult {\n return { ok: false, diagnostic: { file, path, message } };\n}\n\nfunction cloneDefaults(): Record<TKeybindingContext, Record<string, string[]>> {\n return Object.fromEntries(\n Object.entries(DEFAULT_KEYBINDINGS).map(([context, actions]) => [\n context,\n Object.fromEntries(\n Object.entries(actions).map(([action, bindings]) => [action, [...bindings]]),\n ),\n ]),\n ) as Record<TKeybindingContext, Record<string, string[]>>;\n}\n\nfunction bindingValues(value: TJsonValue): readonly string[] | null | undefined {\n if (value === null) return null;\n if (typeof value === 'string') return [value];\n if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === 'string')) {\n return value;\n }\n return undefined;\n}\n\nfunction validateContextBindings(\n context: TKeybindingContext,\n bindings: Record<string, string[]>,\n file: string,\n): TParseKeybindingsResult | undefined {\n const owners = new Map<string, string>();\n const prefixes = new Map<string, string>();\n for (const [action, values] of Object.entries(bindings)) {\n for (const binding of values) {\n const deliveryBinding = keybindingDeliverySignature(binding);\n const owner = owners.get(deliveryBinding);\n const actionPath = `$.bindings.${context}.${action}`;\n if (owner !== undefined && owner !== action) {\n return diagnostic(file, actionPath, `Binding duplicates action ${owner}.`);\n }\n const strokes = deliveryBinding.split(' ');\n if (strokes.length > 1 && TEXT_ENTRY_CONTEXTS.has(context) && isPrintable(strokes[0] ?? '')) {\n return diagnostic(\n file,\n actionPath,\n 'Printable text cannot begin a chord in a text context.',\n );\n }\n owners.set(deliveryBinding, action);\n if (strokes.length > 1) prefixes.set(strokes[0] ?? '', action);\n }\n }\n for (const [binding, action] of owners) {\n const prefixOwner = prefixes.get(binding);\n if (prefixOwner !== undefined) {\n return diagnostic(\n file,\n `$.bindings.${context}.${prefixOwner}`,\n `Chord prefix collides with action ${action}.`,\n );\n }\n }\n return undefined;\n}\n\nfunction isPrintable(stroke: string): boolean {\n return !stroke.includes('+') && [...stroke].length === 1;\n}\n\nfunction warningFor(binding: string, path: string): IKeybindingWarning[] {\n const warnings: IKeybindingWarning[] = [];\n if (binding.split(' ').includes('ctrl+b')) {\n warnings.push({\n code: 'multiplexer-conflict',\n path,\n message: 'Ctrl+B may be captured by a terminal multiplexer.',\n });\n }\n if (/ctrl\\+shift\\+[a-z]/u.test(binding)) {\n warnings.push({\n code: 'modifier-delivery',\n path,\n message: 'This terminal may not distinguish Ctrl+Shift from Ctrl.',\n });\n }\n return warnings;\n}\n\nexport function parseKeybindingsDocument(text: string, file: string): TParseKeybindingsResult {\n let value: TJsonValue;\n try {\n value = JSON.parse(text) as TJsonValue;\n } catch (cause) {\n return diagnostic(file, '$', cause instanceof Error ? cause.message : 'Invalid JSON.');\n }\n if (!isJsonRecord(value)) return diagnostic(file, '$', 'Expected an object.');\n if (value.version !== 1) return diagnostic(file, '$.version', 'Expected version 1.');\n if (!isJsonRecord(value.bindings)) return diagnostic(file, '$.bindings', 'Expected an object.');\n\n const effective = cloneDefaults();\n const warnings: IKeybindingWarning[] = [];\n for (const [rawContext, rawActions] of Object.entries(value.bindings)) {\n if (!(rawContext in KEYBINDING_ACTIONS)) {\n return diagnostic(file, `$.bindings.${rawContext}`, 'Unknown keybinding context.');\n }\n const context = rawContext as TKeybindingContext;\n if (!isJsonRecord(rawActions)) {\n return diagnostic(file, `$.bindings.${context}`, 'Expected an action object.');\n }\n for (const [action, rawBindings] of Object.entries(rawActions)) {\n if (!(KEYBINDING_ACTIONS[context] as readonly string[]).includes(action)) {\n return diagnostic(file, `$.bindings.${context}.${action}`, 'Unknown action.');\n }\n const values = bindingValues(rawBindings);\n const actionPath = `$.bindings.${context}.${action}`;\n if (values === undefined) {\n return diagnostic(file, actionPath, 'Expected a binding, binding list, or null.');\n }\n if (values === null) {\n effective[context][action] = [];\n continue;\n }\n const normalized: string[] = [];\n for (const rawBinding of values) {\n const binding = normalizeBinding(rawBinding);\n if (binding === undefined) return diagnostic(file, actionPath, 'Invalid binding syntax.');\n if (binding.split(' ').includes('ctrl+c')) {\n return diagnostic(file, actionPath, 'Ctrl+C is reserved for two-stage shutdown.');\n }\n normalized.push(binding);\n warnings.push(...warningFor(binding, actionPath));\n }\n effective[context][action] = normalized;\n }\n }\n for (const context of Object.keys(effective) as TKeybindingContext[]) {\n const invalid = validateContextBindings(context, effective[context], file);\n if (invalid !== undefined) return invalid;\n }\n const bindings = Object.freeze(\n Object.fromEntries(\n Object.entries(effective).map(([context, actions]) => [\n context,\n Object.freeze(\n Object.fromEntries(\n Object.entries(actions).map(([action, bindings]) => [action, Object.freeze(bindings)]),\n ),\n ),\n ]),\n ),\n ) as IKeybindingSnapshot['bindings'];\n return {\n ok: true,\n snapshot: Object.freeze({\n generation: 0,\n sourcePath: file,\n bindings,\n warnings: Object.freeze(warnings),\n }),\n };\n}\n\nexport interface IChordState {\n readonly context?: TKeybindingContext;\n readonly generation?: number;\n readonly strokes: readonly string[];\n readonly deadline?: number;\n}\n\nexport function createEmptyChordState(): IChordState {\n return { strokes: [] };\n}\n\nfunction actionBindings(\n snapshot: IKeybindingSnapshot,\n context: TKeybindingContext,\n): [TKeybindingAction, readonly string[]][] {\n return Object.entries(snapshot.bindings[context]) as [TKeybindingAction, readonly string[]][];\n}\n\nexport function resolveKeybindingInput(options: {\n readonly snapshot: IKeybindingSnapshot;\n readonly state: IChordState;\n readonly context: TKeybindingContext;\n readonly input: string;\n readonly key: IKeyInput;\n readonly now: number;\n}): {\n readonly state: IChordState;\n readonly actions: readonly TKeybindingAction[];\n readonly consumed: boolean;\n} {\n const strokes = keyStrokesFromInput(options.input, options.key);\n if (strokes.length === 0) {\n return { state: createEmptyChordState(), actions: [], consumed: false };\n }\n const stateValid =\n options.state.context === options.context &&\n options.state.generation === options.snapshot.generation &&\n (options.state.deadline === undefined || options.now <= options.state.deadline) &&\n !options.key.escape;\n const prior = stateValid ? options.state.strokes : [];\n const entries = actionBindings(options.snapshot, options.context);\n for (const stroke of strokes) {\n const sequence = [...prior, stroke].join(' ');\n const exact = entries\n .filter(([, bindings]) => bindings.includes(sequence))\n .map(([action]) => action);\n if (exact.length > 0) {\n return { state: createEmptyChordState(), actions: exact, consumed: true };\n }\n if (\n entries.some(([, bindings]) => bindings.some((binding) => binding.startsWith(`${sequence} `)))\n ) {\n return {\n state: {\n context: options.context,\n generation: options.snapshot.generation,\n strokes: [...prior, stroke],\n deadline: options.now + CHORD_TIMEOUT_MS,\n },\n actions: [],\n consumed: true,\n };\n }\n }\n if (prior.length > 0) {\n return resolveKeybindingInput({ ...options, state: createEmptyChordState() });\n }\n return { state: createEmptyChordState(), actions: [], consumed: false };\n}\n\nexport function keybindingHints<TContext extends TKeybindingContext>(\n snapshot: IKeybindingSnapshot,\n context: TContext,\n actions: readonly (readonly [\n TKeybindingAction<TContext> | readonly TKeybindingAction<TContext>[],\n string,\n ])[],\n): { keys: string; label: string }[] {\n return actions.flatMap(([actionOrActions, label]) => {\n const grouped = Array.isArray(actionOrActions);\n const actionList = grouped ? actionOrActions : [actionOrActions];\n const bindings = actionList.flatMap((action) => {\n const values = snapshot.bindings[context][action] ?? [];\n return grouped ? values.slice(0, 1) : values;\n });\n const displayed = bindings.map(displayBinding);\n const keys = displayed.every((binding) => /^[↑↓←→]$/u.test(binding))\n ? displayed.join('')\n : displayed.join('/');\n return bindings.length === 0 ? [] : [{ keys, label }];\n });\n}\n","import { useInput } from 'ink';\nimport React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport {\n createEmptyChordState,\n keybindingHints,\n parseKeybindingsDocument,\n resolveKeybindingInput,\n type IChordState,\n type IKeybindingSnapshot,\n type IKeyInput,\n type TKeybindingAction,\n type TKeybindingContext,\n} from './keybinding-registry.js';\n\nimport type { IKeybindingsSource } from './node-keybindings-source.js';\n\nconst parsedDefault = parseKeybindingsDocument('{\"version\":1,\"bindings\":{}}', '<defaults>');\nif (!parsedDefault.ok) throw new Error(parsedDefault.diagnostic.message);\nconst DEFAULT_SNAPSHOT = parsedDefault.snapshot;\n\nconst KeybindingsContext = createContext<IKeybindingSnapshot>(DEFAULT_SNAPSHOT);\n\nexport function KeybindingsProvider({\n source,\n children,\n}: {\n source?: IKeybindingsSource;\n children: React.ReactNode;\n}): React.ReactElement {\n const [snapshot, setSnapshot] = useState(() => source?.getSnapshot() ?? DEFAULT_SNAPSHOT);\n useEffect(() => {\n if (source === undefined) return;\n setSnapshot(source.getSnapshot());\n return source.subscribe(setSnapshot);\n }, [source]);\n return <KeybindingsContext.Provider value={snapshot}>{children}</KeybindingsContext.Provider>;\n}\n\nexport function useKeybindings(): IKeybindingSnapshot {\n return useContext(KeybindingsContext);\n}\n\nexport function useKeybindingHints<TContext extends TKeybindingContext>(\n context: TContext,\n actions: readonly (readonly [\n TKeybindingAction<TContext> | readonly TKeybindingAction<TContext>[],\n string,\n ])[],\n): { keys: string; label: string }[] {\n return keybindingHints(useKeybindings(), context, actions);\n}\n\nexport function useKeybindingActions<\n TContext extends TKeybindingContext,\n TFallback extends TKeybindingContext = TContext,\n>(\n context: TContext,\n handler: (\n actions: readonly (TKeybindingAction<TContext> | TKeybindingAction<TFallback>)[],\n input: string,\n key: IKeyInput,\n consumed: boolean,\n resolvedContext: TContext | TFallback,\n ) => void,\n options: {\n readonly isActive?: boolean;\n /**\n * A key `context` does not bind is resolved again here, so a surface layered over an editor (the\n * autocomplete menu over the chat input) leaves every key it does not use to the editor's bindings.\n */\n readonly fallbackContext?: TFallback;\n } = {},\n): void {\n const snapshot = useKeybindings();\n const stateRef = useRef<IChordState>(createEmptyChordState());\n const fallbackStateRef = useRef<IChordState>(createEmptyChordState());\n const { fallbackContext } = options;\n useEffect(() => {\n stateRef.current = createEmptyChordState();\n fallbackStateRef.current = createEmptyChordState();\n }, [context, fallbackContext, snapshot.generation]);\n useInput(\n (input, key) => {\n const result = resolveKeybindingInput({\n snapshot,\n state: stateRef.current,\n context,\n input,\n key,\n now: Date.now(),\n });\n stateRef.current = result.state;\n if (fallbackContext === undefined || result.consumed) {\n fallbackStateRef.current = createEmptyChordState();\n handler(\n result.actions as readonly TKeybindingAction<TContext>[],\n input,\n key,\n result.consumed,\n context,\n );\n return;\n }\n const fallback = resolveKeybindingInput({\n snapshot,\n state: fallbackStateRef.current,\n context: fallbackContext,\n input,\n key,\n now: Date.now(),\n });\n fallbackStateRef.current = fallback.state;\n handler(\n fallback.actions as readonly TKeybindingAction<TFallback>[],\n input,\n key,\n fallback.consumed,\n fallbackContext,\n );\n },\n { isActive: options.isActive ?? true },\n );\n}\n","/**\n * CLI-2004 — the numbered-list menu (SSOT for its grammar).\n *\n * Every arrow-key menu becomes this in screen-reader mode: `N. <option>` lines followed by one\n * prompt naming the valid range. Arrow navigation and the `> ` cursor are position cues a reader\n * cannot follow — a spoken \"3\" is unambiguous, and the range in the prompt is the affordance.\n *\n * The prompt LITERAL is authored here, not copied from any reference: the surveyed documentation\n * publishes the prompt's NAME and the cancel suffix, never the composed string. This module is the\n * one place it exists, so the menus cannot drift into per-component dialects — the same reason\n * `key-hint-footer.tsx` owns the footer grammar.\n *\n * No colour props: the mode drops colour rather than substituting values, so the palette floor stays\n * green and nothing here depends on a cue a reader cannot hear.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** Appended to the prompt when Escape actually cancels the menu. */\nexport const NUMBERED_LIST_CANCEL_SUFFIX = ' or Escape to cancel';\n\n/** The prompt literal. `Enter selection (1-3)`, plus the cancel suffix where it applies. */\nexport function formatNumberedSelectionPrompt(itemCount: number, cancellable: boolean): string {\n const base = `Enter selection (1-${itemCount})`;\n return cancellable ? `${base}${NUMBERED_LIST_CANCEL_SUFFIX}` : base;\n}\n\n/** One row's number prefix: `1. `, `2. `, … */\nexport function numberedRowPrefix(index: number): string {\n return `${index + 1}. `;\n}\n\nexport interface INumberedSelectionPromptProps {\n /** How many rows the prompt names. Zero ⇒ nothing is rendered: `(1-0)` names no answer. */\n itemCount: number;\n cancellable?: boolean;\n /** Digits typed so far, echoed after the prompt. */\n buffer?: string;\n /** The last entry was out of range ⇒ the same literal is re-printed. */\n invalid?: boolean;\n}\n\n/**\n * The prompt line, separated from `NumberedList` so a menu that renders its own rows — the\n * checklist, whose rows carry a checkbox — asks its question in the same words rather than\n * inventing a dialect.\n */\nexport function NumberedSelectionPrompt({\n itemCount,\n cancellable = false,\n buffer = '',\n invalid = false,\n}: INumberedSelectionPromptProps): React.ReactElement | null {\n if (itemCount === 0) return null;\n const prompt = formatNumberedSelectionPrompt(itemCount, cancellable);\n return (\n <>\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {/* An out-of-range answer re-prints the SAME literal: the range is the correction. */}\n {invalid && <Text>{prompt}</Text>}\n </>\n );\n}\n\nexport interface INumberedListProps {\n /** Heading line, e.g. the menu title or the permission question. */\n title?: string;\n /** Extra context line under the title. */\n description?: string;\n /** The selectable options, in order. */\n options: readonly string[];\n /** Escape cancels ⇒ the prompt says so. */\n cancellable?: boolean;\n /** Digits typed so far, echoed after the prompt. */\n buffer?: string;\n /** The last entry was out of range ⇒ the same literal is re-printed. */\n invalid?: boolean;\n}\n\n/** Render the numbered list and its prompt. */\nexport function NumberedList({\n title,\n description,\n options,\n cancellable = false,\n buffer = '',\n invalid = false,\n}: INumberedListProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n {title !== undefined && title.length > 0 && <Text>{title}</Text>}\n {description !== undefined && description.length > 0 && <Text>{description}</Text>}\n {options.map((option, index) => (\n <Text key={`${index}-${option}`}>\n {numberedRowPrefix(index)}\n {option}\n </Text>\n ))}\n <NumberedSelectionPrompt\n itemCount={options.length}\n cancellable={cancellable}\n buffer={buffer}\n invalid={invalid}\n />\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { useWorkspaceSwitcherSelection } from './execution-workspace-switcher-selection.js';\nimport { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type {\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\n\n/** Footer for the workspace switcher (order: navigate → primary → dismiss). */\nexport const EXECUTION_WORKSPACE_SWITCHER_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Switch' },\n { keys: 'Ctrl+B/Esc', label: 'Close' },\n];\n\n/**\n * CLI-1994: the extra hint shown only while a forked entry is focused. Appended rather than always\n * present, because a key that does nothing on most rows reads as a broken key, not an unused one.\n */\nexport const EXECUTION_WORKSPACE_ATTACH_HINT: IKeyHint = { keys: 'a', label: 'Attach' };\n\ninterface IProps {\n snapshot: IExecutionWorkspaceSnapshot | null;\n selectedEntryId?: string;\n onSelect: (entryId: string) => void;\n onClose: () => void;\n /**\n * CLI-1994: attach to the focused entry's forked session — a VIEW switch onto that record, never\n * a merge. Absent when the surface cannot switch sessions; the control is then not offered.\n */\n onAttach?: (entry: IExecutionWorkspaceEntry) => void;\n}\n\n/** CLI-1994: `attach` is offered by the projection; the switcher only reads what it was handed. */\nfunction offersAttach(entry: IExecutionWorkspaceEntry | undefined): boolean {\n return entry?.controls.includes('attach') === true;\n}\n\nexport default function ExecutionWorkspaceSwitcher({\n snapshot,\n selectedEntryId,\n onSelect,\n onClose,\n onAttach,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const entries = [...(snapshot?.entries ?? [])];\n const { normalized, visibleEntries, applyAction } = useWorkspaceSwitcherSelection({\n entries,\n selectedEntryId,\n onSelect,\n onClose,\n });\n const focusedEntry = entries[normalized.selectedIndex];\n const canAttach = onAttach !== undefined && offersAttach(focusedEntry);\n\n // CLI-2004: the switcher is an arrow-key menu, so the mode gives it numbers and a typed answer.\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && entries.length > 0,\n itemCount: entries.length,\n cancellable: true,\n onSelect: (index) => {\n const entry = entries[index];\n if (entry) onSelect(entry.id);\n },\n onCancel: onClose,\n });\n\n useKeybindingActions(\n 'workspace-switcher',\n (actions) => {\n for (const action of actions) {\n if (action === 'attach' && canAttach && focusedEntry) {\n // Attaching replaces what the terminal is looking at, so the switcher has nothing left to\n // switch between — it closes itself rather than making every caller remember to.\n onAttach(focusedEntry);\n onClose();\n } else if (action === 'previous' || action === 'next' || action === 'select') {\n applyAction(action);\n } else if (action === 'close') {\n applyAction('cancel');\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('workspace-switcher', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Switch'],\n ['close', 'Close'],\n ]);\n const attachHint = useKeybindingHints('workspace-switcher', [['attach', 'Attach']]);\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.focused })}\n paddingX={1}\n >\n <Text color={palette.text.accent} bold>\n Execution workspace\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n {visibleEntries.length === 0 ? (\n <Text dimColor>No workspace entries</Text>\n ) : screenReader ? (\n entries.map((entry, index) => (\n <ExecutionWorkspaceSwitcherRow\n key={entry.id}\n entry={entry}\n isFocused={false}\n selectedEntryId={selectedEntryId}\n rowNumber={index}\n />\n ))\n ) : (\n visibleEntries.map((entry, index) => (\n <ExecutionWorkspaceSwitcherRow\n key={entry.id}\n entry={entry}\n isFocused={normalized.scrollOffset + index === normalized.selectedIndex}\n selectedEntryId={selectedEntryId}\n />\n ))\n )}\n </Box>\n {screenReader ? (\n <ScreenReaderSelectionPrompt\n itemCount={entries.length}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n ) : (\n <KeyHintFooter hints={canAttach ? [...footerHints, ...attachHint] : footerHints} />\n )}\n </Box>\n );\n}\n\n/** The typed-selection prompt, re-printed verbatim after an out-of-range answer. */\nfunction ScreenReaderSelectionPrompt({\n itemCount,\n buffer,\n invalid,\n}: {\n itemCount: number;\n buffer: string;\n invalid: boolean;\n}): React.ReactElement {\n const prompt = formatNumberedSelectionPrompt(itemCount, true);\n return (\n <Box flexDirection=\"column\">\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {invalid && <Text>{prompt}</Text>}\n </Box>\n );\n}\n\nfunction ExecutionWorkspaceSwitcherRow({\n entry,\n isFocused,\n selectedEntryId,\n rowNumber,\n}: {\n entry: IExecutionWorkspaceEntry;\n isFocused: boolean;\n selectedEntryId?: string;\n rowNumber?: number;\n}): React.ReactElement {\n const palette = usePalette();\n const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId });\n return (\n <Text>\n <Text color={isFocused ? palette.text.accent : undefined} bold={isFocused}>\n {rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isFocused\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE}\n </Text>\n <Text color={statusGlyphColor(palette, row.statusKind)}>{`${row.radio} ${row.state}`}</Text>\n <Text\n color={isFocused ? palette.text.accent : undefined}\n bold={isFocused}\n >{` ${row.title}`}</Text>\n <Text dimColor>{` · ${row.statusLabel}`}</Text>\n {row.subtitle ? <Text dimColor>{` · ${row.subtitle}`}</Text> : null}\n {row.preview ? <Text dimColor>{` · ${row.preview}`}</Text> : null}\n </Text>\n );\n}\n","/**\n * CJK-aware cursor geometry and motion.\n *\n * Display columns and character indices are different things when a character is two columns wide;\n * these functions are the translation between them, and the two cursor moves built on it.\n *\n * Extracted from `cjk-text-input-flow.ts` (CLI-2004) by responsibility — that module routes\n * keystrokes, this one measures and moves.\n */\n\nimport stringWidth from 'string-width';\n\nimport type { ICjkTextInputFlowState, TCjkTextInputEffect } from './cjk-text-input-types.js';\n\ninterface ICjkMotionResult {\n state: ICjkTextInputFlowState;\n effect: TCjkTextInputEffect;\n}\n\nexport function displayOffset(chars: string[], charIndex: number, width: number): number {\n let offset = 0;\n for (let i = 0; i < charIndex && i < chars.length; i++) {\n const w = stringWidth(chars[i]!);\n const col = offset % width;\n if (col + w > width) offset += width - col;\n offset += w;\n }\n return offset;\n}\n\nexport function charIndexAtDisplayOffset(chars: string[], target: number, width: number): number {\n let offset = 0;\n for (let i = 0; i < chars.length; i++) {\n if (offset >= target) return i;\n const w = stringWidth(chars[i]!);\n const col = offset % width;\n if (col + w > width) offset += width - col;\n offset += w;\n }\n return chars.length;\n}\n\nexport function moveCursorVertically(\n state: ICjkTextInputFlowState,\n direction: 'up' | 'down',\n availableWidth: number | undefined,\n): ICjkMotionResult {\n if (!availableWidth || availableWidth <= 0) {\n return { state, effect: { type: 'none' } };\n }\n const chars = [...state.value];\n const offset = displayOffset(chars, state.cursor, availableWidth);\n const target = direction === 'up' ? offset - availableWidth : offset + availableWidth;\n if (target < 0) {\n return { state, effect: { type: 'none' } };\n }\n const cursor = charIndexAtDisplayOffset(chars, target, availableWidth);\n if (cursor === state.cursor) {\n return { state, effect: { type: 'none' } };\n }\n return { state: { ...state, cursor }, effect: { type: 'render' } };\n}\n\nexport function moveCursorHorizontally(\n state: ICjkTextInputFlowState,\n direction: 'left' | 'right',\n): ICjkMotionResult {\n if (direction === 'left' && state.cursor > 0) {\n return { state: { ...state, cursor: state.cursor - 1 }, effect: { type: 'render' } };\n }\n if (direction === 'right' && state.cursor < state.value.length) {\n return { state: { ...state, cursor: state.cursor + 1 }, effect: { type: 'render' } };\n }\n return { state, effect: { type: 'none' } };\n}\n","/**\n * CLI-2004 — deletion, and what was deleted.\n *\n * A reader announces the line it is on. When a whole word or the whole line disappears, the only\n * thing it can say is what is LEFT — which is exactly the information you do not need. These helpers\n * return the removed text alongside the new value so the input can say what went.\n *\n * Word and line deletion are added here as well as reported: the flow had only single-character\n * backspace, so `Ctrl+W`/`Ctrl+U` previously fell through to the printable-input path.\n *\n * Extracted from `cjk-text-input-flow.ts` by responsibility — that module routes keystrokes, this\n * one computes edits.\n */\n\n/** The result of one deletion: the new buffer, the new cursor, and the text that was removed. */\nexport interface ICjkDeletion {\n value: string;\n cursor: number;\n deleted: string;\n}\n\n/** Remove the character before the cursor. `undefined` at the start of the buffer. */\nexport function deleteCharBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n return {\n value: value.slice(0, cursor - 1) + value.slice(cursor),\n cursor: cursor - 1,\n deleted: value.slice(cursor - 1, cursor),\n };\n}\n\n/**\n * Remove the word before the cursor — trailing spaces first, then the run of non-space characters,\n * which is the behaviour every shell's `Ctrl+W` has.\n */\nexport function deleteWordBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n let start = cursor;\n while (start > 0 && /\\s/u.test(value[start - 1] ?? '')) start -= 1;\n while (start > 0 && !/\\s/u.test(value[start - 1] ?? '')) start -= 1;\n if (start === cursor) return undefined;\n return {\n value: value.slice(0, start) + value.slice(cursor),\n cursor: start,\n deleted: value.slice(start, cursor),\n };\n}\n\n/** Remove everything before the cursor (`Ctrl+U`). */\nexport function deleteLineBeforeCursor(value: string, cursor: number): ICjkDeletion | undefined {\n if (cursor <= 0) return undefined;\n return { value: value.slice(cursor), cursor: 0, deleted: value.slice(0, cursor) };\n}\n","import { moveCursorHorizontally, moveCursorVertically } from './cjk-cursor-motion.js';\nimport {\n deleteCharBeforeCursor,\n deleteLineBeforeCursor,\n deleteWordBeforeCursor,\n type ICjkDeletion,\n} from './cjk-text-deletion.js';\nimport type {\n ICjkTextInputFlowState,\n TCjkDeletionScope,\n TCjkTextInputEffect,\n} from './cjk-text-input-types.js';\n\nconst PASTE_START = '[200~';\nconst PASTE_END = '[201~';\nconst LAST_ASCII_CONTROL_CODE = 0x1f;\nconst DELETE_CONTROL_CODE = 0x7f;\n\nexport { charIndexAtDisplayOffset, displayOffset } from './cjk-cursor-motion.js';\nexport type {\n ICjkTextInputFlowState,\n TCjkDeletionScope,\n TCjkTextInputEffect,\n} from './cjk-text-input-types.js';\n\nexport interface ICjkTextInputKey {\n ctrl?: boolean;\n tab?: boolean;\n shift?: boolean;\n upArrow?: boolean;\n downArrow?: boolean;\n return?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n backspace?: boolean;\n delete?: boolean;\n}\n\nexport interface ICjkTextInputFlowOptions {\n availableWidth?: number;\n canPaste: boolean;\n enableVerticalNavigation?: boolean;\n}\n\ninterface ICjkTextInputFlowResult {\n state: ICjkTextInputFlowState;\n effect: TCjkTextInputEffect;\n}\n\nexport function createCjkTextInputFlowState(value: string): ICjkTextInputFlowState {\n return { value, cursor: value.length, isPasting: false, pasteBuffer: '' };\n}\n\nexport function syncCjkTextInputFlowState(\n state: ICjkTextInputFlowState,\n value: string,\n cursorHint: number | null,\n): ICjkTextInputFlowState {\n if (value === state.value) {\n return state;\n }\n return {\n ...state,\n value,\n cursor: cursorHint != null ? Math.min(cursorHint, value.length) : value.length,\n };\n}\n\nexport function applyCjkTextInput(\n state: ICjkTextInputFlowState,\n input: string,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n const pasteResult = applyPasteBoundaryInput(state, input, options);\n if (pasteResult !== undefined) return pasteResult;\n const controlResult = applyControlInput(state, input, key, options);\n if (controlResult !== undefined) return controlResult;\n const cursorResult = applyCursorInput(state, key, options);\n if (cursorResult !== undefined) return cursorResult;\n return insertPrintableInput(state, input);\n}\n\nexport function applyCjkTextPaste(\n state: ICjkTextInputFlowState,\n text: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n const normalizedText = text.replace(/\\r\\n?/g, '\\n');\n if (normalizedText.length === 0) {\n return { state, effect: { type: 'none' } };\n }\n if (normalizedText.includes('\\n') && options.canPaste) {\n return { state, effect: { type: 'paste', text: normalizedText, cursor: state.cursor } };\n }\n return insertPrintableInput(state, normalizedText);\n}\n\nfunction applyPasteBoundaryInput(\n state: ICjkTextInputFlowState,\n input: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if (input === PASTE_START || input.startsWith(PASTE_START)) {\n return startBracketedPaste(state, input);\n }\n if (state.isPasting) {\n return continueBracketedPaste(state, input, options);\n }\n return undefined;\n}\n\nfunction applyControlInput(\n state: ICjkTextInputFlowState,\n input: string,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if ((key.ctrl === true && input === 'c') || key.tab === true) {\n return { state, effect: { type: 'none' } };\n }\n if (key.ctrl === true && (input === 'w' || input === 'u')) {\n const scope: TCjkDeletionScope = input === 'w' ? 'word' : 'line';\n const remove = scope === 'word' ? deleteWordBeforeCursor : deleteLineBeforeCursor;\n return applyDeletion(state, remove(state.value, state.cursor), scope);\n }\n if (key.return === true) {\n return { state, effect: { type: 'submit', value: state.value } };\n }\n if (input.length > 1 && (input.includes('\\n') || input.includes('\\r')) && options.canPaste) {\n return {\n state,\n effect: { type: 'paste', text: input.replace(/\\r\\n?/g, '\\n'), cursor: state.cursor },\n };\n }\n return undefined;\n}\n\nfunction applyCursorInput(\n state: ICjkTextInputFlowState,\n key: ICjkTextInputKey,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult | undefined {\n if (key.upArrow === true || key.downArrow === true) {\n if (options.enableVerticalNavigation === false) {\n return { state, effect: { type: 'none' } };\n }\n return moveCursorVertically(\n state,\n key.upArrow === true ? 'up' : 'down',\n options.availableWidth,\n );\n }\n if (key.leftArrow === true) {\n return moveCursorHorizontally(state, 'left');\n }\n if (key.rightArrow === true) {\n return moveCursorHorizontally(state, 'right');\n }\n if (key.backspace === true || key.delete === true) {\n return applyDeletion(state, deleteCharBeforeCursor(state.value, state.cursor));\n }\n return undefined;\n}\n\nexport function filterPrintable(input: string | null | undefined): string {\n if (!input || input.length === 0) return '';\n let output = '';\n for (const char of input) {\n const code = char.charCodeAt(0);\n if (code > LAST_ASCII_CONTROL_CODE && code !== DELETE_CONTROL_CODE) {\n output += char;\n }\n }\n return output;\n}\n\nexport function insertAtCursor(\n value: string,\n cursor: number,\n input: string,\n): { value: string; cursor: number } {\n const next = value.slice(0, cursor) + input + value.slice(cursor);\n return { value: next, cursor: cursor + input.length };\n}\n\nfunction startBracketedPaste(\n state: ICjkTextInputFlowState,\n input: string,\n): ICjkTextInputFlowResult {\n return {\n state: { ...state, isPasting: true, pasteBuffer: input.slice(PASTE_START.length) },\n effect: { type: 'none' },\n };\n}\n\nfunction continueBracketedPaste(\n state: ICjkTextInputFlowState,\n input: string,\n options: ICjkTextInputFlowOptions,\n): ICjkTextInputFlowResult {\n if (input !== PASTE_END && !input.includes(PASTE_END)) {\n return {\n state: { ...state, pasteBuffer: state.pasteBuffer + input },\n effect: { type: 'none' },\n };\n }\n const beforeMarker = input.split(PASTE_END)[0] ?? '';\n const nextState = { ...state, isPasting: false, pasteBuffer: '' };\n return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);\n}\n\n/**\n * Apply a deletion result. A word/line delete carries the removed text so the caller can announce\n * it; a character delete does not — the reader already heard the character go.\n */\nfunction applyDeletion(\n state: ICjkTextInputFlowState,\n deletion: ICjkDeletion | undefined,\n scope?: TCjkDeletionScope,\n): ICjkTextInputFlowResult {\n if (deletion === undefined) {\n return { state, effect: { type: 'none' } };\n }\n return {\n state: { ...state, value: deletion.value, cursor: deletion.cursor },\n effect: {\n type: 'change',\n value: deletion.value,\n ...(scope !== undefined ? { deleted: deletion.deleted, deletedScope: scope } : {}),\n },\n };\n}\n\nfunction insertPrintableInput(\n state: ICjkTextInputFlowState,\n input: string,\n): ICjkTextInputFlowResult {\n const printable = filterPrintable(input);\n if (printable.length === 0) {\n return { state, effect: { type: 'none' } };\n }\n const result = insertAtCursor(state.value, state.cursor, printable);\n return {\n state: { ...state, value: result.value, cursor: result.cursor },\n effect: { type: 'change', value: result.value },\n };\n}\n","/**\n * CLI-061 — deferred submit for the CJK-IME last-character-drop race.\n *\n * IME composition is finalized by the terminal, which writes the final composed character into the pty as its\n * OWN stdin event that arrives JUST AFTER the confirming `\\r`. If we submit synchronously on Enter, the trailing\n * syllable has not yet been applied to the input state, so it is dropped (`안녕하세요` → `안녕하세`). Deferring the\n * submit a few milliseconds lets that trailing event land, after which we re-read the LATEST value and submit it.\n *\n * This is the same app-layer fix Gemini CLI applied on the same Ink (PR #4987/#7556); no framework change and\n * no upstream Ink dependency (Ink #759 stays a watch-only item).\n *\n * Injectable `schedule`/`clear` (defaulting to the real timer) keep this unit deterministically testable —\n * there is NO fake timer shipped in `src`; a test injects its own scheduler.\n */\n\n/**\n * Defer window before a submit fires. Larger = safer against a slow/remote pty delivering the trailing event\n * late, but adds this latency to EVERY submit (including plain ASCII). 50ms matches Gemini's default — long\n * enough for a local IME finalize, short enough to feel instant. This NARROWS the race; on a very slow pty the\n * trailing event can still land after the window (residual, accepted).\n */\nexport const IME_SUBMIT_DEFER_MS = 50;\n\ntype TTimer = ReturnType<typeof setTimeout>;\n\nexport interface IDeferSubmitState {\n timer: TTimer | null;\n /** Gates a SECOND submit / Enter only — never the input pipeline (the trailing char must still be processed). */\n isSubmitting: boolean;\n}\n\nexport function createDeferSubmitState(): IDeferSubmitState {\n return { timer: null, isSubmitting: false };\n}\n\n/**\n * Schedule a deferred submit. At fire time it reads `readLatest()` (the LIVE input value, not a value captured\n * at Enter time) and submits it. A submit already in flight is ignored (no double-submit) — but note the caller\n * MUST keep its input pipeline live during the window so `readLatest()` sees the trailing character.\n */\nexport function scheduleDeferredSubmit(\n state: IDeferSubmitState,\n readLatest: () => string,\n submit: (value: string) => void,\n deferMs: number = IME_SUBMIT_DEFER_MS,\n schedule: (fn: () => void, ms: number) => TTimer = setTimeout,\n): void {\n if (state.isSubmitting) return; // second Enter within the window — ignore, do not double-submit\n state.isSubmitting = true;\n state.timer = schedule(() => {\n state.timer = null;\n state.isSubmitting = false;\n submit(readLatest());\n }, deferMs);\n}\n\n/** Cancel a pending deferred submit (call on unmount so no submit fires after teardown and no timer leaks). */\nexport function cancelDeferredSubmit(\n state: IDeferSubmitState,\n clear: (timer: TTimer) => void = clearTimeout,\n): void {\n if (state.timer !== null) {\n clear(state.timer);\n state.timer = null;\n }\n state.isSubmitting = false;\n}\n","/**\n * CLI-062 — pure flow for real-terminal-cursor positioning during CJK IME composition.\n *\n * Maps the input box's absolute frame-space origin plus the live (value, cursor) to the exact\n * screen cell the hardware cursor must occupy, and guards WHEN positioning is allowed at all.\n * Contract: .design/investigations/2026-07-25-cli-062-ime-cursor-design.md.\n *\n * The wrap math reuses `displayOffset` — the same wrap-aware column accounting the input's\n * vertical navigation already trusts — so the computed cell always matches where ink actually\n * rendered the character under the cursor.\n */\n\nimport { displayOffset } from './cjk-text-input-flow.js';\n\nexport interface ICursorCellInput {\n /** Absolute frame-space column of the input box's first cell. */\n absX: number;\n /** Absolute frame-space row of the input box's first line. */\n absY: number;\n /** Current input value. */\n value: string;\n /** Cursor as a character index into the value (clamped to the value length). */\n cursor: number;\n /** Wrap width in columns; unset/non-positive means the value renders unwrapped. */\n availableWidth?: number;\n}\n\nexport interface ICursorCell {\n x: number;\n y: number;\n}\n\nexport interface IRealCursorGuardInput {\n /** Whether the input box has been measured in the CURRENT yoga layout. */\n hasMeasured: boolean;\n /** Target cursor row (absolute frame space). */\n y: number;\n /** Height of the live frame (ink-root yoga height). */\n frameHeight: number;\n /** Terminal viewport rows. */\n viewportRows: number;\n /** Terminal capability + focus gate (supportsImeCursorPositioning() && focused && showCursor). */\n capability: boolean;\n}\n\n/** Compute the hardware-cursor cell for the current composition point. */\nexport function computeCursorCell(input: ICursorCellInput): ICursorCell {\n const chars = [...input.value];\n const cursor = Math.min(Math.max(input.cursor, 0), chars.length);\n const width =\n input.availableWidth !== undefined && input.availableWidth > 0\n ? input.availableWidth\n : undefined;\n const offset = displayOffset(chars, cursor, width ?? Number.MAX_SAFE_INTEGER);\n if (width === undefined) {\n return { x: input.absX + offset, y: input.absY };\n }\n return { x: input.absX + (offset % width), y: input.absY + Math.floor(offset / width) };\n}\n\n/**\n * Crash-avoidance guard — the SIGSEGV invariants from the CLI-062 contract:\n *\n * - I1: never a GUESSED row — position only with a y measured from the current yoga layout\n * (`hasMeasured`); the historical hardcoded `y: 0` pointed the cursor at the logo area, where\n * Terminal.app's Korean IME `attributedSubstringFromRange:` null-derefs (Apple-side bug).\n * - I2: never into an overflowing frame — when `frameHeight >= viewportRows` ink's bottom-anchor\n * assumption breaks (upstream defect, present in ink 7.0.5 AND 7.1.1: the cursor lands one row\n * high in the fullscreen path, and an overflowing frame clamps `cursorUp` at the screen top —\n * the original crash geometry). Same for a y outside `[0, frameHeight)`.\n * - I5 lives in `supportsImeCursorPositioning()` and arrives here folded into `capability`.\n *\n * I3 (all movement rides ink's synchronized frame writes) and I4 (guard fail → no call → today's\n * rendering) are properties of the hook that consumes this guard (useRealCursorPosition).\n */\nexport function shouldPositionRealCursor(input: IRealCursorGuardInput): boolean {\n if (!input.capability) return false;\n if (!input.hasMeasured) return false; // I1\n if (input.viewportRows <= 0 || input.frameHeight <= 0) return false;\n if (input.frameHeight >= input.viewportRows) return false; // I2 (fullscreen / overflow)\n if (input.y < 0 || input.y >= input.frameHeight) return false; // I2 (out of frame)\n return true;\n}\n","/**\n * CLI-062 — position the REAL terminal cursor at the CJK composition point.\n *\n * Mechanism (contract: .design/investigations/2026-07-25-cli-062-ime-cursor-design.md, POC-PASS):\n * the input box's absolute frame-space origin comes from walking the yoga `parentNode` chain from\n * a `<Box ref>` (the `5195e326b` approach, minus its debug logging); the cell is handed to ink's\n * `useCursor().setCursorPosition`, which converts it into a relative `cursorUp` from the frame\n * bottom INSIDE the same atomic synchronized frame write (invariant I3: never out-of-band — all\n * cursor movement rides ink's ≤30fps frame writes; this hook itself writes NOTHING to any stream,\n * the PoC row-accounting lesson).\n *\n * Measurement happens post-commit (layout is valid there) and is stored in state so the NEXT\n * commit's insertion effect propagates a fresh position with its frame; `useBoxMetrics` provides\n * the layout-change subscription so sibling-driven moves re-measure too. A stale origin self-heals\n * within one frame (ink recomputes layout before onRender and notifies layout listeners).\n */\n\nimport { useBoxMetrics, useCursor, useWindowSize } from 'ink';\nimport { useEffect, useState } from 'react';\n\nimport { computeCursorCell, shouldPositionRealCursor } from '../flows/real-cursor-flow.js';\n\nimport type { DOMElement } from 'ink';\nimport type { RefObject } from 'react';\n\nexport interface IUseRealCursorPositionOptions {\n /** Ref attached to the `<Box>` wrapping the input's text. */\n boxRef: RefObject<DOMElement | null>;\n /** Capability + focus gate: supportsImeCursorPositioning() && focus && showCursor. */\n enabled: boolean;\n /** Live input value (from the input's state ref — fresh at render time). */\n value: string;\n /** Live cursor character index. */\n cursor: number;\n /** Wrap width in columns (matches the input's own wrap math). */\n availableWidth?: number;\n}\n\nexport interface IUseRealCursorPositionResult {\n /** True while the hardware cursor is being positioned — the drawn cursor must be suppressed. */\n realCursorActive: boolean;\n}\n\ninterface IMeasuredOrigin {\n x: number;\n y: number;\n frameHeight: number;\n}\n\n/** Walk the yoga `parentNode` chain to the absolute frame-space origin, plus the root frame height. */\nfunction measureAbsoluteOrigin(element: DOMElement): IMeasuredOrigin | undefined {\n if (element.yogaNode === undefined) return undefined;\n let x = 0;\n let y = 0;\n let current: DOMElement | undefined = element;\n let root: DOMElement = element;\n while (current !== undefined) {\n const layout = current.yogaNode?.getComputedLayout();\n x += layout?.left ?? 0;\n y += layout?.top ?? 0;\n root = current;\n current = current.parentNode;\n }\n const frameHeight = root.yogaNode?.getComputedLayout().height ?? 0;\n return { x, y, frameHeight };\n}\n\nfunction sameOrigin(a: IMeasuredOrigin | undefined, b: IMeasuredOrigin | undefined): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.x === b.x && a.y === b.y && a.frameHeight === b.frameHeight;\n}\n\nexport function useRealCursorPosition(\n options: IUseRealCursorPositionOptions,\n): IUseRealCursorPositionResult {\n const { setCursorPosition } = useCursor();\n // Layout-change subscription: useBoxMetrics re-renders this component whenever the tracked\n // box's metrics change on a root layout commit, and exposes hasMeasured for invariant I1.\n const metrics = useBoxMetrics(options.boxRef);\n const { rows: viewportRows } = useWindowSize();\n const [origin, setOrigin] = useState<IMeasuredOrigin | undefined>(undefined);\n\n // I1: never a guessed row — measure the absolute origin ONLY from a committed yoga layout\n // (post-commit effect; ink ran calculateLayout before onRender, passive effects run after).\n // No dependency array on purpose: any render may follow a layout change, and the setState\n // bails out when the origin is unchanged, so this settles instead of looping.\n useEffect(() => {\n const element = options.boxRef.current;\n const next = element === null ? undefined : measureAbsoluteOrigin(element);\n setOrigin((previous) => (sameOrigin(previous, next) ? previous : next));\n });\n\n const cell =\n origin !== undefined && options.enabled\n ? computeCursorCell({\n absX: origin.x,\n absY: origin.y,\n value: options.value,\n cursor: options.cursor,\n ...(options.availableWidth !== undefined\n ? { availableWidth: options.availableWidth }\n : {}),\n })\n : undefined;\n\n const realCursorActive =\n cell !== undefined &&\n origin !== undefined &&\n shouldPositionRealCursor({\n hasMeasured: metrics.hasMeasured, // I1 — `origin` is already narrowed non-undefined above\n y: cell.y,\n frameHeight: origin.frameHeight, // I2 checked inside the guard\n viewportRows,\n capability: options.enabled, // I5 folded in via supportsImeCursorPositioning()\n });\n\n // Render-phase call is safe: useCursor only records the position in a ref; propagation happens\n // in its insertion effect, inside ink's synchronized frame write (I3). I4: when the guard fails\n // (blur, fullscreen frame, not measured yet) we pass `undefined` — ink hides the hardware cursor\n // and the caller falls back to today's drawn-cursor rendering; on unmount, useCursor's cleanup\n // propagates `undefined` by itself.\n setCursorPosition(realCursorActive && cell !== undefined ? { x: cell.x, y: cell.y } : undefined);\n\n return { realCursorActive };\n}\n","/**\n * SCREEN-2670 — the one place a component reaches the owned write path.\n *\n * Mirrors `screen-reader-context.tsx`: the port is published from `render.tsx`, no component takes\n * it as a prop from its parent, so a missed prop cannot silently leave one component writing past\n * the queue. It also keeps a live writer handle out of the presentation tree, which declares itself\n * free of channels, sessions and mutable state.\n *\n * Outside a provider (every existing test, and every run with the mode off) the port is inert:\n * arming does nothing and writes go straight to the process's own stdout, exactly as today.\n */\n\nimport React, { createContext, useContext } from 'react';\n\nexport interface IScreenReaderPacingPort {\n /** Arm the echo flag for the batch the current keystroke's commit will open. */\n armEchoRelease(): void;\n /** Write a positional sequence (an OSC 133 mark) through the same ordered path. */\n write(text: string): void;\n}\n\nconst inertPort: IScreenReaderPacingPort = {\n armEchoRelease(): void {},\n write(text: string): void {\n process.stdout.write(text);\n },\n};\n\nconst ScreenReaderPacingContext = createContext<IScreenReaderPacingPort>(inertPort);\n\n/** Publish the owned write path to the tree. */\nexport function ScreenReaderPacingProvider({\n port,\n children,\n}: {\n port: IScreenReaderPacingPort;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ScreenReaderPacingContext.Provider value={port}>{children}</ScreenReaderPacingContext.Provider>\n );\n}\n\n/** Read the port. Inert outside a provider. */\nexport function useScreenReaderPacing(): IScreenReaderPacingPort {\n return useContext(ScreenReaderPacingContext);\n}\n","import React, { createContext, useContext } from 'react';\n\n/** Host-selected overrides; absent values leave terminal detection in charge. */\nexport interface ITerminalCapabilityOverrides {\n readonly imeCursorPositioning?: boolean | undefined;\n readonly turnMarks?: boolean | undefined;\n}\n\nconst TerminalCapabilitiesContext = createContext<ITerminalCapabilityOverrides>({});\n\nexport function TerminalCapabilitiesProvider({\n overrides,\n children,\n}: {\n overrides: ITerminalCapabilityOverrides | undefined;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <TerminalCapabilitiesContext.Provider value={overrides ?? {}}>\n {children}\n </TerminalCapabilitiesContext.Provider>\n );\n}\n\nexport function useTerminalCapabilityOverrides(): ITerminalCapabilityOverrides {\n return useContext(TerminalCapabilitiesContext);\n}\n","/**\n * CJK-aware TextInput component for Ink.\n *\n * Replaces ink-text-input with proper wide character support:\n * - Uses string-width for display width calculation\n * - Cursor position based on character index (not display columns)\n * - Renders CJK characters correctly (2 columns each)\n * - Uses refs for value/cursor to avoid React state batching issues\n * (IME sends multiple keystrokes synchronously, state updates are async)\n *\n * Drop-in replacement: same props as ink-text-input.\n */\n\nimport chalk from 'chalk';\nimport { Box, usePaste } from 'ink';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n applyCjkTextInput,\n applyCjkTextPaste,\n createCjkTextInputFlowState,\n syncCjkTextInputFlowState,\n type ICjkTextInputFlowState,\n} from './flows/cjk-text-input-flow.js';\nimport {\n cancelDeferredSubmit,\n createDeferSubmitState,\n scheduleDeferredSubmit,\n type IDeferSubmitState,\n} from './flows/defer-submit.js';\nimport { useRealCursorPosition } from './hooks/useRealCursorPosition.js';\nimport { useKeybindingActions } from './keybindings/keybindings-context.js';\nimport { RenderedText } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReaderPacing } from './screen-reader-pacing-context.js';\nimport { supportsImeCursorPositioning } from './terminal-capabilities.js';\nimport { useTerminalCapabilityOverrides } from './terminal-capabilities-context.js';\nimport { foreground, usePalette } from './theme/index.js';\n\nimport type { IKeyInput, TKeybindingContext } from './keybindings/keybinding-registry.js';\nimport type { DOMElement } from 'ink';\n\ninterface IProps {\n value: string;\n onChange: (value: string) => void;\n onSubmit?: (value: string) => void;\n onPaste?: (text: string, cursorPosition: number) => void;\n placeholder?: string;\n focus?: boolean;\n showCursor?: boolean;\n /** Available width in columns for visual line wrapping navigation */\n availableWidth?: number;\n /** Cursor position hint for external value changes. null = end (default). */\n cursorHint?: number | null;\n /** When false, parent flows own up/down arrow behavior. */\n enableVerticalNavigation?: boolean;\n /**\n * CLI-2004: called with the text a word/line delete removed. A reader announces what is LEFT on\n * the line, so the removed text is the one thing it cannot tell you.\n */\n onDeletedText?: (deleted: string) => void;\n /** Semantic key context; autocomplete consumes its own actions without invoking text actions. */\n keybindingContext?: 'chat-input' | 'text-input' | 'autocomplete-menu';\n}\n\ninterface IInputHandlerOptions {\n stateRef: React.MutableRefObject<ICjkTextInputFlowState>;\n onChange: (value: string) => void;\n onSubmit?: (value: string) => void;\n onPaste?: (text: string, cursorPosition: number) => void;\n availableWidth?: number;\n focus: boolean;\n enableVerticalNavigation: boolean;\n onDeletedText?: (deleted: string) => void;\n forceRender: React.Dispatch<React.SetStateAction<number>>;\n /** CLI-061: deferred-submit state (timer + submit guard). The input pipeline stays live during the window. */\n deferState: IDeferSubmitState;\n keybindingContext: 'chat-input' | 'text-input' | 'autocomplete-menu';\n}\n\nexport default function CjkTextInput({\n value,\n onChange,\n onSubmit,\n onPaste,\n placeholder = '',\n focus = true,\n showCursor = true,\n availableWidth,\n cursorHint = null,\n enableVerticalNavigation = true,\n onDeletedText,\n keybindingContext = 'text-input',\n}: IProps): React.ReactElement {\n const stateRef = useRef<ICjkTextInputFlowState>(createCjkTextInputFlowState(value));\n const [, forceRender] = useState(0);\n // CLI-061: deferred-submit state so a trailing IME character (a stdin event arriving just after Enter) is\n // included in the submitted value. The timer is cancelled on unmount so no submit fires after teardown.\n const deferRef = useRef<IDeferSubmitState>(createDeferSubmitState());\n useEffect(() => () => cancelDeferredSubmit(deferRef.current), []);\n\n // Sync ref when value changes from parent (e.g., setValue(''), tab completion, paste)\n stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);\n\n useCjkTextInputHandlers({\n stateRef,\n onChange,\n onSubmit,\n onPaste,\n availableWidth,\n focus,\n enableVerticalNavigation,\n ...(onDeletedText !== undefined ? { onDeletedText } : {}),\n forceRender,\n deferState: deferRef.current,\n keybindingContext,\n });\n\n // CLI-062: real terminal cursor positioning so the OS IME composition window appears AT the\n // input position. The historical Terminal.app SIGSEGV came from a hardcoded `y: 0` (logo area);\n // the hook only positions with a y measured from the live yoga layout and refuses the ink\n // fullscreen geometry (invariants I1–I5 — see flows/real-cursor-flow.ts and the hook itself).\n // SCREEN-2002: the placeholder's muted colour comes from the resolved theme, not a chalk literal.\n const palette = usePalette();\n const mutedPlaceholder = useMemo(() => foreground(palette.text.muted), [palette.text.muted]);\n const boxRef = useRef<DOMElement | null>(null);\n const terminalCapabilities = useTerminalCapabilityOverrides();\n const { realCursorActive } = useRealCursorPosition({\n boxRef,\n enabled:\n focus &&\n showCursor &&\n supportsImeCursorPositioning({ override: terminalCapabilities.imeCursorPositioning }),\n value: stateRef.current.value,\n cursor: stateRef.current.cursor,\n ...(availableWidth !== undefined ? { availableWidth } : {}),\n });\n\n return (\n <Box ref={boxRef}>\n <RenderedText>\n {renderWithCursor(\n stateRef.current.value,\n stateRef.current.cursor,\n placeholder,\n // I4: the drawn inverse cursor is suppressed ONLY while real positioning is active;\n // any guard failure falls back to exactly today's rendering.\n showCursor && focus && !realCursorActive,\n mutedPlaceholder,\n )}\n </RenderedText>\n </Box>\n );\n}\n\n/**\n * SCREEN-2670: which keys arm the echo exemption. Text insertion and deletion only — a keystroke\n * whose commit repaints the composer. Submit and execute run through this same handler\n * (`translateCjkInput` returns `key.return` for them) and their commit appends the user's prompt\n * line to the transcript, which is exactly the content the park exists to pace; cursor moves and\n * tab commit no text of the composer's own.\n */\nfunction mutatesComposerText(\n action: string | undefined,\n translated: { input: string; key: IKeyInput },\n): boolean {\n if (action === 'delete-backward' || action === 'delete-word' || action === 'delete-line')\n return true;\n return (\n translated.key.return !== true &&\n translated.key.ctrl !== true &&\n translated.key.tab !== true &&\n translated.input.length > 0\n );\n}\n\nfunction useCjkTextInputHandlers(options: IInputHandlerOptions): void {\n const pacing = useScreenReaderPacing();\n usePaste(\n (text) => {\n pacing.armEchoRelease();\n applyCjkFlowSafely(options, () =>\n applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options)),\n );\n },\n { isActive: options.focus },\n );\n\n useKeybindingActions(\n options.keybindingContext,\n (actions, input, key, consumed, resolvedContext) => {\n const translated = translateCjkInput(resolvedContext, actions, input, key, consumed);\n if (translated === undefined) return;\n if (mutatesComposerText(actions[0], translated)) pacing.armEchoRelease();\n applyCjkFlowSafely(options, () =>\n applyCjkTextInput(\n options.stateRef.current,\n translated.input,\n translated.key,\n createFlowOptions(options),\n ),\n );\n },\n {\n isActive: options.focus,\n // #3125: the menu owns only its own keys; editing keys keep the chat input's bindings.\n ...(options.keybindingContext === 'autocomplete-menu'\n ? { fallbackContext: 'chat-input' as const }\n : {}),\n },\n );\n}\n\nfunction translateCjkInput(\n context: TKeybindingContext,\n actions: readonly string[],\n input: string,\n key: IKeyInput,\n consumed: boolean,\n): { input: string; key: IKeyInput } | undefined {\n const action = actions[0];\n if (action === 'submit' || action === 'execute') return { input: '', key: { return: true } };\n if (context === 'autocomplete-menu' && consumed) return undefined;\n if (action === 'cursor-left') return { input: '', key: { leftArrow: true } };\n if (action === 'cursor-right') return { input: '', key: { rightArrow: true } };\n if (action === 'cursor-up') return { input: '', key: { upArrow: true } };\n if (action === 'cursor-down') return { input: '', key: { downArrow: true } };\n if (action === 'delete-backward') return { input: '', key: { backspace: true } };\n if (action === 'delete-word') return { input: 'w', key: { ctrl: true } };\n if (action === 'delete-line') return { input: 'u', key: { ctrl: true } };\n if (consumed) return undefined;\n const rawText =\n key.ctrl !== true &&\n key.meta !== true &&\n !key.return &&\n !key.escape &&\n !key.tab &&\n !key.upArrow &&\n !key.downArrow &&\n !key.leftArrow &&\n !key.rightArrow &&\n !key.backspace &&\n !key.delete;\n return rawText || (key.ctrl === true && input === 'c') || key.tab ? { input, key } : undefined;\n}\n\nfunction createFlowOptions(options: IInputHandlerOptions): {\n availableWidth?: number;\n canPaste: boolean;\n enableVerticalNavigation: boolean;\n} {\n return {\n availableWidth: options.availableWidth,\n canPaste: options.onPaste !== undefined,\n enableVerticalNavigation: options.enableVerticalNavigation,\n };\n}\n\nfunction applyCjkFlowSafely(\n options: IInputHandlerOptions,\n run: () => ReturnType<typeof applyCjkTextInput>,\n): void {\n try {\n const result = run();\n options.stateRef.current = result.state;\n applyCjkTextInputEffect(options, result.effect);\n } catch {\n // allow-fallback: Korean IME in raw mode can produce unexpected byte sequences\n }\n}\n\nfunction applyCjkTextInputEffect(\n options: IInputHandlerOptions,\n effect: ReturnType<typeof applyCjkTextInput>['effect'],\n): void {\n if (effect.type === 'change') {\n if (effect.deleted !== undefined) options.onDeletedText?.(effect.deleted);\n options.onChange(effect.value);\n } else if (effect.type === 'submit') {\n // CLI-061: DEFER the submit and re-read the LIVE `stateRef.current.value` at fire time — never the stale\n // `effect.value` captured at Enter. The input pipeline (this same handler) stays live during the window, so\n // a trailing IME character's stdin event is applied to `stateRef` before the deferred read. The guard only\n // blocks a SECOND submit — it must not gate the input pipeline (that would drop the trailing char).\n const onSubmit = options.onSubmit;\n if (onSubmit) {\n scheduleDeferredSubmit(options.deferState, () => options.stateRef.current.value, onSubmit);\n }\n } else if (effect.type === 'paste') {\n options.onPaste?.(effect.text, effect.cursor);\n } else if (effect.type === 'render') {\n options.forceRender((n) => n + 1);\n }\n}\n\n/**\n * Render text with an inverse-style cursor at the correct position.\n *\n * #2222: the input is sanitized BEFORE the cursor is drawn, and the result renders through\n * `RenderedText` — the SGR here (inverse cursor, muted placeholder) is this component's own styling,\n * and `SafeText` would strip it along with everything else, which is exactly how the drawn cursor\n * disappeared (CLI-062 byte-identical rendering).\n */\nfunction renderWithCursor(\n rawValue: string,\n cursorOffset: number,\n rawPlaceholder: string,\n showCursor: boolean,\n mutedPlaceholder: (text: string) => string,\n): string {\n const value = sanitizeTerminalText(rawValue);\n const placeholder = sanitizeTerminalText(rawPlaceholder);\n if (!showCursor) {\n return value.length > 0 ? value : placeholder ? mutedPlaceholder(placeholder) : '';\n }\n\n if (value.length === 0) {\n if (placeholder.length > 0) {\n return chalk.inverse(placeholder[0]) + mutedPlaceholder(placeholder.slice(1));\n }\n return chalk.inverse(' ');\n }\n\n const chars = [...value];\n let rendered = '';\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i] ?? '';\n rendered += i === cursorOffset ? chalk.inverse(char) : char;\n }\n\n if (cursorOffset >= chars.length) {\n rendered += chalk.inverse(' ');\n }\n\n return rendered;\n}\n","/**\n * FLOW-2006: the line that says a prompt did not come from the person at the keyboard.\n *\n * A deep link can fill the composer, so the provenance has to be visible for as long as the composer\n * holds exactly what the link supplied — otherwise a prompt the user never typed reads exactly like\n * one they did. The moment that text is edited, sent or cleared the label goes: what is there is then\n * the user's own, and a label that outlived the text would be claiming otherwise. Above the long\n * threshold it also carries the character count and says to read the whole thing, because a long\n * prompt pushes its own instructions off screen.\n */\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\n\n/** Above this many characters the notice escalates from a label to a review instruction. */\nexport const EXTERNAL_PROMPT_REVIEW_THRESHOLD = 1000;\nexport const EXTERNAL_PROMPT_NOTICE = 'Prompt from an external link';\n\nexport interface IExternalPromptNoticeProps {\n /** The composer's current text, so the notice can size its warning to what is actually there. */\n readonly value: string;\n}\n\n/** The notice text for a value, or `undefined` when there is nothing to say. */\nexport function externalPromptNotice(value: string): string | undefined {\n if (value.length === 0) return undefined;\n return value.length > EXTERNAL_PROMPT_REVIEW_THRESHOLD\n ? `${EXTERNAL_PROMPT_NOTICE} — ${value.length} characters; scroll and review the full text before sending`\n : EXTERNAL_PROMPT_NOTICE;\n}\n\nexport default function ExternalPromptNotice({\n value,\n}: IExternalPromptNoticeProps): React.ReactElement | null {\n const notice = externalPromptNotice(value);\n if (notice === undefined) return null;\n return <Text dimColor>{notice}</Text>;\n}\n","import { isSlashCommand, tokeniseSlashCommand } from '@robota-sdk/agent-framework';\n\nimport type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nexport interface IAutocompleteInputKey {\n upArrow?: boolean;\n downArrow?: boolean;\n escape?: boolean;\n tab?: boolean;\n backspace?: boolean;\n delete?: boolean;\n}\n\nexport type TAutocompletePopupAction = 'previous' | 'next' | 'close' | 'complete';\nexport type TPendingPromptInputAction = 'cancelQueue';\nexport type TPromptHistoryInputAction = 'previous' | 'next';\n\nexport type TCommandSelectionResult =\n { type: 'insert'; value: string; selectedIndex?: number } | { type: 'submit'; value: string };\n\nexport interface IPasteLabelChange {\n value: string;\n cursorHint: number;\n label: string;\n lineCount: number;\n}\n\nexport interface IPromptHistoryNavigationState {\n selectedIndex: number | null;\n draft: string;\n}\n\nexport interface IPromptHistoryNavigationResult {\n value: string;\n cursorHint: number;\n state: IPromptHistoryNavigationState;\n}\n\nexport function getAutocompletePopupAction(\n key: IAutocompleteInputKey,\n): TAutocompletePopupAction | undefined {\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n if (key.escape === true) return 'close';\n if (key.tab === true) return 'complete';\n return undefined;\n}\n\nexport function getPendingPromptInputAction(\n key: IAutocompleteInputKey,\n): TPendingPromptInputAction | undefined {\n if (key.backspace === true || key.delete === true) {\n return 'cancelQueue';\n }\n return undefined;\n}\n\nexport function getPromptHistoryInputAction(\n key: IAutocompleteInputKey,\n): TPromptHistoryInputAction | undefined {\n if (key.upArrow === true) return 'previous';\n if (key.downArrow === true) return 'next';\n return undefined;\n}\n\nexport function createPromptHistoryNavigationState(): IPromptHistoryNavigationState {\n return { selectedIndex: null, draft: '' };\n}\n\nexport function navigatePromptHistory(\n value: string,\n history: readonly string[],\n state: IPromptHistoryNavigationState,\n action: TPromptHistoryInputAction,\n): IPromptHistoryNavigationResult {\n if (history.length === 0) {\n return { value, cursorHint: value.length, state };\n }\n\n if (action === 'previous') {\n const selectedIndex =\n state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);\n const nextValue = history[selectedIndex] ?? value;\n return {\n value: nextValue,\n cursorHint: nextValue.length,\n state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft },\n };\n }\n\n if (state.selectedIndex === null) {\n return { value, cursorHint: value.length, state };\n }\n\n if (state.selectedIndex < history.length - 1) {\n const selectedIndex = state.selectedIndex + 1;\n const nextValue = history[selectedIndex] ?? value;\n return {\n value: nextValue,\n cursorHint: nextValue.length,\n state: { ...state, selectedIndex },\n };\n }\n\n return {\n value: state.draft,\n cursorHint: state.draft.length,\n state: createPromptHistoryNavigationState(),\n };\n}\n\nexport function appendPromptHistory(history: readonly string[], value: string): string[] {\n const prompt = value.trim();\n if (prompt.length === 0) return [...history];\n if (history[history.length - 1] === prompt) return [...history];\n return [...history, prompt];\n}\n\nexport function extractPromptHistory(entries: readonly IHistoryEntry[]): string[] {\n let prompts: string[] = [];\n for (const entry of entries) {\n if (entry.category !== 'chat' || entry.type !== 'user') continue;\n const data = entry.data as Record<string, TUniversalValue> | undefined;\n if (typeof data?.content !== 'string') continue;\n prompts = appendPromptHistory(prompts, data.content);\n }\n return prompts;\n}\n\nexport function moveAutocompleteSelection(\n selectedIndex: number,\n commandCount: number,\n direction: 'previous' | 'next',\n): number {\n if (commandCount === 0) return 0;\n if (direction === 'previous') {\n return selectedIndex > 0 ? selectedIndex - 1 : commandCount - 1;\n }\n return selectedIndex < commandCount - 1 ? selectedIndex + 1 : 0;\n}\n\nexport function resolveTabCompletion(value: string, command: ICommand): TCommandSelectionResult {\n // Subcommand mode: '/parent filter' — space present after command name\n if (isSlashCommand(value) && value.slice(1).includes(' ')) {\n const { name } = tokeniseSlashCommand(value);\n return { type: 'insert', value: `/${name} ${command.name} ` };\n }\n if (command.subcommands && command.subcommands.length > 0) {\n return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };\n }\n return { type: 'insert', value: `/${command.name} ` };\n}\n\nexport function resolveEnterCommandSelection(\n value: string,\n command: ICommand,\n): TCommandSelectionResult {\n // Subcommand mode: '/parent filter' — space present after command name\n if (isSlashCommand(value) && value.slice(1).includes(' ')) {\n const { name } = tokeniseSlashCommand(value);\n return { type: 'submit', value: `/${name} ${command.name}` };\n }\n if (command.subcommands && command.subcommands.length > 0 && command.runsBare !== true) {\n return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };\n }\n return { type: 'submit', value: `/${command.name}` };\n}\n\nexport function createPasteLabelChange(\n value: string,\n cursorPosition: number,\n pasteId: number,\n text: string,\n): IPasteLabelChange {\n const lineCount = text.split('\\n').length;\n const label = `[Pasted text #${pasteId} +${lineCount} lines]`;\n return {\n value: value.slice(0, cursorPosition) + label + value.slice(cursorPosition),\n cursorHint: cursorPosition + label.length,\n label,\n lineCount,\n };\n}\n\nexport function shouldSubmitInput(text: string): boolean {\n return text.trim().length > 0;\n}\n","/**\n * SCREEN-1993 — the pure half of reverse prompt-history search: filtering with match ranges,\n * collapse-to-newest dedup, the scope cycle and the run split a match is rendered from.\n */\nimport type { IPromptHistoryEntry } from '@robota-sdk/agent-interface-session';\n\nexport type THistorySearchScope = 'all' | 'session' | 'project';\n\n/** `all → session → project → all`: the widest first, narrowing on each press. */\nconst HISTORY_SEARCH_SCOPES: readonly THistorySearchScope[] = ['all', 'session', 'project'];\n\nexport function cycleScope(scope: THistorySearchScope): THistorySearchScope {\n const index = HISTORY_SEARCH_SCOPES.indexOf(scope);\n return HISTORY_SEARCH_SCOPES[(index + 1) % HISTORY_SEARCH_SCOPES.length] ?? 'all';\n}\n\nexport interface IHistorySearchScopeContext {\n readonly sessionId: string;\n readonly project: string;\n}\n\n/** Whether an entry belongs to the scope for this session and project. */\nexport function inScope(\n entry: IPromptHistoryEntry,\n scope: THistorySearchScope,\n context: IHistorySearchScopeContext,\n): boolean {\n switch (scope) {\n case 'all':\n return true;\n case 'session':\n return entry.sessionId === context.sessionId;\n case 'project':\n return entry.project === context.project;\n }\n}\n\n/** Search-time dedup: the first (newest) occurrence of a trimmed text wins; order is preserved. */\nexport function collapseToNewest(entries: readonly IPromptHistoryEntry[]): IPromptHistoryEntry[] {\n const seen = new Set<string>();\n const collapsed: IPromptHistoryEntry[] = [];\n for (const entry of entries) {\n const key = entry.text.trim();\n if (seen.has(key)) continue;\n seen.add(key);\n collapsed.push(entry);\n }\n return collapsed;\n}\n\nexport interface IHistorySearchMatch {\n readonly entry: IPromptHistoryEntry;\n /** `[start, end)` of the query inside `entry.text`; empty when the query is empty. */\n readonly ranges: readonly (readonly [number, number])[];\n}\n\n/** Case-insensitive substring filter; an empty query matches everything with no ranges. */\nexport function filterPrompts(\n entries: readonly IPromptHistoryEntry[],\n query: string,\n): IHistorySearchMatch[] {\n const needle = query.toLowerCase();\n if (needle.length === 0) return entries.map((entry) => ({ entry, ranges: [] }));\n const matches: IHistorySearchMatch[] = [];\n for (const entry of entries) {\n const haystack = entry.text.toLowerCase();\n const ranges: (readonly [number, number])[] = [];\n let from = 0;\n for (;;) {\n const index = haystack.indexOf(needle, from);\n if (index === -1) break;\n ranges.push([index, index + needle.length]);\n from = index + needle.length;\n }\n if (ranges.length > 0) matches.push({ entry, ranges });\n }\n return matches;\n}\n\n/** Text split into plain and highlighted runs, for rendering a match without colour dependence. */\nexport function splitByRanges(\n text: string,\n ranges: readonly (readonly [number, number])[],\n): { readonly text: string; readonly matched: boolean }[] {\n const runs: { text: string; matched: boolean }[] = [];\n let cursor = 0;\n for (const [start, end] of ranges) {\n if (start > cursor) runs.push({ text: text.slice(cursor, start), matched: false });\n runs.push({ text: text.slice(start, end), matched: true });\n cursor = end;\n }\n if (cursor < text.length) runs.push({ text: text.slice(cursor), matched: false });\n return runs;\n}\n","/**\n * SCREEN-1993 — the reverse prompt-history search overlay (Ctrl+R), rendered inside the input area\n * on the `SlashAutocomplete` pattern. The query line, the scope label, the matches newest-first with\n * the query highlighted, the load state and the footer of live keys.\n *\n * The highlight survives `NO_COLOR`: when the process emits no colour, a matched run is wrapped in\n * visible brackets instead of relying on an SGR attribute nothing would render.\n */\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { splitByRanges, type IHistorySearchMatch } from './history-search/history-search-flow.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IHistorySearchView } from './history-search/useHistorySearch.js';\n\n/** Footer for the overlay, in the default bindings. */\nexport const HISTORY_SEARCH_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Ctrl+S', label: 'Scope' },\n { keys: 'Enter/Tab', label: 'Insert' },\n { keys: 'Ctrl+E', label: 'Run' },\n { keys: 'Esc', label: 'Close' },\n];\n\nconst MAX_VISIBLE = 8;\n\nfunction scrollOffset(selectedIndex: number, total: number): number {\n if (total <= MAX_VISIBLE || selectedIndex < MAX_VISIBLE) return 0;\n return Math.min(selectedIndex - MAX_VISIBLE + 1, total - MAX_VISIBLE);\n}\n\n/** `1 unreadable line skipped` / `3 unreadable lines skipped`. */\nfunction formatSkippedLines(count: number): string {\n return `${count} unreadable ${count === 1 ? 'line' : 'lines'} skipped`;\n}\n\nfunction MatchRow(props: {\n readonly match: IHistorySearchMatch;\n readonly selected: boolean;\n readonly rowNumber: number | undefined;\n readonly color: boolean;\n}): React.ReactElement {\n const palette = usePalette();\n const { match, selected, rowNumber, color } = props;\n const indicator =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : selected\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n // A stored prompt may span lines; one visible glyph per newline keeps the row a row and the\n // match ranges aligned (same length).\n const runs = splitByRanges(\n match.entry.text.replace(/\\r\\n|[\\r\\n]/gu, (br) => (br.length === 2 ? ' ↵' : '↵')),\n match.ranges,\n );\n return (\n <Text\n color={selected ? palette.text.accent : undefined}\n dimColor={!selected}\n wrap=\"truncate-end\"\n >\n {indicator}\n {runs.map((run, index) =>\n run.matched ? (\n <Text key={index} bold inverse={color}>\n {color ? run.text : `[${run.text}]`}\n </Text>\n ) : (\n <Text key={index}>{run.text}</Text>\n ),\n )}\n </Text>\n );\n}\n\nfunction StateLine({ view }: { readonly view: IHistorySearchView }): React.ReactElement {\n const palette = usePalette();\n if (view.error !== undefined) {\n return <Text color={palette.text.error}>History could not be read: {view.error}</Text>;\n }\n const parts: string[] = [];\n if (view.matches.length === 0) {\n parts.push(\n view.loading ? 'loading…' : view.query.length === 0 ? 'no stored prompts yet' : 'no match',\n );\n } else {\n parts.push(`${view.matches.length} match${view.matches.length === 1 ? '' : 'es'}`);\n if (view.loading) parts.push('loading…');\n }\n if (view.skippedLines > 0) parts.push(formatSkippedLines(view.skippedLines));\n return <Text dimColor>{parts.join(' · ')}</Text>;\n}\n\nexport default function HistorySearchOverlay({\n view,\n}: {\n readonly view: IHistorySearchView;\n}): React.ReactElement | null {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const color = isInteractiveColorTerminal();\n const footerHints = useKeybindingHints('history-search', [\n [['previous', 'next'], 'Navigate'],\n ['cycle-scope', 'Scope'],\n ['insert', 'Insert'],\n ['execute', 'Run'],\n ['cancel', 'Close'],\n ]);\n if (!view.open) return null;\n const offset = scrollOffset(view.selectedIndex, view.matches.length);\n const visible = view.matches.slice(offset, offset + MAX_VISIBLE);\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.muted })}\n paddingX={1}\n >\n <Text>\n <Text color={palette.text.accent}>(reverse-i-search)</Text> scope: {view.scope} · query:{' '}\n {view.query}\n </Text>\n {visible.map((match, index) => (\n <MatchRow\n key={`${match.entry.at}:${offset + index}`}\n match={match}\n selected={offset + index === view.selectedIndex}\n rowNumber={screenReader ? offset + index : undefined}\n color={color}\n />\n ))}\n <StateLine view={view} />\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","/**\n * Slash command autocomplete hook.\n * Extracted from InputArea.tsx for single-responsibility.\n */\n\nimport React, { useState, useMemo, useEffect } from 'react';\n\nimport type { ITuiCommandQueryPort } from '../tui-app-channel-port.js';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\n/** Parse input to determine autocomplete state */\nfunction parseSlashInput(value: string): {\n isSlash: boolean;\n parentCommand: string;\n filter: string;\n} {\n if (!value.startsWith('/')) return { isSlash: false, parentCommand: '', filter: '' };\n const afterSlash = value.slice(1);\n const spaceIndex = afterSlash.indexOf(' ');\n if (spaceIndex === -1) return { isSlash: true, parentCommand: '', filter: afterSlash };\n const parent = afterSlash.slice(0, spaceIndex);\n const rest = afterSlash.slice(spaceIndex + 1);\n return { isSlash: true, parentCommand: parent, filter: rest };\n}\n\n/** Hook: manage autocomplete state */\nexport function useAutocomplete(\n value: string,\n commandQueryPort: ITuiCommandQueryPort | undefined,\n): {\n showPopup: boolean;\n filteredCommands: ICommand[];\n selectedIndex: number;\n setSelectedIndex: React.Dispatch<React.SetStateAction<number>>;\n isSubcommandMode: boolean;\n setShowPopup: React.Dispatch<React.SetStateAction<boolean>>;\n} {\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [dismissed, setDismissed] = useState(false);\n\n // Reset dismissed when input changes\n const prevValueRef = React.useRef(value);\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (dismissed) setDismissed(false);\n }\n\n const parsed = parseSlashInput(value);\n const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;\n\n const previousParentCommandRef = React.useRef(parsed.parentCommand);\n useEffect(() => {\n if (previousParentCommandRef.current === parsed.parentCommand) return;\n previousParentCommandRef.current = parsed.parentCommand;\n setSelectedIndex(0);\n }, [parsed.parentCommand]);\n\n const filteredCommands = useMemo(() => {\n if (!commandQueryPort || !parsed.isSlash || dismissed) return [];\n if (isSubcommandMode) {\n const subs = commandQueryPort.getSubcommands(parsed.parentCommand);\n if (subs.length === 0) return [];\n if (!parsed.filter) return subs;\n const lower = parsed.filter.toLowerCase();\n return subs.filter((c) => c.name.toLowerCase().startsWith(lower));\n }\n return commandQueryPort.getCommands(parsed.filter);\n }, [\n commandQueryPort,\n parsed.isSlash,\n parsed.parentCommand,\n parsed.filter,\n dismissed,\n isSubcommandMode,\n ]);\n\n const showPopup = parsed.isSlash && filteredCommands.length > 0 && !dismissed;\n\n // Clamp selectedIndex\n if (selectedIndex >= filteredCommands.length && filteredCommands.length > 0) {\n setSelectedIndex(filteredCommands.length - 1);\n }\n\n return {\n showPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n isSubcommandMode,\n setShowPopup: (val) => {\n if (typeof val === 'function') {\n setDismissed((prev) => {\n const nextVal = (val as (prev: boolean) => boolean)(!prev);\n return !nextVal;\n });\n } else {\n setDismissed(!val);\n }\n },\n };\n}\n","/**\n * SCREEN-1993 — the reverse prompt-history search overlay's state.\n *\n * One loader per open: opening starts one read of the injected source under one `AbortController`,\n * and every close — insert, execute or cancel — aborts it. Blocks arrive newest-first and are appended\n * as they land, so the first frame shows the newest prompts before the rest of the file is read. The\n * `session` scope never touches the file: it is the live prompt list the composer already holds.\n *\n * The composer's draft is restored by construction, not by copying: the overlay never writes the\n * input while it is open, so a cancel leaves text and cursor exactly as they were.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport {\n collapseToNewest,\n cycleScope,\n filterPrompts,\n inScope,\n type IHistorySearchMatch,\n type IHistorySearchScopeContext,\n type THistorySearchScope,\n} from './history-search-flow.js';\n\nimport type { IKeyInput } from '../keybindings/keybinding-registry.js';\nimport type {\n IPromptHistoryEntry,\n IPromptHistorySource,\n} from '@robota-sdk/agent-interface-session';\n\n/** What the surface injects to turn the overlay on; absent ⇒ `ctrl+r` does nothing. */\nexport interface IHistorySearchSurface {\n readonly source: IPromptHistorySource;\n readonly sessionId: string;\n readonly project: string;\n}\n\nexport interface IUseHistorySearchOptions {\n readonly surface: IHistorySearchSurface | undefined;\n /** The composer's live prompt list, oldest-first — the `session` scope. */\n readonly sessionPrompts: readonly string[];\n readonly onInsert: (text: string) => void;\n readonly onExecute: (text: string) => void;\n readonly onOpenChange?: ((open: boolean) => void) | undefined;\n}\n\nexport interface IHistorySearchView {\n readonly open: boolean;\n readonly query: string;\n readonly scope: THistorySearchScope;\n readonly selectedIndex: number;\n readonly matches: readonly IHistorySearchMatch[];\n readonly loading: boolean;\n readonly skippedLines: number;\n readonly error: string | undefined;\n}\n\ninterface ILoadedState {\n readonly entries: readonly IPromptHistoryEntry[];\n readonly skippedLines: number;\n}\n\nconst EMPTY_LOADED: ILoadedState = { entries: [], skippedLines: 0 };\n\nfunction isPrintable(input: string, key: IKeyInput): boolean {\n const special =\n key.ctrl === true ||\n key.meta === true ||\n Boolean(key.return || key.escape || key.tab) ||\n Boolean(key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) ||\n Boolean(key.backspace || key.delete);\n return input.length > 0 && !special;\n}\n\nfunction liveEntries(\n prompts: readonly string[],\n context: IHistorySearchScopeContext,\n): IPromptHistoryEntry[] {\n return [...prompts]\n .reverse()\n .map((text) => ({ at: '', sessionId: context.sessionId, project: context.project, text }));\n}\n\ninterface ILoader {\n readonly loaded: ILoadedState;\n readonly loading: boolean;\n readonly error: string | undefined;\n readonly start: () => void;\n readonly stop: () => void;\n /** Screen-reader mode: the list is published only here — per keystroke or at load end. */\n readonly publish: () => void;\n}\n\ninterface ILoaderRun {\n readonly source: IPromptHistorySource;\n readonly signal: AbortSignal;\n readonly pending: { current: ILoadedState };\n readonly publishEachBlock: boolean;\n readonly publish: () => void;\n readonly setError: (message: string) => void;\n readonly setLoading: (loading: boolean) => void;\n}\n\n/**\n * A real source reads synchronously and yields blocks with only a microtask between them; Ink's\n * throttled write never runs inside that, so without this the whole file lands in one frame.\n */\nconst yieldToTerminal = (): Promise<void> => new Promise((resolve) => setImmediate(resolve));\n\nasync function runLoader(run: ILoaderRun): Promise<void> {\n try {\n for await (const block of run.source.read({ signal: run.signal })) {\n if (run.signal.aborted) return;\n run.pending.current = {\n entries: [...run.pending.current.entries, ...block.entries],\n skippedLines: run.pending.current.skippedLines + block.skippedLines,\n };\n if (run.publishEachBlock) {\n run.publish();\n await yieldToTerminal();\n }\n }\n if (!run.signal.aborted) run.publish();\n } catch (cause) {\n if (!run.signal.aborted) run.setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n if (!run.signal.aborted) run.setLoading(false);\n }\n}\n\n/** Reads the source once per open under one abort signal. */\nfunction useHistoryLoader(\n surface: IHistorySearchSurface | undefined,\n screenReader: boolean,\n): ILoader {\n const [loaded, setLoaded] = useState<ILoadedState>(EMPTY_LOADED);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const controller = useRef<AbortController | undefined>(undefined);\n const pending = useRef<ILoadedState>(EMPTY_LOADED);\n const publish = useCallback(() => setLoaded(pending.current), []);\n const stop = useCallback(() => {\n controller.current?.abort();\n controller.current = undefined;\n setLoading(false);\n }, []);\n const start = useCallback(() => {\n stop();\n pending.current = EMPTY_LOADED;\n setLoaded(EMPTY_LOADED);\n setError(undefined);\n if (surface === undefined) return;\n const own = new AbortController();\n controller.current = own;\n setLoading(true);\n void runLoader({\n source: surface.source,\n signal: own.signal,\n pending,\n publishEachBlock: !screenReader,\n publish,\n setError,\n setLoading,\n });\n }, [publish, screenReader, stop, surface]);\n useEffect(() => stop, [stop]);\n return { loaded, loading, error, start, stop, publish };\n}\n\ninterface ISearchKeysDeps {\n readonly open: boolean;\n readonly matches: readonly IHistorySearchMatch[];\n readonly selected: number;\n readonly scope: THistorySearchScope;\n readonly screenReader: boolean;\n readonly loader: ILoader;\n readonly close: () => void;\n readonly setScope: (scope: THistorySearchScope) => void;\n readonly setSelectedIndex: (index: number) => void;\n readonly setQuery: (update: (current: string) => string) => void;\n readonly onInsert: (text: string) => void;\n readonly onExecute: (text: string) => void;\n}\n\n/** The overlay's keys: the bound actions, and otherwise plain typing into the query. */\nfunction useHistorySearchKeys(deps: ISearchKeysDeps): void {\n const editQuery = (update: (current: string) => string): void => {\n deps.setQuery(update);\n deps.setSelectedIndex(0);\n if (deps.screenReader) deps.loader.publish();\n };\n const take = (action: 'insert' | 'execute'): void => {\n const text = deps.matches[deps.selected]?.entry.text;\n if (text === undefined) return;\n deps.close();\n (action === 'insert' ? deps.onInsert : deps.onExecute)(text);\n };\n const move = (step: number): void => {\n if (deps.matches.length === 0) return;\n deps.setSelectedIndex((deps.selected + step + deps.matches.length) % deps.matches.length);\n };\n useKeybindingActions(\n 'history-search',\n (actions, input, key, consumed) => {\n const action = actions[0];\n if (action === 'cancel') deps.close();\n else if (action === 'insert' || action === 'execute') take(action);\n else if (action === 'previous') move(-1);\n else if (action === 'next') move(1);\n else if (action === 'cycle-scope') {\n deps.setScope(cycleScope(deps.scope));\n deps.setSelectedIndex(0);\n } else if (!consumed && (key.backspace || key.delete)) editQuery((q) => q.slice(0, -1));\n else if (!consumed && isPrintable(input, key)) editQuery((q) => q + input);\n },\n { isActive: deps.open },\n );\n}\n\n/** The candidate list for the scope: the live list for `session`, the collapsed file otherwise. */\nfunction useCandidates(\n open: boolean,\n surface: IHistorySearchSurface | undefined,\n scope: THistorySearchScope,\n sessionPrompts: readonly string[],\n entries: readonly IPromptHistoryEntry[],\n): IPromptHistoryEntry[] {\n // Keyed on the surface's values, not its identity: the app rebuilds the object every render.\n const sessionId = surface?.sessionId;\n const project = surface?.project;\n return useMemo(() => {\n if (!open || sessionId === undefined || project === undefined) return [];\n const context = { sessionId, project };\n const scoped =\n scope === 'session'\n ? liveEntries(sessionPrompts, context)\n : entries.filter((entry) => inScope(entry, scope, context));\n return collapseToNewest(scoped);\n }, [entries, open, project, scope, sessionId, sessionPrompts]);\n}\n\ninterface IQueryState {\n readonly query: string;\n readonly scope: THistorySearchScope;\n readonly selectedIndex: number;\n}\n\nconst FRESH_QUERY: IQueryState = { query: '', scope: 'all', selectedIndex: 0 };\n\n/** The query, scope and selection as one state, with the setters the key handler needs. */\nfunction useQueryState(): {\n readonly state: IQueryState;\n readonly reset: () => void;\n readonly setters: Pick<ISearchKeysDeps, 'setScope' | 'setSelectedIndex' | 'setQuery'>;\n} {\n const [state, setState] = useState<IQueryState>(FRESH_QUERY);\n const reset = useCallback(() => setState(FRESH_QUERY), []);\n const setters = useMemo(\n () => ({\n setScope: (scope: THistorySearchScope): void => setState((s) => ({ ...s, scope })),\n setSelectedIndex: (selectedIndex: number): void => setState((s) => ({ ...s, selectedIndex })),\n setQuery: (update: (current: string) => string): void =>\n setState((s) => ({ ...s, query: update(s.query) })),\n }),\n [],\n );\n return { state, reset, setters };\n}\n\n/** Open and close: one loader start per open, one stop per close, the app told of both. */\nfunction useOverlayVisibility(\n loader: ILoader,\n canOpen: boolean,\n reset: () => void,\n onOpenChange: ((open: boolean) => void) | undefined,\n): { readonly open: boolean; readonly openSearch: () => void; readonly closeSearch: () => void } {\n const [open, setOpen] = useState(false);\n const openSearch = useCallback(() => {\n if (!canOpen || open) return;\n reset();\n setOpen(true);\n onOpenChange?.(true);\n loader.start();\n }, [canOpen, loader, onOpenChange, open, reset]);\n const closeSearch = useCallback(() => {\n loader.stop();\n setOpen(false);\n onOpenChange?.(false);\n }, [loader, onOpenChange]);\n return { open, openSearch, closeSearch };\n}\n\nexport function useHistorySearch(options: IUseHistorySearchOptions): {\n readonly view: IHistorySearchView;\n readonly openSearch: () => void;\n readonly closeSearch: () => void;\n} {\n const screenReader = useScreenReader();\n const loader = useHistoryLoader(options.surface, screenReader);\n const { state, reset, setters } = useQueryState();\n const { query, scope } = state;\n const { surface, onInsert, onExecute } = options;\n const { open, openSearch, closeSearch } = useOverlayVisibility(\n loader,\n surface !== undefined,\n reset,\n options.onOpenChange,\n );\n\n const candidates = useCandidates(\n open,\n surface,\n scope,\n options.sessionPrompts,\n loader.loaded.entries,\n );\n const matches = useMemo(() => filterPrompts(candidates, query), [candidates, query]);\n const selected = Math.min(state.selectedIndex, Math.max(0, matches.length - 1));\n useHistorySearchKeys({\n open,\n matches,\n selected,\n scope,\n screenReader,\n loader,\n close: closeSearch,\n onInsert,\n onExecute,\n ...setters,\n });\n\n const { loading, error, loaded } = loader;\n const view = { ...state, open, selectedIndex: selected, matches, loading, error };\n return { view: { ...view, skippedLines: loaded.skippedLines }, openSearch, closeSearch };\n}\n","/**\n * SCREEN-1993 — the input area's side of prompt-history search: the `chat-input.history-search`\n * open binding, and what insert and execute do to the composer.\n *\n * Insert replaces the composer text with the match (cursor at the end); execute submits the match\n * through the same path Enter takes, so it is recorded in the live prompt list like any prompt. While\n * the overlay is open the composer is untouched, which is what makes cancel restore the draft exactly.\n */\nimport { useCallback, useEffect } from 'react';\n\nimport {\n useHistorySearch,\n type IHistorySearchSurface,\n} from '../history-search/useHistorySearch.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { IHistorySearchView } from '../history-search/useHistorySearch.js';\n\n/** What the input area receives from the app; absent ⇒ the feature is off and `ctrl+r` is inert. */\nexport interface IInputAreaHistorySearch extends IHistorySearchSurface {\n /** Lets the app block its own bindings (Esc abort, the switcher) while the overlay is open. */\n readonly onOpenChange?: ((open: boolean) => void) | undefined;\n}\n\nexport interface IUseInputAreaHistorySearchOptions {\n readonly historySearch: IInputAreaHistorySearch | undefined;\n readonly sessionPrompts: readonly string[];\n /** The composer can take keys: no popup, not disabled, nothing queued. */\n readonly composerActive: boolean;\n readonly setValue: (value: string) => void;\n readonly setCursorHint: (hint: number | null) => void;\n readonly submitPrompt: (prompt: string) => void;\n}\n\nexport function useInputAreaHistorySearch(\n options: IUseInputAreaHistorySearchOptions,\n): IHistorySearchView {\n const { historySearch, setValue, setCursorHint, submitPrompt, composerActive } = options;\n const onInsert = useCallback(\n (text: string) => {\n setValue(text);\n setCursorHint(null);\n },\n [setCursorHint, setValue],\n );\n const onExecute = useCallback(\n (text: string) => {\n setValue('');\n submitPrompt(text);\n },\n [setValue, submitPrompt],\n );\n const { view, openSearch, closeSearch } = useHistorySearch({\n surface: historySearch,\n sessionPrompts: options.sessionPrompts,\n onInsert,\n onExecute,\n onOpenChange: historySearch?.onOpenChange,\n });\n useKeybindingActions(\n 'chat-input',\n (actions) => {\n if (actions.includes('history-search')) openSearch();\n },\n { isActive: composerActive && !view.open && historySearch !== undefined },\n );\n // Another overlay or a disabled composer takes the keys: the search cannot stay open under it.\n useEffect(() => {\n if (view.open && !composerActive) closeSearch();\n }, [closeSearch, composerActive, view.open]);\n return view;\n}\n","/**\n * The input area's three key bindings, in one place.\n *\n * Extracted from `InputArea.tsx` (CLI-2004) by responsibility: the component owns the input's state\n * and its rendering; these three `useInput` registrations are the routing between the two, and each\n * is gated on a different combination of the same flags — which is the shape that gets copied wrong\n * when it is inline among the render code.\n */\n\nimport {\n moveAutocompleteSelection,\n navigatePromptHistory,\n type IPromptHistoryNavigationState,\n} from '../flows/input-area-flow.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nexport interface IUseInputAreaKeysInputs {\n value: string;\n isDisabled: boolean;\n /** SCREEN-1993: the history-search overlay owns the keys while it is open. */\n searchOpen: boolean;\n isQueueCancellationDisabled: boolean;\n pendingPrompt?: string | null;\n showPopup: boolean;\n setShowPopup: (shown: boolean) => void;\n filteredCommands: ICommand[];\n selectedIndex: number;\n setSelectedIndex: (update: (previous: number) => number) => void;\n tabCompleteCommand: (command: ICommand) => void;\n promptHistory: string[];\n historyState: IPromptHistoryNavigationState;\n setValue: (value: string) => void;\n setCursorHint: (hint: number | null) => void;\n setHistoryState: (state: IPromptHistoryNavigationState) => void;\n onRequestFocusBackgroundList?: (() => void) | undefined;\n onCancelQueue?: (() => void) | undefined;\n}\n\n/** The autocomplete popup's own keys: move the selection, close it, or take the completion. */\nfunction useAutocompletePopupKeys(inputs: IUseInputAreaKeysInputs): void {\n const { showPopup, isDisabled, filteredCommands, selectedIndex } = inputs;\n useKeybindingActions(\n 'autocomplete-menu',\n (actions) => {\n if (!showPopup) return;\n for (const action of actions) {\n if (action === 'previous' || action === 'next') {\n inputs.setSelectedIndex((previous) =>\n moveAutocompleteSelection(previous, filteredCommands.length, action),\n );\n } else if (action === 'close') {\n inputs.setShowPopup(false);\n } else if (action === 'accept') {\n const command = filteredCommands[selectedIndex];\n if (command) inputs.tabCompleteCommand(command);\n }\n }\n },\n { isActive: showPopup && !isDisabled },\n );\n}\n\n/** Register the autocomplete-popup, prompt-history and queued-prompt bindings. */\nexport function useInputAreaKeys(inputs: IUseInputAreaKeysInputs): void {\n const { showPopup, isDisabled, isQueueCancellationDisabled, pendingPrompt, searchOpen } = inputs;\n\n useAutocompletePopupKeys(inputs);\n\n useKeybindingActions(\n 'chat-input',\n (actions) => {\n const action = actions.find(\n (candidate): candidate is 'history-previous' | 'history-next' =>\n candidate === 'history-previous' || candidate === 'history-next',\n );\n if (action === undefined) return;\n const direction = action === 'history-previous' ? 'previous' : 'next';\n // SCREEN-014: ↓ on an empty input that is not browsing history falls through into the\n // background-work list (where it is a no-op for the input today). The parent decides whether\n // there is a list to focus.\n if (\n direction === 'next' &&\n inputs.historyState.selectedIndex === null &&\n inputs.value.length === 0\n ) {\n inputs.onRequestFocusBackgroundList?.();\n return;\n }\n const result = navigatePromptHistory(\n inputs.value,\n inputs.promptHistory,\n inputs.historyState,\n direction,\n );\n inputs.setValue(result.value);\n inputs.setCursorHint(result.cursorHint);\n inputs.setHistoryState(result.state);\n },\n { isActive: !showPopup && !isDisabled && !pendingPrompt && !searchOpen },\n );\n\n // Backspace cancels queued prompt\n useKeybindingActions(\n 'queued-prompt',\n (actions) => {\n if (actions.includes('cancel') && pendingPrompt) {\n inputs.onCancelQueue?.();\n }\n },\n { isActive: !!pendingPrompt && !isQueueCancellationDisabled },\n );\n}\n","/** Pure builder for the input area's top border line (optional right-aligned session title). */\n\nexport interface IInputTopBorder {\n left: string;\n label: string;\n right: string;\n}\n\nconst TITLE_RIGHT_PAD = 2;\n\n/** Build the top border with an optional session-name title, right-aligned 2 chars from the edge. */\nexport function buildInputTopBorder(innerWidth: number, sessionName?: string): IInputTopBorder {\n if (sessionName) {\n const label = ` \"${sessionName}\" `;\n const leftLen = Math.max(0, innerWidth - label.length - TITLE_RIGHT_PAD);\n return { left: '─'.repeat(leftLen), label, right: '─'.repeat(TITLE_RIGHT_PAD) };\n }\n return { left: '─'.repeat(innerWidth), label: '', right: '' };\n}\n","/**\n * The input area's horizontal rules, and the deletion announcement that replaces them.\n *\n * Extracted from `InputArea.tsx` (CLI-2004). The rules are hand-drawn `<Text>` lines rather than a\n * Yoga-synthesized `Box` border because a synthesized border drops glyphs during rapid re-render at\n * full terminal height (SCREEN-003) — which is exactly why they are not covered by the `borderStyle`\n * suppression the mode applies elsewhere and need their own branch.\n */\n\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\nimport { buildInputTopBorder } from './utils/input-top-border.js';\n\n/** The announcement's exact shape, so the input and its test cannot disagree about it. */\nexport function formatDeletionAnnouncement(deleted: string): string {\n return `[deleted: ${deleted}]`;\n}\n\n/** Top rule, carrying the optional right-aligned session title. Nothing in screen-reader mode. */\nexport function InputTopRule({\n screenReader,\n innerWidth,\n borderColor,\n sessionName,\n}: {\n screenReader: boolean;\n innerWidth: number;\n borderColor: string;\n sessionName?: string;\n}): React.ReactElement | null {\n const palette = usePalette();\n if (screenReader) return null;\n const topBorder = buildInputTopBorder(innerWidth, sessionName);\n return (\n <Text color={borderColor}>\n {topBorder.left}\n {topBorder.label ? (\n <Text backgroundColor={borderColor} color={palette.text.onAccent} bold>\n {topBorder.label}\n </Text>\n ) : null}\n {topBorder.right}\n </Text>\n );\n}\n\n/** Bottom rule (mirrors the top). Nothing in screen-reader mode. */\nexport function InputBottomRule({\n screenReader,\n innerWidth,\n borderColor,\n}: {\n screenReader: boolean;\n innerWidth: number;\n borderColor: string;\n}): React.ReactElement | null {\n if (screenReader) return null;\n return <Text color={borderColor}>{'─'.repeat(innerWidth)}</Text>;\n}\n\n/** `[deleted: <text>]`, rendered once for the most recent word/line delete. */\nexport function DeletionAnnouncement({\n deleted,\n}: {\n deleted: string | null;\n}): React.ReactElement | null {\n if (deleted === null) return null;\n return <Text>{formatDeletionAnnouncement(deleted)}</Text>;\n}\n","import { Box, useStdout } from 'ink';\nimport React, { useState, useEffect } from 'react';\n\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\n/** Footer for the autocomplete popup. */\nexport const SLASH_AUTOCOMPLETE_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Tab', label: 'Complete' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Close' },\n];\n\ninterface IProps {\n /** Filtered list of commands to display */\n commands: ICommand[];\n /** Currently highlighted item index */\n selectedIndex: number;\n /** Whether to show the popup */\n visible: boolean;\n /** Whether showing subcommands (no slash prefix) */\n isSubcommandMode?: boolean;\n}\n\nconst MAX_VISIBLE = 8;\n// border(1×2) + paddingX(1×2) consumed by outer box\nconst OUTER_CHROME = 4;\nconst MIN_ROW_WIDTH = 40;\nconst NAME_COL_MAX = 20;\n\nfunction useRowWidth(): number {\n const { stdout } = useStdout();\n const measure = (): number => Math.max(MIN_ROW_WIDTH, (stdout.columns ?? 80) - OUTER_CHROME);\n const [width, setWidth] = useState(measure);\n\n useEffect(() => {\n const onResize = (): void => setWidth(measure());\n stdout.on('resize', onResize);\n return () => {\n stdout.off('resize', onResize);\n };\n }, [stdout]);\n\n return width;\n}\n\nfunction capName(name: string, colWidth: number): string {\n return name.length > colWidth ? `${name.slice(0, colWidth - 1)}…` : name.padEnd(colWidth);\n}\n\n/** Render a single command row */\nfunction CommandRow(props: {\n cmd: ICommand;\n isSelected: boolean;\n showSlash: boolean;\n rowWidth: number;\n nameColWidth: number;\n /** CLI-2004: row number in the mode, replacing the `> ` cursor. */\n rowNumber?: number;\n}): React.ReactElement {\n const palette = usePalette();\n const { cmd, isSelected, showSlash, rowWidth, nameColWidth, rowNumber } = props;\n const indicator =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isSelected\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n const nameColor = isSelected ? palette.text.accent : undefined;\n const dimmed = !isSelected;\n const namePart = capName(cmd.name, nameColWidth);\n const text = showSlash\n ? `${indicator}/${namePart} ${cmd.description ?? ''}`\n : `${indicator}${namePart} ${cmd.description ?? ''}`;\n\n return (\n <Box width={rowWidth}>\n <Text color={nameColor} dimColor={dimmed} wrap=\"truncate-end\">\n {text}\n </Text>\n </Box>\n );\n}\n\n/** Autocomplete popup showing matching slash commands */\nexport default function SlashAutocomplete({\n commands,\n selectedIndex,\n visible,\n isSubcommandMode,\n}: IProps): React.ReactElement | null {\n const palette = usePalette();\n const rowWidth = useRowWidth();\n const screenReader = useScreenReader();\n const footerHints = useKeybindingHints('autocomplete-menu', [\n [['previous', 'next'], 'Navigate'],\n ['accept', 'Complete'],\n ['execute', 'Select'],\n ['close', 'Close'],\n ]);\n\n if (!visible || commands.length === 0) return null;\n\n const scrollOffset = computeScrollOffset(selectedIndex, commands.length);\n const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);\n\n const nameColWidth = Math.min(\n NAME_COL_MAX,\n Math.max(...visibleCommands.map((c) => c.name.length)),\n );\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.muted })}\n paddingX={1}\n >\n {visibleCommands.map((cmd, i) => (\n <CommandRow\n key={cmd.name}\n cmd={cmd}\n isSelected={scrollOffset + i === selectedIndex}\n showSlash={!isSubcommandMode}\n rowWidth={rowWidth}\n nameColWidth={nameColWidth}\n {...(screenReader ? { rowNumber: scrollOffset + i } : {})}\n />\n ))}\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n\n/** Compute scroll offset to keep selectedIndex visible */\nfunction computeScrollOffset(selectedIndex: number, total: number): number {\n if (total <= MAX_VISIBLE) return 0;\n if (selectedIndex < MAX_VISIBLE) return 0;\n const maxOffset = total - MAX_VISIBLE;\n return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);\n}\n","const PASTE_LABEL_RE = /\\[Pasted text #(\\d+)(?: \\+\\d+ lines)?\\]/g;\n\n/**\n * Replace paste label placeholders with their original content from the store.\n * Labels that have no matching store entry are replaced with empty string.\n */\nexport function expandPasteLabels(text: string, store: Map<number, string>): string {\n return text.replace(PASTE_LABEL_RE, (_, id: string) => store.get(Number(id)) ?? '');\n}\n","/**\n * WaveText — renders text with a subtle wave color animation.\n * Groups of 3-4 characters share the same color, creating a soft flowing effect.\n * The ramp comes from the resolved theme's motion tokens (SCREEN-006, SCREEN-2002): a calm span\n * wide enough to be perceptible (#555→#bbb) at an unhurried 400ms cadence.\n */\n\nimport React, { useState, useEffect } from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { useMotion, useMotionTokens, usePalette } from './theme/index.js';\n\n/** Cadence is not themed: a theme changes colour, not how often the terminal repaints. */\nconst MOTION_INTERVAL_MS = 400;\nconst MOTION_CHARS_PER_GROUP = 4;\n\ninterface IProps {\n text: string;\n}\n\nexport default function WaveText({ text }: IProps): React.ReactElement {\n // Animate only on an interactive color terminal (shared with the markdown color\n // gate via terminal-capabilities). Non-TTY / NO_COLOR / FORCE_COLOR=0 → static,\n // no interval, no motion (SCREEN-006/008).\n //\n // CLI-2004: screen-reader mode is a gate on the same interval, not a second mechanism. A colour\n // ramp is silent to a reader, but the repaint it causes is not — every tick re-emits the line, and\n // the reader announces it again. SCREEN-2002 added `reducedMotion` as a third input to that rule.\n const screenReader = useScreenReader();\n const palette = usePalette();\n const motionTokens = useMotionTokens();\n // SCREEN-2002: the colour gate, screen-reader mode and the reduced-motion setting are ONE rule\n // with one owner. This is its only consumer — the package's only animation.\n const animate = useMotion();\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n if (!animate) return undefined;\n const timer = setInterval(() => {\n setTick((prev) => prev + 1);\n }, MOTION_INTERVAL_MS);\n return () => clearInterval(timer);\n }, [animate]);\n\n if (screenReader) {\n // No colour prop at all: the mode drops the cue rather than substituting a value.\n return <Text>{text}</Text>;\n }\n\n if (!animate) {\n return <Text color={palette.text.muted}>{text}</Text>;\n }\n\n const chars = [...text];\n\n return (\n <Text>\n {chars.map((char, i) => {\n const group = Math.floor(i / MOTION_CHARS_PER_GROUP);\n const colorIndex = (tick + group) % motionTokens.wave.length;\n return (\n <Text key={i} color={motionTokens.wave[colorIndex]}>\n {char}\n </Text>\n );\n })}\n </Text>\n );\n}\n","import { Box, useWindowSize } from 'ink';\nimport React, { useState, useCallback, useEffect, useRef, useMemo } from 'react';\n\nimport CjkTextInput from './CjkTextInput.js';\nimport ExternalPromptNotice from './external-prompt-notice.js';\nimport {\n appendPromptHistory,\n createPasteLabelChange,\n createPromptHistoryNavigationState,\n extractPromptHistory,\n resolveEnterCommandSelection,\n resolveTabCompletion,\n shouldSubmitInput,\n} from './flows/input-area-flow.js';\nimport HistorySearchOverlay from './HistorySearchOverlay.js';\nimport { useAutocomplete } from './hooks/useAutocomplete.js';\nimport { useInputAreaHistorySearch } from './hooks/useInputAreaHistorySearch.js';\nimport { useInputAreaKeys } from './hooks/useInputAreaKeys.js';\nimport { DeletionAnnouncement, InputBottomRule, InputTopRule } from './input-area-rules.js';\nimport { KeyHintFooter } from './key-hint-footer.js';\nimport { useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport SlashAutocomplete from './SlashAutocomplete.js';\nimport { usePalette } from './theme/index.js';\nimport { expandPasteLabels } from './utils/paste-labels.js';\nimport WaveText from './WaveText.js';\n\nimport type { IInputAreaHistorySearch } from './hooks/useInputAreaHistorySearch.js';\nimport type { ITuiCommandQueryPort } from './tui-app-channel-port.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\n\nconst PENDING_PROMPT_DISPLAY_MAX = 50;\nconst PENDING_PROMPT_TAIL_KEEP = 47;\n\ninterface IProps {\n /** FLOW-2006: a deep link's prefill, seeded once. */\n initialValue?: string | undefined;\n /** Told to the controller the first time the prefill is taken. */\n consumeInitialValue?: (() => void) | undefined;\n /** True while the composer's text came from a deep link: renders the provenance notice. */\n externalPromptOrigin?: boolean | undefined;\n onSubmit: (value: string) => void;\n onCancelQueue?: () => void;\n isDisabled: boolean;\n isQueueCancellationDisabled?: boolean;\n isAborting?: boolean;\n pendingPrompt?: string | null;\n /** REMOTE-014 E5: total queued turns (owner + co-drivers); >1 surfaces a co-driver-queued hint. */\n pendingCount?: number;\n commandQueryPort?: ITuiCommandQueryPort;\n sessionName?: string;\n history?: readonly IHistoryEntry[];\n /**\n * SCREEN-014: called when ↓ is pressed on an empty input at the bottom of prompt history, so the\n * parent can move focus into the background-work list (a no-op for the input today).\n */\n onRequestFocusBackgroundList?: () => void;\n /** SCREEN-1993: the stored-prompt search surface; absent ⇒ `ctrl+r` is inert. */\n historySearch?: IInputAreaHistorySearch | undefined;\n}\n\n/**\n * Known limitation: Korean IME last character may be dropped on Enter.\n * This is an Ink raw mode limitation — no compositionstart/compositionend\n * events are available in terminal raw mode.\n * Reference: https://github.com/anthropics/claude-code/issues/3045\n */\n/**\n * Layout constants for InputArea (columns).\n * Used to compute available text width from terminal columns.\n *\n * Side borders removed — only top/bottom horizontal lines remain.\n * paddingLeft={1} adds 1 column inside the box.\n * Prompt \"> \" takes 2 columns.\n */\nconst BORDER_HORIZONTAL = 0;\nconst PADDING_LEFT = 1;\nconst PROMPT_WIDTH = 2;\nconst INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;\nconst DEFAULT_TERMINAL_COLUMNS = 80;\n\nexport default function InputArea({\n onSubmit,\n onCancelQueue,\n isDisabled,\n isQueueCancellationDisabled,\n isAborting,\n pendingPrompt,\n pendingCount,\n commandQueryPort,\n sessionName,\n history,\n onRequestFocusBackgroundList,\n historySearch,\n initialValue,\n consumeInitialValue,\n externalPromptOrigin,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n // FLOW-2006: the deep link's prefill arrives as a prop and is taken exactly once; the controller\n // then drops it, so a remount (the handoff-suspend path) never re-seeds a cleared prompt.\n const [value, setValue] = useState(initialValue ?? '');\n // The text the link supplied, held for as long as it is still what the composer holds. The\n // provenance belongs to THIS TEXT, not to the session: once it is sent or cleared, the next\n // prompt is the user's own and must not be labelled as if a link had written it.\n const [seededValue] = useState(externalPromptOrigin === true ? (initialValue ?? '') : '');\n useEffect(() => {\n if (initialValue === undefined || initialValue.length === 0) return;\n consumeInitialValue?.();\n }, [initialValue, consumeInitialValue]);\n const [cursorHint, setCursorHint] = useState<number | null>(null);\n // CLI-2004: the text the last word/line delete removed, announced once and cleared on the next\n // ordinary edit. A reader announces the line it is ON — what left it is otherwise unrecoverable.\n const screenReader = useScreenReader();\n const [deletedText, setDeletedText] = useState<string | null>(null);\n const [historyState, setHistoryState] = useState(createPromptHistoryNavigationState);\n const [localPromptHistory, setLocalPromptHistory] = useState<string[]>([]);\n const restoredPromptHistory = useMemo(() => extractPromptHistory(history ?? []), [history]);\n const promptHistory = useMemo(\n () =>\n localPromptHistory.reduce<string[]>(\n (prompts, prompt) => appendPromptHistory(prompts, prompt),\n restoredPromptHistory,\n ),\n [restoredPromptHistory, localPromptHistory],\n );\n const pasteStore = useRef<Map<number, string>>(new Map());\n const { columns } = useWindowSize();\n const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;\n const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);\n const pasteIdRef = useRef(0);\n const submitHint = useKeybindingHints('chat-input', [['submit', 'Submit']]);\n\n const {\n showPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n isSubcommandMode,\n setShowPopup,\n } = useAutocomplete(value, commandQueryPort);\n\n const handlePaste = useCallback((text: string, cursorPosition: number) => {\n pasteIdRef.current += 1;\n const id = pasteIdRef.current;\n pasteStore.current.set(id, text);\n setValue((prev) => {\n const change = createPasteLabelChange(prev, cursorPosition, id, text);\n setCursorHint(change.cursorHint);\n return change.value;\n });\n }, []);\n\n const resetHistoryNavigation = useCallback(() => {\n setHistoryState(createPromptHistoryNavigationState());\n }, []);\n\n const recordPromptHistory = useCallback((prompt: string): void => {\n setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));\n }, []);\n\n const submitPrompt = useCallback(\n (prompt: string): void => {\n recordPromptHistory(prompt);\n resetHistoryNavigation();\n onSubmit(prompt);\n },\n [onSubmit, recordPromptHistory, resetHistoryNavigation],\n );\n\n /** Tab: insert command into input field without executing */\n const tabCompleteCommand = useCallback(\n (cmd: ICommand): void => {\n const result = resolveTabCompletion(value, cmd);\n if (result.type === 'insert') {\n setValue(result.value);\n if (result.selectedIndex !== undefined) {\n setSelectedIndex(result.selectedIndex);\n }\n }\n },\n [value, setSelectedIndex],\n );\n\n /** Enter: insert and execute command immediately */\n const enterSelectCommand = useCallback(\n (cmd: ICommand): void => {\n const result = resolveEnterCommandSelection(value, cmd);\n if (result.type === 'insert') {\n setValue(result.value);\n if (result.selectedIndex !== undefined) {\n setSelectedIndex(result.selectedIndex);\n }\n return;\n }\n if (result.type === 'submit') {\n setValue('');\n submitPrompt(result.value);\n }\n },\n [value, submitPrompt, setSelectedIndex],\n );\n\n const handleSubmit = useCallback(\n (text: string): void => {\n if (!shouldSubmitInput(text)) return;\n\n if (showPopup && filteredCommands[selectedIndex]) {\n enterSelectCommand(filteredCommands[selectedIndex]);\n return;\n }\n\n // Expand paste labels before submitting\n const expanded = expandPasteLabels(text.trim(), pasteStore.current);\n\n setValue('');\n // Reset paste state\n pasteStore.current.clear();\n pasteIdRef.current = 0;\n\n submitPrompt(expanded);\n },\n [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt],\n );\n\n const search = useInputAreaHistorySearch({\n historySearch,\n sessionPrompts: promptHistory,\n composerActive: !showPopup && !isDisabled && !pendingPrompt,\n setValue,\n setCursorHint,\n submitPrompt,\n });\n\n useInputAreaKeys({\n value,\n isDisabled,\n searchOpen: search.open,\n isQueueCancellationDisabled: isQueueCancellationDisabled ?? isDisabled,\n pendingPrompt,\n showPopup,\n setShowPopup,\n filteredCommands,\n selectedIndex,\n setSelectedIndex,\n tabCompleteCommand,\n promptHistory,\n historyState,\n setValue,\n setCursorHint,\n setHistoryState,\n onRequestFocusBackgroundList,\n onCancelQueue,\n });\n\n const borderColor = isAborting\n ? palette.border.attention\n : pendingPrompt\n ? palette.border.focused\n : isDisabled\n ? palette.border.muted\n : palette.border.active;\n const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);\n\n return (\n <Box flexDirection=\"column\">\n {showPopup && (\n <SlashAutocomplete\n commands={filteredCommands}\n selectedIndex={selectedIndex}\n visible={showPopup}\n isSubcommandMode={isSubcommandMode}\n />\n )}\n <HistorySearchOverlay view={search} />\n <DeletionAnnouncement deleted={screenReader ? deletedText : null} />\n <InputTopRule\n screenReader={screenReader}\n innerWidth={innerWidth}\n borderColor={borderColor}\n {...(sessionName !== undefined ? { sessionName } : {})}\n />\n <Box paddingLeft={1}>\n {isAborting ? (\n <Text color={palette.text.warning}> Interrupting...</Text>\n ) : pendingPrompt ? (\n <Text color={palette.text.accent}>\n {' '}\n Queued:{' '}\n {pendingPrompt.length > PENDING_PROMPT_DISPLAY_MAX\n ? pendingPrompt.slice(0, PENDING_PROMPT_TAIL_KEEP) + '...'\n : pendingPrompt}{' '}\n {typeof pendingCount === 'number' && pendingCount > 1 && (\n <Text dimColor>(+{pendingCount - 1} co-driver queued) </Text>\n )}\n <Text dimColor>(Backspace to cancel)</Text>\n </Text>\n ) : isDisabled ? (\n <WaveText text=\" Waiting for response... (ESC to interrupt)\" />\n ) : (\n <Box>\n <Text color={palette.text.success} bold>\n {'> '}\n </Text>\n <CjkTextInput\n value={value}\n onChange={(v) => {\n setValue(v);\n resetHistoryNavigation();\n setCursorHint(null); // reset after normal typing\n }}\n onDeletedText={setDeletedText}\n onSubmit={handleSubmit}\n onPaste={handlePaste}\n placeholder=\"Type a message or /help\"\n availableWidth={availableWidth}\n cursorHint={cursorHint}\n enableVerticalNavigation={false}\n keybindingContext={showPopup ? 'autocomplete-menu' : 'chat-input'}\n focus={!search.open}\n />\n </Box>\n )}\n </Box>\n {!screenReader && !isDisabled && !pendingPrompt && !search.open && (\n <KeyHintFooter hints={submitHint} />\n )}\n <InputBottomRule\n screenReader={screenReader}\n innerWidth={innerWidth}\n borderColor={borderColor}\n />\n {seededValue.length > 0 && value === seededValue && <ExternalPromptNotice value={value} />}\n </Box>\n );\n}\n","import { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\n\nconst MAX_PREVIEW_LINES = 4;\nconst SUCCESS_EXIT_CODE = 0;\nconst COMMAND_TOOL_NAMES = new Set(['Shell', 'Bash', 'BackgroundProcess']);\n\nexport interface ICommandOutputInput {\n toolName: string;\n firstArg?: string;\n isRunning?: boolean;\n result?: string;\n toolResultData?: string;\n}\n\nexport interface ICommandOutputSummary {\n status: 'success' | 'error';\n statusLabel: string;\n previewLines: string[];\n omittedLineCount: number;\n transcriptHint?: string;\n}\n\nexport function formatCommandOutputSummary(\n tool: ICommandOutputInput,\n): ICommandOutputSummary | undefined {\n if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return undefined;\n\n const parsed = parseToolResultData(tool.toolResultData);\n const exitCode = getNumberValue(parsed, 'exitCode');\n const successValue = getBooleanValue(parsed, 'success');\n const output = buildOutputText(tool.toolResultData, parsed);\n const lines = trimTrailingBlankLines(splitOutputLines(output));\n // SEC-019: these lines ARE the command's stdout and stderr — the most directly attacker-chosen\n // text in the whole surface. Sanitized where the summary is BUILT rather than where it is\n // rendered, because this function exists to produce display text and a second render site would\n // otherwise have to remember. `statusLabel` and `transcriptHint` are built from numbers and need\n // nothing.\n const previewLines = lines.slice(0, MAX_PREVIEW_LINES).map(sanitizeTerminalText);\n const omittedLineCount = Math.max(0, lines.length - previewLines.length);\n const isFailed =\n tool.result === 'error' ||\n successValue === false ||\n (exitCode !== undefined && exitCode !== SUCCESS_EXIT_CODE);\n\n return {\n status: isFailed ? 'error' : 'success',\n statusLabel: formatStatusLabel(isFailed, exitCode),\n previewLines,\n omittedLineCount,\n transcriptHint:\n omittedLineCount > 0\n ? `... +${omittedLineCount} lines (full output in session transcript)`\n : undefined,\n };\n}\n\nfunction parseToolResultData(value: string): TUniversalValue {\n try {\n return JSON.parse(value) as TUniversalValue;\n } catch {\n return value;\n }\n}\n\nfunction buildOutputText(raw: string, parsed: TUniversalValue): string {\n if (!isUniversalObject(parsed)) return raw;\n\n const output = getStringValue(parsed, 'output');\n if (output !== undefined) return output;\n\n const stdout = getStringValue(parsed, 'stdout');\n const stderr = getStringValue(parsed, 'stderr');\n const error = getStringValue(parsed, 'error');\n const lines: string[] = [];\n if (stdout) lines.push(stdout);\n if (stderr) lines.push(prefixLines(stderr, '[stderr] '));\n if (!stdout && !stderr && error) lines.push(error);\n return lines.join('\\n');\n}\n\nfunction formatStatusLabel(isFailed: boolean, exitCode: number | undefined): string {\n if (exitCode !== undefined && exitCode !== SUCCESS_EXIT_CODE) return `exit ${exitCode}`;\n return isFailed ? 'error' : 'ok';\n}\n\nfunction splitOutputLines(output: string): string[] {\n if (!output) return [];\n return output.replace(/\\r\\n/g, '\\n').split('\\n');\n}\n\nfunction trimTrailingBlankLines(lines: string[]): string[] {\n let end = lines.length;\n while (end > 0 && lines[end - 1]!.trim().length === 0) {\n end -= 1;\n }\n return lines.slice(0, end);\n}\n\nfunction prefixLines(value: string, prefix: string): string {\n return splitOutputLines(value)\n .map((line) => `${prefix}${line}`)\n .join('\\n');\n}\n\nfunction isUniversalObject(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction getStringValue(source: TUniversalValue, key: string): string | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction getNumberValue(source: TUniversalValue, key: string): number | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction getBooleanValue(source: TUniversalValue, key: string): boolean | undefined {\n if (!isUniversalObject(source)) return undefined;\n const value = source[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n","import { marked } from 'marked';\n// @ts-expect-error — marked-terminal has no type declarations\nimport TerminalRenderer from 'marked-terminal';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport {\n diffRowStyles,\n markdownRendererOptions,\n resolveTheme,\n syntaxHighlightTheme,\n} from './theme/index.js';\n\nimport type { IDiffRowStyles, ITuiTheme } from './theme/index.js';\nimport type { Renderer } from 'marked';\n\nconst CODE_BLOCK_INDENT = ' ';\n\nexport interface IRenderMarkdownOptions {\n color?: boolean;\n codeBlockWidth?: number;\n /**\n * SCREEN-2002: the resolved theme. Absent ⇒ the `dark` built-in, which is byte-identical to what\n * this renderer produced before themes existed.\n */\n theme?: ITuiTheme;\n /** SCREEN-2002: `false` renders code blocks as plain indented text, with no highlight SGR. */\n syntaxHighlighting?: boolean;\n /**\n * CLI-2004: flatten tables to `Header: value` sentences. A box-drawn grid is unreadable aloud —\n * a reader announces the rules, not the relationship between a heading and its cell.\n */\n screenReader?: boolean;\n}\n\ninterface IHighlightOptions {\n ignoreIllegals?: boolean;\n /** The cli-highlight theme marked-terminal forwards; absent ⇒ its red/green defaults. */\n theme?: Record<string, (text: string) => string>;\n}\n\ntype TTerminalRendererConstructor = new (\n /** `marked-terminal`'s style options: one chalk instance per token it colours. */\n options?: Record<string, (text: string) => string>,\n highlightOptions?: IHighlightOptions,\n) => Renderer;\n\nconst TerminalRendererConstructor = TerminalRenderer as TTerminalRendererConstructor;\n\nfunction shouldUseColor(option: boolean | undefined): boolean {\n if (option !== undefined) {\n return option;\n }\n return isInteractiveColorTerminal();\n}\n\nfunction isDiffLanguage(language: string | undefined): boolean {\n return language?.trim().toLowerCase() === 'diff';\n}\n\nfunction styleAddedOrRemovedDiffRow(\n line: string,\n rowWidth: number,\n color: boolean,\n styles: IDiffRowStyles,\n): string {\n const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);\n if (!color) {\n return row.trimEnd();\n }\n if (line.startsWith('+')) return styles.added(row);\n if (line.startsWith('-')) return styles.removed(row);\n return row.trimEnd();\n}\n\nfunction colorizeDiffLine(\n line: string,\n color: boolean,\n rowWidth: number,\n styles: IDiffRowStyles,\n): string {\n if (line.startsWith('+') || line.startsWith('-')) {\n return styleAddedOrRemovedDiffRow(line, rowWidth, color, styles);\n }\n const row = `${CODE_BLOCK_INDENT}${line}`;\n if (!color) {\n return row;\n }\n if (line.startsWith('@@')) return styles.hunk(row);\n if (line.startsWith('diff ') || line.startsWith('index ')) return styles.meta(row);\n return row;\n}\n\nfunction resolveDiffRowWidth(lines: readonly string[], requestedWidth: number | undefined): number {\n const minimumWidth = lines.reduce(\n (maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),\n 0,\n );\n if (requestedWidth === undefined) {\n return minimumWidth;\n }\n return Math.max(minimumWidth, requestedWidth);\n}\n\nfunction renderDiffCodeBlock(\n code: string,\n color: boolean,\n codeBlockWidth: number | undefined,\n styles: IDiffRowStyles,\n): string {\n const lines = code.split('\\n');\n const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);\n const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth, styles)).join('\\n');\n return `${body}\\n\\n`;\n}\n\n/**\n * CLI-2004 — flatten a table to `Header: value` lines.\n *\n * The cell and row hooks tag their output with separators marked's own renderers never emit, so the\n * table hook can recover the grid it was given. Reconstructing it from the RENDERED strings is the\n * alternative, and those already carry the SGR the inline renderers added.\n */\nconst CELL_MARK = '\\u0000';\nconst ROW_MARK = '\\u0001';\n\nfunction splitTableCells(row: string): string[] {\n const cells = row.split(ROW_MARK).join('').split(CELL_MARK);\n // The trailing separator leaves one empty tail entry; the cells themselves may legitimately be ''.\n return cells.slice(0, -1);\n}\n\nfunction installTableFlattening(renderer: Renderer): void {\n renderer.tablecell = (content: string): string => `${content}${CELL_MARK}`;\n renderer.tablerow = (content: string): string => `${content}${ROW_MARK}`;\n renderer.table = (header: string, body: string): string => {\n const headers = splitTableCells(header);\n const rows = body\n .split(ROW_MARK)\n .filter((row) => row.length > 0)\n .map((row) => splitTableCells(`${row}${ROW_MARK}`));\n if (headers.length === 0 || rows.length === 0) return '';\n const blocks = rows.map((cells) =>\n headers.map((head, index) => `${head}: ${cells[index] ?? ''}`).join('\\n'),\n );\n // A blank line between rows: the row boundary is the only structure left to hear.\n return `${blocks.join('\\n\\n')}\\n\\n`;\n };\n}\n\nfunction createTerminalRenderer(\n color: boolean,\n codeBlockWidth: number | undefined,\n screenReader: boolean,\n theme: ITuiTheme,\n syntaxHighlighting: boolean,\n): Renderer {\n // SCREEN-2002: both dependencies are told what to colour with. Left to their own defaults, a\n // daltonized theme would still get green/red code blocks from cli-highlight's own theme.\n const renderer = new TerminalRendererConstructor(markdownRendererOptions(theme.markdown), {\n ignoreIllegals: true,\n theme: syntaxHighlightTheme(theme.syntax),\n });\n const renderCode = renderer.code.bind(renderer);\n const styles = diffRowStyles(theme);\n\n renderer.code = (code: string, language: string | undefined, escaped: boolean): string => {\n if (isDiffLanguage(language)) {\n return renderDiffCodeBlock(code, color, codeBlockWidth, styles);\n }\n // Highlighting off: the block renders as plain indented text, the shape the diff path uses.\n if (!syntaxHighlighting) {\n return `${code\n .split('\\n')\n .map((line) => `${CODE_BLOCK_INDENT}${line}`)\n .join('\\n')}\\n\\n`;\n }\n return renderCode(code, language, escaped);\n };\n\n if (screenReader) installTableFlattening(renderer);\n\n return renderer;\n}\n\n/**\n * Render markdown to a terminal-formatted string with colors, bold, etc.\n * Returns the rendered string (may include ANSI escape codes).\n */\nexport function renderMarkdown(md: string, options: IRenderMarkdownOptions = {}): string {\n // SEC-019 (issue #2022): the untrusted string is sanitized BEFORE parsing, never after. The\n // renderer ADDS ANSI — colours, bold, code-block framing — so filtering its output would strip the\n // repository's own presentation along with the attacker's. Filtering its input removes what\n // arrived from outside and leaves what is generated after.\n //\n // This is the choke point every render path shares (MessageList, StreamingIndicator,\n // ToolDiffBlock), which is why it is here rather than at each of the three.\n const safe = sanitizeTerminalText(md);\n const result = marked.parse(safe, {\n renderer: createTerminalRenderer(\n shouldUseColor(options.color),\n options.codeBlockWidth,\n options.screenReader === true,\n resolveTheme(options.theme),\n options.syntaxHighlighting !== false,\n ),\n });\n return typeof result === 'string' ? result.trimEnd() : safe;\n}\n","import { renderMarkdown } from '../render-markdown.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { useSyntaxHighlighting, useTheme } from '../theme/index.js';\n\nimport type { IRenderMarkdownOptions } from '../render-markdown.js';\n\n/** What a call site still decides. The three appearance inputs are no longer among them. */\nexport type TRenderMarkdownExtras = Omit<\n IRenderMarkdownOptions,\n 'theme' | 'syntaxHighlighting' | 'screenReader'\n>;\n\n/**\n * SCREEN-2002 — markdown rendered with this surface's resolved appearance.\n *\n * The three inputs every render site needs — the theme, the syntax-highlighting setting and\n * screen-reader mode — are bound here instead of at each call. That is a deliberate removal of an\n * obligation rather than a convenience.\n *\n * The obligation was forgotten once and guarded four times. `/theme syntax off` shipped persisting\n * a setting no call site passed, so the command reported success and changed nothing; each guard\n * written afterwards measured a PROXY for \"every call site passes it\" — the prop appears in the\n * file, the file matched the regex, the identifier occurs N times — and each held for exactly one\n * review round. A binding removes the thing being proxied: there is no per-call-site argument left\n * to forget, and the floor collapses to a single-consumer ratchet (nothing outside this module\n * imports `renderMarkdown`), which is a property rather than a proxy for one.\n *\n * Screen-reader mode is bound too, uniformly. `StreamingIndicator` previously omitted it, which was\n * correct but only incidentally: it returns its collapsed line before reaching any markdown, so the\n * flag never applied. Binding it removes that coincidence as something a reader has to reconstruct.\n */\nexport function useRenderMarkdown(): (md: string, extras?: TRenderMarkdownExtras) => string {\n const theme = useTheme();\n const syntaxHighlighting = useSyntaxHighlighting();\n const screenReader = useScreenReader();\n return (md, extras = {}) =>\n renderMarkdown(md, { ...extras, theme, syntaxHighlighting, screenReader });\n}\n","/**\n * SCREEN-012: turn a provider-safe projected tool id into the natural command name for display.\n *\n * Slash commands invoked by the model are projected to provider-safe tool ids prefixed with\n * the host-selected prefix (or the neutral default), optionally suffixed with a `_<8 hex>` hash when\n * the name would otherwise exceed the provider length limit (see model-command-tool-projection.ts).\n * For display we recover the command name: strip the prefix and a trailing projection hash. Tool\n * names that are not command projections (e.g. `Shell`, `Read`) are returned unchanged.\n *\n * ## SEC-019: this is where a tool label is made safe, not at each render site\n *\n * A tool name and its first argument come from the model, from an MCP server's manifest, or from a\n * plugin — all untrusted — and they are put on the terminal as text. Sanitizing at the render sites\n * was tried and failed within one review round: `MessageList` has two branches that show a tool name\n * and only one of them got the call, so a name carrying OSC 52 still reached `<Text>` whenever the\n * tool's content happened to parse as a structured summary. Three more sites had the same gap\n * (`StreamingIndicator`, and the label `tool-summary-status` composes for the history view).\n *\n * The defect is not that a call was forgotten. It is that \"make this displayable\" and \"make this\n * safe to display\" were two separate steps, so every new render site had to remember the second one.\n * They are one step here, and a fifth caller is covered because it cannot obtain the display text\n * without going through the function that sanitizes it.\n */\nimport { MODEL_COMMAND_TOOL_PREFIX } from '@robota-sdk/agent-framework';\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\n/** A trailing `_<8 lowercase hex>` projection hash appended to over-long command tool names. */\nconst PROJECTION_HASH_SUFFIX = /_[0-9a-f]{8}$/;\n\nexport function humanizeToolName(\n toolName: string,\n prefix: string = MODEL_COMMAND_TOOL_PREFIX,\n): string {\n if (!toolName.startsWith(prefix)) return sanitizeTerminalText(toolName);\n const body = toolName.slice(prefix.length);\n const withoutHash = body.replace(PROJECTION_HASH_SUFFIX, '');\n return sanitizeTerminalText(withoutHash.length > 0 ? withoutHash : toolName);\n}\n\n/**\n * The first argument of a tool call, as display text.\n *\n * A separate function rather than a bare `sanitizeTerminalText` at the two sites that show it: the\n * argument is the other half of a tool label, it is equally untrusted — a path, a shell command, a\n * URL the model chose — and pairing it with {@link humanizeToolName} is what makes the boundary\n * findable. A render site that reaches for one naturally reaches for the other.\n */\nexport function humanizeToolArgument(firstArg: string | undefined): string {\n return firstArg === undefined ? '' : sanitizeTerminalText(firstArg);\n}\n","import React, { createContext, useContext } from 'react';\n\nconst ProductDisplayNameContext = createContext('Assistant');\nconst ModelCommandToolPrefixContext = createContext<string | undefined>(undefined);\n\nexport function ProductDisplayNameProvider({\n name,\n modelCommandToolPrefix,\n children,\n}: {\n name?: string;\n modelCommandToolPrefix?: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <ProductDisplayNameContext.Provider value={name?.trim() || 'Assistant'}>\n <ModelCommandToolPrefixContext.Provider value={modelCommandToolPrefix}>\n {children}\n </ModelCommandToolPrefixContext.Provider>\n </ProductDisplayNameContext.Provider>\n );\n}\n\nexport function useProductDisplayName(): string {\n return useContext(ProductDisplayNameContext);\n}\n\nexport function useModelCommandToolPrefix(): string | undefined {\n return useContext(ModelCommandToolPrefixContext);\n}\n","/**\n * CLI-2004 — the screen-reader label vocabulary (SSOT).\n *\n * A screen reader has no equivalent of `aria-live`: the only channel to it is which characters land\n * in the terminal buffer, in what order. A searchable prefix on every transcript line is therefore\n * the whole of the \"who is speaking\" affordance, and it has to be stable enough to grep for.\n *\n * PROVIDER-INVARIANT BY CONSTRUCTION. The label derives from the message ROLE, never from the\n * vendor behind it — `assistant:`, not the provider's product name — so the transcript reads\n * identically under every `agent-provider-*`. `screenReaderLabelValues()` exists so a test can\n * assert that negative mechanically instead of trusting review.\n *\n * All nine are lowercase. The reference this vocabulary is modelled on Title-Cases two of its nine;\n * that split is not reproduced, because a reader announces case and the inconsistency is noise.\n */\n\nimport type { TUniversalMessage } from '@robota-sdk/agent-core';\n\n/** The nine label kinds. */\nexport type TScreenReaderLabelKind =\n | 'user'\n | 'assistant'\n | 'thinking'\n | 'tool'\n | 'toolError'\n | 'error'\n | 'warning'\n | 'permissionRequired'\n | 'cost';\n\n/** The label vocabulary. Every spoken prefix in the mode comes from exactly this map. */\nexport const SCREEN_READER_LABELS: Readonly<Record<TScreenReaderLabelKind, string>> = {\n user: 'you:',\n assistant: 'assistant:',\n thinking: 'thinking:',\n tool: 'tool:',\n toolError: 'tool error:',\n error: 'error:',\n warning: 'warning:',\n permissionRequired: 'permission required:',\n cost: 'cost:',\n};\n\n/** Every label value, for mechanical assertions over the vocabulary. */\nexport function screenReaderLabelValues(): readonly string[] {\n return Object.values(SCREEN_READER_LABELS);\n}\n\n/**\n * Map a transcript message role onto the vocabulary. `system` speaks as `warning:` — it is the role\n * the TUI already renders in the warning colour, and the mode may not lean on colour.\n */\nexport function screenReaderLabelForRole(role: TUniversalMessage['role']): string {\n switch (role) {\n case 'user':\n return SCREEN_READER_LABELS.user;\n case 'assistant':\n return SCREEN_READER_LABELS.assistant;\n case 'tool':\n return SCREEN_READER_LABELS.tool;\n case 'system':\n return SCREEN_READER_LABELS.warning;\n }\n}\n","import { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { screenReaderLabelForRole } from './screen-reader-labels.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { TUniversalMessage } from '@robota-sdk/agent-core';\n\n/**\n * Who a transcript line is attributed to, and in what colour. Extracted from MessageList when\n * PEER-007 gave the `user` case a second thing to decide: the label stopped being a constant per\n * role, so it is its own responsibility rather than four inline returns.\n */\n\n/**\n * PEER-007 (issue #1915): the operator's own turns stay `You:`; a turn driven by anyone else is\n * labelled with WHO drove it, so a co-driven transcript can be read after the fact. `owner` is the\n * operator, so it reads as `You:` too; anything else prints as itself (`peer:<session-id>`, `agent`).\n */\nfunction driverLabel(driverId: string): string {\n return driverId === OWNER_DRIVER_ID ? 'You' : driverId;\n}\n\nexport function RoleLabel({\n role,\n driverId,\n}: {\n role: TUniversalMessage['role'];\n driverId?: string;\n}): React.ReactElement {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const productDisplayName = useProductDisplayName();\n if (screenReader) {\n // CLI-2004: the label comes from the ROLE, never the vendor — `assistant:` under every\n // agent-provider-*. No colour: in this mode the searchable word is the whole cue.\n const label =\n role === 'user' && driverId !== undefined && driverId !== OWNER_DRIVER_ID\n ? `${driverId}:`\n : screenReaderLabelForRole(role);\n return <Text>{label} </Text>;\n }\n switch (role) {\n case 'user':\n return (\n <Text color={palette.text.success} bold>\n {driverId !== undefined ? driverLabel(driverId) : 'You'}:{' '}\n </Text>\n );\n case 'assistant':\n return (\n <Text color={palette.text.accent} bold>\n {productDisplayName}:{' '}\n </Text>\n );\n case 'system':\n return (\n <Text color={palette.text.warning} bold>\n System:{' '}\n </Text>\n );\n case 'tool':\n return (\n <Text color={palette.text.emphasis} bold>\n Tool:{' '}\n </Text>\n );\n }\n}\n","/**\n * Persisted tool-summary → status-SSOT mapping (SCREEN-006).\n *\n * MessageList used to hand-roll its own glyph/color mapping for tool summaries, which drifted\n * from `status-glyph.ts` (denied rendered plain yellow vs the SSOT's yellowBright). These\n * helpers route the persisted summary items through `toolStateStatusKind` + `STATUS_SYMBOL`\n * so tool status renders one way everywhere.\n */\n\nimport { humanizeToolArgument, humanizeToolName } from './humanize-tool-name.js';\nimport { STATUS_SYMBOL, toolStateStatusKind } from './status-glyph.js';\n\nimport type { TUiStatusKind } from './status-glyph.js';\nimport type { IToolCallSummary } from './utils/tool-call-extractor.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\n/** A tool item as persisted in a `tool-summary` history entry (loosely-typed session data). */\nexport type TToolSummaryItem = {\n toolName: string;\n firstArg?: string;\n isRunning?: boolean;\n result?: string;\n diffLines?: IToolCallSummary['diffLines'];\n diffFile?: string;\n toolResultData?: string;\n};\n\n/**\n * Map a persisted tool-summary item to the semantic status kind — via the `status-glyph.ts`\n * SSOT. A command-output error (non-zero exit surfaced by `formatCommandOutputSummary`)\n * overrides the recorded result; the caller passes that flag so this module stays free of\n * the command-output dependency.\n */\nexport function toolSummaryStatusKind(\n tool: TToolSummaryItem,\n hasCommandOutputError: boolean,\n): TUiStatusKind {\n if (hasCommandOutputError) return 'error';\n const result: IToolState['result'] =\n tool.result === 'success' || tool.result === 'error' || tool.result === 'denied'\n ? tool.result\n : undefined;\n return toolStateStatusKind({\n toolName: tool.toolName,\n firstArg: tool.firstArg ?? '',\n isRunning: tool.isRunning ?? false,\n result,\n });\n}\n\n/** One-line summary label: SSOT glyph + humanized tool name + first argument. */\nexport function getToolSummaryLabel(\n tool: TToolSummaryItem,\n kind: TUiStatusKind,\n prefix?: string,\n): string {\n const argument = humanizeToolArgument(tool.firstArg);\n return `${STATUS_SYMBOL[kind]} ${humanizeToolName(tool.toolName, prefix)}${argument ? `(${argument})` : ''}`;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { formatCommandOutputSummary } from './command-output-summary.js';\nimport { Text } from './SafeText.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ICommandOutputInput } from './command-output-summary.js';\n\ninterface IProps {\n tool: ICommandOutputInput;\n}\n\nexport default function ToolCommandOutput({ tool }: IProps): React.ReactElement | null {\n const palette = usePalette();\n const summary = formatCommandOutputSummary(tool);\n if (!summary) return null;\n\n // A successful command with no output renders nothing here: the parent\n // (MessageList ToolSummaryEntry) already shows a \"✓ tool(args)\" summary line for\n // every tool, so a second \"✓ ok\" line was a duplicate success marker (SCREEN-007).\n if (\n summary.statusLabel === 'ok' &&\n summary.previewLines.length === 0 &&\n !summary.transcriptHint\n ) {\n return null;\n }\n const color =\n summary.status === 'error' ? statusGlyphColor(palette, 'error') : palette.text.emphasis;\n\n return (\n <Box flexDirection=\"column\" marginLeft={4}>\n {summary.statusLabel !== 'ok' && (\n <Text color={color} dimColor>\n {summary.statusLabel}\n </Text>\n )}\n {summary.previewLines.map((line, index) => (\n <Text key={`${line}-${index}`} color={color} dimColor>\n {line}\n </Text>\n ))}\n {summary.transcriptHint && <Text dimColor>{summary.transcriptHint}</Text>}\n </Box>\n );\n}\n","import type { IDiffLine } from './edit-diff.js';\n\nconst MAX_DIFF_LINES = 12;\nconst TRUNCATED_SHOW = 10;\n\nexport interface IToolDiffSummaryInput {\n file?: string;\n lines: readonly IDiffLine[];\n}\n\nexport interface IToolDiffSummary {\n file?: string;\n markdown: string;\n truncated: boolean;\n remainingLineCount: number;\n}\n\nexport function buildToolDiffSummary(input: IToolDiffSummaryInput): IToolDiffSummary {\n const visibleLines =\n input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;\n const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString()\n .length;\n const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));\n const truncated = input.lines.length > MAX_DIFF_LINES;\n\n return {\n file: input.file,\n markdown: ['```diff', ...body, '```'].join('\\n'),\n truncated,\n remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0,\n };\n}\n\nfunction formatDiffLine(line: IDiffLine, lineNumberWidth: number): string {\n if (line.type === 'hunk') return line.text;\n const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, ' ');\n if (line.type === 'remove') return `- ${lineNumber} | ${line.text}`;\n if (line.type === 'add') return `+ ${lineNumber} | ${line.text}`;\n return ` ${lineNumber} | ${line.text}`;\n}\n\nfunction selectVisibleDiffLines(lines: readonly IDiffLine[]): readonly IDiffLine[] {\n const groups = groupByHunk(lines);\n const visible: IDiffLine[] = [];\n\n for (const group of groups) {\n if (visible.length === 0 && group.length > TRUNCATED_SHOW) {\n return group.slice(0, TRUNCATED_SHOW);\n }\n if (visible.length + group.length > TRUNCATED_SHOW) break;\n visible.push(...group);\n }\n\n return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);\n}\n\nfunction groupByHunk(lines: readonly IDiffLine[]): IDiffLine[][] {\n const groups: IDiffLine[][] = [];\n let current: IDiffLine[] = [];\n\n for (const line of lines) {\n if (line.type === 'hunk' && current.length > 0) {\n groups.push(current);\n current = [line];\n continue;\n }\n current.push(line);\n }\n\n if (current.length > 0) {\n groups.push(current);\n }\n\n return groups;\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\nimport { buildToolDiffSummary } from './utils/tool-diff-summary.js';\n\nimport type { IDiffLine } from './utils/edit-diff.js';\n\ninterface IProps {\n file?: string;\n lines: readonly IDiffLine[];\n}\n\nexport default function ToolDiffBlock({ file, lines }: IProps): React.ReactElement {\n const palette = usePalette();\n const renderMarkdown = useRenderMarkdown();\n const summary = buildToolDiffSummary({ file, lines });\n // CLI-2004: the `\\u2502` gutter is a vertical rule a reader announces on every line it prefixes.\n // The mode drops it — indentation already separates the block from the surrounding transcript.\n const screenReader = useScreenReader();\n const gutter = screenReader ? '' : '\\u2502 ';\n\n return (\n <Box flexDirection=\"column\" marginLeft={4}>\n {summary.file && (\n <Text color={palette.text.emphasis} dimColor>\n {gutter}\n {sanitizeTerminalText(summary.file)}\n </Text>\n )}\n {/* `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours. */}\n <RenderedText>{renderMarkdown(summary.markdown)}</RenderedText>\n {summary.truncated && (\n <Text color={palette.text.emphasis} dimColor>\n {gutter}... and {summary.remainingLineCount} more lines\n </Text>\n )}\n </Box>\n );\n}\n","import { formatTokenCount } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IUsageSnapshot } from '@robota-sdk/agent-interface-analytics';\n\nconst TOKEN_COMPACT_THRESHOLD = 1000;\n\nexport default function UsageSummaryEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const usage = entry.data as IUsageSnapshot | undefined;\n if (!usage) return <></>;\n const prompt = usage.promptTokens !== undefined ? formatUsageTokenCount(usage.promptTokens) : '?';\n const completion =\n usage.completionTokens !== undefined ? formatUsageTokenCount(usage.completionTokens) : '?';\n const total = formatUsageTokenCount(usage.totalTokens);\n // Usage another unit spent (the advisor, a background task) is named, and has no context window\n // of the main thread to report.\n const source =\n usage.source !== undefined && usage.source.scope !== 'main' ? usage.source : undefined;\n const sourceLabel = source ? (source.label ?? source.id ?? source.scope) : undefined;\n const context = source\n ? ''\n : ` · Context ${Math.round(usage.contextUsedPercentage)}% (${formatTokenCount(\n usage.contextUsedTokens,\n )}/${formatTokenCount(usage.contextMaxTokens)})`;\n const costLabel = usage.costStatus === 'unknown' ? 'cost unknown' : `cost ${usage.costStatus}`;\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={palette.text.emphasis} bold>\n {sourceLabel ? `Usage (${sourceLabel}):` : 'Usage:'}{' '}\n </Text>\n <Text dimColor>\n {usage.kind} {total} tokens (in {prompt} / out {completion}){context} · {costLabel}\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction formatUsageTokenCount(tokens: number): string {\n return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : formatTokenCount(tokens);\n}\n","import { isToolMessage, isAssistantMessage } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { formatCommandOutputSummary } from './command-output-summary.js';\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { humanizeToolName } from './humanize-tool-name.js';\nimport { useModelCommandToolPrefix } from './product-display-name-context.js';\nimport { RoleLabel } from './RoleLabel.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS, type TScreenReaderLabelKind } from './screen-reader-labels.js';\nimport { statusGlyphColor } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\nimport { getToolSummaryLabel, toolSummaryStatusKind } from './tool-summary-status.js';\nimport ToolCommandOutput from './ToolCommandOutput.js';\nimport ToolDiffBlock from './ToolDiffBlock.js';\nimport UsageSummaryEntry from './UsageSummaryEntry.js';\n\nimport type { TToolSummaryItem } from './tool-summary-status.js';\nimport type { IToolCallSummary } from './utils/tool-call-extractor.js';\nimport type { IHistoryEntry, TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';\n\ninterface IProps {\n history: IHistoryEntry[];\n}\n\n/**\n * CLI-2004: one transcript label. In screen-reader mode it speaks the shared vocabulary and drops\n * the colour — the searchable word IS the cue there — and outside it renders exactly as before.\n */\nfunction EntryLabel({\n kind,\n text,\n color,\n}: {\n kind: TScreenReaderLabelKind;\n text: string;\n color: string;\n}): React.ReactElement {\n if (useScreenReader()) return <Text>{SCREEN_READER_LABELS[kind]} </Text>;\n return (\n <Text color={color} bold>\n {text}\n </Text>\n );\n}\n\nfunction ToolMessage({ message }: { message: TUniversalMessage }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n if (!isToolMessage(message)) {\n return <></>;\n }\n const toolName = message.name;\n const content = message.content;\n\n // Try to parse structured tool summaries (with diff info)\n let summaries: IToolCallSummary[] | null = null;\n try {\n const parsed = JSON.parse(content) as IToolCallSummary[];\n if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === 'string') {\n summaries = parsed as IToolCallSummary[];\n }\n } catch {\n // Not JSON — fall back to plain text lines\n }\n\n if (summaries) {\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n {toolName && (\n <Text color={palette.text.emphasis} dimColor>\n [{humanizeToolName(toolName, modelCommandToolPrefix)}]\n </Text>\n )}\n </Box>\n <Text> </Text>\n {summaries.map((s, i) => (\n <Box key={i} flexDirection=\"column\">\n <Text color={palette.text.success}>\n {' '}\n {'✓'} {sanitizeTerminalText(s.line)}\n </Text>\n {s.diffLines && s.diffLines.length > 0 && (\n <ToolDiffBlock file={s.diffFile} lines={s.diffLines} />\n )}\n </Box>\n ))}\n </Box>\n );\n }\n\n // Fallback: plain text lines\n const lines = content.split('\\n').filter((l) => l.trim());\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n {toolName && (\n <Text color={palette.text.emphasis} dimColor>\n [{sanitizeTerminalText(toolName)}]\n </Text>\n )}\n </Box>\n <Text> </Text>\n {lines.map((line, i) => (\n <Text key={i} color={palette.text.success}>\n {' '}\n {'✓'} {sanitizeTerminalText(line)}\n </Text>\n ))}\n </Box>\n );\n}\n\n/** ERR-001 G2: a failed turn renders as a styled error block, not a plain system note. */\nfunction ErrorEntryBlock({ message }: { message: TUniversalMessage }): React.ReactElement {\n const palette = usePalette();\n // SEC-019: an error message is untrusted text — it carries provider responses, tool stderr and\n // file contents — and it reaches `<Text>` without going through the markdown renderer, so it does\n // not inherit that path's sanitization.\n const content = sanitizeTerminalText((message.content ?? '').replace(/^Error:\\s*/, ''));\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"error\" text=\"✖ Error: \" color={palette.text.error} />\n </Box>\n <Box marginLeft={2} flexDirection=\"column\">\n <Text color={palette.text.error} wrap=\"wrap\">\n {content}\n </Text>\n <Text dimColor wrap=\"wrap\">\n The session is still alive — type your next prompt when ready.\n </Text>\n </Box>\n </Box>\n );\n}\n\nconst MessageItem = React.memo(function MessageItem({\n message,\n}: {\n message: TUniversalMessage;\n}): React.ReactElement {\n // Every hook this component uses runs BEFORE the early returns: a message whose role or kind\n // changes in place would otherwise render a different NUMBER of hooks and React would throw.\n const renderMarkdown = useRenderMarkdown();\n\n if (isToolMessage(message)) {\n return <ToolMessage message={message} />;\n }\n\n if (message.role === 'system' && message.metadata?.kind === 'error') {\n return <ErrorEntryBlock message={message} />;\n }\n\n const content = message.content ?? '';\n const isInterrupted = message.state === 'interrupted';\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <RoleLabel\n role={message.role}\n {...(typeof message.metadata?.driverId === 'string'\n ? { driverId: message.metadata.driverId }\n : {})}\n />\n </Box>\n <Text> </Text>\n <Box marginLeft={2}>\n {isAssistantMessage(message) ? (\n // `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours.\n <RenderedText wrap=\"wrap\">\n {renderMarkdown(content + (isInterrupted ? '\\n\\n_(interrupted)_' : ''))}\n </RenderedText>\n ) : (\n <Text wrap=\"wrap\">{content}</Text>\n )}\n </Box>\n </Box>\n );\n});\n\nfunction ToolSummaryEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n const data = entry.data as\n | {\n summary?: string;\n tools?: TToolSummaryItem[];\n }\n | undefined;\n const tools = data?.tools;\n const lines = data?.summary?.split('\\n') ?? [];\n\n if (tools && tools.length > 0) {\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n </Box>\n <Text> </Text>\n {tools.map((tool, i) => {\n const kind = toolSummaryStatusKind(\n tool,\n formatCommandOutputSummary(tool)?.status === 'error',\n );\n return (\n <Box key={i} flexDirection=\"column\">\n <Text color={statusGlyphColor(palette, kind)}>\n {' '}\n {getToolSummaryLabel(tool, kind, modelCommandToolPrefix)}\n </Text>\n <ToolCommandOutput tool={tool} />\n {tool.diffLines && tool.diffLines.length > 0 && (\n <ToolDiffBlock file={tool.diffFile} lines={tool.diffLines} />\n )}\n </Box>\n );\n })}\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"tool\" text=\"Tool: \" color={palette.text.emphasis} />\n </Box>\n <Text> </Text>\n {lines.map((line, i) => (\n <Text key={i} color={palette.text.success}>\n {' '}\n {sanitizeTerminalText(line)}\n </Text>\n ))}\n </Box>\n );\n}\n\nfunction EventEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n const palette = usePalette();\n const eventData = entry.data as Record<string, TUniversalValue> | undefined;\n // SEC-019: an event message is BUILT by a neutral package from untrusted parts — a skill name that\n // may come from a plugin, a memory topic the model chose — and interpolated into a sentence. The\n // formatter cannot sanitize for a terminal (the same string reaches the GUI, where escapes are not\n // the threat and stripping them would be arbitrary), so this render site is the boundary.\n const text =\n typeof eventData?.message === 'string'\n ? eventData.message\n : typeof eventData?.content === 'string'\n ? eventData.content\n : undefined;\n // An event without a message is a record kept for persistence or telemetry (a provider-call trace,\n // a usage observation), not a notice; printing its type name only adds noise to the transcript.\n if (text === undefined) return <></>;\n const eventMessage = sanitizeTerminalText(text);\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <EntryLabel kind=\"warning\" text=\"System: \" color={palette.text.warning} />\n </Box>\n <Text> </Text>\n <Box marginLeft={2}>\n <Text wrap=\"wrap\">{eventMessage}</Text>\n </Box>\n </Box>\n );\n}\n\nexport function EntryItem({ entry }: { entry: IHistoryEntry }): React.ReactElement {\n if (entry.category === 'chat') {\n const message = entry.data as TUniversalMessage;\n return <MessageItem message={message} />;\n }\n\n if (entry.type === 'tool-summary') {\n return <ToolSummaryEntry entry={entry} />;\n }\n\n if (entry.type === 'usage-summary') {\n return <UsageSummaryEntry entry={entry} />;\n }\n\n // tool-start/tool-end are recorded in history for persistence but not rendered\n // (StreamingIndicator shows them during streaming, tool-summary shows them after)\n if (entry.type === 'tool-start' || entry.type === 'tool-end') {\n return <></>;\n }\n\n return <EventEntry entry={entry} />;\n}\n\nexport default function MessageList({ history }: IProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n {history.map((entry) => (\n <EntryItem key={entry.id} entry={entry} />\n ))}\n </Box>\n );\n}\n","/**\n * Generic list picker with arrow-key navigation and viewport scrolling.\n * Renders items via a user-supplied renderItem function.\n * Shows a limited number of items at a time; scrolls as the cursor moves.\n */\n\nimport { Box } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { KeyHintFooter, type IKeyHint } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\n\n/** Default number of visible items */\nconst DEFAULT_MAX_VISIBLE = 3;\n\nexport interface IListPickerProps<T> {\n /** Items to display in the list */\n items: T[];\n /** Render function for each item — receives the item and whether it is currently selected */\n renderItem: (item: T, isSelected: boolean) => React.ReactElement;\n /** Called when the user presses Enter on the highlighted item */\n onSelect: (item: T) => void;\n /** Called when the user presses Escape */\n onCancel: () => void;\n /** Maximum number of items visible at once (default: 10) */\n maxVisible?: number;\n /** Key-hint footer shown below the list (default: navigate/select/cancel; `[]` suppresses it) */\n footerHints?: readonly IKeyHint[];\n /** The item highlighted first (default: the first). Out-of-range values fall back to the first. */\n initialIndex?: number;\n}\n\n/** Default affordance footer — Enter always selects and Esc always cancels. */\nexport const LIST_PICKER_DEFAULT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Cancel' },\n];\n\nexport default function ListPicker<T>({\n items,\n renderItem,\n onSelect,\n onCancel,\n maxVisible = DEFAULT_MAX_VISIBLE,\n footerHints = LIST_PICKER_DEFAULT_FOOTER_HINTS,\n initialIndex = 0,\n}: IListPickerProps<T>): React.ReactElement {\n const [state, setState] = useState<ISelectionFlowState>(() => {\n const index = initialIndex >= 0 && initialIndex < items.length ? initialIndex : 0;\n const seeded = createSelectionFlowState(index);\n // Keep the seeded row on screen when it sits past the first window.\n return { ...seeded, scrollOffset: Math.max(0, index - maxVisible + 1) };\n });\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TSelectionInputAction): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: items.length,\n maxVisible,\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onCancel();\n } else if (result.effect.type === 'select') {\n const item = items[result.effect.index];\n if (item !== undefined) {\n onSelect(item);\n }\n }\n },\n [items, maxVisible, onCancel, onSelect],\n );\n\n // CLI-2004: the viewport is a sighted affordance — `↑ N more above` tells a reader nothing about\n // where it is. The mode lists every item with its number and asks for one.\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && items.length > 0,\n itemCount: items.length,\n cancellable: true,\n onSelect: (index) => {\n const item = items[index];\n if (item !== undefined) onSelect(item);\n },\n onCancel,\n });\n\n useKeybindingActions('list-picker', (actions) => actions.forEach(applyAction), {\n isActive: !screenReader,\n });\n const effectiveFooterHints = useKeybindingHints('list-picker', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Select'],\n ['cancel', 'Cancel'],\n ]);\n\n if (items.length === 0) {\n return <Box />;\n }\n\n if (screenReader) {\n const prompt = formatNumberedSelectionPrompt(items.length, true);\n return (\n <Box flexDirection=\"column\">\n {items.map((item, index) => (\n <Box key={index}>\n <Text>{numberedRowPrefix(index)}</Text>\n {renderItem(item, false)}\n </Box>\n ))}\n <Text>\n {prompt}\n {numbered.buffer.length > 0 ? ` ${numbered.buffer}` : ''}\n </Text>\n {numbered.invalid && <Text>{prompt}</Text>}\n </Box>\n );\n }\n\n const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });\n if (normalizedState !== state) {\n stateRef.current = normalizedState;\n }\n const { selectedIndex, scrollOffset } = normalizedState;\n const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = scrollOffset + maxVisible < items.length;\n const hasLess = scrollOffset > 0;\n\n return (\n <Box flexDirection=\"column\">\n {hasLess && <Text dimColor> ↑ {scrollOffset} more above</Text>}\n {visibleItems.map((item, index) => (\n <Box key={scrollOffset + index} marginBottom={1}>\n {renderItem(item, scrollOffset + index === selectedIndex)}\n </Box>\n ))}\n {hasMore && <Text dimColor> ↓ {items.length - scrollOffset - maxVisible} more below</Text>}\n <KeyHintFooter\n hints={\n footerHints === LIST_PICKER_DEFAULT_FOOTER_HINTS ? effectiveFooterHints : footerHints\n }\n />\n </Box>\n );\n}\n","/**\n * Multi-select checklist (CMD-004): arrow-key navigation, Space toggles, Enter confirms once the\n * selection count is within [minSelect, maxSelect], Esc cancels. Used by PendingActionPrompt for an\n * IActionRequest whose `maxSelect` > 1.\n *\n * Note: unlike ListPicker, this renders all options without a scroll viewport — `IActionRequest`'s\n * optional `maxVisible` hint is not yet honored here (multi-select lists are typically short). Adding a\n * viewport is a follow-up if long multi-select lists appear.\n */\n\nimport { Box } from 'ink';\nimport React, { useState } from 'react';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IActionOption } from '@robota-sdk/agent-core';\n\n/**\n * Footer hints for the multi-select checklist. The Enter hint carries a dynamic `(min N)` segment\n * until the selection is confirmable.\n */\nexport function getMultiSelectFooterHints(input: {\n canConfirm: boolean;\n minSelect: number;\n}): readonly IKeyHint[] {\n return [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Space', label: 'Toggle' },\n { keys: 'Enter', label: input.canConfirm ? 'Confirm' : `Confirm (min ${input.minSelect})` },\n { keys: 'Esc', label: 'Cancel' },\n ];\n}\n\n/**\n * One checklist row. The mode swaps the position cue — a spoken number for the `> ` cursor and its\n * colour — and nothing else: the checkbox is what says selected, in both.\n */\nfunction ChecklistRow({\n label,\n index,\n isCursor,\n isChecked,\n screenReader,\n}: {\n label: string;\n index: number;\n isCursor: boolean;\n isChecked: boolean;\n screenReader: boolean;\n}): React.ReactElement {\n const palette = usePalette();\n const box = isChecked ? '[x] ' : '[ ] ';\n if (screenReader) {\n return (\n <Text>\n {numberedRowPrefix(index)}\n {box}\n {label}\n </Text>\n );\n }\n return (\n <Text color={isCursor ? palette.text.accent : undefined}>\n {isCursor ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {box}\n {label}\n </Text>\n );\n}\n\nexport interface IMultiSelectListProps {\n title: string;\n description?: string;\n options: readonly IActionOption[];\n minSelect: number;\n maxSelect: number;\n defaultValues?: readonly string[];\n onConfirm: (values: string[]) => void;\n onCancel: () => void;\n}\n\n/** The arrow-key driver: cursor motion, Space toggles, Enter confirms. Inactive in the mode. */\nfunction useChecklistArrowKeys(inputs: {\n active: boolean;\n itemCount: number;\n setCursor: React.Dispatch<React.SetStateAction<number>>;\n onToggleAtCursor: () => void;\n onConfirm: () => void;\n onCancel: () => void;\n}): void {\n const { active, itemCount, setCursor } = inputs;\n useKeybindingActions(\n 'multi-select',\n (actions) => {\n for (const action of actions) {\n if (action === 'previous') {\n setCursor((c) => (c <= 0 ? itemCount - 1 : c - 1));\n } else if (action === 'next') {\n setCursor((c) => (c >= itemCount - 1 ? 0 : c + 1));\n } else if (action === 'toggle') {\n inputs.onToggleAtCursor();\n } else if (action === 'confirm') {\n inputs.onConfirm();\n } else if (action === 'cancel') {\n inputs.onCancel();\n }\n }\n },\n { isActive: active },\n );\n}\n\n/** How the checklist asks for an answer: a spoken range in the mode, the key hints otherwise. */\nfunction ChecklistAnswer(props: {\n screenReader: boolean;\n itemCount: number;\n numbered: { buffer: string; invalid: boolean };\n canConfirm: boolean;\n minSelect: number;\n}): React.ReactElement {\n const hints = useKeybindingHints('multi-select', [\n [['previous', 'next'], 'Navigate'],\n ['toggle', 'Toggle'],\n ['confirm', props.canConfirm ? 'Confirm' : `Confirm (min ${props.minSelect})`],\n ['cancel', 'Cancel'],\n ]);\n if (props.screenReader) {\n return (\n <NumberedSelectionPrompt\n itemCount={props.itemCount}\n cancellable\n buffer={props.numbered.buffer}\n invalid={props.numbered.invalid}\n />\n );\n }\n return <KeyHintFooter hints={hints} />;\n}\n\n/** The rendered checklist: the rows, and whichever answer affordance the mode calls for. */\nfunction ChecklistView(props: {\n title: string;\n description?: string;\n options: readonly IActionOption[];\n cursor: number;\n selected: ReadonlySet<string>;\n screenReader: boolean;\n canConfirm: boolean;\n minSelect: number;\n numbered: { buffer: string; invalid: boolean };\n}): React.ReactElement {\n const palette = usePalette();\n const { title, description, options, cursor, selected, screenReader, numbered } = props;\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.attention })}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n {description !== undefined && description.length > 0 && <Text dimColor>{description}</Text>}\n {options.map((option, index) => (\n <ChecklistRow\n key={option.value}\n label={option.label}\n index={index}\n isCursor={index === cursor}\n isChecked={selected.has(option.value)}\n screenReader={screenReader}\n />\n ))}\n <ChecklistAnswer\n screenReader={screenReader}\n itemCount={options.length}\n numbered={numbered}\n canConfirm={props.canConfirm}\n minSelect={props.minSelect}\n />\n </Box>\n );\n}\n\n/** The checklist's own state: which row the cursor is on, what is ticked, and how it commits. */\nfunction useChecklistSelection(inputs: {\n options: readonly IActionOption[];\n minSelect: number;\n maxSelect: number;\n defaultValues?: readonly string[];\n onConfirm: (values: string[]) => void;\n}): {\n cursor: number;\n setCursor: React.Dispatch<React.SetStateAction<number>>;\n selected: ReadonlySet<string>;\n toggleAt: (index: number) => void;\n confirmIfAllowed: () => void;\n} {\n const { options, minSelect, maxSelect, defaultValues, onConfirm } = inputs;\n const [cursor, setCursor] = useState(0);\n const [selected, setSelected] = useState<ReadonlySet<string>>(() => new Set(defaultValues ?? []));\n\n const toggleAt = (index: number): void => {\n const option = options[index];\n if (option === undefined) return;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(option.value)) {\n next.delete(option.value);\n } else if (next.size < maxSelect) {\n next.add(option.value);\n }\n return next;\n });\n };\n const confirmIfAllowed = (): void => {\n if (selected.size >= minSelect) onConfirm([...selected]);\n };\n\n return { cursor, setCursor, selected, toggleAt, confirmIfAllowed };\n}\n\nexport default function MultiSelectList({\n title,\n description,\n options,\n minSelect,\n maxSelect,\n defaultValues,\n onConfirm,\n onCancel,\n}: IMultiSelectListProps): React.ReactElement {\n // CLI-2004: the checklist keeps its own reducer (Space toggles, Enter confirms) — what the mode\n // changes is the ROW: a spoken number replaces the `> ` cursor, and typing that number toggles it.\n const screenReader = useScreenReader();\n const { cursor, setCursor, selected, toggleAt, confirmIfAllowed } = useChecklistSelection({\n options,\n minSelect,\n maxSelect,\n defaultValues,\n onConfirm,\n });\n\n // CLI-2004: the checklist answers by number in the mode, through the SAME reducer every other\n // numbered menu uses — `multi` is what keeps it open after a toggle and makes an empty Enter the\n // commit. A local digit branch would have capped it at nine rows and skipped the shared prompt.\n const numbered = useNumberedSelection({\n enabled: screenReader,\n itemCount: options.length,\n cancellable: true,\n multi: true,\n onSelect: toggleAt,\n onConfirm: confirmIfAllowed,\n onCancel,\n });\n\n useChecklistArrowKeys({\n active: !screenReader,\n itemCount: options.length,\n setCursor,\n onToggleAtCursor: () => toggleAt(cursor),\n onConfirm: confirmIfAllowed,\n onCancel,\n });\n\n const canConfirm = selected.size >= minSelect;\n\n return (\n <ChecklistView\n title={title}\n {...(description !== undefined ? { description } : {})}\n options={options}\n cursor={cursor}\n selected={selected}\n screenReader={screenReader}\n canConfirm={canConfirm}\n minSelect={minSelect}\n numbered={numbered}\n />\n );\n}\n","export interface ITextPromptFlowState {\n value: string;\n error?: string;\n resolved: boolean;\n}\n\nexport interface ITextPromptInputKey {\n escape?: boolean;\n return?: boolean;\n backspace?: boolean;\n delete?: boolean;\n ctrl?: boolean;\n meta?: boolean;\n}\n\nexport type TTextPromptInputAction =\n { type: 'cancel' } | { type: 'submit' } | { type: 'delete' } | { type: 'insert'; value: string };\n\nexport type TTextPromptEffect =\n { type: 'none' } | { type: 'cancel' } | { type: 'submit'; value: string };\n\nexport interface ITextPromptFlowOptions {\n allowEmpty: boolean;\n validate?: (value: string) => string | undefined;\n}\n\nexport function createTextPromptFlowState(): ITextPromptFlowState {\n return { value: '', resolved: false };\n}\n\nexport function getTextPromptInputAction(\n input: string,\n key: ITextPromptInputKey,\n): TTextPromptInputAction | undefined {\n if (key.escape === true) {\n return { type: 'cancel' };\n }\n if (key.return === true) {\n return { type: 'submit' };\n }\n if (key.backspace === true || key.delete === true) {\n return { type: 'delete' };\n }\n if (input && key.ctrl !== true && key.meta !== true) {\n return { type: 'insert', value: input };\n }\n return undefined;\n}\n\nexport function applyTextPromptInput(\n state: ITextPromptFlowState,\n action: TTextPromptInputAction,\n options: ITextPromptFlowOptions,\n): { state: ITextPromptFlowState; effect: TTextPromptEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (action.type === 'cancel') {\n return { state: { ...state, resolved: true }, effect: { type: 'cancel' } };\n }\n if (action.type === 'delete') {\n return {\n state: { ...state, value: state.value.slice(0, -1), error: undefined },\n effect: { type: 'none' },\n };\n }\n if (action.type === 'insert') {\n return {\n state: { ...state, value: state.value + action.value, error: undefined },\n effect: { type: 'none' },\n };\n }\n return submitTextPromptValue(state, options);\n}\n\nfunction submitTextPromptValue(\n state: ITextPromptFlowState,\n options: ITextPromptFlowOptions,\n): { state: ITextPromptFlowState; effect: TTextPromptEffect } {\n const trimmed = state.value.trim();\n if (!trimmed && !options.allowEmpty) {\n const emptyError = options.validate?.(trimmed);\n return {\n state: emptyError ? { ...state, error: emptyError } : state,\n effect: { type: 'none' },\n };\n }\n const error = options.validate?.(trimmed);\n if (error !== undefined) {\n return { state: { ...state, error }, effect: { type: 'none' } };\n }\n return { state: { ...state, resolved: true }, effect: { type: 'submit', value: trimmed } };\n}\n","import { Box } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applyTextPromptInput,\n createTextPromptFlowState,\n type ITextPromptFlowState,\n type TTextPromptInputAction,\n} from './flows/text-prompt-flow.js';\nimport { KeyHintFooter, type IKeyHint } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** Footer for the free-text prompt. */\nexport const TEXT_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: 'Enter', label: 'Submit' },\n { keys: 'Esc', label: 'Cancel' },\n];\n\ninterface IProps {\n title: string;\n description?: string;\n placeholder?: string;\n onSubmit: (value: string) => void;\n onCancel: () => void;\n validate?: (value: string) => string | undefined;\n allowEmpty?: boolean;\n masked?: boolean;\n}\n\nexport default function TextPrompt({\n title,\n description,\n placeholder,\n onSubmit,\n onCancel,\n validate,\n allowEmpty = false,\n masked = false,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ITextPromptFlowState>(() => createTextPromptFlowState());\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TTextPromptInputAction): void => {\n const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onCancel();\n } else if (result.effect.type === 'submit') {\n onSubmit(result.effect.value);\n }\n },\n [allowEmpty, validate, onCancel, onSubmit],\n );\n\n const screenReader = useScreenReader();\n\n useKeybindingActions('text-prompt', (actions, input, key, consumed) => {\n for (const action of actions) {\n if (action === 'submit') applyAction({ type: 'submit' });\n else if (action === 'cancel') applyAction({ type: 'cancel' });\n else if (action === 'delete-backward') applyAction({ type: 'delete' });\n }\n if (actions.length === 0 && !consumed && input && key.ctrl !== true && key.meta !== true) {\n applyAction({ type: 'insert', value: input });\n }\n });\n const footerHints = useKeybindingHints('text-prompt', [\n ['submit', 'Submit'],\n ['cancel', 'Cancel'],\n ]);\n\n // CLI-2004: the box is dropped rather than restyled — the mode omits the prop, so no colour\n // literal is introduced and the palette floor stays green.\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.attention })}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n <PromptDescription description={description} />\n <Box marginTop={1}>\n <Text color={palette.text.accent}>&gt; </Text>\n {state.value ? (\n <Text>{masked ? '*'.repeat(state.value.length) : state.value}</Text>\n ) : placeholder ? (\n <Text dimColor>{placeholder}</Text>\n ) : null}\n <Text color={palette.text.accent}>█</Text>\n </Box>\n {state.error && <Text color={palette.text.error}>{state.error}</Text>}\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n\nfunction PromptDescription({ description }: { description?: string }): React.ReactElement | null {\n if (description === undefined || description.length === 0) {\n return null;\n }\n\n return <Text dimColor>{description}</Text>;\n}\n","/**\n * PendingActionPrompt (CMD-004) — the TUI renderer for the unified `IActionRequest`. It derives the\n * presentation from the request's fields (not a kind switch):\n * - no options → free-text (TextPrompt, honoring `masked`/`placeholder`/`allowEmpty`)\n * - options, `maxSelect` > 1 → multi-select checklist (MultiSelectList)\n * - options, `maxSelect` <= 1 → single-select list (ListPicker); with `allowFreeText`, a synthetic\n * \"Type a custom answer…\" entry switches to a text field\n *\n * It always resolves the caller's `onAnswer` with a `TActionResponse` (answer or cancelled).\n */\n\nimport { Box } from 'ink';\nimport React, { useState } from 'react';\n\nimport { SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport ListPicker from './ListPicker.js';\nimport MultiSelectList from './MultiSelectList.js';\nimport { Text } from './SafeText.js';\nimport TextPrompt from './TextPrompt.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IActionOption, IActionRequest, TActionResponse } from '@robota-sdk/agent-core';\n\ninterface IPendingActionPromptProps {\n request: IActionRequest;\n onAnswer: (response: TActionResponse) => void;\n}\n\n/**\n * Synthetic \"type a custom answer\" entry for a single-select with free text. Routed by object identity\n * (reference equality), not by a magic value — so it can never collide with a real option's `value`.\n */\nconst FREE_TEXT_OPTION: IActionOption = { value: '', label: 'Type a custom answer…' };\n\nexport default function PendingActionPrompt({\n request,\n onAnswer,\n}: IPendingActionPromptProps): React.ReactElement {\n const palette = usePalette();\n const [freeTextMode, setFreeTextMode] = useState(false);\n\n const options = request.options ?? [];\n const hasOptions = options.length > 0;\n const maxSelect = request.maxSelect ?? 1;\n const cancel = (): void => onAnswer({ type: 'cancelled' });\n\n // Pure free-text (no options) — or the user chose \"type a custom answer\" on a single-select.\n if (!hasOptions || freeTextMode) {\n return (\n <TextPrompt\n key={`text:${request.id}`}\n title={request.title}\n description={request.description}\n placeholder={request.placeholder}\n allowEmpty={request.allowEmpty}\n masked={request.masked}\n onSubmit={(value) => onAnswer({ type: 'answer', values: [], text: value })}\n onCancel={freeTextMode ? () => setFreeTextMode(false) : cancel}\n />\n );\n }\n\n // Multi-select.\n if (maxSelect > 1) {\n return (\n <MultiSelectList\n title={request.title}\n description={request.description}\n options={options}\n minSelect={request.minSelect ?? 1}\n maxSelect={maxSelect}\n defaultValues={request.default?.values}\n onConfirm={(values) => onAnswer({ type: 'answer', values })}\n onCancel={cancel}\n />\n );\n }\n\n // Single-select, optionally with a \"type your own\" entry (routed by identity, see FREE_TEXT_OPTION).\n const pickerItems: IActionOption[] = request.allowFreeText\n ? [...options, FREE_TEXT_OPTION]\n : [...options];\n // BEHAVIOR-2437: a request's declared default is where the highlight STARTS — for a write\n // confirmation built with `defaultYes: false`, a bare Enter must answer No, not Yes.\n const defaultValue = request.default?.values?.[0];\n const initialIndex =\n defaultValue === undefined\n ? 0\n : Math.max(\n 0,\n pickerItems.findIndex((option) => option.value === defaultValue),\n );\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>{request.title}</Text>\n {request.description !== undefined && request.description.length > 0 && (\n <Text dimColor>{request.description}</Text>\n )}\n <ListPicker<IActionOption>\n items={pickerItems}\n initialIndex={initialIndex}\n maxVisible={request.maxVisible}\n renderItem={(option, isSelected) => (\n <Text color={isSelected ? palette.text.accent : undefined}>\n {isSelected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {option.label}\n </Text>\n )}\n onSelect={(option) => {\n if (option === FREE_TEXT_OPTION) {\n setFreeTextMode(true);\n } else {\n onAnswer({ type: 'answer', values: [option.value] });\n }\n }}\n onCancel={cancel}\n />\n </Box>\n );\n}\n","import {\n applySelectionInput,\n getDirectionalSelectionInputAction,\n type ISelectionFlowState,\n type ISelectionInputKey,\n type TSelectionInputAction,\n} from './selection-flow.js';\n\nexport const PERMISSION_PROMPT_OPTIONS = [\n 'Allow [y]',\n 'Allow always (this session) [s]',\n 'Allow always (this project) [p]',\n 'Deny [n]',\n] as const;\n\n/**\n * Issue #2351: the \"always\" options name the SCOPE they grant — the consent pattern the enforcer\n * remembers (`Bash(git *)`, `Read(/w/src/**)`, `WebFetch(https://h/**)`), not the whole tool.\n */\nexport function permissionPromptOptionsFor(\n scope: string,\n canPersistProjectPermission = true,\n): readonly string[] {\n return [\n PERMISSION_PROMPT_OPTIONS[0],\n `Allow ${scope} always (this session) [s]`,\n canPersistProjectPermission\n ? `Allow ${scope} always (this project) [p]`\n : 'Project-wide approval unavailable',\n PERMISSION_PROMPT_OPTIONS[3],\n ];\n}\n\nexport type TPermissionPromptDecision = true | 'allow-session' | 'allow-project' | false;\nexport type TPermissionPromptInputAction =\n TSelectionInputAction | { type: 'shortcut'; index: number };\n\nexport type TPermissionPromptEffect =\n { type: 'none' } | { type: 'resolve'; decision: TPermissionPromptDecision };\n\nexport function getPermissionPromptInputAction(\n input: string,\n key: ISelectionInputKey,\n): TPermissionPromptInputAction | undefined {\n const action = getDirectionalSelectionInputAction({ ...key, escape: false });\n if (action !== undefined) {\n return action;\n }\n if (input === 'y' || input === '1') {\n return { type: 'shortcut', index: 0 };\n }\n if (input === 's' || input === 'a' || input === '2') {\n return { type: 'shortcut', index: 1 };\n }\n if (input === 'p' || input === '3') {\n return { type: 'shortcut', index: 2 };\n }\n if (input === 'n' || input === 'd' || input === '4') {\n return { type: 'shortcut', index: 3 };\n }\n return undefined;\n}\n\nexport function applyPermissionPromptInput(\n state: ISelectionFlowState,\n action: TPermissionPromptInputAction,\n canPersistProjectPermission = true,\n): { state: ISelectionFlowState; effect: TPermissionPromptEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (typeof action !== 'string') {\n if (action.index === 2 && !canPersistProjectPermission) {\n return { state, effect: { type: 'none' } };\n }\n return resolvePermissionIndex(state, action.index);\n }\n const result = applySelectionInput(state, action, {\n itemCount: PERMISSION_PROMPT_OPTIONS.length,\n });\n if (result.effect.type !== 'select') {\n return { state: result.state, effect: { type: 'none' } };\n }\n if (result.effect.index === 2 && !canPersistProjectPermission) {\n return { state: result.state, effect: { type: 'none' } };\n }\n return {\n state: result.state,\n effect: { type: 'resolve', decision: getPermissionDecision(result.effect.index) },\n };\n}\n\nfunction getPermissionDecision(index: number): TPermissionPromptDecision {\n if (index === 0) return true;\n if (index === 1) return 'allow-session';\n if (index === 2) return 'allow-project';\n return false;\n}\n\nfunction resolvePermissionIndex(\n state: ISelectionFlowState,\n index: number,\n): { state: ISelectionFlowState; effect: TPermissionPromptEffect } {\n return {\n state: { ...state, selectedIndex: index, resolved: true },\n effect: { type: 'resolve', decision: getPermissionDecision(index) },\n };\n}\n","import { consentScopeFor } from '@robota-sdk/agent-framework';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport {\n applyPermissionPromptInput,\n permissionPromptOptionsFor,\n type TPermissionPromptInputAction,\n} from './flows/permission-prompt-flow.js';\nimport { createSelectionFlowState, type ISelectionFlowState } from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedList } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS } from './screen-reader-labels.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IPendingPermissionRequest } from './types.js';\nimport type { TToolArgs } from '@robota-sdk/agent-core';\n\ninterface IProps {\n request: IPendingPermissionRequest;\n /** How long a new request ignores its answer keys; tests pass their own. */\n armDelayMs?: number;\n}\n\n/**\n * The prompt replaces the composer while the user may still be typing, and its keys are ordinary\n * letters and digits (`a` allows for the session). For this long after a request appears its keys\n * answer nothing, so a keystroke meant for the composer cannot grant a permission. The prompt shows\n * no selection cursor until it is armed.\n */\nexport const PERMISSION_PROMPT_ARM_DELAY_MS = 450;\n\n/**\n * Footer for the permission prompt — identical to the confirm prompt's (same horizontal row, same\n * reducer). Esc is deliberately suppressed by the flow (`escape: false` — a permission ask must\n * resolve explicitly; Esc-dismissal would be an implicit deny), so the footer omits it.\n */\nexport const PERMISSION_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '←→', label: 'Navigate' },\n { keys: 'Enter', label: 'Confirm' },\n];\n\nfunction formatArgs(args: TToolArgs): string {\n const entries = Object.entries(args);\n if (entries.length === 0) return '(no arguments)';\n return entries\n .map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)\n .join(', ');\n}\n\nexport default function PermissionPrompt({\n request,\n armDelayMs = PERMISSION_PROMPT_ARM_DELAY_MS,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const canPersistProjectPermission = request.canPersistProjectPermission !== false;\n const [state, setState] = React.useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = React.useRef(state);\n const prevRequestRef = React.useRef(request);\n\n if (prevRequestRef.current !== request) {\n prevRequestRef.current = request;\n const nextState = createSelectionFlowState();\n stateRef.current = nextState;\n setState(nextState);\n }\n\n const [armedRequest, setArmedRequest] = React.useState<IPendingPermissionRequest | undefined>();\n React.useEffect(() => {\n if (armDelayMs <= 0) return undefined;\n const timer = setTimeout(() => setArmedRequest(request), armDelayMs);\n return () => clearTimeout(timer);\n }, [request, armDelayMs]);\n const armed = armDelayMs <= 0 || armedRequest === request;\n\n const applyAction = React.useCallback(\n (action: TPermissionPromptInputAction): void => {\n const result = applyPermissionPromptInput(\n stateRef.current,\n action,\n canPersistProjectPermission,\n );\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'resolve') {\n request.resolve(result.effect.decision);\n }\n },\n [request, canPersistProjectPermission],\n );\n\n const screenReader = useScreenReader();\n const options = permissionPromptOptionsFor(\n consentScopeFor(request.toolName, request.toolArgs),\n canPersistProjectPermission,\n );\n const numbered = useNumberedSelection({\n // Off while arming, so a digit typed then is not buffered for a later Enter to commit.\n enabled: screenReader && armed,\n itemCount: options.length,\n onSelect: (index) => {\n const result = applyPermissionPromptInput(\n stateRef.current,\n {\n type: 'shortcut',\n index,\n },\n canPersistProjectPermission,\n );\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'resolve') request.resolve(result.effect.decision);\n },\n });\n // Digits typed for one request are not an answer to the next.\n const clearNumbered = numbered.clear;\n React.useEffect(() => clearNumbered(), [request, clearNumbered]);\n\n useKeybindingActions(\n 'permission-prompt',\n (actions) => {\n if (!armed) return;\n const shortcutIndex = {\n 'allow-once': 0,\n 'allow-session': 1,\n 'allow-project': 2,\n deny: 3,\n } as const;\n for (const action of actions) {\n if (action in shortcutIndex) {\n applyAction({\n type: 'shortcut',\n index: shortcutIndex[action as keyof typeof shortcutIndex],\n });\n } else if (action === 'previous' || action === 'next' || action === 'confirm') {\n applyAction(action === 'confirm' ? 'select' : action);\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('permission-prompt', [\n [['previous', 'next'], 'Navigate'],\n ['confirm', 'Confirm'],\n ]);\n\n if (screenReader) {\n return (\n <NumberedList\n title={SCREEN_READER_LABELS.permissionRequired}\n description={`${request.toolName} — ${formatArgs(request.toolArgs)}${\n request.requestedByPeer !== undefined\n ? ` (requested by another session: ${request.requestedByPeer})`\n : ''\n }`}\n options={options}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={armed ? palette.border.attention : palette.border.muted}\n paddingX={1}\n >\n <Box>\n <Text color={palette.text.warning} bold>\n [Permission Required]\n </Text>\n {armed ? null : <Text dimColor> keys answer in a moment</Text>}\n </Box>\n <Text>\n Tool:{' '}\n <Text color={palette.text.accent} bold>\n {request.toolName}\n </Text>\n </Text>\n <Text dimColor> {formatArgs(request.toolArgs)}</Text>\n {request.requestedByPeer !== undefined ? (\n <Text color={palette.text.warning}>\n Requested by another session: {request.requestedByPeer}\n </Text>\n ) : null}\n {/* Issue #2351: the \"always\" options carry the consent scope, so they no longer fit one row\n of the prompt box — a row wrapped `Allow [y]` across two lines. One option per line. */}\n <Box marginTop={1} flexDirection=\"column\">\n {options.map((opt, i) => {\n const selected = armed && i === state.selectedIndex;\n return (\n <Box key={opt}>\n <Text\n color={selected ? palette.text.accent : undefined}\n bold={selected}\n dimColor={!armed}\n >\n {selected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {opt}\n </Text>\n </Box>\n );\n })}\n </Box>\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","import {\n applySelectionInput,\n getDirectionalSelectionInputAction,\n type ISelectionFlowState,\n type ISelectionInputKey,\n type TSelectionEffect,\n type TSelectionInputAction,\n} from './selection-flow.js';\n\nexport type TConfirmPromptInputAction = TSelectionInputAction | { type: 'shortcut'; index: number };\n\nexport function getConfirmPromptInputAction(\n input: string,\n key: ISelectionInputKey,\n optionCount: number,\n): TConfirmPromptInputAction | undefined {\n const action = getDirectionalSelectionInputAction({ ...key, escape: false });\n if (action !== undefined) {\n return action;\n }\n if (optionCount === 2 && input === 'y') {\n return { type: 'shortcut', index: 0 };\n }\n if (optionCount === 2 && input === 'n') {\n return { type: 'shortcut', index: 1 };\n }\n return undefined;\n}\n\n/**\n * CLI-2004 — the typed yes/no answer.\n *\n * A two-option arrow menu is the worst shape for a reader: both rows are announced, and which one is\n * selected differs only by a cursor glyph and a colour. In screen-reader mode the prompt asks for a\n * typed answer instead — `y`, `n`, `yes` or `no`, then Enter. Anything else clears the buffer and\n * resolves nothing, so a mistyped answer cannot be read as consent.\n */\nexport interface ITypedConfirmState {\n buffer: string;\n resolved: boolean;\n}\n\nexport function createTypedConfirmState(): ITypedConfirmState {\n return { buffer: '', resolved: false };\n}\n\nconst YES_ANSWERS = ['y', 'yes'];\nconst NO_ANSWERS = ['n', 'no'];\n\nexport function applyTypedConfirmInput(\n state: ITypedConfirmState,\n input: string,\n key: ISelectionInputKey & { backspace?: boolean; delete?: boolean },\n): { state: ITypedConfirmState; effect: TSelectionEffect } {\n if (state.resolved) return { state, effect: { type: 'none' } };\n if (key.backspace === true || key.delete === true) {\n return { state: { ...state, buffer: state.buffer.slice(0, -1) }, effect: { type: 'none' } };\n }\n if (key.return === true) {\n const answer = state.buffer.trim().toLowerCase();\n if (YES_ANSWERS.includes(answer)) {\n return { state: { buffer: '', resolved: true }, effect: { type: 'select', index: 0 } };\n }\n if (NO_ANSWERS.includes(answer)) {\n return { state: { buffer: '', resolved: true }, effect: { type: 'select', index: 1 } };\n }\n return { state: { ...state, buffer: '' }, effect: { type: 'none' } };\n }\n if (/^[a-zA-Z]+$/.test(input)) {\n return { state: { ...state, buffer: state.buffer + input }, effect: { type: 'none' } };\n }\n return { state, effect: { type: 'none' } };\n}\n\nexport function applyConfirmPromptInput(\n state: ISelectionFlowState,\n action: TConfirmPromptInputAction,\n optionCount: number,\n): { state: ISelectionFlowState; effect: TSelectionEffect } {\n if (state.resolved) {\n return { state, effect: { type: 'none' } };\n }\n if (typeof action !== 'string') {\n return {\n state: { ...state, selectedIndex: action.index, resolved: true },\n effect: { type: 'select', index: action.index },\n };\n }\n return applySelectionInput(state, action, { itemCount: optionCount });\n}\n","/**\n * Reusable confirmation prompt with arrow-key selection.\n * Used by model change, permission prompts, and other yes/no confirmations.\n */\n\nimport { Box, useInput } from 'ink';\nimport React, { useState, useRef, useCallback } from 'react';\n\nimport {\n applyConfirmPromptInput,\n applyTypedConfirmInput,\n createTypedConfirmState,\n type ITypedConfirmState,\n type TConfirmPromptInputAction,\n} from './flows/confirm-prompt-flow.js';\nimport { createSelectionFlowState, type ISelectionFlowState } from './flows/selection-flow.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** The typed-answer prompt. Authored here beside the reducer that accepts it. */\nexport const CONFIRM_PROMPT_TYPED_LITERAL = 'Answer y or n and press Enter';\n\n/**\n * Footer for the confirm prompt. Names the canonical keys for a horizontal row (←→; the reducer\n * also accepts ↑↓ as aliases). Esc is deliberately suppressed by the flow (`escape: false` — the\n * prompt must resolve explicitly), so per the affordance contract the footer omits it.\n */\nexport const CONFIRM_PROMPT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '←→', label: 'Navigate' },\n { keys: 'Enter', label: 'Confirm' },\n];\n\ninterface IProps {\n /** Message to display above the options */\n message: string;\n /** Options to select from (default: ['Yes', 'No']) */\n options?: string[];\n /** Callback with the selected index */\n onSelect: (index: number) => void;\n}\n\nexport default function ConfirmPrompt({\n message,\n options = ['Yes', 'No'],\n onSelect,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n const applyAction = useCallback(\n (action: TConfirmPromptInputAction): void => {\n const result = applyConfirmPromptInput(stateRef.current, action, options.length);\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'select') {\n onSelect(result.effect.index);\n }\n },\n [onSelect, options.length],\n );\n\n const screenReader = useScreenReader();\n const [typed, setTyped] = useState<ITypedConfirmState>(createTypedConfirmState);\n const typedRef = useRef(typed);\n\n useInput(\n (input, key) => {\n const result = applyTypedConfirmInput(typedRef.current, input, key);\n typedRef.current = result.state;\n setTyped(result.state);\n if (result.effect.type === 'select') onSelect(result.effect.index);\n },\n { isActive: screenReader },\n );\n\n useKeybindingActions(\n 'confirm-prompt',\n (actions) => {\n for (const action of actions) {\n if (action === 'choose-yes' && options.length === 2) {\n applyAction({ type: 'shortcut', index: 0 });\n } else if (action === 'choose-no' && options.length === 2) {\n applyAction({ type: 'shortcut', index: 1 });\n } else if (action === 'previous' || action === 'next' || action === 'confirm') {\n applyAction(action === 'confirm' ? 'select' : action);\n }\n }\n },\n { isActive: !screenReader },\n );\n const footerHints = useKeybindingHints('confirm-prompt', [\n [['previous', 'next'], 'Navigate'],\n ['confirm', 'Confirm'],\n ]);\n\n if (screenReader) {\n return (\n <Box flexDirection=\"column\">\n <Text>{message}</Text>\n <Text>\n {CONFIRM_PROMPT_TYPED_LITERAL}\n {typed.buffer.length > 0 ? ` ${typed.buffer}` : ''}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={palette.border.attention}\n paddingX={1}\n >\n <Text color={palette.text.warning}>{message}</Text>\n <Box marginTop={1}>\n {options.map((opt, i) => (\n <Box key={opt} marginRight={2}>\n <Text\n color={i === state.selectedIndex ? palette.text.accent : undefined}\n bold={i === state.selectedIndex}\n >\n {i === state.selectedIndex ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {opt}\n </Text>\n </Box>\n ))}\n </Box>\n <KeyHintFooter hints={footerHints} />\n </Box>\n );\n}\n","/**\n * Hook: fetch data for PluginTUI screens.\n * Extracted from PluginTUI.tsx for single-responsibility.\n */\n\nimport { useState, useEffect } from 'react';\n\nimport type { IMenuSelectItem } from '../MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\nexport function usePluginScreenData(\n screen: string,\n marketplace: string | undefined,\n callbacks: ICommandPluginAdapter,\n refreshCounter: number,\n stackLength: number,\n): { items: IMenuSelectItem[]; loading: boolean; error: string | undefined } {\n const [items, setItems] = useState<IMenuSelectItem[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>();\n\n useEffect(() => {\n setItems([]);\n setError(undefined);\n\n if (screen === 'marketplace-list') {\n setLoading(true);\n callbacks\n .marketplaceList()\n .then((sources) => {\n const baseItems: IMenuSelectItem[] = [{ label: 'Add Marketplace', value: '__add__' }];\n const sourceItems: IMenuSelectItem[] = sources.map((s) => ({\n label: s.name,\n value: s.name,\n hint: s.type,\n }));\n setItems([...baseItems, ...sourceItems]);\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n } else if (screen === 'marketplace-browse') {\n const mp = marketplace ?? '';\n setLoading(true);\n callbacks\n .listAvailablePlugins(mp)\n .then((plugins) => {\n setItems(\n plugins.map((p) => ({\n label: p.name,\n value: p.name,\n hint: p.installed ? 'installed' : p.description,\n })),\n );\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n } else if (screen === 'installed-list') {\n setLoading(true);\n callbacks\n .listInstalled()\n .then((plugins) => {\n setItems(\n plugins.map((p) => ({\n label: p.name,\n value: p.name,\n hint: p.description,\n })),\n );\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err));\n setLoading(false);\n });\n }\n }, [stackLength, screen, marketplace, callbacks, refreshCounter]);\n\n return { items, loading, error };\n}\n","import { Box } from 'ink';\nimport React, { useState, useCallback, useRef } from 'react';\n\nimport {\n applySelectionInput,\n createSelectionFlowState,\n normalizeSelectionState,\n type ISelectionFlowState,\n type TSelectionInputAction,\n} from './flows/selection-flow.js';\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport {\n KeyHintFooter,\n SELECTION_INDICATOR,\n SELECTION_INDICATOR_NONE,\n type IKeyHint,\n} from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { NumberedList } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { usePalette } from './theme/index.js';\n\n/** Footer for the interactive menu state. */\nexport const MENU_SELECT_FOOTER_HINTS: readonly IKeyHint[] = [\n { keys: '↑↓', label: 'Navigate' },\n { keys: 'Enter', label: 'Select' },\n { keys: 'Esc', label: 'Back' },\n];\n\n/** Footer for the error state — Esc (cancel) is the only active key. */\nexport const MENU_SELECT_ERROR_FOOTER_HINTS: readonly IKeyHint[] = [{ keys: 'Esc', label: 'Back' }];\n\nexport interface IMenuSelectItem {\n label: string;\n value: string;\n hint?: string;\n}\n\ninterface IProps {\n title: string;\n items: IMenuSelectItem[];\n onSelect: (value: string) => void;\n onBack: () => void;\n loading?: boolean;\n error?: string;\n}\n\nexport default function MenuSelect({\n title,\n items,\n onSelect,\n onBack,\n loading,\n error,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());\n const stateRef = useRef(state);\n const isEnabled = !loading && !error;\n const screenReader = useScreenReader();\n const numbered = useNumberedSelection({\n enabled: screenReader && isEnabled,\n itemCount: items.length,\n cancellable: true,\n onSelect: (index) => {\n const item = items[index];\n if (item !== undefined) onSelect(item.value);\n },\n onCancel: onBack,\n });\n const applyAction = useCallback(\n (action: TSelectionInputAction): void => {\n const result = applySelectionInput(stateRef.current, action, {\n itemCount: items.length,\n enabled: isEnabled,\n });\n stateRef.current = result.state;\n setState(result.state);\n if (result.effect.type === 'cancel') {\n onBack();\n } else if (result.effect.type === 'select') {\n const item = items[result.effect.index];\n if (item !== undefined) {\n onSelect(item.value);\n }\n }\n },\n [isEnabled, items, onBack, onSelect],\n );\n\n useKeybindingActions(\n 'menu-select',\n (actions) => actions.forEach((action) => applyAction(action === 'back' ? 'cancel' : action)),\n { isActive: !screenReader },\n );\n const menuHints = useKeybindingHints('menu-select', [\n [['previous', 'next'], 'Navigate'],\n ['select', 'Select'],\n ['back', 'Back'],\n ]);\n const errorHints = useKeybindingHints('menu-select', [['back', 'Back']]);\n\n const normalizedState = normalizeSelectionState(state, { itemCount: items.length });\n if (normalizedState !== state) {\n stateRef.current = normalizedState;\n }\n const selected = normalizedState.selectedIndex;\n\n if (screenReader) {\n return (\n <NumberedList\n title={title}\n options={loading ? [] : error ? [] : items.map((item) => item.label)}\n description={loading ? 'Loading...' : error}\n cancellable\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={palette.border.attention}\n paddingX={1}\n >\n <Text color={palette.text.warning} bold>\n {title}\n </Text>\n {loading && (\n <Box marginTop={1}>\n <Text dimColor>Loading...</Text>\n </Box>\n )}\n {error && (\n <Box marginTop={1}>\n <Text color={palette.text.error}>{error}</Text>\n </Box>\n )}\n {!loading && !error && (\n <Box flexDirection=\"column\" marginTop={1}>\n {items.map((item, i) => (\n <Box key={item.value}>\n <Text color={i === selected ? palette.text.accent : undefined} bold={i === selected}>\n {i === selected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n {item.label}\n </Text>\n {item.hint && <Text dimColor> {item.hint}</Text>}\n </Box>\n ))}\n </Box>\n )}\n <KeyHintFooter hints={loading ? [] : error ? errorHints : menuHints} />\n </Box>\n );\n}\n","/**\n * Screen-specific selection handlers for PluginTUI.\n * Extracted to keep PluginTUI.tsx under 300 lines.\n */\n\nimport type { IMenuSelectItem } from './MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\ninterface IConfirmState {\n message: string;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\ninterface IMenuContext {\n marketplace?: string;\n pluginId?: string;\n}\n\ninterface INavActions {\n push: (state: { screen: string; context?: IMenuContext }) => void;\n pop: () => void;\n popN: (n: number) => void;\n notify: (content: string) => void;\n setConfirm: (state: IConfirmState | undefined) => void;\n refresh: () => void;\n}\n\nexport function handleMainSelect(value: string, nav: Pick<INavActions, 'push'>): void {\n if (value === 'marketplace') {\n nav.push({ screen: 'marketplace-list' });\n } else if (value === 'installed') {\n nav.push({ screen: 'installed-list' });\n }\n}\n\nexport function handleMarketplaceListSelect(value: string, nav: Pick<INavActions, 'push'>): void {\n if (value === '__add__') {\n nav.push({ screen: 'marketplace-add' });\n } else {\n nav.push({ screen: 'marketplace-action', context: { marketplace: value } });\n }\n}\n\nexport function handleMarketplaceActionSelect(\n value: string,\n marketplace: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'push' | 'pop' | 'popN' | 'notify' | 'setConfirm'>,\n): void {\n if (value === 'browse') {\n nav.push({ screen: 'marketplace-browse', context: { marketplace } });\n } else if (value === 'update') {\n callbacks\n .marketplaceUpdate(marketplace)\n .then(() => {\n nav.notify(`Updated marketplace \"${marketplace}\".`);\n nav.pop();\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n } else if (value === 'remove') {\n nav.setConfirm({\n message: `Remove marketplace \"${marketplace}\" and all its plugins?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .marketplaceRemove(marketplace)\n .then(() => {\n nav.notify(`Removed marketplace \"${marketplace}\".`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n }\n}\n\nexport function handleMarketplaceBrowseSelect(\n value: string,\n marketplace: string,\n items: IMenuSelectItem[],\n nav: Pick<INavActions, 'push'>,\n): void {\n const fullId = `${value}@${marketplace}`;\n const item = items.find((i) => i.value === value);\n if (item?.hint === 'installed') {\n nav.push({ screen: 'installed-action', context: { pluginId: fullId } });\n } else {\n nav.push({ screen: 'marketplace-install-scope', context: { marketplace, pluginId: fullId } });\n }\n}\n\nexport function handleInstallScopeSelect(\n value: string,\n pluginId: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'popN' | 'notify'>,\n): void {\n const scope = value as 'user' | 'project';\n callbacks\n .install(pluginId, scope)\n .then(() => {\n nav.notify(`Installed plugin \"${pluginId}\" (${scope} scope).`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n}\n\nexport function handleInstalledListSelect(\n value: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'notify' | 'setConfirm' | 'refresh'>,\n): void {\n nav.setConfirm({\n message: `Uninstall plugin \"${value}\"?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .uninstall(value)\n .then(() => {\n nav.notify(`Uninstalled plugin \"${value}\".`);\n nav.refresh();\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n}\n\nexport function handleInstalledActionSelect(\n value: string,\n pluginId: string,\n callbacks: ICommandPluginAdapter,\n nav: Pick<INavActions, 'popN' | 'notify' | 'setConfirm'>,\n): void {\n if (value === 'uninstall') {\n nav.setConfirm({\n message: `Uninstall plugin \"${pluginId}\"?`,\n onConfirm: () => {\n nav.setConfirm(undefined);\n callbacks\n .uninstall(pluginId)\n .then(() => {\n nav.notify(`Uninstalled plugin \"${pluginId}\".`);\n nav.popN(2);\n })\n .catch((err) => {\n nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n });\n },\n onCancel: () => nav.setConfirm(undefined),\n });\n }\n}\n","/**\n * PluginTUI — Main orchestrator component for plugin management.\n * Manages a stack of screens: main, marketplace, installed plugins, etc.\n */\n\nimport React, { useState, useCallback } from 'react';\n\nimport ConfirmPrompt from './ConfirmPrompt.js';\nimport { usePluginScreenData } from './hooks/usePluginScreenData.js';\nimport MenuSelect from './MenuSelect.js';\nimport {\n handleMainSelect,\n handleMarketplaceListSelect,\n handleMarketplaceActionSelect,\n handleMarketplaceBrowseSelect,\n handleInstallScopeSelect,\n handleInstalledListSelect,\n handleInstalledActionSelect,\n} from './plugin-tui-handlers.js';\nimport TextPrompt from './TextPrompt.js';\n\nimport type { IMenuSelectItem } from './MenuSelect.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\ntype TScreenId =\n | 'main'\n | 'marketplace-list'\n | 'marketplace-action'\n | 'marketplace-browse'\n | 'marketplace-install-scope'\n | 'marketplace-add'\n | 'installed-list'\n | 'installed-action';\n\ninterface IMenuContext {\n marketplace?: string;\n pluginId?: string;\n}\n\ninterface IMenuState {\n screen: TScreenId;\n context?: IMenuContext;\n}\n\ninterface IConfirmState {\n message: string;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\ninterface IProps {\n callbacks: ICommandPluginAdapter;\n onClose: () => void;\n addMessage?: (msg: { role: string; content: string }) => void;\n}\n\nexport default function PluginTUI({ callbacks, onClose, addMessage }: IProps): React.ReactElement {\n const [stack, setStack] = useState<IMenuState[]>([{ screen: 'main' }]);\n const [confirm, setConfirm] = useState<IConfirmState | undefined>();\n const [refreshCounter, setRefreshCounter] = useState(0);\n\n const current = stack[stack.length - 1] ?? { screen: 'main' };\n\n const push = useCallback((state: IMenuState) => {\n setStack((prev) => [...prev, state]);\n }, []);\n\n const pop = useCallback(() => {\n setStack((prev) => {\n if (prev.length <= 1) {\n onClose();\n return prev;\n }\n return prev.slice(0, -1);\n });\n }, [onClose]);\n\n const popN = useCallback(\n (n: number) => {\n setStack((prev) => {\n const next = prev.slice(0, Math.max(1, prev.length - n));\n if (next.length === 0) {\n onClose();\n return prev;\n }\n return next;\n });\n },\n [onClose],\n );\n\n const notify = useCallback(\n (content: string) => {\n addMessage?.({ role: 'system', content });\n },\n [addMessage],\n );\n\n const refresh = useCallback(() => {\n setRefreshCounter((c) => c + 1);\n }, []);\n\n const setConfirmNav = useCallback(\n (state: IConfirmState | undefined) => setConfirm(state),\n [setConfirm],\n );\n // nav.push accepts a loose { screen: string } shape to satisfy plugin-tui-handlers types;\n // we cast screen to TScreenId which is safe because handlers only push valid screen names.\n const pushNav = useCallback(\n (state: { screen: string; context?: IMenuContext }) =>\n push({ screen: state.screen as TScreenId, context: state.context }),\n [push],\n );\n const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };\n\n const { items, loading, error } = usePluginScreenData(\n current.screen,\n current.context?.marketplace,\n callbacks,\n refreshCounter,\n stack.length,\n );\n\n const handleSelect = useCallback(\n (value: string) => {\n const screen = current.screen;\n const ctx = current.context;\n\n if (screen === 'main') handleMainSelect(value, nav);\n else if (screen === 'marketplace-list') handleMarketplaceListSelect(value, nav);\n else if (screen === 'marketplace-action')\n handleMarketplaceActionSelect(value, ctx?.marketplace ?? '', callbacks, nav);\n else if (screen === 'marketplace-browse')\n handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? '', items, nav);\n else if (screen === 'marketplace-install-scope')\n handleInstallScopeSelect(value, ctx?.pluginId ?? '', callbacks, nav);\n else if (screen === 'installed-list') handleInstalledListSelect(value, callbacks, nav);\n else if (screen === 'installed-action')\n handleInstalledActionSelect(value, ctx?.pluginId ?? '', callbacks, nav);\n },\n [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh],\n );\n\n const handleTextSubmit = useCallback(\n (value: string) => {\n if (current.screen === 'marketplace-add') {\n callbacks\n .marketplaceAdd(value)\n .then((name) => {\n notify(`Added marketplace \"${name}\" from ${value}.`);\n pop();\n })\n .catch((err) => {\n notify(`Error: ${err instanceof Error ? err.message : String(err)}`);\n pop();\n });\n }\n },\n [current.screen, callbacks, notify, pop],\n );\n\n // Confirm overlay intercepts everything\n if (confirm) {\n return (\n <ConfirmPrompt\n message={confirm.message}\n onSelect={(index) => {\n if (index === 0) confirm.onConfirm();\n else confirm.onCancel();\n }}\n />\n );\n }\n\n const screen = current.screen;\n\n if (screen === 'marketplace-add') {\n return (\n <TextPrompt\n title=\"Add Marketplace Source\"\n placeholder=\"owner/repo or git URL\"\n onSubmit={handleTextSubmit}\n onCancel={pop}\n validate={(v) => (!v.includes('/') ? 'Must be owner/repo or a git URL' : undefined)}\n />\n );\n }\n\n if (screen === 'marketplace-action') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Marketplace: ${current.context?.marketplace ?? ''}`}\n items={[\n { label: 'Browse plugins', value: 'browse' },\n { label: 'Update', value: 'update' },\n { label: 'Remove', value: 'remove' },\n ]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n if (screen === 'marketplace-install-scope') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Install scope for \"${current.context?.pluginId ?? ''}\"`}\n items={[\n { label: 'User scope', value: 'user' },\n { label: 'Project scope', value: 'project' },\n ]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n if (screen === 'installed-action') {\n return (\n <MenuSelect\n key={stack.length}\n title={`Plugin: ${current.context?.pluginId ?? ''}`}\n items={[{ label: 'Uninstall', value: 'uninstall' }]}\n onSelect={handleSelect}\n onBack={pop}\n />\n );\n }\n\n // Screens with async items: main, marketplace-list, marketplace-browse, installed-list\n const titleMap: Record<string, string> = {\n main: 'Plugin Management',\n 'marketplace-list': 'Marketplace',\n 'marketplace-browse': `Browse: ${current.context?.marketplace ?? ''}`,\n 'installed-list': 'Installed Plugins',\n };\n\n const staticItemsMap: Record<string, IMenuSelectItem[]> = {\n main: [\n { label: 'Marketplace', value: 'marketplace' },\n { label: 'Installed Plugins', value: 'installed' },\n ],\n };\n\n return (\n <MenuSelect\n key={`${screen}-${stack.length}-${refreshCounter}`}\n title={titleMap[screen] ?? 'Plugin Management'}\n items={staticItemsMap[screen] ?? items}\n onSelect={handleSelect}\n onBack={pop}\n loading={loading}\n error={error}\n />\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ITuiSessionEventNotice } from './tui-session-events.js';\n\nexport interface ISessionEventNoticesProps {\n notices: readonly ITuiSessionEventNotice[];\n}\n\nexport default function SessionEventNotices({\n notices,\n}: ISessionEventNoticesProps): React.ReactElement | null {\n const palette = usePalette();\n if (notices.length === 0) return null;\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n {notices.map((notice) => (\n <Text key={notice.id} color={palette.text.muted}>\n {notice.message}\n </Text>\n ))}\n </Box>\n );\n}\n","/** Every stored session id starts with this, so it tells no two sessions apart. */\nconst SESSION_ID_PREFIX = 'session_';\nconst SHORT_SESSION_ID_LENGTH = 8;\n\n/** A session id short enough for a picker row or a status bar: the start of the part that differs. */\nexport function shortSessionId(id: string): string {\n const distinct = id.startsWith(SESSION_ID_PREFIX) ? id.slice(SESSION_ID_PREFIX.length) : id;\n return distinct === '' ? id : distinct.slice(0, SHORT_SESSION_ID_LENGTH);\n}\n","/**\n * Session picker component for /resume command.\n * Shows a list of sessions for the current cwd.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport ListPicker from './ListPicker.js';\nimport { Text } from './SafeText.js';\nimport { shortSessionId } from './short-session-id.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { ISessionListingEntry } from '@robota-sdk/agent-interface-session';\n\nconst SESSION_PREVIEW_DISPLAY_LENGTH = 60;\n\n/**\n * `● live · 2 clients` on a host that keeps sessions live: the session runs there now, and that many\n * clients are on it. Null for a stored session, and for a host that says neither.\n */\nexport function sessionLiveBadge(session: ISessionListingEntry): string | null {\n const parts: string[] = [];\n if (session.live === true) parts.push('● live');\n if (session.clients !== undefined && session.clients > 0) {\n parts.push(`${session.clients} ${session.clients === 1 ? 'client' : 'clients'}`);\n }\n return parts.length > 0 ? parts.join(' · ') : null;\n}\n\ninterface IProps {\n sessions: readonly ISessionListingEntry[];\n onSelect: (sessionId: string) => void;\n onCancel: () => void;\n}\n\nexport default function SessionPicker({\n sessions,\n onSelect,\n onCancel,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box flexDirection=\"column\" paddingX={1} marginBottom={1}>\n <Text bold color={palette.text.accent}>\n Select a session to resume:\n </Text>\n <ListPicker<ISessionListingEntry>\n items={[...sessions]}\n renderItem={(session: ISessionListingEntry, isSelected: boolean) => {\n const badge = sessionLiveBadge(session);\n const preview = session.preview\n ? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) +\n (session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? '...' : '')\n : '';\n return (\n <Text>\n {isSelected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}\n <Text bold>{session.name ?? shortSessionId(session.id)}</Text>\n {' '}\n <Text dimColor>\n {new Date(session.updatedAt).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </Text>\n {' '}\n <Text dimColor>msgs: {session.messageCount}</Text>\n {badge !== null ? (\n <>\n {' '}\n <Text color={palette.text.success}>{badge}</Text>\n </>\n ) : null}\n {preview ? (\n <>\n {'\\n '}\n <Text dimColor>{preview}</Text>\n </>\n ) : null}\n </Text>\n );\n }}\n onSelect={(session: ISessionListingEntry) => onSelect(session.id)}\n onCancel={onCancel}\n />\n </Box>\n );\n}\n","/**\n * SCREEN-2002: this module is pure, so it names a colour TOKEN, not a colour. `StatusBar` resolves\n * the token against the live theme — a pure function that returned a chalk name could not follow one.\n */\nexport type TStatusActivityKind = 'tools' | 'thinking' | 'background' | 'queued' | 'idle';\n\nexport interface IStatusActivityInput {\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n}\n\n/** The `IThemeColors['text']` key a status activity is rendered in. */\nexport type TStatusActivityTone = 'accent' | 'warning' | 'muted';\n\nexport interface IStatusActivity {\n kind: TStatusActivityKind;\n label: string;\n tone: TStatusActivityTone;\n segments: string[];\n text: string;\n}\n\nconst NO_ACTIVE_ITEMS = 0;\n\nexport function formatStatusActivity(input: IStatusActivityInput): IStatusActivity {\n const base = getPrimaryActivity(input);\n const segments = input.hasPendingPrompt && base.kind !== 'queued' ? ['queued'] : [];\n const text = [base.label, ...segments].join(' · ');\n return { ...base, segments, text };\n}\n\nfunction getPrimaryActivity(\n input: IStatusActivityInput,\n): Omit<IStatusActivity, 'segments' | 'text'> {\n if (input.activeToolCount > NO_ACTIVE_ITEMS) {\n return {\n kind: 'tools',\n label: `Tools (${input.activeToolCount})`,\n tone: 'accent',\n };\n }\n if (input.isThinking) {\n return {\n kind: 'thinking',\n label: 'Thinking',\n tone: 'warning',\n };\n }\n if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {\n return {\n kind: 'background',\n label: `Background (${input.activeBackgroundTaskCount})`,\n tone: 'accent',\n };\n }\n if (input.hasPendingPrompt) {\n return {\n kind: 'queued',\n label: 'Queued',\n tone: 'warning',\n };\n }\n return {\n kind: 'idle',\n label: 'Idle',\n tone: 'muted',\n };\n}\n","import { formatTokenCount } from '@robota-sdk/agent-core';\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { formatStatusActivity } from './status-activity.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type { TModelEffortSelection, TPermissionMode } from '@robota-sdk/agent-core';\n\n/** Threshold boundaries for context percentage color coding */\nconst CONTEXT_YELLOW_THRESHOLD = 70;\nconst CONTEXT_RED_THRESHOLD = 90;\n\n/** Segment separator between status-bar fields (SCREEN-006: single SSOT, was repeated inline 6x). */\nconst SEP = ' | ';\n\ninterface IProps {\n permissionMode: TPermissionMode;\n modelName: string;\n providerDisplayName?: string | undefined;\n sessionId: string;\n isThinking: boolean;\n activeToolCount?: number;\n activeBackgroundTaskCount?: number;\n hasPendingPrompt?: boolean;\n contextPercentage: number;\n contextUsedTokens: number;\n contextMaxTokens: number;\n sessionName?: string;\n gitBranch?: string;\n showGitBranch?: boolean;\n activeAgentLabel?: string;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n /** This terminal only observes the session. */\n readOnly?: boolean;\n}\n\ninterface IStatusLeftProps {\n permissionMode: TPermissionMode;\n modelName: string;\n providerDisplayName?: string | undefined;\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n contextPercentage: number;\n contextUsedTokens: number;\n contextMaxTokens: number;\n sessionName?: string;\n gitBranch?: string;\n showGitBranch: boolean;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n readOnly: boolean;\n}\n\n/** What an observing terminal shows first on its status line. */\nexport const READ_ONLY_STATUS = 'Observing — read only';\n\n/** Return the color for the context percentage indicator */\nfunction getContextColor(percentage: number, palette: IThemeColors): string {\n if (percentage >= CONTEXT_RED_THRESHOLD) return palette.text.error;\n if (percentage >= CONTEXT_YELLOW_THRESHOLD) return palette.text.warning;\n return palette.text.success;\n}\n\nfunction StatusActivityText({\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n}: Pick<\n IStatusLeftProps,\n 'isThinking' | 'activeToolCount' | 'activeBackgroundTaskCount' | 'hasPendingPrompt'\n>): React.ReactElement {\n const palette = usePalette();\n const activity = formatStatusActivity({\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n });\n\n return (\n <Text color={palette.text[activity.tone]} bold={activity.kind !== 'idle'}>\n {activity.text}\n </Text>\n );\n}\n\nfunction ContextText({\n percentage,\n usedTokens,\n maxTokens,\n}: {\n percentage: number;\n usedTokens: number;\n maxTokens: number;\n}): React.ReactElement {\n const palette = usePalette();\n return (\n <Text color={getContextColor(percentage, palette)}>\n Context: {Math.round(percentage)}% ({formatTokenCount(usedTokens)}/\n {formatTokenCount(maxTokens)} tokens)\n </Text>\n );\n}\n\nexport function ReadOnlyText(): React.ReactElement {\n const palette = usePalette();\n return (\n <Text color={palette.text.warning} bold>\n {READ_ONLY_STATUS}\n </Text>\n );\n}\n\nfunction ModeText({ permissionMode }: { permissionMode: TPermissionMode }): React.ReactElement {\n const palette = usePalette();\n return (\n <>\n <Text color={palette.text.accent} bold>\n Mode:\n </Text>{' '}\n <Text>{permissionMode}</Text>\n </>\n );\n}\n\n/**\n * CLI-2004 verdict (l): outside the mode the `default` mode is hidden as noise. In the mode it is\n * ALWAYS rendered — with no key-based cycling to announce, the status line is the only place a\n * reader can find which permission mode it is in, and a hidden `default` leaves nothing to find.\n */\nfunction shouldShowPermissionMode(permissionMode: TPermissionMode, screenReader: boolean): boolean {\n return screenReader || permissionMode !== 'default';\n}\n\nfunction PresetText({ activePresetId }: { activePresetId: string }): React.ReactElement {\n const palette = usePalette();\n return (\n <>\n <Text color={palette.text.accent} bold>\n Preset:\n </Text>{' '}\n <Text>{activePresetId}</Text>\n </>\n );\n}\n\nfunction shouldShowActivePreset(activePresetId: string | undefined): activePresetId is string {\n return activePresetId !== undefined && activePresetId !== 'default';\n}\n\nfunction ProviderText({\n modelName,\n providerDisplayName,\n effort,\n}: {\n modelName: string;\n providerDisplayName?: string | undefined;\n effort?: TModelEffortSelection;\n}): React.ReactElement {\n const provider =\n providerDisplayName !== undefined ? `${providerDisplayName} ${modelName}` : modelName;\n if (providerDisplayName !== undefined) {\n return (\n <Text dimColor>\n {provider}\n {effort !== undefined ? ` Effort: ${effort}` : ''}\n </Text>\n );\n }\n return (\n <Text dimColor>\n {provider}\n {effort !== undefined ? ` Effort: ${effort}` : ''}\n </Text>\n );\n}\n\nfunction StatusLeft(props: IStatusLeftProps): React.ReactElement {\n const palette = usePalette();\n const shouldShowGitBranch =\n props.showGitBranch && props.gitBranch !== undefined && props.gitBranch.length > 0;\n // CLI-2004 § Solution 13: one rule applied to fields of different VOLATILITY. The activity text\n // and the context percentage re-render on their own cadence and on every token, so a reader\n // reviewing the status line would re-announce them continuously while the operator types — they\n // are suppressed. The permission mode, the preset and the model change only when the operator\n // changes them: those are anchors, and they stay.\n const screenReader = useScreenReader();\n const showPermissionMode = shouldShowPermissionMode(props.permissionMode, screenReader);\n const activePresetId = props.activePresetId;\n const showActivePreset = shouldShowActivePreset(activePresetId);\n return (\n <Text>\n {props.readOnly && (\n <>\n <ReadOnlyText />\n {/* In screen-reader mode the next field brings its own separator. */}\n {!screenReader && SEP}\n </>\n )}\n {!screenReader && (\n <StatusActivityText\n isThinking={props.isThinking}\n activeToolCount={props.activeToolCount}\n activeBackgroundTaskCount={props.activeBackgroundTaskCount}\n hasPendingPrompt={props.hasPendingPrompt}\n />\n )}\n {showPermissionMode && (\n <>\n {SEP}\n <ModeText permissionMode={props.permissionMode} />\n </>\n )}\n {showActivePreset && (\n <>\n {SEP}\n <PresetText activePresetId={activePresetId} />\n </>\n )}\n {props.sessionName && (\n <>\n {SEP}\n <Text color={palette.text.session}>{props.sessionName}</Text>\n </>\n )}\n {shouldShowGitBranch && (\n <>\n {SEP}\n <Text dimColor>git: {props.gitBranch}</Text>\n </>\n )}\n {SEP}\n <ProviderText\n modelName={props.modelName}\n providerDisplayName={props.providerDisplayName}\n effort={props.effort}\n />\n {!screenReader && (\n <>\n {SEP}\n <ContextText\n percentage={props.contextPercentage}\n usedTokens={props.contextUsedTokens}\n maxTokens={props.contextMaxTokens}\n />\n </>\n )}\n </Text>\n );\n}\n\nexport default function StatusBar({\n permissionMode,\n modelName,\n providerDisplayName,\n sessionId: _sessionId,\n isThinking,\n activeToolCount = 0,\n activeBackgroundTaskCount = 0,\n hasPendingPrompt = false,\n contextPercentage,\n contextUsedTokens,\n contextMaxTokens,\n sessionName,\n gitBranch,\n showGitBranch = true,\n activeAgentLabel,\n activePresetId,\n effort,\n readOnly = false,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box paddingLeft={1} paddingRight={1} justifyContent=\"space-between\">\n <StatusLeft\n permissionMode={permissionMode}\n modelName={modelName}\n providerDisplayName={providerDisplayName}\n isThinking={isThinking}\n activeToolCount={activeToolCount}\n activeBackgroundTaskCount={activeBackgroundTaskCount}\n hasPendingPrompt={hasPendingPrompt}\n contextPercentage={contextPercentage}\n contextUsedTokens={contextUsedTokens}\n contextMaxTokens={contextMaxTokens}\n sessionName={sessionName}\n gitBranch={gitBranch}\n showGitBranch={showGitBranch}\n activePresetId={activePresetId}\n effort={effort}\n readOnly={readOnly}\n />\n {activeAgentLabel !== undefined && (\n <Text color={palette.text.warning} bold>\n [{activeAgentLabel}]\n </Text>\n )}\n </Box>\n );\n}\n","import { createContext, useContext } from 'react';\n\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\n\nconst TuiCliAdapterContext = createContext<ITuiCliAdapter | null>(null);\n\nexport const TuiCliAdapterProvider = TuiCliAdapterContext.Provider;\n\nexport function useTuiCliAdapter(): ITuiCliAdapter {\n const adapter = useContext(TuiCliAdapterContext);\n if (!adapter) throw new Error('TuiCliAdapterContext not provided');\n return adapter;\n}\n","import React, { useMemo } from 'react';\n\nimport StatusBar, { ReadOnlyText } from './StatusBar.js';\nimport { useTuiCliAdapter } from './tui-cli-adapter-context.js';\n\nimport type { TModelEffortSelection, TPermissionMode } from '@robota-sdk/agent-core';\nimport type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-command';\n\ninterface IProps {\n cwd: string;\n permissionMode: TPermissionMode;\n modelId?: string;\n providerType?: string | undefined;\n sessionId: string;\n isThinking: boolean;\n activeToolCount: number;\n activeBackgroundTaskCount: number;\n hasPendingPrompt: boolean;\n contextState: { percentage: number; usedTokens: number; maxTokens: number };\n sessionName?: string;\n settings: IStatusLineCommandSettings;\n activeAgentLabel?: string;\n activePresetId?: string;\n effort?: TModelEffortSelection;\n gitRefreshToken?: number;\n readOnly?: boolean;\n}\n\nexport default function SessionStatusBar({\n cwd,\n permissionMode,\n modelId,\n providerType,\n sessionId,\n isThinking,\n activeToolCount,\n activeBackgroundTaskCount,\n hasPendingPrompt,\n contextState,\n sessionName,\n settings,\n activeAgentLabel,\n activePresetId,\n effort,\n gitRefreshToken,\n readOnly = false,\n}: IProps): React.ReactElement | null {\n const cliAdapter = useTuiCliAdapter();\n const gitBranch = useMemo(() => cliAdapter.getGitBranch(cwd), [cliAdapter, cwd, gitRefreshToken]);\n const providerDisplayName = useMemo(\n () =>\n providerType !== undefined ? cliAdapter.getProviderDisplayName(providerType) : undefined,\n [cliAdapter, providerType],\n );\n // With the status line off, an observer is still told it cannot change the session.\n if (!settings.enabled) return readOnly ? <ReadOnlyText /> : null;\n\n return (\n <StatusBar\n permissionMode={permissionMode}\n modelName={modelId ?? ''}\n providerDisplayName={providerDisplayName}\n sessionId={sessionId}\n isThinking={isThinking}\n activeToolCount={activeToolCount}\n activeBackgroundTaskCount={activeBackgroundTaskCount}\n hasPendingPrompt={hasPendingPrompt}\n contextPercentage={contextState.percentage}\n contextUsedTokens={contextState.usedTokens}\n contextMaxTokens={contextState.maxTokens}\n sessionName={sessionName}\n gitBranch={gitBranch}\n showGitBranch={settings.gitBranch}\n activeAgentLabel={activeAgentLabel}\n activePresetId={activePresetId}\n effort={effort}\n readOnly={readOnly}\n />\n );\n}\n","/**\n * Streaming indicator — shows real-time tool execution and AI response text.\n * Displayed during session.run() execution.\n */\n\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { useRenderMarkdown } from './hooks/useRenderMarkdown.js';\nimport { humanizeToolArgument, humanizeToolName } from './humanize-tool-name.js';\nimport { RenderedText, Text } from './SafeText.js';\nimport {\n useModelCommandToolPrefix,\n useProductDisplayName,\n} from './product-display-name-context.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { SCREEN_READER_LABELS } from './screen-reader-labels.js';\nimport { STATUS_SYMBOL, statusGlyphColor, toolStateStatusKind } from './status-glyph.js';\nimport { usePalette } from './theme/index.js';\nimport ToolDiffBlock from './ToolDiffBlock.js';\n\nimport type { IThemeColors } from './theme/index.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nfunction getToolStyle(\n t: IToolState,\n palette: IThemeColors,\n): {\n color: string;\n icon: string;\n strikethrough: boolean;\n} {\n const kind = toolStateStatusKind(t);\n return {\n color: statusGlyphColor(palette, kind),\n icon: STATUS_SYMBOL[kind],\n strikethrough: kind === 'error' || kind === 'denied',\n };\n}\n\ninterface IProps {\n text: string;\n activeTools: readonly IToolState[];\n isThinking?: boolean;\n}\n\nfunction ThinkingFallback({ isThinking }: { isThinking: boolean }): React.ReactElement {\n const palette = usePalette();\n if (!isThinking) return <></>;\n return (\n <Box marginBottom={1}>\n <Text color={palette.text.warning}>Thinking...</Text>\n </Box>\n );\n}\n\nfunction ActiveTools({ activeTools }: { activeTools: readonly IToolState[] }): React.ReactElement {\n const palette = usePalette();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.emphasis} bold>\n Tools:\n </Text>\n <Text> </Text>\n {activeTools.map((t, i) => {\n const { color, icon, strikethrough } = getToolStyle(t, palette);\n return (\n <Box key={`${t.toolName}-${i}`} flexDirection=\"column\">\n <Text color={color} strikethrough={strikethrough}>\n {' '}\n {icon} {humanizeToolName(t.toolName, modelCommandToolPrefix)}({humanizeToolArgument(t.firstArg)})\n </Text>\n {t.diffLines && t.diffLines.length > 0 && (\n <ToolDiffBlock file={t.diffFile} lines={t.diffLines} />\n )}\n </Box>\n );\n })}\n </Box>\n );\n}\n\n/**\n * CLI-2004: the whole progress indicator collapses to ONE static line. A spinner is a repaint per\n * frame, and a repaint is an announcement — the reader would hear the same line forever. The line\n * still carries what changed (which tools are running, how far the reply has got), just once per\n * actual change instead of once per frame.\n */\nfunction renderScreenReaderStatus(\n text: string,\n activeTools: readonly IToolState[],\n isThinking: boolean,\n modelCommandToolPrefix?: string,\n): React.ReactElement {\n const running = activeTools\n .map((tool) => humanizeToolName(tool.toolName, modelCommandToolPrefix))\n .join(', ');\n const parts = [\n running.length > 0 ? `${SCREEN_READER_LABELS.tool} ${running}` : undefined,\n text.length > 0 ? text : isThinking ? SCREEN_READER_LABELS.thinking : undefined,\n ].filter((part): part is string => part !== undefined);\n if (parts.length === 0) return <></>;\n return <Text>{parts.join(' — ')}</Text>;\n}\n\nexport default function StreamingIndicator({\n text,\n activeTools,\n isThinking = false,\n}: IProps): React.ReactElement {\n const palette = usePalette();\n const renderMarkdown = useRenderMarkdown();\n const hasTools = activeTools.length > 0;\n const hasText = text.length > 0;\n const screenReader = useScreenReader();\n const productDisplayName = useProductDisplayName();\n const modelCommandToolPrefix = useModelCommandToolPrefix();\n\n if (screenReader) {\n return renderScreenReaderStatus(text, activeTools, isThinking, modelCommandToolPrefix);\n }\n\n if (!hasTools && !hasText) {\n return <ThinkingFallback isThinking={isThinking} />;\n }\n\n return (\n <Box flexDirection=\"column\">\n {hasTools && <ActiveTools activeTools={activeTools} />}\n {hasText && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={palette.text.accent} bold>\n {productDisplayName}:\n </Text>\n <Text> </Text>\n <Box marginLeft={2}>\n {/* `renderMarkdown` sanitizes its input and then styles it; the SGR in its output is ours. */}\n <RenderedText wrap=\"wrap\">{renderMarkdown(text)}</RenderedText>\n </Box>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * SCREEN-2002 — the theme picker.\n *\n * Preview-on-move: the highlighted row IS the live region's theme while the picker is open, because\n * a colour scheme cannot be judged from a swatch beside the thing it does not apply to. Escape\n * restores what was persisted; selecting submits `/theme <id>` so the HOST writes the settings\n * document and the picker never becomes a second writer.\n *\n * The transcript above the picker keeps the theme it was written in — Ink's `<Static>` emits an\n * entry once — so a preview moves the input frame, the status bar and this overlay. That is the\n * region a reader is looking at while choosing, and it is the property that makes a preview honest\n * rather than partial.\n */\nimport { Box } from 'ink';\nimport React from 'react';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { KeyHintFooter, SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport { useScreenReader } from './screen-reader-context.js';\nimport { isInteractiveColorTerminal } from './terminal-capabilities.js';\nimport { usePalette } from './theme/index.js';\n\nimport type { IAppThemePickerViewModel, IThemeToggles } from './hooks/useAppThemeState.js';\nimport type { ITuiTheme } from './theme/index.js';\nimport type { IThemeSkip } from './theme/theme-registry.js';\n\nfunction describeTheme(theme: ITuiTheme, isActive: boolean): string {\n const active = isActive ? ' (current)' : '';\n return `${theme.name} — ${theme.appearance}, ${theme.source}${active}`;\n}\n\nfunction ThemeRow({\n theme,\n isFocused,\n isActive,\n rowNumber,\n}: {\n theme: ITuiTheme;\n isFocused: boolean;\n isActive: boolean;\n rowNumber?: number | undefined;\n}): React.ReactElement {\n const palette = usePalette();\n const marker =\n rowNumber !== undefined\n ? numberedRowPrefix(rowNumber)\n : isFocused\n ? SELECTION_INDICATOR\n : SELECTION_INDICATOR_NONE;\n return (\n <Text\n {...(isFocused && rowNumber === undefined ? { color: palette.text.accent, bold: true } : {})}\n >\n {marker}\n {describeTheme(theme, isActive)}\n </Text>\n );\n}\n\n/** The typed-selection prompt, re-printed verbatim after an out-of-range answer. */\nfunction ScreenReaderThemePrompt({\n itemCount,\n buffer,\n invalid,\n}: {\n itemCount: number;\n buffer: string;\n invalid: boolean;\n}): React.ReactElement {\n const prompt = formatNumberedSelectionPrompt(itemCount, true);\n return (\n <Box flexDirection=\"column\">\n <Text>\n {prompt}\n {buffer.length > 0 ? ` ${buffer}` : ''}\n </Text>\n {invalid && <Text>{prompt}</Text>}\n </Box>\n );\n}\n\nfunction focusedIndexOf(themes: readonly ITuiTheme[], activeThemeId: string): number {\n const index = themes.findIndex((theme) => theme.id === activeThemeId);\n return index < 0 ? 0 : index;\n}\n\n/**\n * Navigation PREVIEWS and only `select` applies, so the highlight and the live region move together\n * without anything being written until the user says so.\n */\n/**\n * Pending, not applied: the toggles travel with the selection and are submitted WITH it, so escape\n * abandons them exactly as it abandons the previewed theme. Applying them live would make escape\n * mean two different things for two controls in the same overlay.\n */\nfunction usePendingToggles(picker: IAppThemePickerViewModel): {\n toggles: IThemeToggles;\n setToggles: React.Dispatch<React.SetStateAction<IThemeToggles>>;\n} {\n const [toggles, setToggles] = React.useState<IThemeToggles>(() => ({\n syntaxHighlighting: picker.syntaxHighlighting,\n reducedMotion: picker.reducedMotion,\n }));\n return { toggles, setToggles };\n}\n\n/**\n * The highlight, and the preview that follows it. Moving WRAPS at both ends rather than stopping —\n * four themes is a short list, and a dead end at the bottom reads as a broken key.\n */\nfunction useFocusedRow(picker: IAppThemePickerViewModel): {\n focusedIndex: number;\n move: (delta: number) => void;\n} {\n const { themes, activeThemeId, preview } = picker;\n const [focusedIndex, setFocusedIndex] = React.useState(() =>\n focusedIndexOf(themes, activeThemeId),\n );\n const move = React.useCallback(\n (delta: number): void => {\n if (themes.length === 0) return;\n setFocusedIndex((current) => {\n const next = (current + delta + themes.length) % themes.length;\n const theme = themes[next];\n if (theme) preview(theme.id);\n return next;\n });\n },\n [preview, themes],\n );\n return { focusedIndex, move };\n}\n\nfunction useThemePickerSelection(\n picker: IAppThemePickerViewModel,\n screenReader: boolean,\n): {\n focusedIndex: number;\n toggles: IThemeToggles;\n numbered: ReturnType<typeof useNumberedSelection>;\n} {\n const { themes, select, cancel } = picker;\n const { focusedIndex, move } = useFocusedRow(picker);\n const { toggles, setToggles } = usePendingToggles(picker);\n const togglesRef = React.useRef(toggles);\n togglesRef.current = toggles;\n const applyAt = React.useCallback(\n (index: number): void => {\n const theme = themes[index];\n if (theme) select(theme.id, togglesRef.current);\n },\n [select, themes],\n );\n\n // CLI-2004: an arrow-key menu gets numbers and a typed answer in the mode. It applies on SELECT\n // rather than on navigation — there is no highlight to hear, so a preview per keystroke would\n // just be repaints a reader has to sit through.\n const numbered = useNumberedSelection({\n enabled: screenReader && themes.length > 0,\n itemCount: themes.length,\n cancellable: true,\n onSelect: applyAt,\n onCancel: cancel,\n });\n\n const toggle = React.useCallback(\n (key: keyof IThemeToggles): void => {\n setToggles((current) => ({ ...current, [key]: !current[key] }));\n },\n [setToggles],\n );\n\n useKeybindingActions(\n 'theme-picker',\n (actions) => {\n for (const action of actions) {\n if (action === 'previous') move(-1);\n else if (action === 'next') move(1);\n else if (action === 'select') applyAt(focusedIndex);\n else if (action === 'toggle-syntax') toggle('syntaxHighlighting');\n else if (action === 'toggle-motion') toggle('reducedMotion');\n else if (action === 'cancel') cancel();\n }\n },\n { isActive: !screenReader },\n );\n return { focusedIndex, toggles, numbered };\n}\n\nfunction ThemeRows({\n themes,\n activeThemeId,\n focusedIndex,\n screenReader,\n}: {\n themes: readonly ITuiTheme[];\n activeThemeId: string;\n focusedIndex: number;\n screenReader: boolean;\n}): React.ReactElement {\n if (themes.length === 0) return <Text dimColor>No themes installed</Text>;\n return (\n <>\n {themes.map((theme, index) => (\n <ThemeRow\n key={theme.id}\n theme={theme}\n isFocused={index === focusedIndex}\n isActive={theme.id === activeThemeId}\n {...(screenReader ? { rowNumber: index } : {})}\n />\n ))}\n </>\n );\n}\n\n/**\n * Under the colour gate a preview shows nothing, so the picker says so in words rather than letting\n * the user conclude the highlight is broken. The gate is the degradation rule this package has\n * always had; this is the one place that has to ADMIT it.\n */\nfunction ColourGateNotice(): React.ReactElement {\n if (isInteractiveColorTerminal()) return <></>;\n return (\n <Text dimColor>Colour is off for this terminal — themes apply, previews cannot show.</Text>\n );\n}\n\n/**\n * The theme files this run found and refused. They are shown, never offered: a row a user cannot\n * pick is still the only place that says why the theme they wrote is not in the list — without it\n * the file just silently is not there, and the startup line has long since scrolled away.\n */\nfunction SkippedRows({ skipped }: { skipped: readonly IThemeSkip[] }): React.ReactElement {\n if (skipped.length === 0) return <></>;\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/*\n Keyed by POSITION, not by id. `IThemeSkip.id` is a LABEL and its contract says so: a\n scope-level failure carries one id for a whole scope, and two scopes can both fail, so two\n rows can share it. A duplicate React key would put a warning on stderr from inside the live\n frame, corrupting the frame it is complaining about.\n\n Recorded limit: no test pins this. Rendering two same-id rows under ink-testing-library\n produced both rows and no warning on `console.error`, so a test asserting the warning's\n ABSENCE is green either way — an unfalsifiable guard, which is worse than none. The reason\n is written here instead of measured.\n */}\n {skipped.map((skip, index) => (\n <Text key={index} dimColor>\n {`${SELECTION_INDICATOR_NONE}Skipped \"${skip.fileName}\" — ${skip.reason}`}\n </Text>\n ))}\n </Box>\n );\n}\n\nfunction TogglesRow({\n toggles,\n reducedMotionPin,\n screenReader,\n}: {\n toggles: IThemeToggles;\n reducedMotionPin?: IAppThemePickerViewModel['reducedMotionPin'];\n screenReader: boolean;\n}): React.ReactElement {\n const on = (value: boolean): string => (value ? 'on' : 'off');\n // The pin is admitted here as it is by `/theme list` and `/theme motion` — and it names what THIS\n // RUN does, not only who decided it. The tier alone is not enough: `--no-reduced-motion` is an\n // override too and pins the opposite value, so a row carrying the chosen value beside the tier\n // can read `motion on (pinned by flag)` on a visibly still run.\n const pinned =\n reducedMotionPin === undefined\n ? ''\n : ` (this run: motion ${on(!reducedMotionPin.reducedMotion)}, pinned by ${reducedMotionPin.tier})`;\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>\n {`syntax ${on(toggles.syntaxHighlighting)} · motion ${on(!toggles.reducedMotion)}${pinned}`}\n </Text>\n {/*\n The mode takes a typed answer, so `s` and `m` are not bound in it — but the state above is\n still worth reading before choosing. So the state stays and the AFFORDANCE changes: this\n names a route the mode can actually take, instead of leaving two keys silently inert.\n */}\n {screenReader && (\n <Text dimColor>Change with /theme syntax on|off or /theme motion on|off</Text>\n )}\n </Box>\n );\n}\n\nfunction ThemePickerFooter({\n screenReader,\n itemCount,\n numbered,\n}: {\n screenReader: boolean;\n itemCount: number;\n numbered: ReturnType<typeof useNumberedSelection>;\n}): React.ReactElement {\n // Footer order: navigate → primary → dismiss, the same as every other picker in this package.\n const footerHints = useKeybindingHints('theme-picker', [\n [['previous', 'next'], 'Preview'],\n ['select', 'Apply'],\n ['toggle-syntax', 'Syntax'],\n ['toggle-motion', 'Motion'],\n ['cancel', 'Cancel'],\n ]);\n if (!screenReader) return <KeyHintFooter hints={footerHints} />;\n return (\n <ScreenReaderThemePrompt\n itemCount={itemCount}\n buffer={numbered.buffer}\n invalid={numbered.invalid}\n />\n );\n}\n\nexport default function ThemePicker({\n picker,\n}: {\n picker: IAppThemePickerViewModel;\n}): React.ReactElement {\n const palette = usePalette();\n const screenReader = useScreenReader();\n const { themes, activeThemeId } = picker;\n const { focusedIndex, toggles, numbered } = useThemePickerSelection(picker, screenReader);\n\n return (\n <Box\n flexDirection=\"column\"\n {...(screenReader\n ? {}\n : { borderStyle: 'round' as const, borderColor: palette.border.focused })}\n paddingX={1}\n >\n <Text color={palette.text.accent} bold>\n Theme\n </Text>\n <ColourGateNotice />\n <TogglesRow\n toggles={toggles}\n reducedMotionPin={picker.reducedMotionPin}\n screenReader={screenReader}\n />\n <Box flexDirection=\"column\" marginTop={1}>\n <ThemeRows\n themes={themes}\n activeThemeId={activeThemeId}\n focusedIndex={focusedIndex}\n screenReader={screenReader}\n />\n </Box>\n <SkippedRows skipped={picker.skipped} />\n <ThemePickerFooter\n screenReader={screenReader}\n itemCount={themes.length}\n numbered={numbered}\n />\n </Box>\n );\n}\n","/**\n * TransportTUI — interactive overlay for transport enable/disable settings.\n *\n * Arrow keys navigate the list, space toggles enabled/disabled, enter/esc closes.\n *\n * TRANS-009: a toggle SAVES a setting. It does not start or stop anything — `registry.setEnabled`\n * writes the settings file and returns, and `startAll` reads `getEnabled()` when a session starts, so\n * the change takes effect at the next start. This component used to render `[enabled]` on the way\n * back from that write, which told the user a transport was running when nothing had been started.\n *\n * The badge therefore names the SAVED setting and the footer says when it applies. And a failed write\n * is rendered rather than swallowed: the previous `.catch(() => setSaving(false))` took the error and\n * discarded it, so a settings file that could not be written looked exactly like a successful save\n * with the row unchanged.\n */\n\nimport { Box } from 'ink';\nimport React, { useState, useCallback } from 'react';\n\nimport { KeyHintFooter } from './key-hint-footer.js';\nimport { useKeybindingActions, useKeybindingHints } from './keybindings/keybindings-context.js';\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\nimport type {\n ITransportEntry,\n ITransportSettingsRegistryView,\n} from '@robota-sdk/agent-interface-transport';\n\nconst TRANSPORT_NAME_WIDTH = 18;\n\ninterface IEntryRowProps {\n entry: ITransportEntry;\n selected: boolean;\n}\n\nfunction TransportEntryRow({ entry, selected }: IEntryRowProps): React.ReactElement {\n const palette = usePalette();\n const enabled = entry.config.enabled;\n const dot = enabled ? '●' : '○';\n // The saved setting, not a running state — see the file header.\n const badge = enabled ? '[on] ' : '[off]';\n const portOpt = entry.config.options?.port;\n const portHint = typeof portOpt === 'number' ? `port: ${portOpt}` : '';\n return (\n <Box>\n <Text color={selected ? palette.text.accent : undefined} bold={selected}>\n {`${dot} ${entry.transport.name.padEnd(TRANSPORT_NAME_WIDTH)} ${badge} ${portHint}`}\n </Text>\n </Box>\n );\n}\n\nfunction useTransportInput(\n entries: ITransportEntry[],\n cursor: number,\n saving: boolean,\n registry: ITransportSettingsRegistryView,\n setCursor: (fn: (c: number) => number) => void,\n setSaving: (v: boolean) => void,\n setError: (v: string | undefined) => void,\n onClose: () => void,\n refresh: () => void,\n): void {\n useKeybindingActions(\n 'transport-settings',\n useCallback(\n (actions: readonly ('previous' | 'next' | 'toggle' | 'close')[]) => {\n if (saving) return;\n if (actions.includes('previous')) {\n setCursor((c) => Math.max(0, c - 1));\n return;\n }\n if (actions.includes('next')) {\n setCursor((c) => Math.min(entries.length - 1, c + 1));\n return;\n }\n if (actions.includes('close')) {\n onClose();\n return;\n }\n if (actions.includes('toggle')) {\n const entry = entries[cursor];\n if (!entry) return;\n setSaving(true);\n setError(undefined);\n registry\n .setEnabled(entry.transport.name, !entry.config.enabled)\n .then(() => {\n refresh();\n setSaving(false);\n })\n .catch((cause: unknown) => {\n // The reason, not just the fact. A settings file that cannot be written and a\n // transport that refuses configuration are different problems for the user.\n setError(cause instanceof Error ? cause.message : 'could not save the setting');\n setSaving(false);\n });\n }\n },\n [saving, entries, cursor, registry, onClose, refresh, setCursor, setSaving, setError],\n ),\n );\n}\n\ninterface IProps {\n registry: ITransportSettingsRegistryView;\n onClose: () => void;\n}\n\nexport default function TransportTUI({ registry, onClose }: IProps): React.ReactElement {\n const palette = usePalette();\n const productDisplayName = useProductDisplayName();\n const [entries, setEntries] = useState(() => registry.getAll());\n const [cursor, setCursor] = useState(0);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const refresh = useCallback((): void => {\n setEntries(registry.getAll());\n }, [registry]);\n const footerHints = useKeybindingHints('transport-settings', [\n [['previous', 'next'], 'Select'],\n ['toggle', 'Toggle'],\n ['close', 'Close'],\n ]);\n\n useTransportInput(\n entries,\n cursor,\n saving,\n registry,\n setCursor,\n setSaving,\n setError,\n onClose,\n refresh,\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n <Text bold>Settings › Transports</Text>\n <Box marginTop={1} flexDirection=\"column\">\n {entries.map((entry, i) => (\n <TransportEntryRow key={entry.transport.name} entry={entry} selected={i === cursor} />\n ))}\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <KeyHintFooter hints={footerHints} />\n <Text dimColor>\n A toggle is saved now and applies the next time {productDisplayName} starts.\n </Text>\n </Box>\n {saving && (\n <Box marginTop={1}>\n <Text color={palette.text.warning}>Saving…</Text>\n </Box>\n )}\n {error !== undefined && (\n <Box marginTop={1}>\n <Text color={palette.text.error}>{`Not saved — ${error}`}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { Box } from 'ink';\nimport React from 'react';\n\nimport { Text } from './SafeText.js';\nimport { usePalette } from './theme/index.js';\n\ninterface IProps {\n message: string;\n}\n\nexport default function UpdateNotice({ message }: IProps): React.ReactElement {\n const palette = usePalette();\n return (\n <Box paddingX={1} marginBottom={1}>\n <Text color={palette.text.warning}>{message}</Text>\n </Box>\n );\n}\n","import { Box, Static } from 'ink';\nimport React from 'react';\n\nimport { AppBanner } from './app-banner.js';\nimport BackgroundTaskPanel from './BackgroundTaskPanel.js';\nimport { ContextWarningBanner } from './ContextWarningBanner.js';\nimport ExecutionWorkspaceDetailPane from './ExecutionWorkspaceDetailPane.js';\nimport ExecutionWorkspaceSwitcher from './ExecutionWorkspaceSwitcher.js';\nimport InputArea from './InputArea.js';\nimport { useKeybindings } from './keybindings/keybindings-context.js';\nimport { EntryItem } from './MessageList.js';\nimport PendingActionPrompt from './PendingActionPrompt.js';\nimport PermissionPrompt from './PermissionPrompt.js';\nimport PluginTUI from './PluginTUI.js';\nimport { Text } from './SafeText.js';\nimport SessionEventNotices from './SessionEventNotices.js';\nimport SessionPicker from './SessionPicker.js';\nimport SessionStatusBar from './SessionStatusBar.js';\nimport StreamingIndicator from './StreamingIndicator.js';\nimport { usePalette } from './theme/index.js';\nimport ThemePicker from './ThemePicker.js';\nimport TransportTUI from './TransportTUI.js';\nimport UpdateNotice from './UpdateNotice.js';\n\nimport type { IAppViewModel } from './app-view-model.js';\n\nfunction Transcript({ model }: { model: IAppViewModel }): React.ReactElement {\n return (\n <Static key={model.transcriptKey} items={model.staticItems}>\n {(item) =>\n item.kind === 'banner' ? (\n <AppBanner key=\"logo\" version={item.version} />\n ) : (\n <EntryItem key={item.entry.id} entry={item.entry} />\n )\n }\n </Static>\n );\n}\n\nfunction Activity({ model }: { model: IAppViewModel }): React.ReactElement {\n const palette = usePalette();\n const { background, stream } = model;\n return (\n <Box flexDirection=\"column\" paddingX={1} flexGrow={1}>\n {background.selectedEntry && background.selectedEntry.kind !== 'main_thread' && (\n <ExecutionWorkspaceDetailPane\n entry={background.selectedEntry}\n page={background.detail.page}\n loading={background.detail.loading}\n error={background.detail.error}\n />\n )}\n {model.isShuttingDown && <Text color={palette.text.warning}>Shutting down...</Text>}\n {(stream.isThinking || stream.activeTools.length > 0) && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <StreamingIndicator\n text={stream.text}\n activeTools={stream.activeTools}\n isThinking={stream.isThinking}\n />\n {stream.isStalled && (\n <Text color={palette.text.warning}>\n ⚠ Still waiting on the provider — the network may be stalled. Esc to interrupt.\n </Text>\n )}\n </Box>\n )}\n {!stream.isThinking && stream.lastErrorMessage && (\n <Text color={palette.text.error}>\n ✖ Last turn failed — the session is alive; type your next prompt when ready.\n </Text>\n )}\n <BackgroundTaskPanel entries={background.entries} focusedIndex={background.focusedIndex} />\n </Box>\n );\n}\n\nfunction Overlays({ model }: { model: IAppViewModel }): React.ReactElement {\n if (model.coordinationError !== undefined || model.coordinationPending) return <></>;\n const { background, plugin, sessionPicker, transport } = model;\n const pendingUserAction = model.pendingUserAction;\n return (\n <>\n {background.switcherVisible && (\n <ExecutionWorkspaceSwitcher\n snapshot={background.snapshot}\n selectedEntryId={background.selectedEntryId}\n onSelect={background.select}\n onClose={background.closeSwitcher}\n onAttach={background.attachToFork}\n />\n )}\n {model.permissionRequest && <PermissionPrompt request={model.permissionRequest} />}\n {pendingUserAction && (\n <PendingActionPrompt\n request={pendingUserAction}\n onAnswer={(response) => model.resolveUserAction(pendingUserAction, response)}\n />\n )}\n {plugin.visible && (\n <PluginTUI\n callbacks={plugin.callbacks}\n onClose={plugin.close}\n addMessage={(message) => plugin.addMessage(message.content)}\n />\n )}\n {transport.visible && transport.registry && (\n <TransportTUI registry={transport.registry} onClose={transport.close} />\n )}\n {sessionPicker.visible && (\n <SessionPicker\n sessions={sessionPicker.sessions}\n onSelect={sessionPicker.select}\n onCancel={sessionPicker.cancel}\n />\n )}\n {model.theme.picker.visible && <ThemePicker picker={model.theme.picker} />}\n </>\n );\n}\n\nfunction PromptAndStatus({ model }: { model: IAppViewModel }): React.ReactElement {\n const { input } = model;\n return (\n <>\n <ContextWarningBanner percentage={model.contextPercentage} />\n <InputArea\n onSubmit={input.submit}\n onCancelQueue={input.cancelQueue}\n isDisabled={input.disabled}\n isQueueCancellationDisabled={input.queueCancellationDisabled}\n isAborting={input.isAborting}\n pendingPrompt={input.pendingPrompt}\n pendingCount={input.pendingCount}\n commandQueryPort={input.commandQueryPort}\n sessionName={input.sessionName}\n history={input.history}\n onRequestFocusBackgroundList={input.focusBackgroundList}\n historySearch={input.historySearch}\n initialValue={input.initialValue}\n consumeInitialValue={input.consumeInitialValue}\n externalPromptOrigin={input.externalPromptOrigin}\n />\n <SessionStatusBar {...model.status} />\n </>\n );\n}\n\n/** Pure presentation tree: it receives no channel, session, registry, or mutable state manager. */\nexport default function AppPresentation({\n viewModel,\n}: {\n viewModel: IAppViewModel;\n}): React.ReactElement {\n const palette = usePalette();\n const keybindings = useKeybindings();\n return (\n <Box flexDirection=\"column\">\n <Transcript model={viewModel} />\n {!viewModel.handoffSuspended && (\n <>\n {viewModel.updateNotice && <UpdateNotice message={viewModel.updateNotice} />}\n {viewModel.coordinationError && (\n <Text color={palette.text.error}>\n {viewModel.coordinationError} Press Enter to retry.\n </Text>\n )}\n {keybindings.diagnostic && (\n <Text color={palette.text.error}>\n {`Keybindings ${keybindings.diagnostic.file} ${keybindings.diagnostic.path}: ${keybindings.diagnostic.message} Last valid bindings remain active.`}\n </Text>\n )}\n {keybindings.warnings.map((warning) => (\n <Text key={`${warning.path}:${warning.code}`} color={palette.text.warning}>\n {`Keybindings ${warning.path}: ${warning.message}`}\n </Text>\n ))}\n {viewModel.theme.unknownThemeNotice !== undefined && (\n <Text color={palette.text.warning}>{viewModel.theme.unknownThemeNotice}</Text>\n )}\n <SessionEventNotices notices={viewModel.sessionEventNotices} />\n <Activity model={viewModel} />\n <Overlays model={viewModel} />\n <PromptAndStatus model={viewModel} />\n </>\n )}\n </Box>\n );\n}\n","/**\n * SCREEN-010 / CLI-2004 — what gets committed to the terminal's native scrollback.\n *\n * The App renders exactly this list inside a single Ink `<Static>`, so each item is written once and\n * never repainted. Building the list is a pure function so the ONE decision that varies — whether\n * the startup banner is committed at all — is assertable without mounting the App.\n */\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\n/** A committed item: the startup banner, or one conversation entry. */\nexport type TStaticItem =\n | { readonly kind: 'banner'; readonly version: string }\n | { readonly kind: 'entry'; readonly entry: IHistoryEntry };\n\nexport interface IBuildStaticItemsInputs {\n history: readonly IHistoryEntry[];\n version: string | undefined;\n /**\n * Screen-reader mode. The banner is ASCII art with no spoken meaning — a reader announces it\n * character by character before the session has said anything — so the mode omits the item\n * entirely rather than styling it differently.\n */\n screenReader: boolean;\n}\n\nconst FALLBACK_VERSION = '0.0.0';\n\n/** Build the `<Static>` item list. */\nexport function buildStaticItems(inputs: IBuildStaticItemsInputs): TStaticItem[] {\n const banner: TStaticItem[] = inputs.screenReader\n ? []\n : [{ kind: 'banner', version: inputs.version ?? FALLBACK_VERSION }];\n return [...banner, ...inputs.history.map((entry): TStaticItem => ({ kind: 'entry', entry }))];\n}\n","/**\n * Interrupt-signal policy for the interactive TUI (CLI-075 / RUNTIME-33).\n *\n * The first Ctrl+C or termination signal runs the graceful shutdown (persist session, cancel\n * background work, drain queues). A second interrupt arriving while a shutdown is already in flight\n * is a force-quit: the user is telling us the graceful path is wedged, so we exit immediately rather\n * than no-op. Exit code 130 = 128 + SIGINT(2), the conventional \"terminated by Ctrl+C\" code.\n */\n\n/** Conventional \"terminated by SIGINT\" exit code (128 + 2). */\nexport const FORCE_QUIT_EXIT_CODE = 130;\n\nexport interface IHandleInterruptParams {\n /** Whether a graceful shutdown is already in progress. */\n isShuttingDown: boolean;\n /** Runs the graceful shutdown (only on the first interrupt). */\n graceful: () => void;\n /** Force-quits the process (defaults to `process.exit`); injectable for tests. */\n forceExit?: (code: number) => void;\n}\n\n/**\n * Route an interrupt (Ctrl+C / SIGINT / SIGTERM) to either the graceful path or a force-quit.\n * Keeping this pure and side-effect-injectable makes the second-signal contract unit-testable\n * without spawning a process.\n */\nexport function handleInterrupt(params: IHandleInterruptParams): void {\n const forceExit = params.forceExit ?? ((code: number): void => process.exit(code));\n if (params.isShuttingDown) {\n forceExit(FORCE_QUIT_EXIT_CODE);\n return;\n }\n params.graceful();\n}\n","/**\n * CLI-2004 — the audible attention signal.\n *\n * Three triggers, all of them \"the session now needs you and you cannot see that it does\": a reply\n * finished, a prompt or dialog wants an answer, and a tool that ran long enough that you had stopped\n * watching has finished.\n *\n * The long-tool threshold is a NAMED CONSTANT rather than a copied literal. It comes from a single\n * reference, so it is the kind of number that has to be tunable and attributable — not folded into\n * an inline `> 5000`.\n *\n * Written outside Ink for the same reason as `terminal-marks.ts`: BEL is terminal framing, not\n * rendered content.\n */\n\n/** A tool that took longer than this rings on completion. Single-reference; tunable by design. */\nexport const LONG_TOOL_BELL_MS = 5000;\n\n/** The bell byte. */\nexport const BELL = '\\x07';\n\nexport interface IAttentionBellOptions {\n /** Screen-reader mode. Off ⇒ nothing is ever written. */\n enabled: boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n /** Injected clock so the threshold is testable without real time. */\n now?: () => number;\n /** Override the long-tool threshold. */\n longToolBellMs?: number;\n}\n\nexport interface IAttentionBell {\n /** The assistant finished a reply. */\n replyCompleted(): void;\n /** A prompt or dialog mounted and is waiting for an answer. */\n promptMounted(): void;\n /** Record a tool's start so its duration can be judged at completion. */\n toolStarted(toolId: string): void;\n /** A tool finished — rings only if its recorded start is older than the threshold. */\n toolCompleted(toolId: string): void;\n}\n\n/**\n * Build the bell. A tool whose start was never recorded never rings: the signal is \"the thing you\n * stopped waiting for is done\", and without a start there is no duration to judge, so silence is the\n * correct answer rather than a guessed one.\n */\nexport function createAttentionBell(options: IAttentionBellOptions): IAttentionBell {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n const now = options.now ?? ((): number => Date.now());\n const threshold = options.longToolBellMs ?? LONG_TOOL_BELL_MS;\n const startedAt = new Map<string, number>();\n\n const ring = (): void => {\n if (!options.enabled) return;\n write(BELL);\n };\n\n return {\n replyCompleted: ring,\n promptMounted: ring,\n toolStarted(toolId: string): void {\n if (!options.enabled) return;\n startedAt.set(toolId, now());\n },\n toolCompleted(toolId: string): void {\n const started = startedAt.get(toolId);\n startedAt.delete(toolId);\n if (started === undefined) return;\n if (now() - started <= threshold) return;\n ring();\n },\n };\n}\n","/**\n * CLI-2004 — OSC 133 shell-integration marks at agent-turn boundaries.\n *\n * `OSC 133 ; A` marks prompt start, `; B` prompt end, `; C` pre-execution and `; D` execution\n * finished. Terminals that implement the protocol turn those into jump-to-previous-prompt\n * navigation, which is how a reader reviews a long session without scrolling line by line.\n *\n * Written OUTSIDE Ink, following the `use-terminal-title.ts` carve-out: `sanitize-terminal-text.ts`\n * strips OSC from untrusted text and the `tui-safe-text-boundary` scan permits only `SafeText` to\n * import Ink's `Text`, so an escape sequence this package writes deliberately cannot go through a\n * component. Unlike the title writer there is no interpolated payload here at all — the four\n * sequences are constants — so nothing needs sanitizing.\n *\n * A terminal that does not implement OSC 133 discards the unknown sequence silently, so emission is\n * safe wherever it is on; `supportsTurnMarks()` (terminal-capabilities.ts) carries the documented\n * negatives rather than a capability probe.\n */\n\n/** The four boundary marks, in the order one turn emits them. */\nexport type TTurnMark = 'promptStart' | 'promptEnd' | 'turnStart' | 'turnEnd';\n\nconst MARK_CODES: Readonly<Record<TTurnMark, string>> = {\n promptStart: 'A',\n promptEnd: 'B',\n turnStart: 'C',\n turnEnd: 'D',\n};\n\n/** The exact byte sequence for one mark. */\nexport function turnMarkSequence(mark: TTurnMark): string {\n return `\\x1b]133;${MARK_CODES[mark]}\\x07`;\n}\n\nexport interface ITurnMarkWriterOptions {\n /** Screen-reader mode. Off ⇒ nothing is ever written. */\n enabled: boolean;\n /** Documented per-terminal support gate. */\n supported: () => boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n}\n\nexport interface ITurnMarkWriter {\n emit(mark: TTurnMark): void;\n}\n\n/**\n * Build the mark writer. Both gates are read at emit time, not construction time, so a test can flip\n * the support gate between emissions without rebuilding the writer.\n */\nexport function createTurnMarkWriter(options: ITurnMarkWriterOptions): ITurnMarkWriter {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n return {\n emit(mark: TTurnMark): void {\n if (!options.enabled || !options.supported()) return;\n write(turnMarkSequence(mark));\n },\n };\n}\n","/**\n * CLI-2004 — the non-visual half of a turn: the bell and the OSC 133 boundary marks.\n *\n * Both are byte-stream events, not rendering, so they live in a hook rather than a component: the\n * App says WHEN a turn starts, ends, or wants an answer, and this decides what reaches the terminal.\n * When the mode is off both writers are inert, so the App has exactly one code path.\n *\n * One turn emits `A` (prompt start) → `B` (prompt end) → `C` (turn start) → `D` (turn end): the\n * prompt is marked while the user is being asked for input, and the execution pair brackets the\n * agent's work. That is the boundary a reader jumps between.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { createAttentionBell } from '../attention-bell.js';\nimport { useScreenReaderPacing } from '../screen-reader-pacing-context.js';\nimport { supportsTurnMarks } from '../terminal-capabilities.js';\nimport { useTerminalCapabilityOverrides } from '../terminal-capabilities-context.js';\nimport { createTurnMarkWriter } from '../terminal-marks.js';\n\nimport type { IAttentionBell } from '../attention-bell.js';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseScreenReaderTurnSignalsInputs {\n enabled: boolean;\n /** The agent is working (a turn is in flight). */\n isThinking: boolean;\n /** Tools currently reported as active by the session. */\n activeTools: readonly IToolState[];\n /** A permission ask or user-action dialog is mounted and waiting for an answer. */\n awaitingAnswer: boolean;\n}\n\n/**\n * Stable key for one tool run within a turn.\n *\n * `executionId` where the session supplies one; otherwise the position, which `onToolEnd` preserves\n * (it replaces the entry at its index rather than removing it).\n */\nfunction toolKey(tool: IToolState, index: number): string {\n return tool.executionId ?? `${tool.toolName}#${index}`;\n}\n\n/** The keys of the tools currently RUNNING — not merely present in the array. */\nfunction runningKeys(activeTools: readonly IToolState[]): Set<string> {\n const running = new Set<string>();\n activeTools.forEach((tool, index) => {\n if (tool.isRunning) running.add(toolKey(tool, index));\n });\n return running;\n}\n\n/** Wire the bell and the turn marks to the App's turn lifecycle. */\nexport function useScreenReaderTurnSignals(inputs: IUseScreenReaderTurnSignalsInputs): void {\n const { enabled, isThinking, activeTools, awaitingAnswer } = inputs;\n const bell = useMemo<IAttentionBell>(() => createAttentionBell({ enabled }), [enabled]);\n // SCREEN-2670: the marks are POSITIONAL — the terminal records the line they arrive on — so they\n // travel through the owned write path, ordered with the commit they belong to. Outside a provider\n // the port writes to the process's own stdout, which is exactly today's path.\n const pacing = useScreenReaderPacing();\n const terminalCapabilities = useTerminalCapabilityOverrides();\n const marks = useMemo(\n () =>\n createTurnMarkWriter({\n enabled,\n supported: () => supportsTurnMarks({ override: terminalCapabilities.turnMarks }),\n write: (text) => pacing.write(text),\n }),\n [enabled, pacing, terminalCapabilities.turnMarks],\n );\n const wasThinking = useRef(false);\n const runningTools = useRef<ReadonlySet<string>>(new Set());\n\n // Turn boundaries: B/C when work starts, D/A when it ends and the prompt comes back.\n useEffect(() => {\n if (isThinking && !wasThinking.current) {\n marks.emit('promptEnd');\n marks.emit('turnStart');\n } else if (!isThinking && wasThinking.current) {\n marks.emit('turnEnd');\n marks.emit('promptStart');\n bell.replyCompleted();\n }\n wasThinking.current = isThinking;\n }, [isThinking, bell, marks]);\n\n // The very first prompt of the session is marked once, before anything has been asked.\n useEffect(() => {\n marks.emit('promptStart');\n }, [marks]);\n\n // A prompt or dialog that wants an answer rings once on mount.\n useEffect(() => {\n if (awaitingAnswer) bell.promptMounted();\n }, [awaitingAnswer, bell]);\n\n // Long-running tools: record each start, ring on the completions that outlived the threshold.\n //\n // Completion is the `isRunning` true→false transition, NOT the entry leaving the array. The\n // session's `onToolEnd` replaces the finished entry in place (same index, same name, so the same\n // key) and only `onThinking(false)` empties the array — so a membership test would report every\n // tool as completing at turn end, judging its duration against the whole turn.\n useEffect(() => {\n const running = runningKeys(activeTools);\n for (const key of running) {\n if (!runningTools.current.has(key)) bell.toolStarted(key);\n }\n for (const key of runningTools.current) {\n if (!running.has(key)) bell.toolCompleted(key);\n }\n runningTools.current = running;\n }, [activeTools, bell]);\n}\n","import { useApp, useInput } from 'ink';\nimport { useEffect } from 'react';\n\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\nimport { useScreenReader } from '../screen-reader-context.js';\nimport { handleInterrupt } from '../shutdown-signal.js';\nimport { useScreenReaderTurnSignals } from './useScreenReaderTurnSignals.js';\n\nimport type { IPendingPermissionRequest } from '../types.js';\nimport type { IActionRequest, TSessionEndReason } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\ninterface IOptions {\n readonly isThinking: boolean;\n readonly isShuttingDown: boolean;\n /** An observer stops nothing: neither the drivers' turn nor their loop. */\n readonly readOnly: boolean;\n /** Attached to a session a host runs: Ctrl-] detaches, like Ctrl-C and `/exit` there. */\n readonly attached: boolean;\n readonly permissionRequest: IPendingPermissionRequest | null;\n readonly pendingUserAction: IActionRequest | null;\n readonly pluginVisible: boolean;\n readonly transportVisible: boolean;\n readonly sessionPickerVisible: boolean;\n readonly workspaceSwitcherVisible: boolean;\n /** SCREEN-1993: the history-search overlay owns Esc and the switcher key while open. */\n readonly historySearchOpen: boolean;\n readonly selectedEntry: IExecutionWorkspaceEntry | undefined;\n readonly backgroundListFocused: boolean;\n readonly mainThreadEntryId: string | undefined;\n readonly activeTools: readonly IToolState[];\n readonly abort: () => void;\n readonly stopWaitingLoop: () => Promise<void>;\n readonly toggleWorkspaceSwitcher: () => void;\n readonly selectWorkspaceEntry: (entryId: string) => void;\n readonly shutdown: (reason?: TSessionEndReason) => Promise<void>;\n readonly recoveryError: string | undefined;\n readonly recoveryPending: boolean;\n readonly coordinationBlocked: boolean;\n /** SCREEN-2002: the theme picker owns its keys while it is open, like every sibling overlay. */\n readonly themePickerVisible: boolean;\n readonly retryRecovery: () => void;\n}\n\nfunction overlaysBlockKeys(options: IOptions): boolean {\n return Boolean(\n options.permissionRequest ||\n options.pendingUserAction ||\n options.pluginVisible ||\n options.transportVisible ||\n options.sessionPickerVisible ||\n options.workspaceSwitcherVisible ||\n options.historySearchOpen ||\n options.themePickerVisible ||\n options.coordinationBlocked,\n );\n}\n\nfunction useEscapeBindings(options: IOptions): void {\n const context = options.isThinking ? 'thinking' : 'background-detail';\n useKeybindingActions(context, (actions) => {\n if (overlaysBlockKeys(options)) return;\n if (actions.includes('abort') && options.isThinking) {\n if (!options.readOnly) options.abort();\n return;\n }\n const selected = options.selectedEntry;\n if (\n actions.includes('return-to-main') &&\n selected &&\n selected.kind !== 'main_thread' &&\n options.mainThreadEntryId\n ) {\n options.selectWorkspaceEntry(options.mainThreadEntryId);\n return;\n }\n if (\n actions.includes('return-to-main') &&\n !options.backgroundListFocused &&\n !options.readOnly\n ) {\n void options.stopWaitingLoop();\n }\n });\n}\n\nfunction useWorkspaceSwitcherBinding(options: IOptions): void {\n useKeybindingActions('app', (actions) => {\n if (!actions.includes('open-workspace-switcher')) return;\n if (\n options.permissionRequest ||\n options.pendingUserAction ||\n options.pluginVisible ||\n options.sessionPickerVisible ||\n options.historySearchOpen ||\n options.themePickerVisible ||\n options.coordinationBlocked ||\n options.isShuttingDown\n )\n return;\n options.toggleWorkspaceSwitcher();\n });\n}\n\nfunction useRecoveryBinding(options: IOptions): void {\n useKeybindingActions('recovery', (actions) => {\n if (\n options.recoveryError !== undefined &&\n !options.recoveryPending &&\n actions.includes('retry')\n ) {\n options.retryRecovery();\n }\n });\n}\n\n/** Ctrl-]: the raw byte, or `]` with ctrl where the terminal reports modifiers. */\nconst DETACH_BYTE = '\\x1d';\n\nfunction isDetachKey(input: string, ctrl: boolean): boolean {\n return input === DETACH_BYTE || (ctrl && input === ']');\n}\n\nfunction useShutdownBindings(options: IOptions): void {\n const { exit } = useApp();\n const requestShutdown = (reason: TSessionEndReason): void => {\n handleInterrupt({\n isShuttingDown: options.isShuttingDown,\n graceful: () => void options.shutdown(reason).finally(() => exit()),\n });\n };\n useInput((input, key) => {\n if (key.ctrl && input === 'c') requestShutdown('prompt_input_exit');\n // Ctrl-] is the attach client's detach key. On a terminal running its own session there is\n // nothing to detach from, and quitting that session is left to Ctrl-C and `/exit`.\n else if (options.attached && isDetachKey(input, key.ctrl)) requestShutdown('prompt_input_exit');\n });\n useEffect(() => {\n const onSignal = (): void => requestShutdown('other');\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n return () => {\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n };\n });\n}\n\nexport function useAppInputBindings(options: IOptions): boolean {\n useEscapeBindings(options);\n useRecoveryBinding(options);\n useWorkspaceSwitcherBinding(options);\n useShutdownBindings(options);\n const screenReader = useScreenReader();\n useScreenReaderTurnSignals({\n enabled: screenReader,\n isThinking: options.isThinking,\n activeTools: options.activeTools,\n awaitingAnswer: options.permissionRequest !== null || options.pendingUserAction !== null,\n });\n return screenReader;\n}\n","/**\n * Hook: uses the host plugin adapter, or an explicit unsupported adapter when none is provided.\n *\n * A missing host capability must fail visibly. Returning successful no-ops would make the plugin\n * screen claim mutations succeeded while changing nothing.\n */\n\nimport { useMemo } from 'react';\n\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\n\nfunction unsupported(): Promise<never> {\n return Promise.reject(new Error('Plugin management is unavailable in this host.'));\n}\n\nfunction createUnsupportedPluginAdapter(): ICommandPluginAdapter {\n return {\n listInstalled: unsupported,\n listAvailablePlugins: unsupported,\n install: unsupported,\n uninstall: unsupported,\n enable: unsupported,\n disable: unsupported,\n marketplaceAdd: unsupported,\n marketplaceRemove: unsupported,\n marketplaceUpdate: unsupported,\n marketplaceList: unsupported,\n reloadPlugins: unsupported,\n };\n}\n\nexport function resolvePluginCallbacks(adapter?: ICommandPluginAdapter): ICommandPluginAdapter {\n return adapter ?? createUnsupportedPluginAdapter();\n}\n\nexport function usePluginCallbacks(adapter?: ICommandPluginAdapter): ICommandPluginAdapter {\n return useMemo(() => resolvePluginCallbacks(adapter), [adapter]);\n}\n","import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport { listResumableSessionSummaries } from '@robota-sdk/agent-framework';\nimport { useCallback, useEffect } from 'react';\n\nimport { usePluginCallbacks } from './usePluginCallbacks.js';\n\nimport type {\n IAppPluginViewModel,\n IAppSessionPickerViewModel,\n IAppTransportViewModel,\n} from '../app-view-model.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type {\n IInteractiveSessionStore,\n ISessionListingEntry,\n} from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IOptions {\n readonly cwd: string;\n readonly pluginAdapter: ICommandPluginAdapter | undefined;\n readonly pluginVisible: boolean;\n readonly setPluginVisible: (visible: boolean) => void;\n readonly transportRegistry: ITransportRegistryView | undefined;\n readonly transportVisible: boolean;\n readonly setTransportVisible: (visible: boolean) => void;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n /** The host's sessions, when the channel's host keeps them; they replace the local store's. */\n readonly hostSessions: readonly ISessionListingEntry[] | undefined;\n readonly sessionPickerVisible: boolean;\n readonly setSessionPickerVisible: (visible: boolean) => void;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\nconst NO_SESSIONS_NOTICE = 'No saved sessions to resume in this workspace.';\n\nexport interface IAppOverlays {\n readonly plugin: IAppPluginViewModel;\n readonly transport: IAppTransportViewModel;\n readonly sessionPicker: IAppSessionPickerViewModel;\n}\n\nexport function useAppOverlays(options: IOptions): IAppOverlays {\n const pluginCallbacks = usePluginCallbacks(options.pluginAdapter);\n const addPluginMessage = useCallback(\n (content: string) => options.addEntry(messageToHistoryEntry(createSystemMessage(content))),\n [options.addEntry],\n );\n const selectSession = useCallback(\n (sessionId: string) => {\n options.setSessionPickerVisible(false);\n void options.onSessionSwitch(sessionId);\n },\n [options.onSessionSwitch, options.setSessionPickerVisible],\n );\n const cancelSessionPicker = useCallback(() => {\n options.setSessionPickerVisible(false);\n options.addEntry(messageToHistoryEntry(createSystemMessage('Session resume cancelled.')));\n }, [options.addEntry, options.setSessionPickerVisible]);\n const sessions =\n options.hostSessions ?? listResumableSessionSummaries(options.sessionStore, options.cwd);\n // A picker with nothing to pick would only hold the prompt: say so and give the prompt back.\n const nothingToResume = options.sessionPickerVisible && sessions.length === 0;\n useEffect(() => {\n if (!nothingToResume) return;\n options.setSessionPickerVisible(false);\n options.addEntry(messageToHistoryEntry(createSystemMessage(NO_SESSIONS_NOTICE)));\n }, [nothingToResume, options.addEntry, options.setSessionPickerVisible]);\n return {\n plugin: {\n visible: options.pluginVisible,\n callbacks: pluginCallbacks,\n close: () => options.setPluginVisible(false),\n addMessage: addPluginMessage,\n },\n transport: {\n visible: options.transportVisible,\n registry: options.transportRegistry,\n close: () => options.setTransportVisible(false),\n },\n sessionPicker: {\n visible: options.sessionPickerVisible && !nothingToResume,\n sessions,\n select: selectSession,\n cancel: cancelSessionPicker,\n },\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\ninterface IOptions {\n readonly selectedEntry: IExecutionWorkspaceEntry | undefined;\n readonly submit: (input: string) => Promise<void>;\n readonly sendAgentJob: (taskId: string, input: string) => Promise<void>;\n readonly isThinking: boolean;\n}\n\nexport interface IAppSubmissionState {\n readonly submit: (input: string) => Promise<void>;\n readonly gitRefreshToken: number;\n}\n\nexport function useAppSubmissionState(options: IOptions): IAppSubmissionState {\n const [gitRefreshToken, setGitRefreshToken] = useState(0);\n const routedSubmit = useCallback(\n async (input: string): Promise<void> => {\n const entry = options.selectedEntry;\n if (entry && entry.kind !== 'main_thread' && entry.controls.includes('send')) {\n await options.sendAgentJob(entry.sourceId, input);\n return;\n }\n await options.submit(input);\n },\n [options.selectedEntry, options.sendAgentJob, options.submit],\n );\n const submit = useCallback(\n async (input: string): Promise<void> => {\n setGitRefreshToken((token) => token + 1);\n await routedSubmit(input);\n },\n [routedSubmit],\n );\n const wasThinkingRef = useRef(false);\n useEffect(() => {\n if (wasThinkingRef.current && !options.isThinking) {\n setGitRefreshToken((token) => token + 1);\n }\n wasThinkingRef.current = options.isThinking;\n }, [options.isThinking]);\n return { submit, gitRefreshToken };\n}\n","import { useAppInputBindings } from './useAppInputBindings.js';\nimport { useAppOverlays } from './useAppOverlays.js';\nimport { useAppSubmissionState } from './useAppSubmissionState.js';\n\nimport type { IAppOverlays } from './useAppOverlays.js';\nimport type { IAppScreenState } from './useAppScreenState.js';\nimport type { IAppSubmissionState } from './useAppSubmissionState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IOptions {\n readonly cwd: string;\n readonly state: ITuiChannelState;\n readonly shell: IAppScreenState;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly pluginAdapter: ICommandPluginAdapter | undefined;\n readonly transportRegistry: ITransportRegistryView | undefined;\n readonly recoveryError: string | undefined;\n readonly recoveryPending: boolean;\n readonly coordinationBlocked: boolean;\n readonly retryRecovery: () => void;\n}\n\nexport interface IAppInteractionState {\n readonly submission: IAppSubmissionState;\n readonly overlays: IAppOverlays;\n readonly screenReader: boolean;\n}\n\nexport function useAppInteractionState(options: IOptions): IAppInteractionState {\n const { screens, workspace } = options.shell;\n const submission = useAppSubmissionState({\n selectedEntry: workspace.background.selectedEntry,\n submit: screens.handleSubmit,\n sendAgentJob: options.state.sendAgentJob,\n isThinking: options.state.isThinking,\n });\n const screenReader = useAppInputBindings({\n isThinking: options.state.isThinking,\n isShuttingDown: options.state.isShuttingDown,\n readOnly: options.state.readOnly,\n attached: options.state.attached,\n permissionRequest: options.state.permissionRequest,\n pendingUserAction: options.state.pendingUserAction,\n pluginVisible: screens.showPluginTUI,\n transportVisible: screens.showTransportTUI,\n sessionPickerVisible: screens.showSessionPicker,\n workspaceSwitcherVisible: workspace.background.switcherVisible,\n historySearchOpen: options.shell.historySearchOpen,\n themePickerVisible: screens.showThemePicker,\n selectedEntry: workspace.background.selectedEntry,\n backgroundListFocused: workspace.isBackgroundListFocused,\n mainThreadEntryId: workspace.mainThreadEntryId,\n activeTools: options.state.activeTools,\n abort: options.state.handleAbort,\n stopWaitingLoop: options.state.handleStopWaitingLoop,\n toggleWorkspaceSwitcher: workspace.toggleSwitcher,\n selectWorkspaceEntry: options.state.selectExecutionWorkspaceEntry,\n shutdown: options.state.handleShutdown,\n recoveryError: options.recoveryError,\n recoveryPending: options.recoveryPending,\n coordinationBlocked: options.coordinationBlocked,\n retryRecovery: options.retryRecovery,\n });\n const overlays = useAppOverlays({\n cwd: options.cwd,\n pluginAdapter: options.pluginAdapter,\n pluginVisible: screens.showPluginTUI,\n setPluginVisible: screens.setShowPluginTUI,\n transportRegistry: options.transportRegistry,\n transportVisible: screens.showTransportTUI,\n setTransportVisible: screens.setShowTransportTUI,\n sessionStore: options.sessionStore,\n hostSessions: options.state.hostSessions,\n sessionPickerVisible: screens.showSessionPicker,\n setSessionPickerVisible: screens.setShowSessionPicker,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.state.addEntry,\n });\n return { submission, overlays, screenReader };\n}\n","import { useEffect } from 'react';\n\nimport { useProductDisplayName } from './product-display-name-context.js';\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\n/**\n * Set the terminal's window title from the session name.\n *\n * Split out of `App.tsx` under SEC-019 (issue #2022) by responsibility: this is the one place the TUI\n * writes an escape sequence to stdout directly rather than through Ink, which makes it the one place\n * where a value is interpolated INTO terminal syntax rather than rendered as content. Keeping it\n * inside a 600-line component is what let it sit unexamined next to code that never touches an\n * escape.\n *\n * `sessionName` is untrusted — a session can be named from a prompt, and a name containing BEL or ESC\n * terminates the OSC early, so everything after it is read by the terminal as its own command.\n *\n * Both the host-selected product name and the session name are sanitized separately from the\n * `\\x1b]0;` … `\\x07` framing this module deliberately writes.\n */\nexport function useTerminalTitle(sessionName: string | undefined): void {\n const productDisplayName = useProductDisplayName();\n useEffect(() => {\n const safeName = sanitizeTerminalText(sessionName ?? '');\n const safeProductName = sanitizeTerminalText(productDisplayName);\n const title = safeName ? `${safeProductName} — ${safeName}` : safeProductName;\n process.stdout.write(`\\x1b]0;${title}\\x07`);\n }, [sessionName, productDisplayName]);\n}\n","/**\n * TERM-002: React glue for the terminal-handoff controller.\n *\n * Registers the App's suspend/resume into the controller. On suspend the App renders nothing (see\n * App.tsx) so Ink unmounts its input hooks and releases raw mode, handing the real terminal to the\n * child process; on resume the App re-renders.\n */\nimport { useEffect, useState } from 'react';\n\nimport type { TerminalHandoffController } from '../terminal-handoff-controller.js';\n\n/** Time for React/Ink to commit the empty render (unmount input, release raw mode) before handoff. */\nconst SUSPEND_COMMIT_DELAY_MS = 50;\n\nexport function useTerminalHandoffSuspension(\n controller: TerminalHandoffController | undefined,\n): boolean {\n const [suspended, setSuspended] = useState(false);\n useEffect(() => {\n if (!controller) return;\n return controller.registerSuspendHooks({\n suspend: async () => {\n setSuspended(true);\n await new Promise<void>((resolve) => setTimeout(resolve, SUSPEND_COMMIT_DELAY_MS));\n },\n resume: () => setSuspended(false),\n });\n }, [controller]);\n return suspended;\n}\n","import { useCallback, useEffect, useState } from 'react';\n\nimport { useTerminalTitle } from '../use-terminal-title.js';\nimport { useTerminalHandoffSuspension } from './useTerminalHandoffSuspension.js';\n\nimport type { ITuiAppChannelPort } from '../tui-app-channel-port.js';\n\nexport interface IAppLifecycleState {\n readonly sessionName: string | undefined;\n readonly setSessionName: (name: string) => void;\n readonly handoffSuspended: boolean;\n readonly updateNotice: string | undefined;\n readonly startError: string | undefined;\n readonly startPending: boolean;\n readonly retryStart: () => void;\n}\n\nfunction useChannelLifecycle(channel: ITuiAppChannelPort): {\n startError: string | undefined;\n startPending: boolean;\n retryStart: () => void;\n} {\n const [startError, setStartError] = useState<string | undefined>();\n const [startPending, setStartPending] = useState(true);\n const retryStart = useCallback((): void => {\n setStartPending(true);\n void channel\n .start()\n .then(() => setStartError(undefined))\n .catch((cause) => {\n const message = cause instanceof Error ? cause.message : String(cause);\n setStartError(`TUI start failed: ${message}`);\n })\n .finally(() => setStartPending(false));\n }, [channel]);\n useEffect(() => {\n retryStart();\n }, [channel, retryStart]);\n return { startError, startPending, retryStart };\n}\n\nfunction useStartupUpdateNotice(source?: Promise<string | undefined>): string | undefined {\n const [notice, setNotice] = useState<string | undefined>();\n useEffect(() => {\n let mounted = true;\n source\n ?.then((value) => {\n if (mounted && value !== undefined) setNotice(value);\n })\n .catch(() => {\n // Startup update checks are best-effort and must not disrupt the TUI.\n });\n return () => {\n mounted = false;\n };\n }, [source]);\n return notice;\n}\n\nexport function useAppLifecycleState(\n channel: ITuiAppChannelPort,\n startupUpdateNotice?: Promise<string | undefined>,\n): IAppLifecycleState {\n const channelLifecycle = useChannelLifecycle(channel);\n const [sessionName, setSessionName] = useState<string | undefined>(channel.sessionName);\n useEffect(() => {\n if (channel.sessionName !== undefined && channel.sessionName !== sessionName) {\n setSessionName(channel.sessionName);\n }\n }, [channel, channel.sessionName, sessionName]);\n useTerminalTitle(sessionName);\n const handoffSuspended = useTerminalHandoffSuspension(channel.terminalHandoffController);\n const updateNotice = useStartupUpdateNotice(startupUpdateNotice);\n return {\n sessionName,\n setSessionName,\n handoffSuspended,\n updateNotice,\n ...channelLifecycle,\n };\n}\n","import { readAppearanceSettings } from '@robota-sdk/agent-framework';\nimport { useCallback, useState } from 'react';\n\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { ITuiCliAdapter } from '../tui-cli-adapter.js';\nimport type { IAppearanceSettings } from '@robota-sdk/agent-interface-command';\n\nfunction readFromAdapter(cliAdapter: ITuiCliAdapter): IAppearanceSettings {\n return readAppearanceSettings(cliAdapter.readSettings(cliAdapter.getUserSettingsPath()));\n}\n\n/**\n * SCREEN-2002 — the persisted appearance, plus a from-disk `refresh()`.\n *\n * Refresh-on-result, the shape `useStatusLineSettings` already uses: the HOST applies\n * `appearance-settings-patch` through the settings adapter, so the renderer learns about a change by\n * RE-READING the document when a command result arrives. The TUI never writes these keys itself,\n * which is what keeps one writer for one file.\n */\nexport function useAppearanceSettings(): [IAppearanceSettings, () => void] {\n const cliAdapter = useTuiCliAdapter();\n const [appearance, setAppearance] = useState<IAppearanceSettings>(() =>\n readFromAdapter(cliAdapter),\n );\n const refresh = useCallback((): void => {\n setAppearance(readFromAdapter(cliAdapter));\n }, [cliAdapter]);\n return [appearance, refresh];\n}\n","/**\n * Loads the inline detail page for the selected background-execution entry.\n *\n * Extracted from `App.tsx` (CLI-2004): three pieces of state and one async effect that exist only\n * for the detail pane, sitting in the middle of the component's input handlers. One responsibility,\n * one module — and the App keeps room for the concerns that actually belong to it.\n */\n\nimport { useEffect, useState } from 'react';\n\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\n\nexport interface IExecutionDetailState {\n page: IExecutionDetailPage | null;\n error: string | undefined;\n loading: boolean;\n}\n\nexport interface IUseExecutionDetailPageInputs {\n /** The selected entry, or undefined / the main thread when no detail is shown. */\n entry: IExecutionWorkspaceEntry | undefined;\n /** Re-read trigger: the snapshot the entry came from. */\n snapshot: IExecutionWorkspaceSnapshot | null;\n read: (entryId: string) => Promise<IExecutionDetailPage>;\n}\n\n/** Load (and cancel-on-change) the detail page for the selected entry. */\nexport function useExecutionDetailPage(\n inputs: IUseExecutionDetailPageInputs,\n): IExecutionDetailState {\n const [page, setPage] = useState<IExecutionDetailPage | null>(null);\n const [error, setError] = useState<string | undefined>();\n const [loading, setLoading] = useState(false);\n const { entry, snapshot, read } = inputs;\n\n useEffect(() => {\n if (!entry || entry.kind === 'main_thread') {\n setPage(null);\n setError(undefined);\n setLoading(false);\n return;\n }\n\n let isCurrent = true;\n setLoading(true);\n setError(undefined);\n read(entry.id)\n .then((loaded) => {\n if (!isCurrent) return;\n setPage(loaded);\n setLoading(false);\n })\n .catch((cause: Error) => {\n if (!isCurrent) return;\n setError(cause.message);\n setLoading(false);\n });\n\n return () => {\n isCurrent = false;\n };\n }, [snapshot, read, entry]);\n\n return { page, error, loading };\n}\n","/**\n * CLI-1994: the TUI half of `attach` — turning the workspace entry the operator picked into either\n * a session switch or a stated refusal.\n *\n * The DECISION is not made here. `resolveExecutionAttach` is the framework's, beside the projection\n * that decides when the control is offered, so the surface cannot invent a second answer to \"may\n * this be attached to\". What this module owns is the surface's part: the refusal reaches the\n * operator as a message, and the switch goes through the SAME session-switch path the session\n * picker uses — a new channel from the factory, the previous one stopped first.\n *\n * A switch is a VIEW change. Nothing is merged: the parent session's record is not read or written,\n * and the two records stay separate exactly as `/fork` left them.\n */\n\nimport { resolveExecutionAttach } from '@robota-sdk/agent-framework';\n\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\n\n/** The `switch-session` member of the command layer's UI-intent union, narrowed to itself. */\n/**\n * What an attach did: the session this terminal was pointed at.\n *\n * Deliberately NOT a `TCommandUiIntent` member. Attach starts at a keypress in this surface's own\n * background panel, not at a command, so routing it through the requester-routed intent bus would\n * add a hop with no second consumer at the end of it — and the intent's `useSideEffects` case had no\n * emitter, which is a reader with no writer.\n */\nexport interface IForkAttachOutcome {\n readonly sessionId: string;\n}\n\nexport interface IForkAttachDeps {\n /** Whether the forked record is still in the store this surface would switch onto. */\n readonly hasSessionRecord: (sessionId: string) => boolean;\n /** The surface's existing session-switch path — the one the session picker calls. */\n readonly switchSession: (sessionId: string) => void;\n /** How a refusal reaches the operator (a system entry in the transcript). */\n readonly notify: (message: string) => void;\n /** Optional host-owned command for reopening a completed fork outside the attach view. */\n readonly formatResumeCommand?: (sessionId: string) => string;\n}\n\n/**\n * Attach to `entry`'s forked session, or tell the operator why not.\n *\n * Returns the UI intent that was emitted, so a caller — and a test — can see the switch as the\n * command layer's own vocabulary rather than as a side effect. `undefined` means refused, and the\n * refusal has already been delivered through `notify`.\n */\nexport function attachToForkedSession(\n entry: IExecutionWorkspaceEntry,\n deps: IForkAttachDeps,\n): IForkAttachOutcome | undefined {\n const outcome = resolveExecutionAttach(entry, { hasSessionRecord: deps.hasSessionRecord });\n if (outcome.type === 'refused') {\n const resumeCommand = outcome.resumeSessionId && deps.formatResumeCommand?.(outcome.resumeSessionId);\n deps.notify(resumeCommand ? `${outcome.reason} Open it with: ${resumeCommand}` : outcome.reason);\n return undefined;\n }\n deps.switchSession(outcome.sessionId);\n return { sessionId: outcome.sessionId };\n}\n","/**\n * CLI-1994: the App's `attach` handler, as a hook rather than another closure inside `App.tsx`.\n *\n * `App.tsx` is a file the size floor has already frozen as debt, where the rule is \"split instead of\n * extending\" — so the handler lives beside the other App hooks. The decision itself is neither here\n * nor there: `attachToForkedSession` asks the framework, which owns when `attach` is offered and\n * when it is honoured.\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport { useCallback } from 'react';\n\nimport { attachToForkedSession } from '../flows/fork-attach-flow.js';\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseForkAttachOptions {\n /** Absent when the surface persists nothing; attach then refuses, saying so. */\n readonly sessionStore: IInteractiveSessionStore | undefined;\n /** The surface's session-switch path — the same one the session picker takes. */\n readonly onSessionSwitch: (sessionId: string) => void;\n /** Where a refusal is written, so it reaches the operator instead of disappearing. */\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\n/**\n * Attach to a forked conversation: point this terminal at the fork's own session record. A VIEW\n * switch, never a merge — the parent's record is not read, written or joined either way.\n */\nexport function useForkAttach({\n sessionStore,\n onSessionSwitch,\n addEntry,\n}: IUseForkAttachOptions): (entry: IExecutionWorkspaceEntry) => void {\n const cliAdapter = useTuiCliAdapter();\n return useCallback(\n (entry: IExecutionWorkspaceEntry): void => {\n const notify = (message: string): void =>\n addEntry(messageToHistoryEntry(createSystemMessage(message)));\n // A surface composed without a session store cannot open another session's record. Said\n // plainly rather than reported as \"the record is missing\", which is a different fact.\n if (sessionStore === undefined) {\n notify('This surface has no session store, so it cannot attach to a forked session.');\n return;\n }\n attachToForkedSession(entry, {\n hasSessionRecord: (sessionId) => sessionStore.load(sessionId).status === 'valid',\n switchSession: onSessionSwitch,\n notify,\n ...(cliAdapter.formatResumeCommand === undefined\n ? {}\n : { formatResumeCommand: cliAdapter.formatResumeCommand }),\n });\n },\n [addEntry, cliAdapter, onSessionSwitch, sessionStore],\n );\n}\n","/**\n * The background panel's two data concerns as one call: what the selected entry's detail page says,\n * and what happens when the operator attaches to a forked one.\n *\n * Both belong to the same surface and both are read by the same component, so `App` asks for them\n * once rather than wiring two hooks whose inputs it has to keep in step. Split out because CLI-2004\n * had already brought `App.tsx` to its size budget by extracting the detail read, and CLI-1994 adds\n * the attach handler beside it — growing the file was the alternative the size rule forbids.\n */\n\nimport { useMemo } from 'react';\n\nimport { useExecutionDetailPage } from './useExecutionDetailPage.js';\nimport { useForkAttach } from './useForkAttach.js';\n\nimport type { IExecutionDetailState } from './useExecutionDetailPage.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceEntry,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IUseBackgroundPanelOptions {\n /** Which entry the switcher has selected; resolved against the snapshot below. */\n readonly selectedEntryId: string | undefined;\n /** The panel's data, and the re-read trigger — the detail follows a running task's output. */\n readonly snapshot: IExecutionWorkspaceSnapshot | null;\n readonly read: (entryId: string) => Promise<IExecutionDetailPage>;\n /** Absent ⇒ this surface cannot open another session's record, and attach says so. */\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => void;\n readonly addEntry: (entry: IHistoryEntry) => void;\n}\n\nexport interface IBackgroundPanel {\n /** The selected entry itself, which the switcher and the detail view both render. */\n readonly entry: IExecutionWorkspaceEntry | undefined;\n readonly detail: IExecutionDetailState;\n readonly attachToFork: (entry: IExecutionWorkspaceEntry) => void;\n}\n\n/** Wire the background panel's detail read and its attach handler together. */\nexport function useBackgroundPanel(inputs: IUseBackgroundPanelOptions): IBackgroundPanel {\n const { snapshot, selectedEntryId } = inputs;\n const entry = useMemo(\n () => snapshot?.entries.find((candidate) => candidate.id === selectedEntryId),\n [snapshot, selectedEntryId],\n );\n const detail = useExecutionDetailPage({ entry, snapshot, read: inputs.read });\n const attachToFork = useForkAttach({\n sessionStore: inputs.sessionStore,\n onSessionSwitch: inputs.onSessionSwitch,\n addEntry: inputs.addEntry,\n });\n return { entry, detail, attachToFork };\n}\n","/**\n * SCREEN-014: pure reducer for keyboard navigation of the inline background-work list.\n *\n * Kept out of the App component so the navigation rules are exhaustively unit-testable without\n * rendering the whole TUI. The App's `useInput` translates the resulting action into focus/selection\n * state changes.\n */\n\n/** A key event relevant to background-list navigation (subset of Ink's key object). */\nexport interface IBackgroundFocusKey {\n upArrow: boolean;\n downArrow: boolean;\n return: boolean;\n escape: boolean;\n}\n\nexport type TBackgroundFocusAction =\n | { type: 'move'; index: number | null } // null = focus returns to the prompt input\n | { type: 'open'; index: number } // open the highlighted task's inline detail\n | { type: 'exit' } // leave the list, focus the prompt input\n | { type: 'none' }; // unhandled key — ignore\n\n/**\n * Resolve a keypress while the background list holds focus.\n *\n * - ↑ moves up; ↑ past the first item returns focus to the input (`move` to `null`).\n * - ↓ moves down, clamped at the last item.\n * - Enter opens the highlighted task.\n * - Esc (or an emptied list) exits to the input.\n */\nexport function resolveBackgroundFocusKey(\n current: number,\n entryCount: number,\n key: IBackgroundFocusKey,\n): TBackgroundFocusAction {\n if (entryCount <= 0) return { type: 'exit' };\n if (key.upArrow) return { type: 'move', index: current > 0 ? current - 1 : null };\n if (key.downArrow) {\n return { type: 'move', index: current < entryCount - 1 ? current + 1 : current };\n }\n if (key.return) return { type: 'open', index: Math.min(Math.max(current, 0), entryCount - 1) };\n if (key.escape) return { type: 'exit' };\n return { type: 'none' };\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport {\n countActiveBackgroundWorkspaceEntries,\n getDefaultBackgroundWorkspaceEntries,\n} from '../execution-workspace-view-model.js';\nimport { useBackgroundPanel } from './useBackgroundPanel.js';\nimport { resolveBackgroundFocusKey } from '../flows/background-focus-flow.js';\nimport { useKeybindingActions } from '../keybindings/keybindings-context.js';\n\nimport type { IAppBackgroundViewModel } from '../app-view-model.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\nexport interface IAppWorkspaceState {\n readonly background: IAppBackgroundViewModel;\n readonly activeBackgroundTaskCount: number;\n readonly activeAgentLabel: string | undefined;\n readonly mainThreadEntryId: string | undefined;\n readonly isSelectedEntryInteractive: boolean;\n readonly isBackgroundListFocused: boolean;\n readonly openSwitcher: () => void;\n readonly toggleSwitcher: () => void;\n readonly focusBackgroundList: () => void;\n}\n\ninterface IOptions {\n readonly snapshot: IExecutionWorkspaceSnapshot | null;\n readonly selectedEntryId: string | undefined;\n readonly select: (entryId: string) => void;\n readonly read: (entryId: string) => Promise<IExecutionDetailPage>;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly addEntry: (entry: IHistoryEntry) => void;\n readonly navigationEnabled: boolean;\n readonly switcherVisible: boolean;\n readonly setSwitcherVisible: (visible: boolean | ((current: boolean) => boolean)) => void;\n}\n\ninterface ISwitcherActions {\n readonly open: () => void;\n readonly toggle: () => void;\n readonly close: () => void;\n}\n\nfunction useBackgroundListKeys(\n focusedIndex: number | null,\n entryIds: readonly string[],\n setFocusedIndex: (index: number | null) => void,\n select: (entryId: string) => void,\n enabled: boolean,\n): void {\n useKeybindingActions(\n 'background-list',\n (actions) => {\n if (focusedIndex === null) return;\n const action = resolveBackgroundFocusKey(focusedIndex, entryIds.length, {\n upArrow: actions.includes('previous'),\n downArrow: actions.includes('next'),\n return: actions.includes('open'),\n escape: actions.includes('close'),\n });\n if (action.type === 'move') setFocusedIndex(action.index);\n if (action.type === 'open') {\n const entryId = entryIds[action.index];\n if (entryId) select(entryId);\n setFocusedIndex(null);\n }\n if (action.type === 'exit') setFocusedIndex(null);\n },\n { isActive: focusedIndex !== null && enabled },\n );\n}\n\nfunction useBackgroundFocus(\n entryIds: readonly string[],\n select: (entryId: string) => void,\n enabled: boolean,\n): { focusedIndex: number | null; focusList: () => void } {\n const [focusedIndex, setFocusedIndex] = useState<number | null>(null);\n useEffect(() => {\n setFocusedIndex((index) => {\n if (index === null || entryIds.length === 0) return null;\n return Math.min(index, entryIds.length - 1);\n });\n }, [entryIds.length]);\n useBackgroundListKeys(focusedIndex, entryIds, setFocusedIndex, select, enabled);\n const focusList = useCallback(() => {\n if (entryIds.length > 0) setFocusedIndex(0);\n }, [entryIds.length]);\n return { focusedIndex, focusList };\n}\n\nfunction useSwitcherActions(setVisible: IOptions['setSwitcherVisible']): ISwitcherActions {\n const open = useCallback(() => setVisible(true), [setVisible]);\n const toggle = useCallback(() => setVisible((visible) => !visible), [setVisible]);\n const close = useCallback(() => setVisible(false), [setVisible]);\n return { open, toggle, close };\n}\n\nexport function useAppWorkspaceState(options: IOptions): IAppWorkspaceState {\n const entries = useMemo(\n () => getDefaultBackgroundWorkspaceEntries(options.snapshot),\n [options.snapshot],\n );\n const { focusedIndex, focusList } = useBackgroundFocus(\n entries.map((entry) => entry.id),\n options.select,\n options.navigationEnabled,\n );\n\n const panel = useBackgroundPanel({\n selectedEntryId: options.selectedEntryId,\n snapshot: options.snapshot,\n read: options.read,\n sessionStore: options.sessionStore,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.addEntry,\n });\n const switcher = useSwitcherActions(options.setSwitcherVisible);\n const selected = panel.entry;\n return {\n background: {\n entries,\n focusedIndex,\n selectedEntry: selected,\n detail: panel.detail,\n switcherVisible: options.switcherVisible,\n snapshot: options.snapshot,\n selectedEntryId: options.selectedEntryId,\n select: options.select,\n closeSwitcher: switcher.close,\n attachToFork: panel.attachToFork,\n },\n activeBackgroundTaskCount: countActiveBackgroundWorkspaceEntries(options.snapshot),\n activeAgentLabel: selected?.kind === 'main_thread' ? undefined : selected?.title,\n mainThreadEntryId: options.snapshot?.entries.find((entry) => entry.kind === 'main_thread')?.id,\n isSelectedEntryInteractive:\n !selected || selected.kind === 'main_thread' || selected.controls.includes('send'),\n isBackgroundListFocused: focusedIndex !== null,\n openSwitcher: switcher.open,\n toggleSwitcher: switcher.toggle,\n focusBackgroundList: focusList,\n };\n}\n","/**\n * CMD-004 Phase 2 Stage C — the TUI as a pure renderer.\n *\n * The session layer (the host) executes every command host action via `ICommandHostAdapters`\n * BEFORE the command result returns (language change, settings reset, exit/restart, rename,\n * statusline patch, remote control — see `interactive-session-host-actions.ts`). This hook only:\n *\n * 1. renders the four UI screens from the requester-routed `ui_intent` session event\n * (`show-plugin-manager` / `show-settings` / `show-session-picker` / `show-agent-switcher`) —\n * the surface that issued the command renders the intent; intents stamped with another\n * surface's driver id (or unattributed) are ignored;\n * 2. reflects the broadcast `session_renamed` event into the rendered title (the host performed\n * the rename — the TUI never mutates the session);\n * 3. refreshes the statusline display after a slash-command result arrives by RE-READING the\n * persisted settings (refresh-on-result — the host applied any patch via the settings adapter).\n */\n\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\n\nimport type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';\nimport type { ITuiSessionUiEventPort } from '../tui-app-channel-port.js';\nimport type { ISessionRenamedEvent, IUiIntentEvent } from '@robota-sdk/agent-interface-session';\n\ninterface IUiEventHandlers {\n setSessionName: (name: string) => void;\n refreshStatusLineSettings: () => void;\n /** SCREEN-2002: re-read the persisted appearance after a slash command may have patched it. */\n refreshAppearanceSettings: () => void;\n openAgentSwitcher?: (() => void) | undefined;\n}\n\ninterface IScreenSetters {\n setShowPluginTUI: (show: boolean) => void;\n setShowTransportTUI: (show: boolean) => void;\n setShowSessionPicker: (show: boolean) => void;\n setShowThemePicker: (show: boolean) => void;\n}\n\n/** Subscribe to `ui_intent` (requester-routed) + `session_renamed` (broadcast); returns cleanup. */\nfunction subscribeToSessionUiEvents(\n uiEventPort: ITuiSessionUiEventPort,\n screens: IScreenSetters,\n handlersRef: { current: IUiEventHandlers },\n): () => void {\n const onUiIntent = (event: IUiIntentEvent): void => {\n // Requester-routed: this surface is the local operator (OWNER_DRIVER_ID). Intents issued by\n // other surfaces — or unattributed ones — are theirs to render, not ours.\n if (event.requesterDriverId !== OWNER_DRIVER_ID) return;\n switch (event.intent.type) {\n case 'show-plugin-manager':\n screens.setShowPluginTUI(true);\n return;\n case 'show-settings':\n screens.setShowTransportTUI(true);\n return;\n case 'show-session-picker':\n screens.setShowSessionPicker(true);\n return;\n case 'show-agent-switcher':\n handlersRef.current.openAgentSwitcher?.();\n return;\n case 'show-theme-picker':\n screens.setShowThemePicker(true);\n return;\n }\n };\n const onSessionRenamed = (event: ISessionRenamedEvent): void => {\n handlersRef.current.setSessionName(event.name);\n };\n uiEventPort.on('ui_intent', onUiIntent);\n uiEventPort.on('session_renamed', onSessionRenamed);\n return () => {\n uiEventPort.off('ui_intent', onUiIntent);\n uiEventPort.off('session_renamed', onSessionRenamed);\n };\n}\n\n/**\n * Latest-callback refs so the session subscription binds exactly once per session: App recreates\n * some callbacks every render, and re-subscribing on each identity change would churn listeners.\n */\nfunction useLatestHandlers(handlers: IUiEventHandlers): { current: IUiEventHandlers } {\n const ref = useRef(handlers);\n ref.current = handlers;\n return ref;\n}\n\nexport function useSideEffects({\n uiEventPort,\n baseHandleSubmit,\n setSessionName,\n refreshStatusLineSettings,\n refreshAppearanceSettings,\n showSessionPickerOnStart,\n openAgentSwitcher,\n}: IUseSideEffectsOptions): IUseSideEffectsResult {\n const [showPluginTUI, setShowPluginTUI] = useState(false);\n const [showSessionPicker, setShowSessionPicker] = useState(showSessionPickerOnStart ?? false);\n const [showTransportTUI, setShowTransportTUI] = useState(false);\n const [showThemePicker, setShowThemePicker] = useState(false);\n\n const handlersRef = useLatestHandlers({\n setSessionName,\n refreshStatusLineSettings,\n refreshAppearanceSettings,\n openAgentSwitcher,\n });\n\n useLayoutEffect(\n () =>\n subscribeToSessionUiEvents(\n uiEventPort,\n { setShowPluginTUI, setShowTransportTUI, setShowSessionPicker, setShowThemePicker },\n handlersRef,\n ),\n [uiEventPort],\n );\n\n const handleSubmit = useCallback(\n async (input: string): Promise<void> => {\n await baseHandleSubmit(input);\n // Refresh-on-result: a slash command's result has arrived; the host may have patched the\n // persisted statusline settings — re-read them for the status bar.\n if (input.trimStart().startsWith('/')) {\n handlersRef.current.refreshStatusLineSettings();\n handlersRef.current.refreshAppearanceSettings();\n }\n },\n [baseHandleSubmit],\n );\n\n return {\n handleSubmit,\n showPluginTUI,\n showSessionPicker,\n showTransportTUI,\n showThemePicker,\n setShowPluginTUI,\n setShowSessionPicker,\n setShowTransportTUI,\n setShowThemePicker,\n };\n}\n","import { DEFAULT_STATUS_LINE_COMMAND_SETTINGS } from '@robota-sdk/agent-framework';\nimport { useCallback, useState } from 'react';\n\nimport { useTuiCliAdapter } from '../tui-cli-adapter-context.js';\n\nimport type { ITuiCliAdapter } from '../tui-cli-adapter.js';\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\nimport type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-command';\n\nfunction readStatusLineSettings(\n settings: Record<string, TUniversalValue>,\n): IStatusLineCommandSettings {\n const defaults = { ...DEFAULT_STATUS_LINE_COMMAND_SETTINGS };\n const raw = settings.statusline;\n if (!isRecord(raw)) {\n return defaults;\n }\n return {\n enabled: typeof raw.enabled === 'boolean' ? raw.enabled : defaults.enabled,\n gitBranch: typeof raw.gitBranch === 'boolean' ? raw.gitBranch : defaults.gitBranch,\n };\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return (\n value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction readFromAdapter(cliAdapter: ITuiCliAdapter): IStatusLineCommandSettings {\n return readStatusLineSettings(cliAdapter.readSettings(cliAdapter.getUserSettingsPath()));\n}\n\n/**\n * Statusline settings as persisted on disk, plus a from-disk `refresh()`.\n *\n * CMD-004 Phase 2 Stage C: the HOST applies `statusline-settings-patch` via the settings adapter\n * (the TUI no longer writes settings), so the renderer refreshes by RE-READING the persisted\n * document when a command result arrives (refresh-on-result).\n */\nexport function useStatusLineSettings(): [IStatusLineCommandSettings, () => void] {\n const cliAdapter = useTuiCliAdapter();\n const [settings, setSettings] = useState<IStatusLineCommandSettings>(() =>\n readFromAdapter(cliAdapter),\n );\n const refresh = useCallback((): void => {\n setSettings(readFromAdapter(cliAdapter));\n }, [cliAdapter]);\n return [settings, refresh];\n}\n","import { useState } from 'react';\n\nimport { useAppearanceSettings } from './useAppearanceSettings.js';\nimport { useAppWorkspaceState } from './useAppWorkspaceState.js';\nimport { useSideEffects } from './useSideEffects.js';\nimport { useStatusLineSettings } from './useStatusLineSettings.js';\n\nimport type { IUseSideEffectsResult } from './side-effects-types.js';\nimport type { IAppWorkspaceState } from './useAppWorkspaceState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type {\n IAppearanceSettings,\n IStatusLineCommandSettings,\n} from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\n\ninterface IOptions {\n readonly state: ITuiChannelState;\n readonly setSessionName: (name: string) => void;\n readonly sessionStore: IInteractiveSessionStore | undefined;\n readonly onSessionSwitch: (sessionId: string) => Promise<void>;\n readonly showSessionPickerOnStart: boolean | undefined;\n readonly coordinationBlocked: boolean;\n}\n\nexport interface IAppScreenState {\n readonly screens: IUseSideEffectsResult;\n readonly workspace: IAppWorkspaceState;\n readonly statusSettings: IStatusLineCommandSettings;\n /** SCREEN-2002: the persisted appearance, re-read after every slash-command result. */\n readonly appearance: IAppearanceSettings;\n /** SCREEN-1993: the input area's history-search overlay is open and owns the keys. */\n readonly historySearchOpen: boolean;\n readonly setHistorySearchOpen: (open: boolean) => void;\n}\n\nexport function useAppScreenState(options: IOptions): IAppScreenState {\n const [workspaceSwitcherVisible, setWorkspaceSwitcherVisible] = useState(false);\n const [historySearchOpen, setHistorySearchOpen] = useState(false);\n const [statusSettings, refreshStatusSettings] = useStatusLineSettings();\n const [appearance, refreshAppearance] = useAppearanceSettings();\n const screens = useSideEffects({\n uiEventPort: options.state.uiEventPort,\n baseHandleSubmit: options.state.handleSubmit,\n setSessionName: options.setSessionName,\n refreshStatusLineSettings: refreshStatusSettings,\n refreshAppearanceSettings: refreshAppearance,\n showSessionPickerOnStart: options.showSessionPickerOnStart,\n openAgentSwitcher: () => setWorkspaceSwitcherVisible(true),\n });\n const navigationEnabled =\n options.state.permissionRequest === null &&\n options.state.pendingUserAction === null &&\n !screens.showPluginTUI &&\n !screens.showTransportTUI &&\n !screens.showSessionPicker &&\n !screens.showThemePicker &&\n !workspaceSwitcherVisible &&\n !historySearchOpen &&\n !options.coordinationBlocked;\n const workspace = useAppWorkspaceState({\n snapshot: options.state.executionWorkspaceSnapshot,\n selectedEntryId: options.state.selectedExecutionEntryId,\n select: options.state.selectExecutionWorkspaceEntry,\n read: options.state.readExecutionWorkspaceDetail,\n sessionStore: options.sessionStore,\n onSessionSwitch: options.onSessionSwitch,\n addEntry: options.state.addEntry,\n navigationEnabled,\n switcherVisible: workspaceSwitcherVisible,\n setSwitcherVisible: setWorkspaceSwitcherVisible,\n });\n return {\n screens,\n workspace,\n statusSettings,\n appearance,\n historySearchOpen,\n setHistorySearchOpen,\n };\n}\n","/**\n * SCREEN-2002 — the theme catalogue, and the port the command layer asks through.\n *\n * The registry is the only thing that turns an ID into a theme. Work unit 1 put the built-ins\n * behind `listBuiltInThemes()`; work unit 3 adds user and plugin themes to the same list, and\n * nothing downstream has to learn where a theme came from — a row carries its `source` so the\n * picker and `/theme list` can SAY where it came from, which is a different question.\n *\n * An unknown id resolves to the default and reports the id it could not find. Resolving silently\n * would make a removed plugin look like a theme that stopped working, and the id is the only thing\n * that can name the cause.\n */\nimport { DEFAULT_THEME_ID, listBuiltInThemes, resolveTheme } from './built-in-themes.js';\n\n// The registry's default source, re-exported from the module that USES it. The package's public\n// surface takes this route rather than the theme barrel: the barrel re-exports `theme-context.tsx`,\n// and a public re-export chain that reaches a `.tsx` is what `sdk-public-surface` refuses.\nexport { listBuiltInThemes } from './built-in-themes.js';\n\nimport type { ITuiTheme } from './theme-contracts.js';\nimport type {\n IThemeAppearanceState,\n IThemeCatalogueEntry,\n IThemeCataloguePort,\n TReducedMotionOverride,\n} from '@robota-sdk/agent-interface-command';\n\n/**\n * A theme file that was found and refused. It is carried BESIDE the themes rather than among them,\n * because a surface must be able to show the file without ever resolving to it — an id that can be\n * listed and cannot be applied is the shape of the bug this avoids.\n */\nexport interface IThemeSkip {\n /**\n * The id the file WOULD have had, so a `/theme <id>` that fails has a visible reason — or, when\n * the file's own NAME is what made an id impossible, the quoted name in its place. It is a label,\n * not a key: two files can carry the same one.\n */\n readonly id: string;\n readonly fileName: string;\n /** The path-named diagnostic, verbatim from `parseThemeDocument`. */\n readonly reason: string;\n}\n\nexport interface IThemeResolution {\n readonly theme: ITuiTheme;\n /** The id that was asked for and not found. Absent ⇒ the request was satisfied. */\n readonly unknownId?: string;\n}\n\nexport interface IThemeRegistry {\n list(): readonly ITuiTheme[];\n get(id: string): ITuiTheme | undefined;\n /** The theme for an id, falling back to the default and NAMING what it could not find. */\n resolve(id: string | undefined): IThemeResolution;\n /** The theme files that were found and refused, with the reason each was refused for. */\n skipped(): readonly IThemeSkip[];\n}\n\nexport function createThemeRegistry(\n themes: readonly ITuiTheme[] = listBuiltInThemes(),\n skipped: readonly IThemeSkip[] = [],\n): IThemeRegistry {\n const byId = new Map(themes.map((theme) => [theme.id, theme]));\n return {\n list: () => themes,\n skipped: () => skipped,\n get: (id) => byId.get(id),\n resolve: (id) => {\n if (id === undefined || id === DEFAULT_THEME_ID) {\n return { theme: resolveTheme(byId.get(DEFAULT_THEME_ID)) };\n }\n const found = byId.get(id);\n if (found) return { theme: found };\n return { theme: resolveTheme(byId.get(DEFAULT_THEME_ID)), unknownId: id };\n },\n };\n}\n\n/** The line a run prints once when its persisted theme is not installed. */\nexport function formatUnknownThemeNotice(id: string): string {\n return `Theme \"${id}\" is not installed — using \"${DEFAULT_THEME_ID}\".`;\n}\n\nfunction toEntry(theme: ITuiTheme): IThemeCatalogueEntry {\n return {\n id: theme.id,\n name: theme.name,\n appearance: theme.appearance,\n source: theme.source,\n };\n}\n\nexport interface IThemeCataloguePortOptions {\n readonly registry: IThemeRegistry;\n /**\n * Re-read on every call, not captured once: `/theme` reports what is PERSISTED, and the host\n * applies a patch by writing the settings document. A snapshot would answer with the appearance\n * the process started with, so the command would report the change it just made as not having\n * happened.\n */\n readonly readAppearance: () => IThemeAppearanceState['settings'];\n /**\n * The pin on reduced motion for this run, when something above the settings applied one — WHICH\n * tier and WHAT it pinned, as one value. Two optional fields would let a caller supply the tier\n * alone, and a default for the missing half is exactly the wrong answer this pair exists to stop:\n * `--no-reduced-motion` is an override too and pins the opposite value.\n */\n readonly reducedMotionPin?:\n { readonly tier: TReducedMotionOverride; readonly reducedMotion: boolean } | undefined;\n}\n\nexport function createThemeCataloguePort(options: IThemeCataloguePortOptions): IThemeCataloguePort {\n return {\n listThemes: () => options.registry.list().map(toEntry),\n getTheme: (id) => {\n const theme = options.registry.get(id);\n return theme ? toEntry(theme) : undefined;\n },\n getAppearance: () => ({\n settings: options.readAppearance(),\n ...(options.reducedMotionPin === undefined\n ? {}\n : { reducedMotionPin: options.reducedMotionPin }),\n }),\n };\n}\n","import { useCallback, useMemo, useState } from 'react';\n\nimport { createThemeRegistry, formatUnknownThemeNotice } from '../theme/theme-registry.js';\n\nimport type { ITuiTheme } from '../theme/theme-contracts.js';\nimport type { IThemeRegistry, IThemeSkip } from '../theme/theme-registry.js';\nimport type {\n IAppearanceSettings,\n IThemeAppearanceState,\n TReducedMotionOverride,\n} from '@robota-sdk/agent-interface-command';\n\n/** The tier that pinned motion for this run and what it pinned — the contract's own pair. */\ntype IReducedMotionPin = NonNullable<IThemeAppearanceState['reducedMotionPin']>;\n\nexport interface IThemeToggles {\n readonly syntaxHighlighting: boolean;\n readonly reducedMotion: boolean;\n}\n\nexport interface IAppThemePickerViewModel {\n readonly visible: boolean;\n readonly themes: readonly ITuiTheme[];\n /** Theme files that were found and refused — shown as rows that cannot be chosen. */\n readonly skipped: readonly IThemeSkip[];\n /** The id that is PERSISTED — the row the picker returns to when it is cancelled. */\n readonly activeThemeId: string;\n /** The PERSISTED toggles the picker seeds itself from and submits alongside the theme. */\n readonly syntaxHighlighting: boolean;\n readonly reducedMotion: boolean;\n /**\n * Set when a tier above the settings pinned motion for this run — the tier AND what it pinned,\n * as one value, for the reason `IThemeAppearanceState` states: the tier alone cannot say which\n * way a run went.\n */\n readonly reducedMotionPin?: IReducedMotionPin | undefined;\n /** Move the highlight: the live region re-renders in that theme, nothing is written. */\n readonly preview: (id: string) => void;\n /**\n * Commit: submits `/theme <id> syntax <on|off> motion <on|off>` — ONE patch through the command\n * path, because the HOST owns writing the settings document. Three separate submissions would be\n * three chances to half-apply what the user saw as one decision.\n */\n readonly select: (id: string, toggles: IThemeToggles) => void;\n /** Leave: the preview is dropped and the persisted theme is what remains. */\n readonly cancel: () => void;\n}\n\nexport interface IAppThemeViewModel {\n /** The theme every component renders with right now — the preview when one is open. */\n readonly resolved: ITuiTheme;\n /** Whether motion is suppressed for this run, after the flag/env/settings chain. */\n readonly reducedMotion: boolean;\n /** Whether fenced code blocks are highlighted. Every markdown render site reads this. */\n readonly syntaxHighlighting: boolean;\n /** Present once, when the persisted id names a theme this build does not have. */\n readonly unknownThemeNotice?: string;\n readonly picker: IAppThemePickerViewModel;\n}\n\ninterface IOptions {\n readonly appearance: IAppearanceSettings;\n readonly registry?: IThemeRegistry | undefined;\n readonly reducedMotion?: boolean | undefined;\n /** Which tier decided it, when that was not the settings. */\n readonly reducedMotionOverride?: TReducedMotionOverride | undefined;\n readonly visible: boolean;\n readonly setVisible: (visible: boolean) => void;\n readonly submit: (input: string) => void;\n}\n\n/**\n * SCREEN-2002 — which theme the live region renders with, and the picker over it.\n *\n * Two inputs, one answer. The PERSISTED appearance is the baseline; a preview is a local overlay\n * that exists only while the picker is open. Selecting does not write — it submits `/theme <id>`,\n * so the host applies the patch and the refresh-on-result path re-reads it. That keeps ONE writer\n * for the settings document and means the picker cannot persist something `/theme` would refuse.\n *\n * `reducedMotion` arrives already resolved from the CLI (settings ← env ← flag). It is not\n * recomputed here, because a second place deciding the same question is how the two start\n * disagreeing — and it is still not the final say: `useMotion()` gates on the colour gate and\n * screen-reader mode above it.\n */\nexport function useAppThemeState(options: IOptions): IAppThemeViewModel {\n const registry = useMemo(() => options.registry ?? createThemeRegistry(), [options.registry]);\n const [previewId, setPreviewId] = useState<string | undefined>(undefined);\n const resolution = useMemo(\n () => registry.resolve(previewId ?? options.appearance.theme),\n [registry, previewId, options.appearance.theme],\n );\n\n const { setVisible, submit } = options;\n const cancel = useCallback((): void => {\n setPreviewId(undefined);\n setVisible(false);\n }, [setVisible]);\n const { syntaxHighlighting, reducedMotion } = options.appearance;\n const select = useCallback(\n (id: string, toggles: IThemeToggles): void => {\n setPreviewId(undefined);\n setVisible(false);\n const parts = [`/theme ${id}`];\n // Only what the user actually CHANGED. Sending all three would make choosing a theme report\n // \"syntax highlighting on, motion on\" as applied to someone who touched neither — and on a\n // run with a reduced-motion flag it would also drag out the pinned-for-this-run notice for a\n // setting they never asked about.\n if (toggles.syntaxHighlighting !== syntaxHighlighting) {\n parts.push(`syntax ${toggles.syntaxHighlighting ? 'on' : 'off'}`);\n }\n if (toggles.reducedMotion !== reducedMotion) {\n // `motion on` means ANIMATE, which is `reducedMotion: false` — the inversion lives in the\n // command's parser, and this is the one place that has to speak its vocabulary.\n parts.push(`motion ${toggles.reducedMotion ? 'off' : 'on'}`);\n }\n submit(parts.join(' '));\n },\n [reducedMotion, setVisible, submit, syntaxHighlighting],\n );\n\n // A preview of an unknown id is impossible (the rows come from the registry), so the notice can\n // only be about what is PERSISTED — and it is suppressed while previewing so it does not read as\n // a complaint about the row the user is looking at.\n const unknownId = previewId === undefined ? resolution.unknownId : undefined;\n // One expression for \"what motion IS for this run\", read by both the view model and the picker:\n // two copies of the same `??` chain are two things to keep in step.\n const reducedMotionForRun = options.reducedMotion ?? options.appearance.reducedMotion;\n return {\n resolved: resolution.theme,\n reducedMotion: reducedMotionForRun,\n syntaxHighlighting: options.appearance.syntaxHighlighting,\n ...(unknownId === undefined ? {} : { unknownThemeNotice: formatUnknownThemeNotice(unknownId) }),\n picker: {\n visible: options.visible,\n themes: registry.list(),\n skipped: registry.skipped(),\n activeThemeId: options.appearance.theme,\n syntaxHighlighting: options.appearance.syntaxHighlighting,\n reducedMotion: options.appearance.reducedMotion,\n // Both halves or neither. A tier with no resolved value would have to be paired with the\n // PERSISTED one, which is the contradiction this pair exists to stop — so a caller that\n // supplies only the tier gets no pin rather than a wrong one, and the row simply says\n // nothing about a pin instead of naming the wrong direction.\n ...(options.reducedMotionOverride === undefined || options.reducedMotion === undefined\n ? {}\n : {\n reducedMotionPin: {\n tier: options.reducedMotionOverride,\n reducedMotion: options.reducedMotion,\n },\n }),\n preview: setPreviewId,\n select,\n cancel,\n },\n };\n}\n","/**\n * useTuiChannel — React hook that subscribes to the bounded TUI channel port.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\n\nimport type {\n ITuiAppChannelPort,\n ITuiCommandQueryPort,\n ITuiRuntimeStatusSnapshot,\n ITuiSessionUiEventPort,\n} from '../tui-app-channel-port.js';\nimport type { ITuiSessionEventNotice } from '../tui-session-events.js';\nimport type { IPendingPermissionRequest } from '../types.js';\nimport type {\n IActionRequest,\n IHistoryEntry,\n TActionResponse,\n TPermissionMode,\n TSessionEndReason,\n} from '@robota-sdk/agent-core';\nimport type {\n IExecutionDetailPage,\n IExecutionWorkspaceSnapshot,\n} from '@robota-sdk/agent-interface-execution';\nimport type { ISessionListingEntry, IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiChannelState {\n uiEventPort: ITuiSessionUiEventPort;\n commandQueryPort: ITuiCommandQueryPort;\n history: readonly IHistoryEntry[];\n addEntry: (entry: IHistoryEntry) => void;\n streamingText: string;\n activeTools: readonly IToolState[];\n isThinking: boolean;\n isAborting: boolean;\n /** ERR-001 G2: humanized message of the last failed turn (null once the next turn starts). */\n lastErrorMessage: string | null;\n /** ERR-001 G3: no provider activity for a while during a turn — connection may be stalled. */\n isStalled: boolean;\n sessionEventNotices: readonly ITuiSessionEventNotice[];\n isShuttingDown: boolean;\n pendingPrompt: string | null;\n /** REMOTE-014 E5: total queued turns across all drivers (owner + co-drivers); >1 means a co-driver is queued. */\n pendingCount: number;\n executionWorkspaceSnapshot: IExecutionWorkspaceSnapshot | null;\n selectedExecutionEntryId?: string;\n permissionRequest: IPendingPermissionRequest | null;\n /** CMD-004: the unified action awaiting a user answer, or null. */\n pendingUserAction: IActionRequest | null;\n contextState: { percentage: number; usedTokens: number; maxTokens: number };\n /** The host's sessions for the picker, when the channel's host keeps them. */\n hostSessions: readonly ISessionListingEntry[] | undefined;\n /** Changes when the transcript starts over for another session under this channel. */\n transcriptGeneration: number;\n /** This terminal only observes the session. */\n readOnly: boolean;\n /** This terminal is attached to a session a host runs; leaving only detaches it. */\n attached: boolean;\n handleSubmit: (input: string) => Promise<void>;\n handleAbort: () => void;\n handleCancelQueue: () => void;\n handleStopWaitingLoop: () => Promise<void>;\n handleShutdown: (reason?: TSessionEndReason) => Promise<void>;\n sendAgentJob: (taskId: string, input: string) => Promise<void>;\n resolveUserAction: (request: IActionRequest, response: TActionResponse) => void;\n getRuntimeStatusSnapshot: (fallback: TPermissionMode) => ITuiRuntimeStatusSnapshot;\n selectExecutionWorkspaceEntry: (entryId: string) => void;\n readExecutionWorkspaceDetail: (entryId: string) => Promise<IExecutionDetailPage>;\n}\n\ninterface IHistoryReadableSession {\n getFullHistory(): IHistoryEntry[];\n}\n\ninterface IHistorySyncManager {\n syncHistory(entries: IHistoryEntry[]): void;\n}\n\nexport function applyCompactEventToManager(\n interactiveSession: IHistoryReadableSession,\n manager: IHistorySyncManager,\n): void {\n manager.syncHistory(interactiveSession.getFullHistory());\n}\n\nexport function useTuiChannel(channel: ITuiAppChannelPort): ITuiChannelState {\n const [, forceRender] = useState(0);\n\n useEffect(() => channel.subscribe(() => forceRender((n) => n + 1)), [channel]);\n const snapshot = channel.getSnapshot();\n\n // SCREEN-014 fix: these are consumed in `useEffect` dependency arrays in App. `channel` is stable\n // (created once), so memoize them — a fresh closure each render made the detail-loading effect\n // re-run every render and `setState`-loop (\"Maximum update depth exceeded\") whenever a background\n // entry was selected.\n const selectExecutionWorkspaceEntry = useCallback(\n (id: string) => channel.selectExecutionWorkspaceEntry(id),\n [channel],\n );\n const readExecutionWorkspaceDetail = useCallback(\n (id: string) => channel.readExecutionWorkspaceDetail(id),\n [channel],\n );\n\n return {\n uiEventPort: channel.getSessionUiEventPort(),\n commandQueryPort: channel.getCommandQueryPort(),\n history: snapshot.history,\n addEntry: (entry) => channel.addEntry(entry),\n streamingText: snapshot.streamingText,\n activeTools: snapshot.activeTools,\n isThinking: snapshot.isThinking,\n isAborting: snapshot.isAborting,\n lastErrorMessage: snapshot.lastErrorMessage,\n isStalled: snapshot.isStalled,\n sessionEventNotices: snapshot.sessionEventNotices,\n isShuttingDown: snapshot.isShuttingDown,\n pendingPrompt: snapshot.pendingPrompt,\n pendingCount: snapshot.pendingCount,\n executionWorkspaceSnapshot: snapshot.executionWorkspaceSnapshot,\n selectedExecutionEntryId: snapshot.selectedExecutionEntryId,\n permissionRequest: snapshot.permissionRequest,\n pendingUserAction: snapshot.pendingUserAction,\n contextState: snapshot.contextState,\n hostSessions: snapshot.hostSessions,\n transcriptGeneration: snapshot.transcriptGeneration ?? 0,\n readOnly: snapshot.readOnly === true,\n attached: snapshot.attached === true,\n handleSubmit: (input) => channel.handleInput(input),\n handleAbort: () => channel.abort(),\n handleCancelQueue: () => channel.cancelQueue(),\n handleStopWaitingLoop: () => channel.stopWaitingSelfPacedLoop(),\n handleShutdown: (reason) => channel.shutdown({ reason }),\n sendAgentJob: (taskId, input) => channel.sendAgentJob(taskId, input),\n resolveUserAction: (request, response) => channel.resolveUserAction(request, response),\n getRuntimeStatusSnapshot: (fallback) => channel.getRuntimeStatusSnapshot(fallback),\n selectExecutionWorkspaceEntry,\n readExecutionWorkspaceDetail,\n };\n}\n","import { useCallback, useMemo, useState } from 'react';\n\nimport { buildStaticItems } from '../app-static-items.js';\nimport { useAppInteractionState } from './useAppInteractionState.js';\nimport { useAppLifecycleState } from './useAppLifecycleState.js';\nimport { useAppScreenState } from './useAppScreenState.js';\nimport { useAppThemeState } from './useAppThemeState.js';\nimport { useTuiChannel } from './useTuiChannel.js';\n\nimport type { IBuildStaticItemsInputs } from '../app-static-items.js';\nimport type { IAppInputViewModel, IAppStatusViewModel, IAppViewModel } from '../app-view-model.js';\nimport type { ITuiAppChannelPort, ITuiRuntimeStatusSnapshot } from '../tui-app-channel-port.js';\nimport type { IAppInteractionState } from './useAppInteractionState.js';\nimport type { IAppLifecycleState } from './useAppLifecycleState.js';\nimport type { IAppScreenState } from './useAppScreenState.js';\nimport type { IAppThemeViewModel } from './useAppThemeState.js';\nimport type { ITuiChannelState } from './useTuiChannel.js';\nimport type { IThemeRegistry } from '../theme/theme-registry.js';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { IPromptHistorySource } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\nexport interface IUseAppControllerOptions {\n cwd: string;\n providerType?: string | undefined;\n modelId?: string;\n permissionMode?: TPermissionMode;\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: a deep link's prefill, handed to the composer exactly once. */\n initialInput?: string;\n initialInputOrigin?: 'external-link';\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n pluginAdapter?: ICommandPluginAdapter;\n sessionSwitchError?: string;\n sessionSwitchPending?: boolean;\n channel: ITuiAppChannelPort;\n onSessionSwitch: (sessionId: string) => Promise<void>;\n onRetrySessionSwitch: () => void;\n /** SCREEN-1993: the stored-prompt source and this run's project key; both or neither. */\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n /** SCREEN-2002: the catalogue this surface renders from; absent ⇒ the built-ins. */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n}\n\ninterface ICoordinationState {\n readonly error: string | undefined;\n readonly pending: boolean;\n readonly blocked: boolean;\n}\n\ninterface IComposition {\n readonly props: IUseAppControllerOptions;\n readonly state: ITuiChannelState;\n readonly lifecycle: IAppLifecycleState;\n readonly shell: IAppScreenState;\n readonly interaction: IAppInteractionState;\n readonly staticItems: IAppViewModel['staticItems'];\n readonly runtime: ITuiRuntimeStatusSnapshot;\n readonly coordination: ICoordinationState;\n readonly theme: IAppThemeViewModel;\n}\n\nfunction getCoordinationState(\n props: IUseAppControllerOptions,\n lifecycle: IAppLifecycleState,\n): ICoordinationState {\n const error = props.sessionSwitchError ?? lifecycle.startError;\n const pending = Boolean(props.sessionSwitchPending || lifecycle.startPending);\n return { error, pending, blocked: error !== undefined || pending };\n}\n\nfunction buildHistorySearch(composition: IComposition): IAppInputViewModel['historySearch'] {\n const { promptHistorySource, promptHistoryProject } = composition.props;\n if (promptHistorySource === undefined || promptHistoryProject === undefined) return undefined;\n return {\n source: promptHistorySource,\n project: promptHistoryProject,\n sessionId: composition.runtime.sessionId,\n onOpenChange: composition.shell.setHistorySearchOpen,\n };\n}\n\nfunction buildInput(composition: IComposition): IAppInputViewModel {\n const { screens, workspace } = composition.shell;\n const state = composition.state;\n const overlayOpen = Boolean(\n state.permissionRequest ||\n state.pendingUserAction ||\n screens.showPluginTUI ||\n screens.showTransportTUI ||\n screens.showSessionPicker ||\n screens.showThemePicker ||\n workspace.background.switcherVisible,\n );\n const interactionBlocked =\n composition.coordination.blocked ||\n overlayOpen ||\n state.isShuttingDown ||\n !workspace.isSelectedEntryInteractive ||\n workspace.isBackgroundListFocused;\n return {\n submit: composition.interaction.submission.submit,\n cancelQueue: state.handleCancelQueue,\n disabled: interactionBlocked || (state.isThinking && state.pendingPrompt !== null),\n // An observer keeps the composer for `/exit` and this terminal's own commands, which the channel\n // runs; the queue it would cancel is the drivers'.\n queueCancellationDisabled: interactionBlocked || state.readOnly,\n isAborting: state.isAborting,\n pendingPrompt: state.pendingPrompt,\n pendingCount: state.pendingCount,\n commandQueryPort: state.commandQueryPort,\n sessionName: composition.lifecycle.sessionName,\n history: state.history,\n focusBackgroundList: workspace.focusBackgroundList,\n historySearch: buildHistorySearch(composition),\n };\n}\n\nfunction buildStatus(composition: IComposition): IAppStatusViewModel {\n const { props, runtime, state } = composition;\n const workspace = composition.shell.workspace;\n return {\n cwd: props.cwd,\n permissionMode: runtime.permissionMode,\n modelId: runtime.modelId ?? props.modelId,\n providerType: props.providerType,\n sessionId: runtime.sessionId,\n isThinking: state.isThinking,\n activeToolCount: state.activeTools.length,\n activeBackgroundTaskCount: workspace.activeBackgroundTaskCount,\n hasPendingPrompt: state.pendingPrompt !== null,\n contextState: state.contextState,\n sessionName: composition.lifecycle.sessionName,\n settings: composition.shell.statusSettings,\n activeAgentLabel: workspace.activeAgentLabel,\n activePresetId: runtime.activePresetId,\n effort: runtime.effort,\n gitRefreshToken: composition.interaction.submission.gitRefreshToken,\n readOnly: state.readOnly,\n };\n}\n\nfunction buildViewModel(composition: IComposition): IAppViewModel {\n const { interaction, lifecycle, shell, state } = composition;\n return {\n staticItems: composition.staticItems,\n transcriptKey: state.transcriptGeneration,\n handoffSuspended: lifecycle.handoffSuspended,\n updateNotice: lifecycle.updateNotice,\n coordinationError: composition.coordination.error,\n coordinationPending: composition.coordination.pending,\n sessionEventNotices: state.sessionEventNotices,\n isShuttingDown: state.isShuttingDown,\n stream: {\n text: state.streamingText,\n activeTools: state.activeTools,\n isThinking: state.isThinking,\n isStalled: state.isStalled,\n lastErrorMessage: state.lastErrorMessage,\n },\n background: shell.workspace.background,\n permissionRequest: state.permissionRequest,\n pendingUserAction: state.pendingUserAction,\n resolveUserAction: state.resolveUserAction,\n ...interaction.overlays,\n theme: composition.theme,\n contextPercentage: state.contextState.percentage,\n input: buildInput(composition),\n status: buildStatus(composition),\n };\n}\n\n/** The live theme and its picker, composed from the shell's persisted appearance and the shell's props. */\nfunction useControllerTheme(\n props: IUseAppControllerOptions,\n shell: IAppScreenState,\n): IAppThemeViewModel {\n return useAppThemeState({\n appearance: shell.appearance,\n registry: props.themeRegistry,\n reducedMotion: props.reducedMotion,\n reducedMotionOverride: props.reducedMotionOverride,\n visible: shell.screens.showThemePicker,\n setVisible: shell.screens.setShowThemePicker,\n submit: (input) => void shell.screens.handleSubmit(input),\n });\n}\n\n/**\n * FLOW-2006: the deep link's prefill is owned HERE, not by the composer's `useState` initializer.\n * `AppPresentation` unmounts the prompt subtree while a terminal handoff is suspended, so a\n * component-local initializer would re-seed a prompt the user had already cleared or submitted.\n */\nfunction useExternalPrefill(\n initialInput: string | undefined,\n origin: IUseAppControllerOptions['initialInputOrigin'],\n): Pick<IAppInputViewModel, 'initialValue' | 'consumeInitialValue' | 'externalPromptOrigin'> {\n const [initialValue, setInitialValue] = useState(initialInput);\n const consumeInitialValue = useCallback(() => setInitialValue(undefined), []);\n return { initialValue, consumeInitialValue, externalPromptOrigin: origin === 'external-link' };\n}\n\n/** The scrollback Ink commits once: the banner (unless suppressed) and the settled history. */\nfunction useStaticItems(\n history: IBuildStaticItemsInputs['history'],\n version: IBuildStaticItemsInputs['version'],\n screenReader: IBuildStaticItemsInputs['screenReader'],\n): IAppViewModel['staticItems'] {\n return useMemo(\n () => buildStaticItems({ history, version, screenReader }),\n [history, screenReader, version],\n );\n}\n\n/** Compose responsibility-specific hooks into the only view model accepted by presentation. */\nexport function useAppController(props: IUseAppControllerOptions): IAppViewModel {\n const prefill = useExternalPrefill(props.initialInput, props.initialInputOrigin);\n const state = useTuiChannel(props.channel);\n const lifecycle = useAppLifecycleState(props.channel, props.startupUpdateNotice);\n const coordination = getCoordinationState(props, lifecycle);\n const shell = useAppScreenState({\n state,\n setSessionName: lifecycle.setSessionName,\n sessionStore: props.sessionStore,\n onSessionSwitch: props.onSessionSwitch,\n showSessionPickerOnStart: props.showSessionPickerOnStart,\n coordinationBlocked: coordination.blocked,\n });\n const interaction = useAppInteractionState({\n cwd: props.cwd,\n state,\n shell,\n sessionStore: props.sessionStore,\n onSessionSwitch: props.onSessionSwitch,\n pluginAdapter: props.pluginAdapter,\n transportRegistry: props.transportRegistry,\n recoveryError: coordination.error,\n recoveryPending: coordination.pending,\n coordinationBlocked: coordination.blocked,\n retryRecovery:\n props.sessionSwitchError !== undefined ? props.onRetrySessionSwitch : lifecycle.retryStart,\n });\n const staticItems = useStaticItems(state.history, props.version, interaction.screenReader);\n const runtime = state.getRuntimeStatusSnapshot(props.permissionMode ?? 'default');\n const theme = useControllerTheme(props, shell);\n const viewModel = buildViewModel({\n props,\n state,\n lifecycle,\n shell,\n interaction,\n staticItems,\n runtime,\n coordination,\n theme,\n });\n return { ...viewModel, input: { ...viewModel.input, ...prefill } };\n}\n\nexport type { IAppViewModel } from '../app-view-model.js';\n","import React from 'react';\n\nimport AppPresentation from './AppPresentation.js';\nimport { useAppController } from './hooks/useAppController.js';\nimport { ThemeProvider } from './theme/index.js';\n\nimport type { IUseAppControllerOptions } from './hooks/useAppController.js';\n\nexport type IAppViewProps = IUseAppControllerOptions;\n\n/** Controller boundary: the channel terminates here and only a view model reaches presentation. */\nexport default function AppView(props: IAppViewProps): React.ReactElement {\n const viewModel = useAppController(props);\n // SCREEN-2002: the provider sits HERE rather than above the controller, because the resolved\n // theme is part of the view model — a provider higher up would have to be fed a theme nobody had\n // resolved yet, and the controller's own hooks read no colours.\n return (\n <ThemeProvider\n theme={viewModel.theme.resolved}\n reducedMotion={viewModel.theme.reducedMotion}\n syntaxHighlighting={viewModel.theme.syntaxHighlighting}\n >\n <AppPresentation viewModel={viewModel} />\n </ThemeProvider>\n );\n}\n","import React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport AppView from './AppView.js';\nimport { TuiCliAdapterProvider } from './tui-cli-adapter-context.js';\n\nimport type { IThemeRegistry } from './theme/theme-registry.js';\nimport type { ITuiAppChannelPort } from './tui-app-channel-port.js';\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\nimport type { IPromptHistorySource } from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\ninterface IProps {\n cwd: string;\n /** The composition root narrows every concrete channel to this port before React receives it. */\n createChannel: (resumeSessionId?: string) => ITuiAppChannelPort;\n /**\n * The session lives on a host this terminal is attached to: a switch is asked of the host, and the\n * channel follows the host's `session_switched`, so the App keeps its channel. Absent ⇒ the App\n * switches by building a channel for the chosen session.\n */\n requestSessionSwitch?: (sessionId: string) => Promise<void>;\n providerOverride?: string | undefined;\n providerType?: string | undefined;\n modelId?: string;\n permissionMode?: TPermissionMode;\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n resumeSessionId?: string;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: a deep link's prefill, consumed once by the composer. */\n initialInput?: string;\n initialInputOrigin?: 'external-link';\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n pluginAdapter?: ICommandPluginAdapter;\n cliAdapter: ITuiCliAdapter;\n /** SCREEN-1993: the stored-prompt source and project key for the input area's search. */\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n /** SCREEN-2002: the theme catalogue this run renders from; absent ⇒ the built-ins. */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n}\n\ninterface IActiveChannel {\n readonly state: { channel: ITuiAppChannelPort; sessionId: string | undefined };\n readonly showPicker: boolean;\n readonly error: string | undefined;\n readonly switching: boolean;\n readonly switchSession: (sessionId: string) => Promise<void>;\n readonly retrySwitch: () => void;\n}\n\nfunction useActiveChannel(props: IProps): IActiveChannel {\n const [state, setState] = useState(() => ({\n channel: props.createChannel(props.resumeSessionId),\n sessionId: props.resumeSessionId,\n }));\n const [showPicker, setShowPicker] = useState(props.showSessionPickerOnStart ?? false);\n const [error, setError] = useState<string | undefined>();\n const [isSwitching, setIsSwitching] = useState(false);\n const [failedTarget, setFailedTarget] = useState<string | undefined>();\n const switching = useRef(false);\n const mounted = useRef(true);\n useEffect(\n () => () => {\n mounted.current = false;\n },\n [],\n );\n const switchSession = useCallback(\n async (sessionId: string): Promise<void> => {\n if (switching.current) return;\n switching.current = true;\n setIsSwitching(true);\n try {\n setShowPicker(false);\n if (props.requestSessionSwitch) {\n await props.requestSessionSwitch(sessionId);\n } else {\n await state.channel.stop();\n if (!mounted.current) return;\n setState({ channel: props.createChannel(sessionId), sessionId });\n }\n if (!mounted.current) return;\n setFailedTarget(undefined);\n setError(undefined);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n setError(`Session switch failed: ${message}`);\n setFailedTarget(sessionId);\n } finally {\n switching.current = false;\n setIsSwitching(false);\n }\n },\n [props.createChannel, props.requestSessionSwitch, state.channel],\n );\n const retrySwitch = useCallback((): void => {\n if (failedTarget !== undefined) void switchSession(failedTarget);\n }, [failedTarget, switchSession]);\n return { state, showPicker, error, switching: isSwitching, switchSession, retrySwitch };\n}\n\n/** React composition shell. Concrete framework objects stay outside the React tree. */\nexport default function App(props: IProps): React.ReactElement {\n const active = useActiveChannel(props);\n\n return (\n <TuiCliAdapterProvider value={props.cliAdapter}>\n <AppView\n key={active.state.sessionId ?? '__new__'}\n cwd={props.cwd}\n channel={active.state.channel}\n onSessionSwitch={active.switchSession}\n onRetrySessionSwitch={active.retrySwitch}\n sessionSwitchError={active.error}\n sessionSwitchPending={active.switching}\n showSessionPickerOnStart={active.showPicker}\n {...(props.initialInput !== undefined ? { initialInput: props.initialInput } : {})}\n {...(props.initialInputOrigin !== undefined\n ? { initialInputOrigin: props.initialInputOrigin }\n : {})}\n {...(props.providerType !== undefined ? { providerType: props.providerType } : {})}\n {...(props.modelId !== undefined ? { modelId: props.modelId } : {})}\n {...(props.permissionMode !== undefined ? { permissionMode: props.permissionMode } : {})}\n {...(props.version !== undefined ? { version: props.version } : {})}\n {...(props.sessionStore !== undefined ? { sessionStore: props.sessionStore } : {})}\n {...(props.startupUpdateNotice !== undefined\n ? { startupUpdateNotice: props.startupUpdateNotice }\n : {})}\n {...(props.transportRegistry !== undefined\n ? { transportRegistry: props.transportRegistry }\n : {})}\n {...(props.pluginAdapter !== undefined ? { pluginAdapter: props.pluginAdapter } : {})}\n {...(props.promptHistorySource !== undefined\n ? { promptHistorySource: props.promptHistorySource }\n : {})}\n {...(props.promptHistoryProject !== undefined\n ? { promptHistoryProject: props.promptHistoryProject }\n : {})}\n {...(props.themeRegistry !== undefined ? { themeRegistry: props.themeRegistry } : {})}\n {...(props.reducedMotion !== undefined ? { reducedMotion: props.reducedMotion } : {})}\n {...(props.reducedMotionOverride !== undefined\n ? { reducedMotionOverride: props.reducedMotionOverride }\n : {})}\n />\n </TuiCliAdapterProvider>\n );\n}\n","/**\n * SCREEN-1992: whether the user is at the terminal.\n *\n * Two sources feed one boolean. Focus reporting (DECSET 1004) was REQUESTED, not confirmed — a\n * terminal that does not implement the mode discards the request and never answers — so focus\n * becomes authoritative only once the first `CSI I`/`CSI O` has actually arrived; from then on\n * keystrokes carry no attention meaning. Until then, and wherever the mode is off, the only signal\n * is input: silence past the idle threshold means \"away\", the next keystroke means \"back\".\n */\nimport { MS_PER_MINUTE } from './time-units.js';\n\nconst DEFAULT_IDLE_THRESHOLD_MINUTES = 5;\nexport const DEFAULT_IDLE_THRESHOLD_MS = DEFAULT_IDLE_THRESHOLD_MINUTES * MS_PER_MINUTE;\n\nexport type TAttentionChangeKind = 'lost' | 'returned';\n\nexport interface IAttentionChange {\n readonly kind: TAttentionChangeKind;\n /** ISO timestamp of the transition, from the injected clock. */\n readonly at: string;\n}\n\nexport type TAttentionListener = (change: IAttentionChange) => void;\n\n/** What a consumer of attention sees: the level, and every transition. */\nexport interface IAttentionSource {\n readonly attended: boolean;\n /** Which source decides the level right now — recorded so a degraded run is never a silent default. */\n readonly source: 'focus' | 'idle';\n subscribe(listener: TAttentionListener): () => void;\n}\n\nexport interface IAttentionTrackerOptions {\n /** Whether DECSET 1004 was requested; the first focus event is what makes it authoritative. */\n readonly focusReporting: boolean;\n readonly now: () => number;\n readonly idleThresholdMs?: number;\n}\n\nexport class AttentionTracker implements IAttentionSource {\n private attendedState = true;\n private focusObserved = false;\n private readonly listeners = new Set<TAttentionListener>();\n private idleTimer: ReturnType<typeof setTimeout> | undefined;\n private readonly idleThresholdMs: number;\n\n constructor(private readonly options: IAttentionTrackerOptions) {\n this.idleThresholdMs = options.idleThresholdMs ?? DEFAULT_IDLE_THRESHOLD_MS;\n }\n\n get attended(): boolean {\n return this.attendedState;\n }\n\n get source(): 'focus' | 'idle' {\n return this.focusObserved ? 'focus' : 'idle';\n }\n\n subscribe(listener: TAttentionListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Arms the idle fallback; the first focus event disarms it for good. */\n start(): void {\n if (this.focusObserved) return;\n this.armIdleTimer();\n }\n\n focusIn(): void {\n this.observeFocus();\n this.transition(true);\n }\n\n focusOut(): void {\n this.observeFocus();\n this.transition(false);\n }\n\n /** Any key the user pressed. Only the idle source reads it. */\n keystroke(): void {\n if (this.focusObserved) return;\n this.transition(true);\n this.armIdleTimer();\n }\n\n private observeFocus(): void {\n if (!this.options.focusReporting) {\n throw new Error('AttentionTracker: a focus event arrived although focus reporting is off.');\n }\n if (this.focusObserved) return;\n this.focusObserved = true;\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n }\n\n dispose(): void {\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n this.listeners.clear();\n }\n\n private armIdleTimer(): void {\n if (this.idleTimer !== undefined) clearTimeout(this.idleTimer);\n this.idleTimer = setTimeout(() => {\n this.idleTimer = undefined;\n this.transition(false);\n }, this.idleThresholdMs);\n // Never keep the process alive for an attention timer.\n if (typeof this.idleTimer === 'object' && 'unref' in this.idleTimer) this.idleTimer.unref();\n }\n\n private transition(attended: boolean): void {\n if (this.attendedState === attended) return;\n this.attendedState = attended;\n const change: IAttentionChange = {\n kind: attended ? 'returned' : 'lost',\n at: new Date(this.options.now()).toISOString(),\n };\n for (const listener of this.listeners) listener(change);\n }\n}\n","import { Readable } from 'node:stream';\n\n/**\n * SCREEN-1992: the stdin proxy handed to Ink's `render({ stdin })`.\n *\n * Ink tokenizes `ESC [ I` as a keypress and every `useInput` listener would receive `[I`, so the\n * composer would type it. The proxy sits in front of the real stdin, strips the two focus sequences\n * while mode 1004 is negotiated, reports focus and keystrokes, and forwards every other byte\n * unchanged — including a split escape sequence and a bracketed paste, which are Ink's to parse.\n */\nconst ESC = 0x1b;\nconst FOCUS_IN = '\\x1b[I';\nconst FOCUS_OUT = '\\x1b[O';\n\nexport interface IFocusInputFilterResult {\n /** The bytes Ink receives, byte-identical to the input apart from the stripped focus sequences. */\n readonly forwarded: Buffer;\n readonly focusIn: number;\n readonly focusOut: number;\n}\n\n/** Pure: split one chunk into focus events and the bytes that remain. */\nexport function filterFocusSequences(chunk: Buffer, negotiated: boolean): IFocusInputFilterResult {\n if (!negotiated || !chunk.includes(ESC)) return { forwarded: chunk, focusIn: 0, focusOut: 0 };\n const text = chunk.toString('latin1');\n let focusIn = 0;\n let focusOut = 0;\n let forwarded = '';\n let index = 0;\n while (index < text.length) {\n if (text.startsWith(FOCUS_IN, index)) {\n focusIn += 1;\n index += FOCUS_IN.length;\n } else if (text.startsWith(FOCUS_OUT, index)) {\n focusOut += 1;\n index += FOCUS_OUT.length;\n } else {\n forwarded += text[index];\n index += 1;\n }\n }\n return { forwarded: Buffer.from(forwarded, 'latin1'), focusIn, focusOut };\n}\n\nexport interface IFocusReportingStdinHooks {\n /** Whether mode 1004 is currently negotiated; read per chunk so a handoff can flip it. */\n readonly negotiated: () => boolean;\n readonly onFocusIn: () => void;\n readonly onFocusOut: () => void;\n /** Any byte that was not a focus sequence. */\n readonly onKeystroke: () => void;\n}\n\ntype TStdinDataListener = (chunk: Buffer | string) => void;\n\n/** What ink 7.1.1 reads from the stream it is given (`App.js` input handling, `ink.js` kitty probe). */\ntype TInkStdinSurface = Pick<\n Readable,\n 'read' | 'setEncoding' | 'unshift' | 'on' | 'addListener' | 'removeListener'\n> & {\n readonly isTTY: boolean;\n // `tty.ReadStream` types these as returning itself; Ink discards the return value.\n setRawMode(mode: boolean): TInkStdinSurface;\n ref(): TInkStdinSurface;\n unref(): TInkStdinSurface;\n};\n\n/** The subset of the real stdin the proxy touches (what `process.stdin` provides). */\nexport interface IFocusReportingStdinSource {\n readonly isTTY?: boolean;\n on(event: 'data', listener: TStdinDataListener): void;\n off(event: 'data', listener: TStdinDataListener): void;\n setRawMode?: (mode: boolean) => void;\n ref?: () => void;\n unref?: () => void;\n}\n\nexport class FocusReportingStdin extends Readable {\n private readonly onData = (data: Buffer | string): void => {\n const chunk = typeof data === 'string' ? Buffer.from(data) : data;\n const result = filterFocusSequences(chunk, this.hooks.negotiated());\n for (let i = 0; i < result.focusOut; i += 1) this.hooks.onFocusOut();\n for (let i = 0; i < result.focusIn; i += 1) this.hooks.onFocusIn();\n if (result.forwarded.length === 0) return;\n this.hooks.onKeystroke();\n this.push(result.forwarded);\n };\n\n constructor(\n private readonly source: IFocusReportingStdinSource,\n private readonly hooks: IFocusReportingStdinHooks,\n ) {\n super({ read() {} });\n source.on('data', this.onData);\n }\n\n get isTTY(): boolean {\n return this.source.isTTY === true;\n }\n\n setRawMode(mode: boolean): this {\n this.source.setRawMode?.(mode);\n return this;\n }\n\n ref(): this {\n this.source.ref?.();\n return this;\n }\n\n unref(): this {\n this.source.unref?.();\n return this;\n }\n\n /**\n * Ink declares `stdin?: NodeJS.ReadStream` (a `tty.ReadStream`, which cannot be constructed\n * without an fd) but touches only the members named in `TInkStdinSurface` — verified against ink\n * 7.1.1's App and Ink classes. Assigning `this` to that surface is a typed check that every one of\n * them exists here; the final narrowing is from the surface Ink reads to the type it declares.\n */\n asInkStdin(): NodeJS.ReadStream {\n const surface: TInkStdinSurface = this;\n return surface as NodeJS.ReadStream;\n }\n\n /** Detach from the underlying stdin; the proxy emits nothing afterwards. */\n detach(): void {\n this.source.off('data', this.onData);\n }\n}\n","/**\n * CLI-2004 — the first line the process prints.\n *\n * Two mutually exclusive lines, and never more than one:\n * - the mode is ON → `[Screen reader mode: on via flag|env|settings]`. It is the only\n * self-diagnostic in the design: when someone reports \"I passed the flag and nothing changed\",\n * this line says whether the mode is on and which input turned it on.\n * - the mode is OFF and the environment looks like a reader is present → one advisory line naming\n * the flag. That is the whole of this project's answer to the auto-detection question: opt-in\n * keeps determinism, and a false positive costs one line of text instead of reshaping a sighted\n * user's interface.\n *\n * Printed before Ink's first frame and never during a turn, so it cannot interleave with output a\n * reader is consuming.\n */\n\n/** Which input turned the mode on. Mirrors the CLI resolver's channel. */\nexport type TScreenReaderChannel = 'flag' | 'env' | 'settings';\n\nexport interface IScreenReaderAnnouncementInputs {\n enabled: boolean;\n channel?: TScreenReaderChannel | undefined;\n /** True when the mode is off but the environment suggests a reader is running. */\n hint?: boolean | undefined;\n}\n\n/** The confirmation / advisory line, or `undefined` when neither applies. Pure. */\nexport function screenReaderAnnouncement(\n inputs: IScreenReaderAnnouncementInputs,\n): string | undefined {\n if (inputs.enabled) {\n // A mode that is on always names a channel; `settings` is the lowest tier and the safe read\n // when a caller enables the mode without saying how.\n return `[Screen reader mode: on via ${inputs.channel ?? 'settings'}]`;\n }\n if (inputs.hint === true) {\n return '[Screen reader mode: off — run with --screen-reader]';\n }\n return undefined;\n}\n\n/** Write the line (with its newline) when there is one. Silent otherwise. */\nexport function writeScreenReaderAnnouncement(\n inputs: IScreenReaderAnnouncementInputs,\n write: (text: string) => void = (text) => {\n process.stdout.write(text);\n },\n): void {\n const line = screenReaderAnnouncement(inputs);\n if (line === undefined) return;\n write(`${line}\\n`);\n}\n","/**\n * CLI-2004 — the startup quiet period.\n *\n * A terminal has no `aria-live`. The ONLY way to influence what a reader announces is which bytes\n * land in the buffer and WHEN, which is why this wait exists at all: it lets the reader finish\n * speaking the confirmation line before the first prompt frame overwrites the region it is reading.\n * A keypress ends it early.\n *\n * THE PRE-WRITE PARK (SCREEN-2670) is the second wait § Decision verdict (i) adopted. It lives in\n * `screen-reader-stdout.ts`: an owned write path handed to Ink as `stdout`, which separates COMMITS\n * in time so a diff-based reader's snapshot can fall between two of them. This module only resolves\n * its interval. The default is PROVISIONAL: screen-reader mode runs Ink unthrottled, so no frame\n * interval derives it, and the governing timescale — the reader's own sampling cadence — is not\n * measurable from this harness. `50` matches the sole product precedent until it is measured\n * against a real reader (recorded as an open item in this package's SPEC).\n *\n * The DEFAULT IS MEASURED AGAINST THIS RENDER LOOP, not copied: `DEFAULT_STARTUP_QUIET_MS` is the\n * observed boot-to-first-prompt interval of this binary (~450 ms in the PTY harness) doubled, so the\n * confirmation line is spoken before the prompt lands without the reference's 3 s stall, which is\n * tuned to a heavier boot.\n *\n * The cap is a sanity bound, not policy: a value above it is clamped AND REPORTED. Nothing here\n * silently substitutes a number — an unparseable value is refused with a note on stderr and the\n * default stands (No-Fallback: the refusal is visible).\n */\n\n/** Sanity bound on the startup quiet period (10 minutes). */\nexport const STARTUP_QUIET_MS_MAX = 600_000;\n/** Boot-to-first-prompt of this binary, doubled. */\nexport const DEFAULT_STARTUP_QUIET_MS = 900;\n\n/** Sanity bound on the pre-write park (5 seconds), matching the product precedent verdict (i) adopted. */\nexport const PREPARK_MS_MAX = 5000;\n/** PROVISIONAL — taken to match the sole product precedent, pending measurement against a reader. */\nexport const DEFAULT_PREPARK_MS = 50;\n\n/** The resolved pacing. `0` whenever the mode is off. */\nexport interface IScreenReaderPacing {\n startupQuietMs: number;\n /** Interval parked in front of each printable commit after the first. `0` disables the park. */\n preparkMs: number;\n}\n\n/** Raw timing choices supplied by the embedding host, with names for visible diagnostics. */\nexport interface IScreenReaderPacingOverrides {\n readonly startupQuiet?: { readonly raw: string | undefined; readonly label: string };\n readonly prepark?: { readonly raw: string | undefined; readonly label: string };\n}\n\nexport interface IResolvePacingInputs {\n /** Screen-reader mode. Off ⇒ the wait is 0 regardless of host overrides. */\n enabled: boolean;\n /** Absent values use the renderer's numeric defaults; no environment is read here. */\n overrides?: IScreenReaderPacingOverrides;\n /** Where a clamp or a refusal is reported. Defaults to stderr. */\n warn?: (message: string) => void;\n}\n\nfunction reportTo(warn: IResolvePacingInputs['warn']): (message: string) => void {\n return (\n warn ??\n ((message: string): void => {\n process.stderr.write(`${message}\\n`);\n })\n );\n}\n\n/**\n * Read the duration variable. Absent ⇒ the default. Non-numeric or negative ⇒ the default WITH a\n * note (never a silent 0). Above the bound ⇒ the bound WITH a note. `0` is honoured exactly — it is\n * the documented way to ask for no wait at all.\n */\nfunction resolveDuration(\n raw: string | undefined,\n name: string,\n fallback: number,\n bound: number,\n warn: (message: string) => void,\n): number {\n if (raw === undefined || raw.trim() === '') return fallback;\n const parsed = Number(raw.trim());\n if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 0) {\n warn(\n `${name}: ignoring \"${raw}\" — expected a whole number of milliseconds. Using ${fallback}.`,\n );\n return fallback;\n }\n if (parsed > bound) {\n warn(`${name}: clamping ${parsed} to the ${bound} ms sanity bound.`);\n return bound;\n }\n return parsed;\n}\n\n/** Resolve the wait once, at startup. */\nexport function resolvePacing(inputs: IResolvePacingInputs): IScreenReaderPacing {\n if (!inputs.enabled) return { startupQuietMs: 0, preparkMs: 0 };\n const warn = reportTo(inputs.warn);\n return {\n startupQuietMs: resolveDuration(\n inputs.overrides?.startupQuiet?.raw,\n inputs.overrides?.startupQuiet?.label ?? 'startup quiet period',\n DEFAULT_STARTUP_QUIET_MS,\n STARTUP_QUIET_MS_MAX,\n warn,\n ),\n preparkMs: resolveDuration(\n inputs.overrides?.prepark?.raw,\n inputs.overrides?.prepark?.label ?? 'pre-write park',\n DEFAULT_PREPARK_MS,\n PREPARK_MS_MAX,\n warn,\n ),\n };\n}\n\n/** The one-shot keypress source that can settle the startup wait early. */\nexport interface IKeypressSource {\n once(event: 'data', listener: () => void): void;\n off(event: 'data', listener: () => void): void;\n resume?(): void;\n pause?(): void;\n /** Present on a TTY stdin. Without raw mode a lone key produces no `data` event. */\n isTTY?: boolean;\n setRawMode?(mode: boolean): void;\n}\n\n/**\n * Put a TTY stdin into raw mode for the duration of the wait, returning the undo.\n *\n * Without this the promise below only settles on Enter: in canonical mode the terminal buffers a\n * line, so \"any keypress ends the wait\" — which this package's SPEC states — would be false on a\n * real terminal and true only against a synthetic source. Ink sets raw mode itself once it renders;\n * this window closes before that, and restores whatever was set before.\n */\nfunction withRawMode(keys: IKeypressSource | undefined): () => void {\n if (keys?.isTTY !== true || typeof keys.setRawMode !== 'function') return (): void => {};\n keys.setRawMode(true);\n return (): void => {\n keys.setRawMode?.(false);\n };\n}\n\n/**\n * Wait out the startup quiet period. Resolves at the deadline, or as soon as a key is pressed —\n * whichever comes first. A `0` wait resolves without touching the keypress source at all.\n *\n * The keystroke that ends the wait is CONSUMED, which is the documented behaviour: the wait exists\n * so the reader is not interrupted, and the key that says \"I am ready\" is the answer to it.\n */\nexport async function awaitStartupQuietPeriod(\n ms: number,\n keys?: IKeypressSource,\n): Promise<'elapsed' | 'keypress'> {\n if (ms <= 0) return 'elapsed';\n const restoreRawMode = withRawMode(keys);\n return new Promise<'elapsed' | 'keypress'>((resolve) => {\n const settle = (outcome: 'elapsed' | 'keypress'): void => {\n restoreRawMode();\n keys?.pause?.();\n resolve(outcome);\n };\n const onKey = (): void => {\n clearTimeout(timer);\n settle('keypress');\n };\n const timer = setTimeout(() => {\n if (keys !== undefined) keys.off('data', onKey);\n settle('elapsed');\n }, ms);\n // Do not hold the process open on the pacing timer alone.\n timer.unref?.();\n if (keys !== undefined) {\n keys.once('data', onKey);\n keys.resume?.();\n }\n });\n}\n","/**\n * SCREEN-2670 — the pre-write park: an owned write path between Ink and the terminal.\n *\n * A diff-based screen reader computes what to speak from successive terminal snapshots. In\n * screen-reader mode Ink runs UNTHROTTLED (`ink.js:193`), so commits land back to back and a reader\n * that samples between two of them sees a torn or already-overwritten region. The park separates\n * commits IN TIME so a snapshot can fall between them. It does not move the cursor: every frame Ink\n * writes already begins with `eraseLines`, which ends in `cursorLeft`, so column zero is where the\n * frame starts anyway (CLI-062 invariant I3 — no cursor sequence is written here).\n *\n * THE UNIT IS A COMMIT, NOT A CHUNK. One commit in the mode is up to four `write()` calls in one\n * synchronous run (`ink.js:371-412`): synchronized-output begin, `erase + staticOutput`, the frame,\n * synchronized-output end. Parking each would pay the interval three times and leave the erased\n * region on screen between the erase and the frame — the torn state the park exists to prevent. So\n * the chunks of one synchronous run form ONE batch (closed on `process.nextTick`), parked once in\n * front and released contiguously.\n *\n * WHAT IS NEVER PARKED:\n * - the first batch of a session (there is no earlier commit to separate it from);\n * - a batch with no printable content — Ink's empty-string barrier (`ink.js:588`, `:647`), an OSC\n * 133 mark, a bare control sequence — which still queues BEHIND anything pending so order holds;\n * - a batch stamped with the echo flag: the composer's own key handler arms it for text-mutating\n * keys only, it is read when the batch OPENS and carried on it (so a keystroke queued behind a\n * parked reply keeps its exemption), and it expires on `setImmediate` whether or not a batch took\n * it, so a keystroke that commits nothing cannot un-park a later transcript batch.\n *\n * NOTHING IS EVER DROPPED. Ink keeps its own erase bookkeeping (`lastOutputHeight`) from the frame\n * it believes it wrote, and writes each `<Static>` item exactly once — so a batch that never reaches\n * the terminal corrupts the next erase and loses a transcript line for good. When a newer printable\n * batch closes while an older one is still parked, the OLDER one is released at once, contiguously\n * and without its park, so at most one batch ever waits and memory stays bounded on a fast stream;\n * the park then applies to the newest commit only.\n */\n\nimport { sanitizeTerminalText } from './sanitize-terminal-text.js';\n\nimport type { IScreenReaderPacingPort } from './screen-reader-pacing-context.js';\n\n/** Ink's own fallbacks when the stream reports no size. */\nconst DEFAULT_COLUMNS = 80;\nconst DEFAULT_ROWS = 24;\n\ntype TWriteCallback = (error?: Error | null) => void;\n\n/**\n * What Ink 7.1.1 touches on the stream it is given (`ink.js`, `log-update.js`): the write, the\n * dimensions, the TTY flag, the resize listener pair, and the writable-state probes.\n */\nexport type TParkedStdoutSource = Pick<\n NodeJS.WriteStream,\n | 'write'\n | 'columns'\n | 'rows'\n | 'isTTY'\n | 'destroyed'\n | 'writable'\n | 'writableEnded'\n | 'writableLength'\n | 'writableNeedDrain'\n | 'on'\n | 'off'\n>;\n\nexport interface ICreateParkedStdoutOptions {\n stdout: TParkedStdoutSource;\n /** Milliseconds between the previous printable release and the next parked one. */\n preparkMs: number;\n /** Injected clock for tests. Defaults to `Date.now`. */\n now?: () => number;\n}\n\ninterface IChunk {\n text: string;\n callback: TWriteCallback | undefined;\n}\n\ninterface IBatch {\n chunks: IChunk[];\n /** Read at formation, never at release. */\n echo: boolean;\n printable: boolean;\n /** Ink's `write('', cb)` — the exit barrier, which must reach the stream in order. */\n barrier: boolean;\n}\n\nexport interface IParkedStdout {\n /** The stream to hand Ink as `stdout`. One stable object: Ink keys its instance map by it. */\n asInkStdout(): NodeJS.WriteStream;\n /** The composer's key handler arms this for a text-mutating key. Expires on `setImmediate`. */\n armEchoRelease(): void;\n /** Write through the same ordered path — for the OSC 133 marks, which are positional. */\n write(chunk: string | Uint8Array, callback?: TWriteCallback): boolean;\n /**\n * Release everything queued NOW, in order and without waiting out a park, and keep parking what\n * comes later — for a terminal handoff, so nothing lands on top of the child's output.\n */\n drain(): Promise<void>;\n /** Release everything still queued, in order and without further parking; resolves when written. */\n flush(): Promise<void>;\n}\n\n/** The members Ink reads from `options.stdout`, narrowed as `TInkStdinSurface` narrows stdin. */\ntype TInkStdoutSurface = TParkedStdoutSource;\n\nfunction toText(chunk: string | Uint8Array): string {\n return typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');\n}\n\n/** Printable = something a reader could speak: visible text after every escape is stripped. */\nfunction isPrintable(text: string): boolean {\n return sanitizeTerminalText(text).trim().length > 0;\n}\n\nclass ParkedStdout implements TInkStdoutSurface {\n private readonly source: TParkedStdoutSource;\n private readonly preparkMs: number;\n private readonly now: () => number;\n private open: IBatch | undefined;\n private readonly queue: IBatch[] = [];\n private timer: NodeJS.Timeout | undefined;\n private lastPrintableReleaseAt: number | undefined;\n private echoArmed = false;\n private echoExpiryScheduled = false;\n private flushing = false;\n private drainWaiters: Array<() => void> = [];\n\n constructor(options: ICreateParkedStdoutOptions) {\n this.source = options.stdout;\n this.preparkMs = options.preparkMs;\n this.now = options.now ?? Date.now;\n this.on = ((...args: Parameters<NodeJS.WriteStream['on']>) =>\n this.source.on(...args)) as NodeJS.WriteStream['on'];\n this.off = ((...args: Parameters<NodeJS.WriteStream['off']>) =>\n this.source.off(...args)) as NodeJS.WriteStream['off'];\n }\n\n // ── the surface Ink reads ──────────────────────────────────────────────────────────────────\n\n get columns(): number {\n return this.source.columns ?? DEFAULT_COLUMNS;\n }\n\n get rows(): number {\n return this.source.rows ?? DEFAULT_ROWS;\n }\n\n get isTTY(): boolean {\n return this.source.isTTY ?? false;\n }\n\n get destroyed(): boolean {\n return this.source.destroyed ?? false;\n }\n\n get writable(): boolean {\n return this.source.writable ?? true;\n }\n\n get writableEnded(): boolean {\n return this.source.writableEnded ?? false;\n }\n\n get writableLength(): number {\n return this.source.writableLength ?? 0;\n }\n\n get writableNeedDrain(): boolean {\n return this.source.writableNeedDrain ?? false;\n }\n\n // Ink registers and removes its `resize` listener through these and discards the return value.\n readonly on: NodeJS.WriteStream['on'];\n readonly off: NodeJS.WriteStream['off'];\n\n /**\n * The write is deferred, so the boolean is the source's CURRENT backpressure state — what the\n * same call would have returned a moment ago. Ink does not read it; a caller that does is told\n * the truth about the stream it is ultimately writing to.\n */\n write(\n chunk: string | Uint8Array,\n encodingOrCallback?: BufferEncoding | TWriteCallback,\n maybeCallback?: TWriteCallback,\n ): boolean {\n const callback = typeof encodingOrCallback === 'function' ? encodingOrCallback : maybeCallback;\n const text = toText(chunk);\n let batch = this.open;\n if (batch === undefined) {\n const opened: IBatch = {\n chunks: [],\n echo: this.echoArmed,\n printable: false,\n barrier: false,\n };\n batch = opened;\n this.open = opened;\n // One synchronous run = one batch. nextTick drains before the check phase, so a batch opened\n // in the turn that armed the echo flag always closes before the flag's setImmediate expiry.\n process.nextTick(() => this.close(opened));\n }\n batch.chunks.push({ text, callback });\n if (text.length === 0) batch.barrier = true;\n else if (!batch.printable && isPrintable(text)) batch.printable = true;\n return !this.writableNeedDrain;\n }\n\n // ── the owned-writer surface ──────────────────────────────────────────────────────────────\n\n asInkStdout(): NodeJS.WriteStream {\n const surface: TInkStdoutSurface = this;\n return surface as NodeJS.WriteStream;\n }\n\n armEchoRelease(): void {\n this.echoArmed = true;\n if (this.echoExpiryScheduled) return;\n this.echoExpiryScheduled = true;\n setImmediate(() => {\n this.echoArmed = false;\n this.echoExpiryScheduled = false;\n });\n }\n\n drain(): Promise<void> {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n for (const waiting of this.queue.splice(0)) this.release(waiting);\n return this.settled();\n }\n\n flush(): Promise<void> {\n this.flushing = true;\n return this.drain();\n }\n\n /** Resolves once no batch is open or queued. */\n private settled(): Promise<void> {\n if (this.open === undefined && this.queue.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n this.drainWaiters.push(resolve);\n });\n }\n\n // ── batching and release ──────────────────────────────────────────────────────────────────\n\n private close(batch: IBatch): void {\n if (this.open !== batch) return;\n this.open = undefined;\n // A newer printable commit closing behind a parked one: release the older one NOW rather than\n // drop it — Ink's erase bookkeeping and its once-only Static writes both assume it landed.\n if (this.isParked(batch)) {\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n for (const waiting of this.queue.splice(0)) this.release(waiting);\n }\n this.queue.push(batch);\n this.pump();\n }\n\n /** The first printable release of a session is never parked: nothing precedes it to separate. */\n private isParked(batch: IBatch): boolean {\n return (\n this.preparkMs > 0 &&\n this.lastPrintableReleaseAt !== undefined &&\n batch.printable &&\n !batch.echo &&\n !this.flushing\n );\n }\n\n private pump(): void {\n // Always recompute from the head: a superseded head leaves a stale timer behind, and the batch\n // now at the head may be a control-only one that must not wait on it.\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n while (this.queue.length > 0) {\n const head = this.queue[0] as IBatch;\n if (this.isParked(head)) {\n const due = (this.lastPrintableReleaseAt ?? 0) + this.preparkMs;\n const wait = due - this.now();\n if (wait > 0) {\n // Kept referenced: this timer guards queued output and Ink's exit barrier, whose callback\n // resolves `waitUntilExit()`; the process must not exit with a frame still parked.\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.pump();\n }, wait);\n return;\n }\n }\n this.queue.shift();\n this.release(head);\n }\n if (this.open === undefined) {\n const waiters = this.drainWaiters;\n this.drainWaiters = [];\n for (const resolve of waiters) resolve();\n }\n }\n\n private release(batch: IBatch): void {\n for (const chunk of batch.chunks) {\n this.source.write(chunk.text, chunk.callback);\n }\n if (batch.printable) this.lastPrintableReleaseAt = this.now();\n }\n}\n\n/** Build the owned write path. Constructed only when the mode is on and `preparkMs > 0`. */\nexport function createParkedStdout(options: ICreateParkedStdoutOptions): IParkedStdout {\n return new ParkedStdout(options);\n}\n\n/** The port the render tree publishes: arming and the positional write, bound to one proxy. */\nexport function toPacingPort(parked: IParkedStdout): IScreenReaderPacingPort {\n return {\n armEchoRelease: () => parked.armEchoRelease(),\n write: (text) => {\n parked.write(text);\n },\n };\n}\n","/**\n * SCREEN-1992 — DECSET/DECRST 1004: ask the terminal to report focus changes (`CSI I` / `CSI O`).\n *\n * Written OUTSIDE Ink, following the `terminal-marks.ts` carve-out: the two sequences are constants,\n * so nothing needs sanitizing, and no component may emit an escape sequence. A terminal that does not\n * implement the mode discards the request; `supportsFocusReporting()` (terminal-capabilities.ts)\n * carries the gate, and `negotiated` is what the stdin filter and the attention tracker consult.\n */\nconst FOCUS_REPORTING_ENABLE = '\\x1b[?1004h';\nconst FOCUS_REPORTING_DISABLE = '\\x1b[?1004l';\n\nexport interface IFocusReportingWriterOptions {\n /** The capability gate, read at enable time. */\n supported: () => boolean;\n /** Injected sink; defaults to the process's own stdout. */\n write?: (text: string) => void;\n}\n\nexport interface IFocusReportingWriter {\n /** True between a successful `enable()` and the next `disable()`. */\n readonly negotiated: boolean;\n enable(): void;\n disable(): void;\n}\n\nexport function createFocusReportingWriter(\n options: IFocusReportingWriterOptions,\n): IFocusReportingWriter {\n const write =\n options.write ??\n ((text: string): void => {\n process.stdout.write(text);\n });\n let negotiated = false;\n return {\n get negotiated(): boolean {\n return negotiated;\n },\n enable(): void {\n if (negotiated || !options.supported()) return;\n negotiated = true;\n write(FOCUS_REPORTING_ENABLE);\n },\n disable(): void {\n if (!negotiated) return;\n negotiated = false;\n write(FOCUS_REPORTING_DISABLE);\n },\n };\n}\n","/**\n * TERM-002: TUI implementation of the `ITerminalHandoff` transport contract.\n *\n * Manual suspend/resume (NOT Ink 7.1.0 `suspendTerminal`): while a child process owns the real\n * terminal, the App renders nothing so Ink unmounts its input hooks and releases raw mode; the Ink\n * frame is cleared; the caller's `fn` runs the child with inherited stdio; then the App re-renders.\n *\n * The framework (`InteractiveSession`) owns the orchestration (exclusivity, fast-fail) — this class\n * only performs the actual screen release/reclaim. It spawns nothing itself (platform-neutral).\n */\nimport type { ITerminalHandoff } from '@robota-sdk/agent-interface-session';\n\n/** How the App suspends/resumes its rendering, registered into the controller on mount. */\nexport interface ITuiSuspendHooks {\n /** Render nothing (release Ink input/raw mode); resolves once that has committed. */\n suspend(): Promise<void>;\n /** Resume rendering and force a redraw. */\n resume(): void;\n}\n\n/**\n * SCREEN-1992: terminal modes the TUI negotiated (focus reporting) are released before a child owns\n * the terminal and re-negotiated once it is reclaimed, so the child never receives `CSI I`/`CSI O`\n * and a child that reset the mode does not leave the TUI blind afterwards.\n */\nexport interface ITerminalModeHooks {\n /** May be async: SCREEN-2670 drains the parked screen-reader output before the child gets the TTY. */\n preSuspend(): void | Promise<void>;\n postResume(): void;\n}\n\n/** Minimal slice of the Ink render instance the controller needs. */\nexport interface IInkClearable {\n clear(): void;\n}\n\nexport class TerminalHandoffController implements ITerminalHandoff {\n private hooks?: ITuiSuspendHooks;\n private instance?: IInkClearable;\n private modeHooks?: ITerminalModeHooks;\n\n /**\n * The App registers how to suspend/resume Ink. Returns an unregister function for cleanup on\n * unmount. Only a mounted App provides these hooks, which gates `canHandoffTerminal`.\n */\n registerSuspendHooks(hooks: ITuiSuspendHooks): () => void {\n this.hooks = hooks;\n return () => {\n if (this.hooks === hooks) this.hooks = undefined;\n };\n }\n\n /** render.tsx registers the terminal-mode bracket (SCREEN-1992) before `render()`. */\n setTerminalModeHooks(hooks: ITerminalModeHooks | undefined): void {\n this.modeHooks = hooks;\n }\n\n /** render.tsx supplies the Ink instance (for `clear()`) after `render()` returns. */\n setInkInstance(instance: IInkClearable): void {\n this.instance = instance;\n }\n\n get canHandoffTerminal(): boolean {\n return (\n process.stdin.isTTY === true && process.stdout.isTTY === true && this.hooks !== undefined\n );\n }\n\n async runWithTerminal<T>(fn: () => Promise<T>): Promise<T> {\n const hooks = this.hooks;\n if (!this.canHandoffTerminal || hooks === undefined) {\n throw new Error(\n 'TUI terminal handoff unavailable: no interactive TTY, or the App is not mounted.',\n );\n }\n await hooks.suspend();\n await this.modeHooks?.preSuspend();\n this.instance?.clear();\n // Releasing Ink's React input hooks (empty render) is not enough: the parent process still holds\n // a raw-mode TTY read on stdin, which (a) steals input from the inherited child and (b) starves\n // the parent event loop so the child's exit is never observed — the handoff would hang forever.\n // Explicitly hand stdin to the child by dropping raw mode and pausing the parent's reader; Ink\n // re-grabs stdin (raw mode + resume) when its input hooks re-mount on resume.\n const stdin = process.stdin;\n if (stdin.isTTY && typeof stdin.setRawMode === 'function') stdin.setRawMode(false);\n stdin.pause();\n try {\n return await fn();\n } finally {\n // Always reclaim the screen, even when the child failed. Re-rendering re-mounts the input\n // hooks, which is what restores raw mode and resumes the parent's stdin reader.\n if (stdin.isTTY && typeof stdin.setRawMode === 'function') stdin.setRawMode(true);\n stdin.resume();\n hooks.resume();\n this.modeHooks?.postResume();\n }\n }\n}\n","import { MINUTES_PER_HOUR, MS_PER_MINUTE } from './time-units.js';\n\nimport type { TExecutionNormalizedState } from '@robota-sdk/agent-interface-execution';\n\n/**\n * SCREEN-1992: one line for what happened while the user was away.\n *\n * Counts only while unattended; `onReturned` renders the interval and resets it. Nothing here\n * inspects the session — the coordinator feeds it the channel events it already receives.\n */\nexport const RECAP_MAX_WIDTH = 120;\n\n/** The framework's `TTurnSource` plus whatever a future source adds; only `agent-wakeup` is special here. */\ntype TTurnSource = string;\n\nconst TERMINAL_STATES: ReadonlySet<TExecutionNormalizedState> = new Set([\n 'completed',\n 'failed',\n 'stopped',\n]);\n\nexport class IntervalRecap {\n private lostAt: number | undefined;\n private lastTurnSource: TTurnSource = 'user';\n private turnsFinished = 0;\n private wakeTurnsFinished = 0;\n private needsInputCount = 0;\n private errorCount = 0;\n /** Every entry's latest state, attended or not — the baseline an interval starts from. */\n private readonly currentStates = new Map<string, TExecutionNormalizedState>();\n /** Entries that were not terminal when attention was lost and are terminal now. */\n private readonly reachedTerminal = new Map<string, TExecutionNormalizedState>();\n private baseline = new Map<string, TExecutionNormalizedState>();\n private baselinePending = false;\n\n get away(): boolean {\n return this.lostAt !== undefined;\n }\n\n onLost(atIso: string): void {\n this.lostAt = Date.parse(atIso);\n this.reset();\n this.baseline = new Map(this.currentStates);\n // Lost before any snapshot arrived (a channel wired while the user is already away): the first\n // snapshot is the baseline, so a resumed session's restored terminal tasks are not \"news\".\n this.baselinePending = this.currentStates.size === 0;\n }\n\n /** The recap line for the interval just ended, or undefined when there is nothing to say. */\n onReturned(atIso: string): string | undefined {\n if (this.lostAt === undefined) return undefined;\n const elapsedMs = Math.max(0, Date.parse(atIso) - this.lostAt);\n this.lostAt = undefined;\n const parts = this.parts();\n this.reset();\n if (parts.length === 0) return undefined;\n return bound(`While away ${formatElapsed(elapsedMs)}: ${parts.join(' · ')}`);\n }\n\n turnSource(source: TTurnSource): void {\n this.lastTurnSource = source;\n }\n\n turnCompleted(): void {\n if (!this.away) return;\n this.turnsFinished += 1;\n if (this.lastTurnSource === 'agent-wakeup') this.wakeTurnsFinished += 1;\n }\n\n needsInput(): void {\n if (this.away) this.needsInputCount += 1;\n }\n\n error(): void {\n if (this.away) this.errorCount += 1;\n }\n\n /**\n * A workspace entry's current state. Only an entry that REACHED a terminal state during the\n * interval is counted: one already terminal when attention was lost is old news, and one that\n * left the terminal state again (a resumed schedule) is dropped.\n */\n entryState(entryId: string, state: TExecutionNormalizedState): void {\n this.currentStates.set(entryId, state);\n if (!this.away) return;\n if (this.baselinePending) {\n this.baseline.set(entryId, state);\n return;\n }\n const before = this.baseline.get(entryId);\n const wasTerminal = before !== undefined && TERMINAL_STATES.has(before);\n if (!TERMINAL_STATES.has(state) || wasTerminal) {\n this.reachedTerminal.delete(entryId);\n return;\n }\n this.reachedTerminal.set(entryId, state);\n }\n\n /** The end of one snapshot: a pending baseline is now complete. */\n snapshotComplete(): void {\n this.baselinePending = false;\n }\n\n private parts(): string[] {\n const parts: string[] = [];\n if (this.turnsFinished > 0) {\n const wake = this.wakeTurnsFinished > 0 ? ` (${this.wakeTurnsFinished} wake)` : '';\n parts.push(`${this.turnsFinished} ${plural(this.turnsFinished, 'turn')} finished${wake}`);\n }\n if (this.needsInputCount > 0) parts.push(`${this.needsInputCount} needs input`);\n const byState = { completed: 0, failed: 0, stopped: 0 };\n for (const state of this.reachedTerminal.values()) {\n if (state in byState) byState[state as keyof typeof byState] += 1;\n }\n for (const state of ['completed', 'failed', 'stopped'] as const) {\n if (byState[state] > 0) parts.push(`${byState[state]} ${state}`);\n }\n if (this.errorCount > 0) {\n parts.push(`${this.errorCount} ${plural(this.errorCount, 'error')}`);\n }\n return parts;\n }\n\n private reset(): void {\n this.turnsFinished = 0;\n this.wakeTurnsFinished = 0;\n this.needsInputCount = 0;\n this.errorCount = 0;\n this.reachedTerminal.clear();\n }\n}\n\nfunction plural(count: number, noun: string): string {\n return count === 1 ? noun : `${noun}s`;\n}\n\nfunction formatElapsed(elapsedMs: number): string {\n const totalMinutes = Math.floor(elapsedMs / MS_PER_MINUTE);\n if (totalMinutes < 1) return '<1m';\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n if (hours === 0) return `${minutes}m`;\n return minutes === 0 ? `${hours}h` : `${hours}h ${minutes}m`;\n}\n\nfunction bound(line: string): string {\n return line.length <= RECAP_MAX_WIDTH ? line : `${line.slice(0, RECAP_MAX_WIDTH - 1)}…`;\n}\n","import { IntervalRecap } from './interval-recap.js';\n\nimport type { IAttentionSource } from './attention-tracker.js';\nimport type { IExecutionWorkspaceSnapshot } from '@robota-sdk/agent-interface-execution';\n\n/**\n * SCREEN-1992: joins the attention level to the recap accumulator for one channel.\n *\n * The tracker outlives channels (it is the terminal's), the recap belongs to the session being\n * shown; the coordinator subscribes for the channel's lifetime and pushes each recap line into the\n * channel's notice store. It lives here rather than in `tui-state-manager.ts` (at its line ceiling).\n */\nexport interface IAttentionCoordinatorOptions {\n readonly source: IAttentionSource;\n readonly onRecap: (line: string) => void;\n readonly now?: () => number;\n}\n\nexport class AttentionCoordinator {\n private readonly recap = new IntervalRecap();\n private unsubscribe: (() => void) | undefined;\n private readonly now: () => number;\n\n constructor(private readonly options: IAttentionCoordinatorOptions) {\n this.now = options.now ?? Date.now;\n }\n\n wire(): void {\n if (this.unsubscribe !== undefined) return;\n // A channel created while the user is already away starts its interval now.\n if (!this.options.source.attended) this.recap.onLost(new Date(this.now()).toISOString());\n this.unsubscribe = this.options.source.subscribe((change) => {\n if (change.kind === 'lost') {\n this.recap.onLost(change.at);\n return;\n }\n const line = this.recap.onReturned(change.at);\n if (line !== undefined) this.options.onRecap(line);\n });\n }\n\n unwire(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n }\n\n onTurnSource = (source: string): void => {\n this.recap.turnSource(source);\n };\n\n onComplete = (): void => {\n this.recap.turnCompleted();\n };\n\n onError = (): void => {\n this.recap.error();\n };\n\n onNeedsInput = (): void => {\n this.recap.needsInput();\n };\n\n onWorkspaceSnapshot = (snapshot: IExecutionWorkspaceSnapshot): void => {\n for (const entry of snapshot.entries) {\n // The main thread's turns are counted from `complete`; its idle `completed` is not an event.\n if (entry.kind === 'main_thread') continue;\n this.recap.entryState(entry.id, entry.state);\n }\n this.recap.snapshotComplete();\n };\n}\n","/**\n * Bounded polling helper for asynchronous session initialization.\n *\n * Distinguishes benign \"not yet initialized\" errors (retried until a timeout)\n * from real failures (surfaced immediately), so the TUI can show an error\n * instead of spinning forever.\n */\n\nexport type TSessionInitFailure =\n { kind: 'timeout'; lastError: Error | undefined } | { kind: 'error'; error: Error };\n\nexport interface ISessionInitPollerOptions {\n /** Throws while the session is not ready; returns normally when ready. */\n check: () => void;\n intervalMs: number;\n timeoutMs: number;\n /** Returns true when an error means \"keep polling\". Defaults to /not initialized/i. */\n isBenignError?: (error: Error) => boolean;\n onReady: () => void;\n onFailure: (failure: TSessionInitFailure) => void;\n}\n\nexport interface ISessionInitPoller {\n start(): void;\n stop(): void;\n}\n\nfunction defaultIsBenignError(error: Error): boolean {\n return /not initialized/i.test(error.message);\n}\n\nexport function createSessionInitPoller(options: ISessionInitPollerOptions): ISessionInitPoller {\n const isBenign = options.isBenignError ?? defaultIsBenignError;\n let timer: ReturnType<typeof setInterval> | null = null;\n let elapsedMs = 0;\n let lastBenignError: Error | undefined;\n\n function stop(): void {\n if (timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n }\n\n function tick(): void {\n elapsedMs += options.intervalMs;\n try {\n options.check();\n } catch (raw) {\n // allow-fallback: poller's purpose is to classify and route this error, never to swallow it\n const error = raw instanceof Error ? raw : new Error(String(raw));\n if (!isBenign(error)) {\n stop();\n options.onFailure({ kind: 'error', error });\n return;\n }\n lastBenignError = error;\n if (elapsedMs >= options.timeoutMs) {\n stop();\n options.onFailure({ kind: 'timeout', lastError: lastBenignError });\n }\n return;\n }\n stop();\n options.onReady();\n }\n\n return {\n start(): void {\n if (timer !== null) return;\n elapsedMs = 0;\n timer = setInterval(tick, options.intervalMs);\n },\n stop,\n };\n}\n","/**\n * Renders a system-command result into the TUI state (CMD-004 Phase 2).\n *\n * Pure rendering only: host actions were already executed (and consumed) by the session layer,\n * screen navigation arrives via the requester-routed `ui_intent` session event (`useSideEffects`),\n * and state-change notifications arrive as broadcast session events (`session_renamed`,\n * `history_cleared` — bound in `TuiInteractionChannel`). The only result-carried hint consumed\n * here is `data.pluginRegistryReloaded` — the requester-local command-registry/autocomplete\n * refresh (the semantic plugin reload already ran host-side inside the command).\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { TuiStateManager } from '../tui-state-manager.js';\nimport type { CommandRegistry } from '@robota-sdk/agent-framework';\nimport type { ICommandResult } from '@robota-sdk/agent-interface-command';\nimport type {\n ISessionConversationRead,\n ISessionLifecycle,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ICommandResultSession extends ISessionLifecycle, ISessionConversationRead {}\n\nexport function applySystemCommandResult(\n result: ICommandResult,\n interactiveSession: ICommandResultSession,\n registry: CommandRegistry,\n manager: TuiStateManager,\n reloadPluginCommandSource?: (registry: CommandRegistry) => void,\n): void {\n if (result.data?.['pluginRegistryReloaded'] === true) {\n reloadPluginCommandSource?.(registry);\n }\n manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));\n\n if (interactiveSession.isInitialized) {\n const ctx = interactiveSession.getContextState();\n manager.setContextState({\n percentage: ctx.usedPercentage,\n usedTokens: ctx.usedTokens,\n maxTokens: ctx.maxTokens,\n });\n }\n}\n","/** A typed `/name args` line, split the way every TUI channel dispatches it. */\nexport interface ISlashCommandInput {\n /** Lower-cased, without the slash. */\n readonly name: string;\n readonly args: string;\n}\n\nexport function parseSlashCommandInput(input: string): ISlashCommandInput {\n const parts = input.slice(1).split(/\\s+/);\n return { name: parts[0]?.toLowerCase() ?? '', args: parts.slice(1).join(' ') };\n}\n","import type { TSessionEndReason } from '@robota-sdk/agent-core';\n\nexport interface ITuiChannelShutdownRequest {\n reason: TSessionEndReason;\n message: string;\n}\n\nexport interface ITuiChannelLifecycleOperations {\n start(): Promise<void>;\n stop(): Promise<void>;\n beginShutdown(): void;\n shutdownSession(request: ITuiChannelShutdownRequest): Promise<void>;\n}\n\n/** Marks a startup failure whose rollback also failed, so retrying cannot duplicate live resources. */\nexport class TuiChannelStartRollbackError extends AggregateError {}\n\n/** Owns channel lifecycle transitions and bounds SDK shutdown without owning presentation state. */\nexport class TuiChannelLifecycleCoordinator {\n private started = false;\n private startPromise: Promise<void> | undefined;\n private teardownBegun = false;\n private stopped = false;\n private shuttingDown = false;\n private shutdownPromise: Promise<void> | undefined;\n private stopPromise: Promise<void> | undefined;\n\n constructor(\n private readonly operations: ITuiChannelLifecycleOperations,\n private readonly defaultShutdownTimeoutMs: number,\n ) {}\n\n get isShuttingDown(): boolean {\n return this.shuttingDown;\n }\n\n /** False before startup and immediately when teardown begins, even if teardown later fails. */\n get isActiveForPeerStatus(): boolean {\n return this.started && !this.teardownBegun && !this.shuttingDown;\n }\n\n async start(): Promise<void> {\n if (this.teardownBegun) {\n throw new Error('Cannot start a channel after teardown has begun.');\n }\n if (this.started) return;\n const pendingStart = (this.startPromise ??= this.operations.start());\n try {\n await pendingStart;\n this.started = true;\n } catch (error) {\n if (error instanceof TuiChannelStartRollbackError) this.teardownBegun = true;\n throw error;\n } finally {\n if (this.startPromise === pendingStart) this.startPromise = undefined;\n }\n }\n\n async stop(): Promise<void> {\n this.teardownBegun = true;\n if (this.stopped) return;\n this.stopPromise ??= this.performStop();\n try {\n await this.stopPromise;\n } catch (error) {\n // A failed teardown is retryable; claiming stopped here would strand owned resources.\n this.stopPromise = undefined;\n throw error;\n }\n }\n\n private async performStop(): Promise<void> {\n const pendingStart = this.startPromise;\n let startError: Error | undefined;\n if (pendingStart !== undefined) {\n try {\n await pendingStart;\n } catch (cause) {\n startError = cause instanceof Error ? cause : new Error(String(cause));\n }\n }\n let stopError: Error | undefined;\n try {\n await this.operations.stop();\n } catch (error) {\n stopError = error instanceof Error ? error : new Error(String(error));\n }\n this.shutdownPromise ??= this.shutdownSessionBounded(\n { reason: 'other', message: 'channel stopped' },\n this.defaultShutdownTimeoutMs,\n );\n await this.shutdownPromise;\n if (stopError !== undefined && startError !== undefined) {\n throw new AggregateError(\n [startError, stopError],\n 'TUI channel start and teardown both failed.',\n );\n }\n if (stopError !== undefined) throw stopError;\n this.started = false;\n this.stopped = true;\n if (startError !== undefined) throw startError;\n }\n\n async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {\n this.teardownBegun = true;\n if (!this.shuttingDown) {\n this.shuttingDown = true;\n this.operations.beginShutdown();\n }\n const pendingStop = this.stopPromise;\n if (pendingStop !== undefined) {\n await pendingStop.catch(() => undefined); // allow-fallback: the stop caller owns teardown failure\n return;\n }\n this.shutdownPromise ??= this.performShutdown(options);\n await this.shutdownPromise;\n }\n\n private async performShutdown(options?: {\n reason?: TSessionEndReason;\n timeoutMs?: number;\n }): Promise<void> {\n const pendingStart = this.startPromise;\n if (pendingStart !== undefined) {\n await pendingStart.catch(() => undefined); // allow-fallback: the start caller owns startup failure\n }\n await this.shutdownSessionBounded(\n { reason: options?.reason ?? 'prompt_input_exit', message: 'CLI shutdown' },\n options?.timeoutMs ?? this.defaultShutdownTimeoutMs,\n );\n }\n\n private async shutdownSessionBounded(\n request: ITuiChannelShutdownRequest,\n timeoutMs: number,\n ): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n timer.unref?.();\n });\n await Promise.race([\n this.operations.shutdownSession(request).catch(() => undefined), // allow-fallback: shutdown is best-effort during process exit and remains timeout-bounded\n timeout,\n ]);\n if (timer) clearTimeout(timer);\n }\n}\n","import type { IPendingPermissionRequest } from './types.js';\nimport type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';\nimport type { TPermissionResultValue } from '@robota-sdk/agent-interface-session';\n\ninterface IUserActionQueueEntry {\n presentedRequest: IActionRequest;\n resolve: (response: TActionResponse) => void;\n id?: string;\n}\n\ninterface IPermissionQueueEntry {\n toolName: string;\n toolArgs: TToolArgs;\n canPersistProjectPermission?: boolean;\n requestedByPeer?: string;\n resolve: (result: TPermissionResultValue) => void;\n id?: string;\n}\n\n/** Serializes action prompts and guarantees every pending promise is settled during teardown. */\nexport class TuiUserActionQueue {\n private entries: IUserActionQueueEntry[] = [];\n private processing = false;\n\n constructor(private readonly onChange: () => void) {}\n\n get current(): IActionRequest | null {\n return this.processing ? (this.entries[0]?.presentedRequest ?? null) : null;\n }\n\n enqueue(request: IActionRequest, id?: string): Promise<TActionResponse> {\n return new Promise<TActionResponse>((resolve) => {\n this.entries.push({\n presentedRequest: { ...request },\n resolve,\n ...(id !== undefined ? { id } : {}),\n });\n this.processNext();\n });\n }\n\n resolveCurrent(expectedRequest: IActionRequest, response: TActionResponse): void {\n if (this.entries[0]?.presentedRequest !== expectedRequest) return;\n const pending = this.entries.shift();\n if (!pending) return;\n this.processing = false;\n this.onChange();\n pending.resolve(response);\n this.processNext();\n }\n\n dismissById(id: string): boolean {\n const dismissed = this.entries.filter((entry) => entry.id === id);\n if (dismissed.length === 0) return false;\n this.entries = this.entries.filter((entry) => entry.id !== id);\n this.processing = false;\n for (const entry of dismissed) entry.resolve({ type: 'cancelled' });\n this.processNext();\n return true;\n }\n\n cancelAll(): void {\n const queued = this.entries;\n this.entries = [];\n this.processing = false;\n for (const pending of queued) pending.resolve({ type: 'cancelled' });\n this.onChange();\n }\n\n private processNext(): void {\n if (this.processing) return;\n if (!this.entries[0]) {\n this.onChange();\n return;\n }\n this.processing = true;\n this.onChange();\n }\n}\n\n/** Serializes permission prompts and denies every pending request during teardown. */\nexport class TuiPermissionQueue {\n private entries: IPermissionQueueEntry[] = [];\n private processing = false;\n private currentRequest: IPendingPermissionRequest | null = null;\n\n constructor(private readonly onChange: () => void) {}\n\n get current(): IPendingPermissionRequest | null {\n return this.currentRequest;\n }\n\n enqueue(\n toolName: string,\n toolArgs: TToolArgs,\n id?: string,\n canPersistProjectPermission?: boolean,\n requestedByPeer?: string,\n ): Promise<TPermissionResultValue> {\n return new Promise<TPermissionResultValue>((resolve) => {\n this.entries.push({\n toolName,\n toolArgs,\n resolve,\n ...(id !== undefined ? { id } : {}),\n ...(canPersistProjectPermission !== undefined ? { canPersistProjectPermission } : {}),\n ...(requestedByPeer !== undefined ? { requestedByPeer } : {}),\n });\n this.processNext();\n });\n }\n\n dismissById(id: string): boolean {\n const dismissed = this.entries.filter((entry) => entry.id === id);\n if (dismissed.length === 0) return false;\n const dismissedCurrent = this.entries[0]?.id === id;\n this.entries = this.entries.filter((entry) => entry.id !== id);\n for (const entry of dismissed) entry.resolve(false);\n if (dismissedCurrent) {\n this.processing = false;\n this.currentRequest = null;\n this.processNext();\n }\n return true;\n }\n\n cancelAll(): void {\n const queued = this.entries;\n this.entries = [];\n this.processing = false;\n this.currentRequest = null;\n for (const pending of queued) pending.resolve(false);\n this.onChange();\n }\n\n private processNext(): void {\n if (this.processing) return;\n const next = this.entries[0];\n if (!next) {\n this.currentRequest = null;\n this.onChange();\n return;\n }\n this.processing = true;\n this.currentRequest = {\n toolName: next.toolName,\n toolArgs: next.toolArgs,\n ...(next.canPersistProjectPermission !== undefined\n ? { canPersistProjectPermission: next.canPersistProjectPermission }\n : {}),\n ...(next.requestedByPeer !== undefined ? { requestedByPeer: next.requestedByPeer } : {}),\n resolve: (result) => {\n if (this.entries[0] !== next) return;\n this.entries.shift();\n this.processing = false;\n this.currentRequest = null;\n next.resolve(result);\n setTimeout(() => this.processNext(), 0);\n },\n };\n this.onChange();\n }\n}\n","import { createUserMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\n/** The part of a session this needs: who is driving the turn now, if it can say. */\nexport interface IActiveDriverSource {\n getActiveDriverId?: () => string | null;\n}\n\n/**\n * PEER-007 (issue #1915): the transcript echo of a user message, attributed to the ACTIVE turn's\n * driver so a peer's message is labelled as theirs the moment it appears rather than reading as the\n * operator's own for the whole turn. The id is the framework's DERIVED one (`peer:<session-id>`),\n * never peer-supplied text.\n *\n * The lookup is guarded because attribution is an ADDITION to the message, never a precondition for\n * it: a session that cannot say who drove the turn must still get the message on screen. Unguarded,\n * a throw here loses the message entirely and the transcript shows that nothing happened.\n */\nexport function attributedUserEcho(content: string, session: IActiveDriverSource): IHistoryEntry {\n let driverId: string | null = null;\n try {\n driverId = session.getActiveDriverId?.() ?? null;\n } catch {\n driverId = null;\n }\n return messageToHistoryEntry(\n createUserMessage(content, driverId ? { metadata: { driverId } } : {}),\n );\n}\n","import type { TuiStateManager } from './tui-state-manager.js';\nimport type {\n IInteractiveSession,\n IInteractiveSessionEvents,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiSessionEventBinding {\n event: TInteractiveEventName;\n handler: (...args: never[]) => void;\n}\n\nexport interface ITuiSessionBinder {\n <E extends TInteractiveEventName>(event: E, handler: IInteractiveSessionEvents[E]): void;\n}\n\nexport function bindTuiSessionNoticeEvents(\n bind: ITuiSessionBinder,\n manager: TuiStateManager,\n): void {\n bind('plan_event', (payload) => manager.addSessionEventNotice({ event: 'plan_event', payload }));\n bind('context_file_refreshed', (payload) =>\n manager.addSessionEventNotice({ event: 'context_file_refreshed', payload }),\n );\n bind('branch_event', (payload) =>\n manager.addSessionEventNotice({ event: 'branch_event', payload }),\n );\n}\n\n/** Isolate a TUI-owned projection failure from the session operation that already committed. */\nexport function bindTuiSessionEvent<E extends TInteractiveEventName>(\n session: Pick<IInteractiveSession, 'on'>,\n event: E,\n handler: IInteractiveSessionEvents[E],\n onDeliveryError: (error: Error, event: E) => void,\n bindings: ITuiSessionEventBinding[],\n): void {\n const ownedHandler = ((...args: never[]): void => {\n try {\n (handler as (...values: never[]) => void)(...args);\n } catch (error) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n try {\n onDeliveryError(normalized, event);\n } catch {\n // Diagnostics cannot make a committed session operation fail.\n }\n }\n }) as IInteractiveSessionEvents[E];\n session.on(event, ownedHandler);\n bindings.push({ event, handler: ownedHandler as (...args: never[]) => void });\n}\n","import { printablePeerDriver } from '@robota-sdk/agent-core';\n\nimport { attributedUserEcho } from './attributed-user-echo.js';\nimport { bindTuiSessionEvent, bindTuiSessionNoticeEvents } from './tui-session-binding.js';\n\nimport type { AttentionCoordinator } from './attention/attention-coordinator.js';\nimport type { ITuiSessionEventBinding } from './tui-session-binding.js';\nimport type { TuiStateManager } from './tui-state-manager.js';\nimport type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';\nimport type { InteractiveSession } from '@robota-sdk/agent-framework';\nimport type { IExecutionWorkspaceEvent } from '@robota-sdk/agent-interface-execution';\nimport type {\n IExecutionResult,\n IInteractiveSessionEvents,\n TInteractiveEventName,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface ITuiSessionEventProjectorOptions {\n session: InteractiveSession;\n manager: TuiStateManager;\n requestPermission: (\n toolName: string,\n toolArgs: TToolArgs,\n id: string,\n canPersistProjectPermission?: boolean,\n requestedByPeer?: string,\n ) => Promise<TPermissionResultValue>;\n askUser: (request: IActionRequest, id: string) => Promise<TActionResponse>;\n dismissPrompt: (id: string) => void;\n onDeliveryError?: (error: Error, event: TInteractiveEventName) => void;\n /** SCREEN-1992: fed the events the interval recap counts; absent ⇒ nothing is counted. */\n attention?: AttentionCoordinator;\n}\n\n/** Projects runtime session events into TUI state and owns the complete listener lifecycle. */\nexport class TuiSessionEventProjector {\n private readonly bindings: ITuiSessionEventBinding[] = [];\n\n constructor(private readonly options: ITuiSessionEventProjectorOptions) {}\n\n wire(): void {\n if (this.bindings.length > 0) return;\n const { session, manager, attention } = this.options;\n\n const onUserMessage = (content: string): void => {\n manager.addUserEcho(attributedUserEcho(content, session));\n };\n const syncHistory = (): void => manager.syncHistory(session.getFullHistory());\n const onComplete = (result: IExecutionResult): void => {\n manager.onComplete(result);\n attention?.onComplete();\n syncHistory();\n };\n const onError = (error: Error): void => {\n manager.onError(error);\n attention?.onError();\n syncHistory();\n };\n const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void => {\n manager.syncExecutionWorkspaceSnapshot(event.snapshot);\n attention?.onWorkspaceSnapshot(event.snapshot);\n };\n\n this.bind('user_message', onUserMessage);\n this.bind('text_delta', manager.onTextDelta);\n this.bind('tool_start', manager.onToolStart);\n this.bind('tool_end', manager.onToolEnd);\n this.bind('thinking', manager.onThinking);\n this.bind('complete', onComplete);\n this.bind('interrupted', manager.onInterrupted);\n this.bind('error', onError);\n this.bind('context_update', manager.onContextUpdate);\n this.bind('compact', syncHistory);\n this.bind('skill_activation', syncHistory);\n this.bind('memory_event', syncHistory);\n this.bind('execution_workspace_event', onExecutionWorkspaceEvent);\n this.bind('history_cleared', () => manager.clearHistory());\n // SCREEN-1992: subscribed regardless, so the channel's subscribed set equals the classification.\n this.bind('turn_source', (source) => attention?.onTurnSource(source));\n bindTuiSessionNoticeEvents(this.bind.bind(this), manager);\n\n const onPermissionRequest: IInteractiveSessionEvents['permission_request'] = ({\n id,\n toolName,\n toolArgs,\n canPersistProjectPermission,\n requesterDriverId,\n }) => {\n attention?.onNeedsInput();\n // A peer turn's ask names the peer, printed only as a plain identifier.\n const requestedByPeer = requesterDriverId?.startsWith('peer:')\n ? printablePeerDriver(requesterDriverId)\n : undefined;\n void this.options\n .requestPermission(toolName, toolArgs, id, canPersistProjectPermission, requestedByPeer)\n .then((result) => session.resolvePermission(id, result))\n .catch(() => session.resolvePermission(id, false));\n };\n const onAskRequest: IInteractiveSessionEvents['ask_request'] = ({ id, request }) => {\n attention?.onNeedsInput();\n void this.options\n .askUser(request, id)\n .then((response) => session.resolveAsk(id, response))\n .catch(() => session.resolveAsk(id, { type: 'cancelled' }));\n };\n this.bind('permission_request', onPermissionRequest);\n this.bind('ask_request', onAskRequest);\n this.bind('prompt_resolved', ({ id }) => this.options.dismissPrompt(id));\n attention?.wire();\n }\n\n unwire(): void {\n const { session } = this.options;\n this.options.attention?.unwire();\n for (const { event, handler } of this.bindings) {\n session.off(event, handler as IInteractiveSessionEvents[typeof event]);\n }\n this.bindings.length = 0;\n }\n\n private bind<E extends TInteractiveEventName>(\n event: E,\n handler: IInteractiveSessionEvents[E],\n ): void {\n bindTuiSessionEvent(\n this.options.session,\n event,\n handler,\n (error) => {\n const report = this.options.onDeliveryError;\n if (report) report(error, event);\n else this.options.manager.addSessionEventDeliveryError(error, event);\n },\n this.bindings,\n );\n }\n}\n","/**\n * ARCH-005 — the TUI's session-option mapping, split out of `TuiInteractionChannel`.\n *\n * Pure: it maps the channel's already-resolved options onto the framework's `TInteractiveSessionOptions`.\n * RUNTIME-001 — the channel then builds through the shared `buildRuntimeSession` seam (agent-framework),\n * never a private `new InteractiveSession`: one session-construction SSOT across the TUI, print, and\n * `--serve`.\n */\n\nimport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\nimport type { TInteractiveSessionOptions } from '@robota-sdk/agent-framework';\n\n/** Map the channel's resolved options onto the framework session-construction options. */\nexport function buildTuiSessionOptions(\n opts: ITuiInteractionChannelOptions,\n): TInteractiveSessionOptions {\n return {\n cwd: opts.cwd,\n ...(opts.livePromptTrace ? { livePromptTrace: opts.livePromptTrace } : {}),\n provider: opts.provider,\n ...(opts.providerErrorGuidance !== undefined\n ? { providerErrorGuidance: opts.providerErrorGuidance }\n : {}),\n ...(opts.promptFileReferenceTag !== undefined\n ? { promptFileReferenceTag: opts.promptFileReferenceTag }\n : {}),\n ...(opts.modelCommandToolPrefix !== undefined\n ? { modelCommandToolPrefix: opts.modelCommandToolPrefix }\n : {}),\n ...(opts.subagentHookEnvironmentNames !== undefined\n ? { subagentHookEnvironmentNames: opts.subagentHookEnvironmentNames }\n : {}),\n ...(opts.observerFailureWarningCode !== undefined\n ? { observerFailureWarningCode: opts.observerFailureWarningCode }\n : {}),\n ...(opts.commandHookShell !== undefined ? { commandHookShell: opts.commandHookShell } : {}),\n ...(opts.bare === true ? { bare: true } : {}),\n ...(opts.skipConfiguredHooks === true ? { skipConfiguredHooks: true } : {}),\n ...(opts.orgPolicy !== undefined ? { orgPolicy: opts.orgPolicy } : {}),\n ...(opts.projectAccess !== undefined ? { projectAccess: opts.projectAccess } : {}),\n ...(opts.projectSettingsPaths !== undefined\n ? { projectSettingsPaths: opts.projectSettingsPaths }\n : {}),\n ...(opts.baselinePermissionAllow !== undefined\n ? { baselinePermissionAllow: opts.baselinePermissionAllow }\n : {}),\n ...(opts.taskContext !== undefined ? { taskContext: opts.taskContext } : {}),\n ...(opts.contributionSources !== undefined\n ? { contributionSources: opts.contributionSources }\n : {}),\n ...(opts.skillRoots !== undefined ? { skillRoots: opts.skillRoots } : {}),\n ...(opts.userSettingsSources !== undefined\n ? { userSettingsSources: opts.userSettingsSources }\n : {}),\n ...(opts.editCheckpointStore !== undefined\n ? { editCheckpointStore: opts.editCheckpointStore }\n : {}),\n ...(opts.externalEventVerifierFactory !== undefined\n ? { externalEventVerifierFactory: opts.externalEventVerifierFactory }\n : {}),\n ...(opts.externalEventGrantHistory !== undefined\n ? { externalEventGrantHistory: opts.externalEventGrantHistory }\n : {}),\n // CLI-076: forward the resolved model so `--model` takes effect rather than falling through to the\n // session's config/default model.\n ...(opts.model !== undefined ? { model: opts.model } : {}),\n ...(opts.effort !== undefined ? { effort: opts.effort } : {}),\n ...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),\n ...(opts.maxOutputTokens !== undefined ? { maxOutputTokens: opts.maxOutputTokens } : {}),\n ...(opts.presetSystemPrompt !== undefined\n ? { presetSystemPrompt: opts.presetSystemPrompt }\n : {}),\n ...(opts.responseFormat !== undefined ? { responseFormat: opts.responseFormat } : {}),\n ...(opts.outputStyle !== undefined ? { outputStyle: opts.outputStyle } : {}),\n permissionMode: opts.permissionMode,\n maxTurns: opts.maxTurns,\n // REMOTE-007: no injected permission/ask handlers — the TUI subscribes to the session's\n // transport-neutral `permission_request`/`ask_request` events (wireSessionEvents) and answers via\n // `resolvePermission`/`resolveAsk`. The local Ink queues + rendering are unchanged.\n sessionStore: opts.sessionStore,\n disableSessionLoops: opts.disableSessionLoops,\n resolveDefaultLoopPrompt: opts.resolveDefaultLoopPrompt,\n resumeSessionId: opts.resumeSessionId,\n forkSession: opts.forkSession,\n ...(opts.workspaceMovedFrom !== undefined\n ? { workspaceMovedFrom: opts.workspaceMovedFrom }\n : {}),\n sessionName: opts.sessionName,\n // The session names itself after its first real turn, unless it already has a name.\n autoName: true,\n backgroundTaskRunners: opts.backgroundTaskRunners,\n ...(opts.toolCallHandoff !== undefined ? { toolCallHandoff: opts.toolCallHandoff } : {}),\n subagentRunnerFactory: opts.subagentRunnerFactory,\n ...(opts.agentDefinitions !== undefined ? { agentDefinitions: opts.agentDefinitions } : {}),\n ...(opts.agentDefinitionRoots !== undefined\n ? { agentDefinitionRoots: opts.agentDefinitionRoots }\n : {}),\n ...(opts.pluginDirectories !== undefined ? { pluginDirectories: opts.pluginDirectories } : {}),\n ...(opts.additionalTools !== undefined ? { additionalTools: opts.additionalTools } : {}),\n ...(opts.defaultTools !== undefined ? { defaultTools: opts.defaultTools } : {}),\n ...(opts.sandboxClient !== undefined ? { sandboxClient: opts.sandboxClient } : {}),\n commandModules: opts.commandModules,\n commandHostAdapters: opts.commandHostAdapters,\n shellExec: opts.shellExec,\n ...(opts.remoteCommandPolicy ? { remoteCommandPolicy: opts.remoteCommandPolicy } : {}),\n language: opts.language,\n agentName: opts.agentName,\n activePresetId: opts.activePresetId,\n persona: opts.persona,\n systemPrompt: opts.systemPrompt,\n appendSystemPrompt: opts.appendSystemPrompt,\n allowedTools: opts.allowedTools,\n deniedTools: opts.deniedTools,\n enableParallelSubagents: opts.enableParallelSubagents,\n selfVerification: opts.selfVerification,\n terminalHandoff: opts.terminalHandoff,\n // #1844: the session reads these when `/provider switch` hot-swaps. Absent, the switch throws\n // \"Unknown provider: <name>. Currently supported: \" with an EMPTY list — measured, not inferred.\n ...(opts.providerDefinitions ? { providerDefinitions: opts.providerDefinitions } : {}),\n // SELFHOST-008 P6: forward the surface-resolved memory fields only when present (absent ⇒ OFF).\n ...(opts.memoryStore ? { memoryStore: opts.memoryStore } : {}),\n ...(opts.automaticMemory ? { automaticMemory: opts.automaticMemory } : {}),\n ...(opts.recallMemory ? { recallMemory: opts.recallMemory } : {}),\n // SCREEN-1993: the owner-prompt append (absent ⇒ the controller records nothing).\n ...(opts.promptHistory ? { promptHistory: opts.promptHistory } : {}),\n };\n}\n","import type {\n IBranchEvent,\n IContextFileRefreshedEvent,\n IPlanApprovalEvent,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\nexport type TTuiSessionEventClassification = 'channel' | 'react-side-effect' | 'non-surface';\n\n/** Exhaustive TUI-owner policy over the shared session-event vocabulary. */\nexport const TUI_SESSION_EVENT_CLASSIFICATION = {\n text_delta: 'channel',\n tool_start: 'channel',\n tool_end: 'channel',\n thinking: 'channel',\n complete: 'channel',\n error: 'channel',\n context_update: 'channel',\n compact: 'channel',\n interrupted: 'channel',\n skill_activation: 'channel',\n background_task_event: 'non-surface',\n background_job_group_event: 'non-surface',\n execution_workspace_event: 'channel',\n user_message: 'channel',\n turn_source: 'channel',\n context_file_refreshed: 'channel',\n memory_event: 'channel',\n goal_event: 'non-surface',\n plan_event: 'channel',\n branch_event: 'channel',\n permission_request: 'channel',\n ask_request: 'channel',\n prompt_resolved: 'channel',\n ui_intent: 'react-side-effect',\n session_renamed: 'react-side-effect',\n history_cleared: 'channel',\n // #3189: a host's session slot emits it on a switch; the TUI switches by remounting its own\n // channel onto the next session, so it never follows a slot.\n session_switched: 'non-surface',\n // A host forwards it to attached clients as `session_status`; this channel owns its session and\n // reads the status from it on each render.\n status_changed: 'non-surface',\n} as const satisfies Record<TInteractiveEventName, TTuiSessionEventClassification>;\n\nexport interface ITuiSessionEventNotice {\n id: string;\n event:\n | 'plan_event'\n | 'context_file_refreshed'\n | 'branch_event'\n | 'delivery-error'\n /** SCREEN-1992: TUI-originated — the interval recap on return, not a session event. */\n | 'attention-recap';\n message: string;\n}\n\nexport type TTuiNoticeInput =\n | { event: 'plan_event'; payload: IPlanApprovalEvent }\n | { event: 'context_file_refreshed'; payload: IContextFileRefreshedEvent }\n | { event: 'branch_event'; payload: IBranchEvent };\n\n/** Pure deterministic projection from domain event to TUI render state. */\nexport function createTuiSessionEventNotice(\n id: string,\n input: TTuiNoticeInput,\n): ITuiSessionEventNotice {\n switch (input.event) {\n case 'plan_event':\n return {\n id,\n event: input.event,\n message: `Plan ${input.payload.type.replaceAll('_', ' ')}`,\n };\n case 'context_file_refreshed':\n return {\n id,\n event: input.event,\n message: `Context refreshed: ${input.payload.filePath}`,\n };\n case 'branch_event':\n return {\n id,\n event: input.event,\n message: `Branch ${input.payload.kind.replaceAll('_', ' ')}: ${input.payload.branchId} @ ${input.payload.checkpointId}`,\n };\n }\n}\n","import {\n createTuiSessionEventNotice,\n type ITuiSessionEventNotice,\n type TTuiNoticeInput,\n} from './tui-session-events.js';\n\nimport type { TInteractiveEventName } from '@robota-sdk/agent-interface-session';\n\nconst NOTICE_LIMIT = 50;\n\n/** Bounded append-only render projection, deliberately separate from canonical session history. */\nexport class TuiSessionNoticeStore {\n private sequence = 0;\n readonly notices: ITuiSessionEventNotice[] = [];\n\n add(input: TTuiNoticeInput): void {\n this.append(createTuiSessionEventNotice(this.nextId(), input));\n }\n\n addDeliveryError(error: Error, event: TInteractiveEventName): void {\n this.append({\n id: this.nextId(),\n event: 'delivery-error',\n message: `Session event delivery failed (${event}): ${error.message}`,\n });\n }\n\n /** SCREEN-1992: one line for the unattended interval that just ended. */\n addAttentionRecap(line: string): void {\n this.append({ id: this.nextId(), event: 'attention-recap', message: line });\n }\n\n private nextId(): string {\n this.sequence += 1;\n return `session-event-${this.sequence}`;\n }\n\n private append(notice: ITuiSessionEventNotice): void {\n this.notices.push(notice);\n const overflow = this.notices.length - NOTICE_LIMIT;\n if (overflow > 0) this.notices.splice(0, overflow);\n }\n}\n","/**\n * TuiStateManager — pure TypeScript rendering state manager.\n *\n * Converts InteractiveSession events into rendering state without a React dependency.\n * The React hook subscribes to onChange and reads state for rendering.\n */\n\nimport { TuiSessionNoticeStore } from './tui-session-notice-store.js';\n\nimport type { TTuiNoticeInput } from './tui-session-events.js';\nimport type { IContextWindowState, IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IExecutionWorkspaceSnapshot } from '@robota-sdk/agent-interface-execution';\nimport type {\n IExecutionResult,\n IToolState,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\n/** Debounce interval for streaming text notify (limits renderMarkdown frequency) */\nconst STREAMING_DEBOUNCE_MS = 300;\n/** ERR-001 G3: with no provider activity for this long while thinking, hint that the\n * connection may be stalled (well under the 120s provider idle timeout). */\nconst STALL_HINT_MS = 15_000;\n/**\n * TUI view of the core context-window state. The token fields are derived from the agent-core\n * SSOT (`IContextWindowState`) via `Pick` so they stay structurally tied to it; `percentage` is an\n * explicit display-facing mirror of `IContextWindowState.usedPercentage`.\n */\nexport type TContextState = Pick<IContextWindowState, 'usedTokens' | 'maxTokens'> & {\n /** Mirror of `IContextWindowState.usedPercentage`, named for the TUI display layer. */\n percentage: number;\n};\n\n/** Create a debounced notify — schedules at most one call per interval. */\nfunction createDebouncedNotify(\n notify: () => void,\n ms: number,\n): { schedule: () => void; flush: () => void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n return {\n schedule() {\n if (!timer) {\n timer = setTimeout(() => {\n timer = null;\n notify();\n }, ms);\n }\n },\n flush() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n };\n}\n\nexport class TuiStateManager {\n // ── Rendering state ───────────────────────────────────────────\n history: IHistoryEntry[] = [];\n /**\n * Notices this terminal added itself (command results, init errors). They are not in the session's\n * history, so each remembers how many session entries preceded it and is re-inserted there on sync.\n */\n private localNotices: { readonly entry: IHistoryEntry; readonly afterSessionEntries: number }[] =\n [];\n streamingText = '';\n activeTools: IToolState[] = [];\n isThinking = false;\n isAborting = false;\n pendingPrompt: string | null = null;\n contextState: TContextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };\n executionWorkspaceSnapshot: IExecutionWorkspaceSnapshot | null = null;\n selectedExecutionEntryId: string | undefined;\n /** ERR-001 G2: humanized message of the last failed turn; cleared when the next turn starts. */\n lastErrorMessage: string | null = null;\n /** ERR-001 G3: no stream/tool activity for STALL_HINT_MS while thinking. */\n isStalled = false;\n private readonly sessionNoticeStore = new TuiSessionNoticeStore();\n /** Bounded render-only projection; never merged into canonical session history. */\n readonly sessionEventNotices = this.sessionNoticeStore.notices;\n\n /** Called after any state change. React hook sets this to trigger re-render. */\n onChange: (() => void) | null = null;\n\n // ── Internal ──────────────────────────────────────────────────\n private streamBuf = '';\n private debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);\n private stallTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** (Re)start the dead-air timer — any provider activity proves the connection is alive. */\n private armStallTimer(): void {\n this.clearStallTimer();\n this.stallTimer = setTimeout(() => {\n this.stallTimer = null;\n this.isStalled = true;\n this.notify();\n }, STALL_HINT_MS);\n }\n\n private clearStallTimer(): void {\n if (this.stallTimer) {\n clearTimeout(this.stallTimer);\n this.stallTimer = null;\n }\n if (this.isStalled) {\n this.isStalled = false;\n }\n }\n\n private notify(): void {\n this.onChange?.();\n }\n\n // ── Event handlers (InteractiveSession → state) ───────────────\n\n onTextDelta = (delta: string): void => {\n if (this.isThinking) this.armStallTimer();\n this.streamBuf += delta;\n this.streamingText = this.streamBuf;\n this.debouncedStreamNotify.schedule();\n };\n\n onToolStart = (state: IToolState): void => {\n // RUNTIME-39: a running tool is legitimate activity, not a stalled provider connection —\n // suppress the dead-air hint for the duration of the tool run (re-armed in onToolEnd).\n this.clearStallTimer();\n this.activeTools = [...this.activeTools, state];\n this.notify();\n };\n\n onToolEnd = (state: IToolState): void => {\n // findLastIndex: when same tool runs concurrently, match the most recently started instance\n const idx = this.activeTools.findLastIndex((t) => t.toolName === state.toolName && t.isRunning);\n if (idx !== -1) {\n const updated = [...this.activeTools];\n updated[idx] = state;\n this.activeTools = updated;\n }\n // RUNTIME-39: re-arm the dead-air watch only once no tool is still running and the turn is\n // still in progress — otherwise the provider is again the thing we're waiting on.\n if (this.isThinking && !this.activeTools.some((t) => t.isRunning)) {\n this.armStallTimer();\n }\n this.notify();\n };\n\n onThinking = (thinking: boolean): void => {\n this.isThinking = thinking;\n if (thinking) {\n // Clear at START of new execution (preserves previous result until next)\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.lastErrorMessage = null;\n this.armStallTimer();\n } else {\n this.isAborting = false;\n this.activeTools = [];\n this.clearStallTimer();\n }\n this.notify();\n };\n\n onComplete = (result: Pick<IExecutionResult, 'contextState'>): void => {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.contextState = {\n percentage: result.contextState.usedPercentage,\n usedTokens: result.contextState.usedTokens,\n maxTokens: result.contextState.maxTokens,\n };\n this.notify();\n };\n\n onInterrupted = (): void => {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n };\n\n onError = (error?: Error): void => {\n // The partial answer is preserved as an interrupted history entry by the framework\n // (ERR-001); clearing the local stream buffer here no longer loses it.\n this.clearStallTimer();\n this.lastErrorMessage = error?.message ?? 'Unknown error';\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n };\n\n onContextUpdate = (state: IContextWindowState): void => {\n this.setContextState({\n percentage: state.usedPercentage,\n usedTokens: state.usedTokens,\n maxTokens: state.maxTokens,\n });\n };\n\n // ── State updates from external sources ───────────────────────\n\n /**\n * Sync history from InteractiveSession — authoritative SSOT replace (SCREEN-010: no front-windowing).\n * The session history grows monotonically during a session, so Ink `<Static>` (count-based) renders\n * only the newly-appended tail; committed lines already in terminal scrollback are never re-printed.\n */\n syncHistory(entries: IHistoryEntry[]): void {\n if (entries.length === 0) return;\n this.history = this.withLocalNotices(entries);\n this.notify();\n }\n\n /**\n * A host's history as a terminal attached to it last read it, then the echoes of prompts the host\n * has not recorded yet. Unlike `syncHistory`, an empty history is an answer: the host's session\n * has no entries.\n */\n syncHostHistory(entries: readonly IHistoryEntry[], echoes: readonly IHistoryEntry[]): void {\n this.history = [...this.withLocalNotices(entries), ...echoes];\n this.notify();\n }\n\n private withLocalNotices(entries: readonly IHistoryEntry[]): IHistoryEntry[] {\n // `<Static>` counts what it already printed, so a local notice dropped here would shift every\n // later index by one and the next committed entry (the assistant's answer) would never print.\n const merged: IHistoryEntry[] = [];\n let next = 0;\n for (const notice of this.localNotices) {\n const upTo = Math.min(notice.afterSessionEntries, entries.length);\n while (next < upTo) merged.push(entries[next++]!);\n merged.push(notice.entry);\n }\n while (next < entries.length) merged.push(entries[next++]!);\n return merged;\n }\n\n /** Add a notice this terminal owns; it survives the next session-history sync in place. */\n addEntry(entry: IHistoryEntry): void {\n this.localNotices.push({\n entry,\n afterSessionEntries: this.history.length - this.localNotices.length,\n });\n this.history = [...this.history, entry];\n this.notify();\n }\n\n /** Echo the user's prompt at once; the session's own user entry replaces it on the next sync. */\n addUserEcho(entry: IHistoryEntry): void {\n this.history = [...this.history, entry];\n this.notify();\n }\n\n clearHistory(): void {\n this.history = [];\n this.localNotices = [];\n this.debouncedStreamNotify.flush();\n this.streamBuf = '';\n this.streamingText = '';\n this.activeTools = [];\n this.notify();\n }\n\n /** Update pending prompt state */\n setPendingPrompt(prompt: string | null): void {\n this.pendingPrompt = prompt;\n this.notify();\n }\n\n /** Set aborting flag */\n setAborting(aborting: boolean): void {\n this.isAborting = aborting;\n this.notify();\n }\n\n /** Update context state */\n setContextState(state: TContextState): void {\n this.contextState = state;\n this.notify();\n }\n\n addSessionEventNotice(input: TTuiNoticeInput): void {\n this.sessionNoticeStore.add(input);\n this.notify();\n }\n\n addAttentionRecap(line: string): void {\n this.sessionNoticeStore.addAttentionRecap(line);\n this.notify();\n }\n\n addSessionEventDeliveryError(error: Error, event: TInteractiveEventName): void {\n this.sessionNoticeStore.addDeliveryError(error, event);\n this.notify();\n }\n\n syncExecutionWorkspaceSnapshot(snapshot: IExecutionWorkspaceSnapshot): void {\n const currentSelection = this.selectedExecutionEntryId;\n const hasCurrentSelection =\n currentSelection !== undefined &&\n snapshot.entries.some((entry) => entry.id === currentSelection);\n const selectedExecutionEntryId = hasCurrentSelection\n ? currentSelection\n : (snapshot.selectedEntryId ?? snapshot.entries[0]?.id);\n this.executionWorkspaceSnapshot = {\n ...snapshot,\n ...(selectedExecutionEntryId ? { selectedEntryId: selectedExecutionEntryId } : {}),\n };\n this.selectedExecutionEntryId = selectedExecutionEntryId;\n this.notify();\n }\n\n selectExecutionWorkspaceEntry(entryId: string): void {\n if (!this.executionWorkspaceSnapshot?.entries.some((entry) => entry.id === entryId)) return;\n this.selectedExecutionEntryId = entryId;\n this.executionWorkspaceSnapshot = {\n ...this.executionWorkspaceSnapshot,\n selectedEntryId: entryId,\n };\n this.notify();\n }\n\n /**\n * RUNTIME-52: release every timer this manager owns and detach the render callback. Called by the\n * channel's teardown (`stop()`); after dispose the manager must never fire `onChange` or trip the\n * stall hint on a discarded session.\n */\n dispose(): void {\n this.clearStallTimer();\n this.debouncedStreamNotify.flush();\n this.onChange = null;\n }\n}\n","/**\n * What Esc on an idle prompt says after asking the session to stop its waiting self-paced loop.\n * The session decides which loop, if any, stops; both channels show its answer in the same words.\n */\n\nimport type { TWaitingLoopStopOutcome } from '@robota-sdk/agent-interface-session';\n\n/** The reason the session records for a loop Esc stopped. */\nexport const WAITING_LOOP_STOP_REASON = 'Loop stopped by Esc';\n\n/** The notice for an outcome; none when no loop was waiting. */\nexport function waitingLoopStopNotice(outcome: TWaitingLoopStopOutcome): string | undefined {\n switch (outcome.kind) {\n case 'none':\n return undefined;\n case 'several':\n return outcome.message;\n case 'stopped':\n return `Loop ${outcome.loopId} stopped by Esc.`;\n case 'failed':\n return outcome.message;\n }\n}\n","/**\n * TuiInteractionChannel — session-owning presentation surface for the Ink TUI.\n *\n * Moves session lifecycle (InteractiveSession, CommandRegistry, TuiStateManager)\n * out of React hooks and into a plain TypeScript class.\n */\n\nimport { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';\nimport { CommandRegistry, buildRuntimeSession } from '@robota-sdk/agent-framework';\n\nimport { AttentionCoordinator } from './attention/attention-coordinator.js';\nimport { MS_PER_SECOND } from './attention/time-units.js';\nimport { createSessionInitPoller } from './flows/session-init-poller.js';\nimport { applySystemCommandResult } from './hooks/command-result-handler.js';\nimport { parseSlashCommandInput } from './slash-command-input.js';\nimport {\n TuiChannelLifecycleCoordinator,\n TuiChannelStartRollbackError,\n} from './tui-channel-lifecycle-coordinator.js';\nimport { TuiPermissionQueue, TuiUserActionQueue } from './tui-interaction-queues.js';\nimport { TuiSessionEventProjector } from './tui-session-event-projector.js';\nimport { buildTuiSessionOptions } from './tui-session-options.js';\nimport { TuiStateManager } from './tui-state-manager.js';\nimport { WAITING_LOOP_STOP_REASON, waitingLoopStopNotice } from './waiting-loop-stop-notice.js';\n\nimport type { IAttentionSource } from './attention/attention-tracker.js';\nimport type { ISessionInitPoller, TSessionInitFailure } from './flows/session-init-poller.js';\nimport type { TerminalHandoffController } from './terminal-handoff-controller.js';\nimport type {\n ITuiAppChannelPort,\n ITuiChannelSnapshot,\n ITuiCommandQueryPort,\n ITuiRuntimeStatusSnapshot,\n ITuiSessionUiEventPort,\n} from './tui-app-channel-port.js';\nimport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\nimport type { IPendingPermissionRequest } from './types.js';\nimport type {\n IHistoryEntry,\n TPermissionMode,\n TSessionEndReason,\n TToolArgs,\n} from '@robota-sdk/agent-core';\n// CMD-004 unified action contract (SSOT in agent-core).\nimport type {\n IActionRequest,\n TActionResponse as TUserActionResponse,\n} from '@robota-sdk/agent-core';\nimport type { InteractiveSession } from '@robota-sdk/agent-framework';\nimport type { IExecutionDetailPage } from '@robota-sdk/agent-interface-execution';\nimport type { ICommandInfo, TPermissionResultValue } from '@robota-sdk/agent-interface-session';\n\nconst SESSION_INIT_POLL_MS = 200;\nconst SESSION_INIT_TIMEOUT_MS = 15000;\n/** Upper bound on the graceful session shutdown so a wedged subsystem cannot block process exit\n * (CLI-075 / RUNTIME-33; api-boundary \"safely cancelled within a configurable timeout\"). */\nconst SHUTDOWN_TIMEOUT_MS = 5000;\n\nexport type { ITuiInteractionChannelOptions } from './tui-channel-options.js';\n\nexport class TuiInteractionChannel implements ITuiAppChannelPort {\n readonly stateManager: TuiStateManager;\n\n private readonly interactiveSession: InteractiveSession;\n private readonly registry: CommandRegistry;\n private readonly opts: ITuiInteractionChannelOptions;\n\n private submitHandler: ((text: string) => Promise<void>) | null = null;\n\n private readonly userActions: TuiUserActionQueue;\n private readonly permissions: TuiPermissionQueue;\n private readonly eventProjector: TuiSessionEventProjector;\n /** SCREEN-1992: joins the terminal's attention to this channel's recap; absent without a source. */\n private readonly attention: AttentionCoordinator | undefined;\n private readonly lifecycle: TuiChannelLifecycleCoordinator;\n availableCommands: ICommandInfo[] = [];\n sessionName: string | undefined;\n\n private initPoller: ISessionInitPoller | null = null;\n\n /** TERM-002: the App registers its Ink suspend/resume hooks into this controller. */\n get terminalHandoffController(): TerminalHandoffController | undefined {\n return this.opts.terminalHandoff;\n }\n /** Set by React hook to trigger re-render on state change */\n onChange: (() => void) | null = null;\n\n constructor(opts: ITuiInteractionChannelOptions) {\n this.opts = opts;\n this.sessionName = opts.sessionName;\n this.stateManager = new TuiStateManager();\n this.stateManager.onChange = () => this.onChange?.();\n this.userActions = new TuiUserActionQueue(() => this.onChange?.());\n this.permissions = new TuiPermissionQueue(() => this.onChange?.());\n\n this.interactiveSession = this.createSession();\n this.registry = this.createRegistry();\n this.attention = this.createAttention(opts.attention);\n this.eventProjector = new TuiSessionEventProjector({\n session: this.interactiveSession,\n manager: this.stateManager,\n ...(this.attention ? { attention: this.attention } : {}),\n requestPermission: (toolName, toolArgs, id, canPersistProjectPermission, requestedByPeer) =>\n this.permissions.enqueue(\n toolName,\n toolArgs,\n id,\n canPersistProjectPermission,\n requestedByPeer,\n ),\n askUser: (request, id) => this.askUser(request, id),\n dismissPrompt: (id) => this.dismissPromptById(id),\n ...(opts.onSessionEventDeliveryError\n ? { onDeliveryError: opts.onSessionEventDeliveryError }\n : {}),\n });\n this.lifecycle = new TuiChannelLifecycleCoordinator(\n {\n start: () => this.startRuntime(),\n stop: async () => {\n this.eventProjector.unwire();\n this.cancelAllPermissions();\n this.cancelAllUserActions();\n this.stopInitCheck();\n this.onChange = null;\n this.stateManager.dispose();\n await this.stopTransports();\n },\n beginShutdown: () => {\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.stateManager.addEntry(\n messageToHistoryEntry(createSystemMessage('Shutting down...')),\n );\n this.onChange?.();\n },\n shutdownSession: async ({ reason, message }) => {\n await this.interactiveSession.shutdown({ reason, message });\n },\n },\n SHUTDOWN_TIMEOUT_MS,\n );\n }\n\n private async startRuntime(): Promise<void> {\n let transportStartAttempted = false;\n try {\n this.eventProjector.wire();\n this.syncRestoredHistory();\n this.startInitCheck();\n if (this.opts.transportRegistry) {\n transportStartAttempted = true;\n await this.opts.bindTransports?.(this.interactiveSession);\n await this.opts.transportRegistry.startAll();\n }\n } catch (startError) {\n this.eventProjector.unwire();\n this.stopInitCheck();\n if (transportStartAttempted && this.opts.transportRegistry) {\n try {\n await this.stopTransports();\n } catch (rollbackError) {\n const rollbackErrors =\n rollbackError instanceof AggregateError ? rollbackError.errors : [rollbackError];\n throw new TuiChannelStartRollbackError(\n [startError, ...rollbackErrors],\n 'TUI channel start failed and transport rollback also failed.',\n );\n }\n }\n throw startError;\n }\n }\n\n private async stopTransports(): Promise<void> {\n if (!this.opts.transportRegistry) return;\n const result = await this.opts.transportRegistry.stopAll();\n if (result.errors.length > 0) {\n throw new AggregateError(result.errors, 'TUI transport teardown failed.');\n }\n }\n\n get permissionRequest(): IPendingPermissionRequest | null {\n return this.permissions.current;\n }\n\n /** CMD-004: the action currently awaiting a user answer, or null. */\n get pendingUserAction(): IActionRequest | null {\n return this.userActions.current;\n }\n\n get isShuttingDown(): boolean {\n return this.lifecycle.isShuttingDown;\n }\n\n get isActiveForPeerStatus(): boolean {\n return this.lifecycle.isActiveForPeerStatus;\n }\n\n private createSession(): InteractiveSession {\n return buildRuntimeSession(buildTuiSessionOptions(this.opts));\n }\n\n private createRegistry(): CommandRegistry {\n const registry = new CommandRegistry();\n for (const module of this.opts.commandModules ?? []) {\n registry.addModule(module);\n }\n this.opts.reloadPluginCommandSource?.(registry);\n return registry;\n }\n\n onSubmit(handler: (text: string) => Promise<void>): void {\n this.submitHandler = handler;\n }\n\n setAvailableCommands(commands: ICommandInfo[]): void {\n this.availableCommands = commands;\n this.onChange?.();\n }\n\n setBusy(busy: boolean): void {\n this.stateManager.onThinking(busy);\n }\n\n async start(): Promise<void> {\n await this.lifecycle.start();\n }\n\n /**\n * Full, idempotent channel teardown (CLI-075). Unwires every session listener, drains both\n * request queues, stops the init poller, disposes the render-state manager, stops transports, and\n * — unless a graceful `shutdown()` already ran — shuts the underlying session down so a discarded\n * or switched-away channel releases its background tasks, subagent processes, and timers.\n */\n async stop(): Promise<void> {\n await this.lifecycle.stop();\n }\n\n // ── Additional methods for App.tsx ───────────────────────────\n\n getSession(): InteractiveSession {\n return this.interactiveSession;\n }\n\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n subscribe(onChange: () => void): () => void {\n this.onChange = onChange;\n return () => {\n if (this.onChange === onChange) this.onChange = null;\n };\n }\n\n getSnapshot(): ITuiChannelSnapshot {\n const manager = this.stateManager;\n return {\n history: manager.history,\n streamingText: manager.streamingText,\n activeTools: manager.activeTools,\n isThinking: manager.isThinking,\n isAborting: manager.isAborting,\n lastErrorMessage: manager.lastErrorMessage,\n isStalled: manager.isStalled,\n sessionEventNotices: manager.sessionEventNotices,\n isShuttingDown: this.isShuttingDown,\n pendingPrompt: manager.pendingPrompt,\n pendingCount: this.interactiveSession.getPendingCount(),\n executionWorkspaceSnapshot: manager.executionWorkspaceSnapshot,\n ...(manager.selectedExecutionEntryId !== undefined\n ? { selectedExecutionEntryId: manager.selectedExecutionEntryId }\n : {}),\n permissionRequest: this.permissionRequest,\n pendingUserAction: this.pendingUserAction,\n contextState: manager.contextState,\n };\n }\n\n getCommandQueryPort(): ITuiCommandQueryPort {\n return this.registry;\n }\n\n getSessionUiEventPort(): ITuiSessionUiEventPort {\n return this.interactiveSession;\n }\n\n getRuntimeStatusSnapshot(fallbackPermissionMode: TPermissionMode): ITuiRuntimeStatusSnapshot {\n try {\n const session = this.interactiveSession.getSession();\n const activePresetId = session.getActivePresetId?.();\n const effort = session.getModelEffort();\n return {\n permissionMode: session.getPermissionMode(),\n sessionId: session.getSessionId(),\n ...(activePresetId !== undefined ? { activePresetId } : {}),\n ...(effort !== undefined ? { effort } : {}),\n };\n } catch {\n return { permissionMode: fallbackPermissionMode, sessionId: '' };\n }\n }\n\n addEntry(entry: IHistoryEntry): void {\n this.stateManager.addEntry(entry);\n }\n\n abort(): void {\n this.stateManager.setAborting(true);\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.interactiveSession.abort();\n }\n\n cancelQueue(): void {\n this.interactiveSession.cancelQueue();\n this.cancelAllUserActions();\n this.cancelAllPermissions();\n this.stateManager.setPendingPrompt(null);\n }\n\n /** Esc on an idle prompt: the session decides which waiting loop, if any, stops. */\n async stopWaitingSelfPacedLoop(): Promise<void> {\n const outcome = await this.interactiveSession.stopWaitingSelfPacedLoop(WAITING_LOOP_STOP_REASON);\n const notice = waitingLoopStopNotice(outcome);\n if (notice !== undefined) this.addEntry(messageToHistoryEntry(createSystemMessage(notice)));\n }\n\n async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {\n await this.lifecycle.shutdown(options);\n }\n\n selectExecutionWorkspaceEntry(entryId: string): void {\n this.stateManager.selectExecutionWorkspaceEntry(entryId);\n }\n\n async readExecutionWorkspaceDetail(entryId: string): Promise<IExecutionDetailPage> {\n return this.interactiveSession.readExecutionWorkspaceDetail(entryId);\n }\n\n async sendAgentJob(taskId: string, input: string): Promise<void> {\n await this.interactiveSession.sendAgentJob(taskId, input);\n }\n\n setSessionName(name: string): void {\n this.sessionName = name;\n this.interactiveSession.setName(name);\n this.onChange?.();\n }\n\n // ── CMD-004 unified ask path ─────────────────────────────────\n\n /**\n * Queue an ask and resolve when the user answers. Reached via the `ask_request` event handler\n * (which passes the framework prompt `id` for co-drive dismissal) and by any direct\n * `IInteractionChannel.askUser` caller (no id).\n */\n async askUser(request: IActionRequest, id?: string): Promise<TUserActionResponse> {\n return this.userActions.enqueue(request, id);\n }\n\n /** Called by App's PendingActionPrompt when the user answers (or cancels) the pending action. */\n resolveUserAction(request: IActionRequest, response: TUserActionResponse): void {\n this.userActions.resolveCurrent(request, response);\n }\n\n /** Resolve every queued/in-flight ask as cancelled (abort, shutdown). */\n private cancelAllUserActions(): void {\n this.userActions.cancelAll();\n }\n\n async handleInput(input: string): Promise<void> {\n if (!input.startsWith('/')) {\n await this.interactiveSession.submit(input);\n this.stateManager.setPendingPrompt(this.interactiveSession.getPendingPrompt());\n return;\n }\n await this.handleSlashCommand(input);\n }\n\n private async handleSlashCommand(input: string): Promise<void> {\n const { name: cmd, args } = parseSlashCommandInput(input);\n\n const result = await this.interactiveSession.executeCommand(cmd, args);\n if (result) {\n // CMD-004 Stage E: `data.sessionExecution` is the requester-local \"a session turn is now\n // running\" hint (formerly the `session-execution-started` effect).\n if (result.data?.['sessionExecution'] === true) {\n this.stateManager.setPendingPrompt(this.interactiveSession.getPendingPrompt());\n return;\n }\n applySystemCommandResult(\n result,\n this.interactiveSession,\n this.registry,\n this.stateManager,\n this.opts.reloadPluginCommandSource,\n );\n return;\n }\n\n this.stateManager.addEntry(\n messageToHistoryEntry(createSystemMessage(`Unknown command \"/${cmd}\". Type /help for help.`)),\n );\n }\n\n // ── Private helpers ──────────────────────────────────────────\n\n private handlePermissionRequest(\n toolName: string,\n toolArgs: TToolArgs,\n id?: string,\n ): Promise<TPermissionResultValue> {\n return this.permissions.enqueue(toolName, toolArgs, id);\n }\n\n /**\n * REMOTE-007 co-drive: another surface answered the prompt with this framework `id`, so dismiss the\n * still-showing local dialog. Resolving the local entry (cancelled) is a no-op on the framework side\n * — that id is already settled — but it clears the TUI's queue + render state.\n */\n private dismissPromptById(id: string): void {\n this.userActions.dismissById(id);\n this.permissions.dismissById(id);\n }\n\n /**\n * Resolve every queued/in-flight permission request as `false` (deny) — abort, cancelQueue,\n * shutdown, stop. Symmetric to `cancelAllUserActions()`: tearing down must neither leave a tool's\n * permission promise dangling (the tool would hang) nor silently grant it (CLI-075 RUNTIME-32).\n */\n private cancelAllPermissions(): void {\n this.permissions.cancelAll();\n }\n\n private syncRestoredHistory(): void {\n if (this.stateManager.history.length === 0) {\n const restored = this.interactiveSession.getFullHistory();\n if (restored.length > 0) {\n this.stateManager.syncHistory(restored);\n }\n }\n }\n\n private startInitCheck(): void {\n this.stopInitCheck();\n this.initPoller = createSessionInitPoller({\n check: () => this.runInitCheck(),\n intervalMs: SESSION_INIT_POLL_MS,\n timeoutMs: SESSION_INIT_TIMEOUT_MS,\n onReady: () => undefined,\n onFailure: (failure) => this.onInitFailure(failure),\n });\n this.initPoller.start();\n }\n\n /** Throws while the session is not ready; the init poller classifies the error. */\n private runInitCheck(): void {\n const ctx = this.interactiveSession.getContextState();\n const restoredSessionName = this.interactiveSession.getName();\n if (restoredSessionName && restoredSessionName !== this.sessionName) {\n this.sessionName = restoredSessionName;\n this.onChange?.();\n }\n this.stateManager.setContextState({\n percentage: ctx.usedPercentage,\n usedTokens: ctx.usedTokens,\n maxTokens: ctx.maxTokens,\n });\n const restored = this.interactiveSession.getFullHistory();\n if (restored.length > 0) {\n this.stateManager.syncHistory(restored);\n }\n this.syncExecutionWorkspace();\n }\n\n /** SCREEN-1992: the recap lands in this channel's notice store; no source ⇒ no coordinator. */\n private createAttention(source: IAttentionSource | undefined): AttentionCoordinator | undefined {\n if (!source) return undefined;\n return new AttentionCoordinator({\n source,\n onRecap: (line) => this.stateManager.addAttentionRecap(line),\n });\n }\n\n private onInitFailure(failure: TSessionInitFailure): void {\n const message =\n failure.kind === 'timeout'\n ? `Session initialization timed out after ${SESSION_INIT_TIMEOUT_MS / MS_PER_SECOND}s${\n failure.lastError ? ` (last error: ${failure.lastError.message})` : ''\n }`\n : `Session initialization failed: ${failure.error.message}`;\n this.stateManager.onError();\n this.stateManager.addEntry({\n id: `session-init-error-${Date.now()}`,\n timestamp: new Date(),\n category: 'event',\n type: 'session-init-error',\n data: { message },\n });\n }\n\n private stopInitCheck(): void {\n this.initPoller?.stop();\n this.initPoller = null;\n }\n\n private syncExecutionWorkspace(): void {\n try {\n // allow-fallback: session may not be initialized yet; swallow until ready\n this.stateManager.syncExecutionWorkspaceSnapshot(\n this.interactiveSession.getExecutionWorkspaceSnapshot({\n selectedEntryId: this.stateManager.selectedExecutionEntryId,\n }),\n );\n } catch {\n // allow-fallback: session may not be initialized yet; swallow until ready\n /* Session not initialized yet */\n }\n }\n}\n","/**\n * The host's history as a terminal attached over the wire knows it, read one page at a time.\n *\n * The transcript is committed to the terminal once and counted (Ink `<Static>`), so what this shows\n * must only ever grow at its end. Two rules keep it so:\n * - a history read is shown when its last page arrives, never half-read;\n * - a prompt's echo is held back while the host has entries this terminal has not read yet, since\n * the echo would take the place one of them belongs in. It is shown once the read catches up,\n * unless the host has recorded the prompt by then.\n */\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type { IWireHistoryEntry, TServerMessage } from '@robota-sdk/agent-transport/client';\n\ntype THistoryPage = Extract<TServerMessage, { type: 'history' }>;\n\nexport interface IWireHistorySyncOptions {\n /** Ask the host for the page from `fromIndex`; false when it could not be sent. */\n readonly request: (fromIndex: number) => boolean;\n /** Show the host's history as known, then the echoes of prompts the host has not recorded yet. */\n readonly show: (entries: readonly IHistoryEntry[], echoes: readonly IHistoryEntry[]) => void;\n /** A page's entries as the terminal holds them. */\n readonly revive: (entries: readonly IWireHistoryEntry[]) => IHistoryEntry[];\n}\n\ninterface IHistoryRead {\n /** From the start of the history, replacing what is known; otherwise after what is known. */\n readonly reload: boolean;\n readonly entries: IHistoryEntry[];\n /** Where the next page must start. */\n next: number;\n}\n\ninterface IPromptEcho {\n readonly entry: IHistoryEntry;\n readonly content: string;\n /** How many host entries were known when the prompt was echoed: the host records it after them. */\n readonly after: number;\n shown: boolean;\n}\n\nfunction isUserEntryWith(entry: IHistoryEntry, content: string): boolean {\n if (entry.category !== 'chat' || entry.type !== 'user') return false;\n const data = entry.data;\n return typeof data === 'object' && data !== null && 'content' in data && data.content === content;\n}\n\nexport class WireHistorySync {\n private known: IHistoryEntry[] = [];\n private read: IHistoryRead | undefined;\n /** What to read once the current read ends; a reload covers a tail read. */\n private followUp: 'tail' | 'reload' | undefined;\n private echoes: IPromptEcho[] = [];\n\n constructor(private readonly options: IWireHistorySyncOptions) {}\n\n /** The host has entries this terminal has not read yet. */\n get behind(): boolean {\n return this.read !== undefined || this.followUp !== undefined;\n }\n\n /** The host added entries after the ones known: read from there. */\n readTail(): void {\n if (this.read !== undefined) {\n this.followUp ??= 'tail';\n return;\n }\n this.start(false);\n }\n\n /** The host's history may differ anywhere: read all of it again. */\n reload(): void {\n if (this.read !== undefined) {\n this.followUp = 'reload';\n return;\n }\n this.start(true);\n }\n\n /**\n * Read all of it again now, giving up on a read in flight: its reply may never come (frames were\n * lost). A reply that still comes is told apart by where it starts.\n */\n restart(): void {\n this.read = undefined;\n this.followUp = undefined;\n this.start(true);\n }\n\n /** Another session, or none: nothing known, echoed or being read belongs to what comes next. */\n reset(): void {\n this.known = [];\n this.read = undefined;\n this.followUp = undefined;\n this.echoes = [];\n }\n\n /**\n * One page from the host. Only the page the current read asked for is taken. Any page is the\n * host's history as of when it was sent, so one that answers a read given up on and happens to\n * start where this read needs is as good as its own answer.\n */\n onPage(page: THistoryPage): void {\n const read = this.read;\n if (read === undefined) return;\n if (page.startIndex !== read.next) {\n // The host holds fewer entries than this read counted on: its history is not the one known.\n if (page.total < read.next) this.restart();\n return;\n }\n read.entries.push(...this.options.revive(page.entries));\n read.next += page.entries.length;\n if (read.next < page.total && page.entries.length > 0) {\n if (!this.options.request(read.next)) this.read = undefined;\n return;\n }\n this.read = undefined;\n this.known = read.reload ? read.entries : [...this.known, ...read.entries];\n const followUp = this.followUp;\n this.followUp = undefined;\n if (followUp !== undefined) this.start(followUp === 'reload');\n this.show();\n }\n\n /** A prompt a turn started with, shown at once unless the host has entries not read yet. */\n echo(entry: IHistoryEntry, content: string): void {\n this.echoes.push({ entry, content, after: this.known.length, shown: false });\n this.show();\n }\n\n private start(reload: boolean): void {\n const from = reload ? 0 : this.known.length;\n this.read = { reload, entries: [], next: from };\n if (!this.options.request(from)) this.read = undefined;\n }\n\n private show(): void {\n this.echoes = this.unrecordedEchoes();\n if (!this.behind) for (const echo of this.echoes) echo.shown = true;\n this.options.show(\n this.known,\n this.echoes.filter((echo) => echo.shown).map((echo) => echo.entry),\n );\n }\n\n /** The echoes whose prompt the host has not recorded, as far as this terminal has read. */\n private unrecordedEchoes(): IPromptEcho[] {\n let searchFrom = 0;\n return this.echoes.filter((echo) => {\n const from = Math.max(searchFrom, echo.after);\n const index = this.known.findIndex(\n (entry, position) => position >= from && isUserEntryWith(entry, echo.content),\n );\n if (index === -1) return true;\n searchFrom = index + 1;\n return false;\n });\n }\n}\n","/**\n * #3189: the commands that belong to the terminal the user sits at (`/shell`, `/editor`, `/theme`,\n * `/keybindings`). A terminal attached to a workspace daemon runs these in its own process — on its\n * own terminal, in its own working directory — and sends nothing to the daemon.\n *\n * The attached terminal routes by the set it is given here, never by the daemon's catalog, so an\n * older daemon or a catalog that arrives late cannot send one of these commands to the daemon.\n *\n * The in-process session applies a command's host actions and emits its UI intents; here the\n * terminal does both itself. The one host action these commands produce, the appearance settings\n * patch, is written through the writer the composition root gives. Anything else is refused by name\n * rather than skipped, because a command that asked for an action nobody performed would otherwise\n * report success.\n */\nimport { SessionTerminalHandoffGate, isAppearanceSettingsPatch } from '@robota-sdk/agent-framework';\n\nimport type {\n ICommandHostTerminalHandoff,\n ICommandHostWorkspace,\n} from '@robota-sdk/agent-framework';\nimport type {\n ICommandResult,\n TAppearanceSettingsPatch,\n TCommandUiIntent,\n} from '@robota-sdk/agent-interface-command';\nimport type { ITerminalHandoff } from '@robota-sdk/agent-interface-session';\n\n/** What a client-run command may use: this terminal's handoff and working directory. */\nexport type TTuiClientCommandHost = ICommandHostTerminalHandoff & ICommandHostWorkspace;\n\n/**\n * A command this terminal runs itself. Declared here rather than imported: the command package's\n * `createTerminalClientCommands` produces objects of this shape, and this package does not depend on\n * the command package.\n */\nexport interface ITuiClientCommand {\n readonly name: string;\n execute(host: TTuiClientCommandHost, args: string): ICommandResult | Promise<ICommandResult>;\n}\n\n/** The client-run commands, and how this terminal writes what they change. */\nexport interface ITuiClientCommands {\n readonly commands: readonly ITuiClientCommand[];\n /** Writes the appearance keys of the user's settings; resolves when written. */\n writeAppearanceSettings(patch: TAppearanceSettingsPatch): void | Promise<void>;\n}\n\n/** A client command's answer: the result to show, and the screens it asked this terminal to open. */\nexport interface ITuiClientCommandOutcome {\n /** Host actions and UI intents are consumed: this carries only message, success and data. */\n readonly result: ICommandResult;\n /** Empty unless the command succeeded and every host action was applied. */\n readonly uiIntents: readonly TCommandUiIntent[];\n}\n\nconst APPEARANCE_PATCH = 'appearance-settings-patch';\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction refused(message: string): ITuiClientCommandOutcome {\n return { result: { success: false, message }, uiIntents: [] };\n}\n\n/** The client command named `name`, or undefined when there is none or no set was given. */\nexport function findTuiClientCommand(\n commands: ITuiClientCommands | undefined,\n name: string,\n): ITuiClientCommand | undefined {\n return commands?.commands.find((command) => command.name === name);\n}\n\n/**\n * Runs a client command and applies what it asks for on this terminal.\n *\n * A failed result passes through unchanged: its actions were never requested to run. Every host\n * action is checked before any is written, so a result that carries an invalid patch or an action\n * this terminal cannot perform writes nothing and becomes an explicit failure. A command that throws\n * rejects, as it does in the in-process session.\n */\nexport async function runTuiClientCommand(\n command: ITuiClientCommand,\n args: string,\n host: TTuiClientCommandHost,\n writers: Pick<ITuiClientCommands, 'writeAppearanceSettings'>,\n): Promise<ITuiClientCommandOutcome> {\n const result = await command.execute(host, args);\n if (!result.success) return { result, uiIntents: [] };\n\n const patches: TAppearanceSettingsPatch[] = [];\n for (const action of result.hostActions ?? []) {\n if (action.type !== APPEARANCE_PATCH) {\n return refused(`Cannot apply '${action.type}' on this terminal.`);\n }\n if (!isAppearanceSettingsPatch(action.patch)) {\n return refused(`Failed to apply '${APPEARANCE_PATCH}': invalid appearance settings patch`);\n }\n patches.push(action.patch);\n }\n\n for (const patch of patches) {\n try {\n await writers.writeAppearanceSettings(patch);\n } catch (error) {\n return refused(`Failed to apply '${APPEARANCE_PATCH}': ${errorMessage(error)}`);\n }\n }\n\n return {\n result: {\n message: result.message,\n success: result.success,\n ...(result.data !== undefined ? { data: result.data } : {}),\n },\n uiIntents: result.uiIntents ?? [],\n };\n}\n\n/**\n * The host a client command runs against: this terminal's own handoff, behind the same gate the\n * in-process session puts in front of it (one handoff at a time, and a refusal instead of a hang\n * when there is no interactive terminal). Build it once per attached terminal, so that exclusivity\n * spans every command it runs.\n *\n * Without a handoff, `canHandoffTerminal()` is false and `/shell` and `/editor` take their refusal\n * path. Commands run here were typed by the user at this terminal, so the invocation source is\n * always `'user'`.\n */\nexport function createTuiClientCommandHost(options: {\n readonly terminalHandoff: ITerminalHandoff | undefined;\n readonly cwd: string;\n}): TTuiClientCommandHost {\n const gate = new SessionTerminalHandoffGate(options.terminalHandoff);\n const { cwd } = options;\n return {\n canHandoffTerminal: () => gate.canHandoffTerminal(),\n runWithTerminal: (fn) => gate.runWithTerminal(fn),\n getCwd: () => cwd,\n getCommandInvocationSource: () => 'user',\n };\n}\n","/**\n * Pure projections from session-protocol frames into what the TUI renders, for a terminal attached to\n * a session over the wire. Kept apart from the channel so each is testable without a connection.\n */\n\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n ICommand,\n ICommandListEntry,\n ICommandSkillListEntry,\n ICommandSubcommandEntry,\n} from '@robota-sdk/agent-interface-command';\nimport type { IWireHistoryEntry } from '@robota-sdk/agent-transport/client';\n\n/**\n * The transcript names a prompt by the driver that sent it, and \"You\" for the operator. Over the wire\n * this terminal is a driver the host named, so its own prompts are shown as the operator's.\n */\nexport function displayDriverId(\n driverId: string | undefined,\n ownDriverId: string | undefined,\n): string | undefined {\n return driverId !== undefined && driverId === ownDriverId ? OWNER_DRIVER_ID : driverId;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Who this terminal is on the wire: the driver id the host gave it, and when it attached. The host\n * numbers the terminals attached to it from one each time it starts, and a prompt keeps its driver id\n * in the stored history, so an entry from before this terminal attached may carry the same id for\n * another terminal. Only what this terminal could have sent is shown as the user's.\n */\nexport interface IOwnDriver {\n readonly driverId: string;\n /** Milliseconds since the epoch. */\n readonly since: number;\n}\n\n/**\n * A chat entry's `data` is the message itself, whose own `timestamp` also crossed as a string. Any\n * ISO string is accepted: a resumed session sends strings for what was a `Date` in memory.\n */\nfunction reviveChatMessage(data: unknown, ownDriverId: string | undefined): unknown {\n if (!isRecord(data)) return data;\n let revived = data;\n if (typeof data['timestamp'] === 'string') {\n revived = { ...revived, timestamp: new Date(data['timestamp']) };\n }\n const metadata = data['metadata'];\n if (ownDriverId !== undefined && isRecord(metadata) && metadata['driverId'] === ownDriverId) {\n revived = { ...revived, metadata: { ...metadata, driverId: OWNER_DRIVER_ID } };\n }\n return revived;\n}\n\n/**\n * The session's history as the TUI holds it: timestamps back to `Date`s. Only a chat entry's `data`\n * is a message; an event entry's is the event as recorded, and is left as it came.\n */\nexport function toHistoryEntries(\n entries: readonly IWireHistoryEntry[],\n own: IOwnDriver | undefined,\n): IHistoryEntry[] {\n return entries.map((entry) => {\n const timestamp = new Date(entry.timestamp);\n if (entry.category !== 'chat' || entry.data === undefined) return { ...entry, timestamp };\n const ownDriverId =\n own !== undefined && timestamp.getTime() >= own.since ? own.driverId : undefined;\n return { ...entry, timestamp, data: reviveChatMessage(entry.data, ownDriverId) };\n });\n}\n\n/** A subcommand as the `/` menu offers it after its command's name. */\nfunction toSubcommand(subcommand: ICommandSubcommandEntry): ICommand {\n return {\n name: subcommand.name,\n description: subcommand.description,\n source: 'builtin',\n ...(subcommand.displayName !== undefined ? { displayName: subcommand.displayName } : {}),\n ...(subcommand.argumentHint !== undefined ? { argumentHint: subcommand.argumentHint } : {}),\n };\n}\n\n/** What the `/` menu offers: the host's commands, then the skills a user may invoke. */\nexport function toCommandCatalog(\n commands: readonly ICommandListEntry[],\n skills: readonly ICommandSkillListEntry[],\n): ICommand[] {\n return [\n ...commands.map((command): ICommand => ({\n name: command.name,\n description: command.description,\n source: 'builtin',\n modelInvocable: command.modelInvocable,\n ...(command.displayName !== undefined ? { displayName: command.displayName } : {}),\n ...(command.example !== undefined ? { example: command.example } : {}),\n ...(command.argumentHint !== undefined ? { argumentHint: command.argumentHint } : {}),\n ...(command.subcommands !== undefined && command.subcommands.length > 0\n ? { subcommands: command.subcommands.map(toSubcommand) }\n : {}),\n })),\n ...skills\n .filter((skill) => skill.userInvocable)\n .map((skill): ICommand => ({\n name: skill.name,\n description: skill.description,\n source: skill.source,\n modelInvocable: skill.modelInvocable,\n userInvocable: skill.userInvocable,\n ...(skill.argumentHint !== undefined ? { argumentHint: skill.argumentHint } : {}),\n ...(skill.context !== undefined ? { context: skill.context } : {}),\n ...(skill.agent !== undefined ? { agent: skill.agent } : {}),\n })),\n ];\n}\n\n/** A command's subcommands, found by name as the in-process command registry finds them. */\nexport function findSubcommands(catalog: readonly ICommand[], commandName: string): ICommand[] {\n const lower = commandName.toLowerCase();\n const command = catalog.find(\n (entry) => entry.name.toLowerCase() === lower && entry.subcommands !== undefined,\n );\n return command?.subcommands ?? [];\n}\n\n/** The same prefix match the in-process command registry applies. */\nexport function filterCommandCatalog(catalog: readonly ICommand[], filter?: string): ICommand[] {\n if (!filter) return [...catalog];\n const lower = filter.toLowerCase();\n return catalog.filter((command) => command.name.toLowerCase().startsWith(lower));\n}\n","/**\n * WireTuiChannel — the full TUI's channel for a session this terminal is attached to over the\n * session protocol (a daemon's session).\n *\n * The in-process channel owns its session; this one owns only its view of one. Each frame is\n * projected into the same render state the in-process event projector builds, and what the user does\n * goes back as a client message. Leaving — stop, shutdown, `/exit`, a closed connection — only\n * detaches: nothing sent from here ends the host's session, and a question left open stays open for\n * the other clients.\n *\n * The commands that belong to this terminal (`/shell`, `/editor`, `/theme`, `/keybindings`) never\n * reach the host: they run here, on this terminal and in its working directory.\n *\n * An observing terminal (`role: 'observe'`) sends only the reads an observer may send. Anything the\n * user does that would change the session is refused here with a notice; `/exit` and this terminal's\n * own commands still run.\n */\n\nimport {\n createSystemMessage,\n messageToHistoryEntry,\n printablePeerDriver,\n} from '@robota-sdk/agent-core';\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\nimport { isObserverMessageType } from '@robota-sdk/agent-transport/client';\n\nimport { AttentionCoordinator } from './attention/attention-coordinator.js';\nimport { attributedUserEcho } from './attributed-user-echo.js';\nimport { shortSessionId } from './short-session-id.js';\nimport { parseSlashCommandInput } from './slash-command-input.js';\nimport { TuiChannelLifecycleCoordinator } from './tui-channel-lifecycle-coordinator.js';\nimport { TuiPermissionQueue, TuiUserActionQueue } from './tui-interaction-queues.js';\nimport { TuiStateManager } from './tui-state-manager.js';\nimport { waitingLoopStopNotice } from './waiting-loop-stop-notice.js';\nimport { WireHistorySync } from './wire-history-sync.js';\nimport {\n createTuiClientCommandHost,\n findTuiClientCommand,\n runTuiClientCommand,\n} from './wire-tui-client-commands.js';\nimport {\n displayDriverId,\n filterCommandCatalog,\n findSubcommands,\n toCommandCatalog,\n toHistoryEntries,\n} from './wire-tui-projection.js';\n\nimport type {\n IAttachedSessionConnection,\n TAttachedSessionEnd,\n} from './attached-session-connection.js';\nimport type { IAttentionSource } from './attention/attention-tracker.js';\nimport type { TerminalHandoffController } from './terminal-handoff-controller.js';\nimport type {\n ITuiClientCommand,\n ITuiClientCommands,\n TTuiClientCommandHost,\n} from './wire-tui-client-commands.js';\nimport type { IOwnDriver } from './wire-tui-projection.js';\nimport type {\n ITuiAppChannelPort,\n ITuiChannelSnapshot,\n ITuiCommandQueryPort,\n ITuiRuntimeStatusSnapshot,\n ITuiSessionUiEventPort,\n TTuiSessionUiEventName,\n} from './tui-app-channel-port.js';\nimport type {\n IActionRequest,\n IHistoryEntry,\n TActionResponse,\n TPermissionMode,\n TSessionEndReason,\n} from '@robota-sdk/agent-core';\nimport type { ICommand } from '@robota-sdk/agent-interface-command';\nimport type { IExecutionDetailPage } from '@robota-sdk/agent-interface-execution';\nimport type {\n IAskRequestEvent,\n IInteractiveSessionEvents,\n IPermissionRequestEvent,\n ISessionListingEntry,\n ISessionRenamedEvent,\n ISessionStatusSnapshot,\n IUiIntentEvent,\n TWaitingLoopStopOutcome,\n} from '@robota-sdk/agent-interface-session';\nimport type { TClientMessage, TServerMessage } from '@robota-sdk/agent-transport/client';\n\ninterface IWireTuiChannelBaseOptions {\n readonly connection: IAttachedSessionConnection;\n /** The driver id the host gave this connection; the transcript shows its prompts as the user's. */\n readonly driverId?: string;\n readonly sessionName?: string;\n readonly terminalHandoff?: TerminalHandoffController;\n readonly attention?: IAttentionSource;\n /** Told once, when this terminal stops showing the session: the user left or the connection closed. */\n readonly onEnd?: (reason: TAttachedSessionEnd) => void;\n /**\n * How the host let this terminal on: `'drive'` (the default) or `'observe'`, which may only read\n * the session. The host refuses anything else from an observer; this channel does not send it.\n */\n readonly role?: 'drive' | 'observe';\n}\n\nexport type TWireTuiChannelOptions = IWireTuiChannelBaseOptions &\n (\n | {\n /** The commands this terminal runs itself instead of sending them to the host. */\n readonly clientCommands: ITuiClientCommands;\n /** This terminal's working directory, where its own commands run. */\n readonly cwd: string;\n }\n | { readonly clientCommands?: undefined }\n );\n\n/** Everything a view of the session reads beside its history, asked again after a session switch. */\nconst SNAPSHOT_REQUESTS = [\n 'get-context',\n 'get-commands',\n 'get-status',\n 'get-executing',\n 'get-pending',\n 'get-execution-workspace',\n] as const;\n/**\n * A question asked before this terminal attached is not sent to it again unless it asks. An observer\n * never asks: it answers no question.\n */\nconst DRIVER_SNAPSHOT_REQUESTS = [...SNAPSHOT_REQUESTS, 'get-prompts'] as const;\n/** After a command the host may have changed the status, the context window or the catalog. */\nconst COMMAND_REFRESH_REQUESTS = ['get-context', 'get-status', 'get-commands'] as const;\n/** In a shared session these would end it for everyone; here they only take this terminal away. */\nconst DETACH_COMMANDS: ReadonlySet<string> = new Set(['exit', 'quit']);\n/** Screens that need this process's own plugin adapter or transport registry. */\nconst IN_PROCESS_SCREENS: ReadonlySet<IUiIntentEvent['intent']['type']> = new Set([\n 'show-plugin-manager',\n 'show-settings',\n]);\nconst ATTACHED = 'while attached to a daemon';\nconst READ_ONLY_NOTICE =\n 'Read only: this terminal observes the session and cannot change it. /exit detaches.';\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** The React-facing `ui_intent` / `session_renamed` port, fed from frames instead of a session. */\nclass WireSessionUiEvents implements ITuiSessionUiEventPort {\n private readonly handlers: {\n [E in TTuiSessionUiEventName]: Set<IInteractiveSessionEvents[E]>;\n } = { ui_intent: new Set(), session_renamed: new Set() };\n\n on<E extends TTuiSessionUiEventName>(event: E, handler: IInteractiveSessionEvents[E]): void {\n this.handlers[event].add(handler);\n }\n\n off<E extends TTuiSessionUiEventName>(event: E, handler: IInteractiveSessionEvents[E]): void {\n this.handlers[event].delete(handler);\n }\n\n emitUiIntent(event: IUiIntentEvent): void {\n for (const handler of [...this.handlers.ui_intent]) handler(event);\n }\n\n emitSessionRenamed(event: ISessionRenamedEvent): void {\n for (const handler of [...this.handlers.session_renamed]) handler(event);\n }\n}\n\nexport class WireTuiChannel implements ITuiAppChannelPort {\n sessionName: string | undefined;\n\n private manager: TuiStateManager;\n private readonly userActions: TuiUserActionQueue;\n private readonly permissions: TuiPermissionQueue;\n private readonly attention: AttentionCoordinator | undefined;\n private readonly lifecycle: TuiChannelLifecycleCoordinator;\n private readonly uiEvents = new WireSessionUiEvents();\n /** One host for every client command, so one terminal handoff at a time spans all of them. */\n private readonly clientCommands:\n { readonly set: ITuiClientCommands; readonly host: TTuiClientCommandHost } | undefined;\n private readonly commandQueryPort: ITuiCommandQueryPort = {\n getCommands: (filter) => filterCommandCatalog(this.commandCatalog, filter),\n getSubcommands: (name) => findSubcommands(this.commandCatalog, name),\n };\n /** Only reads cross the wire from here, and nothing the user does changes the session. */\n private readonly readOnly: boolean;\n private commandCatalog: ICommand[] = [];\n private status: ISessionStatusSnapshot | undefined;\n /** How many prompts wait in the host's queue, the one `pendingPrompt` shows first among them. */\n private pendingCount = 0;\n private hostSessions: readonly ISessionListingEntry[] | undefined;\n private sessionListSequence = 0;\n /** Only the answer to the latest listing is shown: an earlier one may predate a switch. */\n private sessionListRequestId: string | undefined;\n /**\n * The picker `/resume` asked for, opened on the host's answer: what this terminal held since it\n * attached may be stale or missing, and an empty picker would only block the prompt.\n */\n private pendingPicker: IUiIntentEvent | undefined;\n private readonly hostHistory: WireHistorySync;\n /** Set when the channel attaches: history entries older than that are not this terminal's. */\n private own: IOwnDriver | undefined;\n /**\n * Commands sent and not yet answered, by request id; `handleInput` settles when the host's answer\n * is shown. The host echoes the id, so an answer settles only the command it answers.\n */\n private readonly pendingCommands = new Map<string, () => void>();\n private commandSequence = 0;\n /** Detail pages asked for and not yet answered, by request id. */\n private readonly pendingDetails = new Map<\n string,\n { readonly resolve: (page: IExecutionDetailPage) => void; readonly reject: (error: Error) => void }\n >();\n private detailSequence = 0;\n /** Esc's loop stops waiting for the host's outcome, by request id. */\n private readonly pendingLoopStops = new Map<\n string,\n (outcome: TWaitingLoopStopOutcome | undefined) => void\n >();\n private loopStopSequence = 0;\n /**\n * Inputs sent to a background task, by task id, oldest first. The host's control result names the\n * task but no request, so a result settles the oldest send to that task.\n */\n private readonly pendingTaskSends = new Map<string, ((message?: string) => void)[]>();\n /**\n * The switch the picker asked for, settled by the host's answer: `session_switched`, or a refusal\n * that names its request (or, from an older host, a protocol error that names none). A refusal\n * never settles a command.\n */\n private pendingSessionChange: { readonly requestId: string; readonly settle: () => void } | undefined;\n private sessionChangeSequence = 0;\n /** Bumped on a session switch: a question from a session no longer shown is never answered. */\n private promptGeneration = 0;\n /** Questions on screen or waiting for it: the host may send one again (`get-prompts`). */\n private readonly shownPromptIds = new Set<string>();\n /** Questions another client settled: dismissing them here answers nothing. */\n private readonly settledPromptIds = new Set<string>();\n /**\n * Bumped when the transcript starts over for another session. The terminal prints its transcript\n * once, counting what it printed, so a new one is printed from its start rather than past that count.\n */\n private transcriptGeneration = 0;\n private readonly unsubscribers: (() => void)[] = [];\n /** From here on nothing is sent: the terminal left, and the session is not this terminal's to end. */\n private detached = false;\n private ended = false;\n private onChange: (() => void) | null = null;\n\n constructor(private readonly options: TWireTuiChannelOptions) {\n this.sessionName = options.sessionName;\n this.readOnly = options.role === 'observe';\n this.manager = this.createManager();\n this.clientCommands =\n options.clientCommands !== undefined\n ? {\n set: options.clientCommands,\n host: createTuiClientCommandHost({\n terminalHandoff: options.terminalHandoff,\n cwd: options.cwd,\n }),\n }\n : undefined;\n this.hostHistory = new WireHistorySync({\n request: (fromIndex) =>\n this.send(fromIndex === 0 ? { type: 'get-history' } : { type: 'get-history', fromIndex }),\n show: (entries, echoes) => this.manager.syncHostHistory(entries, echoes),\n revive: (entries) => toHistoryEntries(entries, this.own),\n });\n this.userActions = new TuiUserActionQueue(() => this.notify());\n this.permissions = new TuiPermissionQueue(() => this.notify());\n this.attention = options.attention\n ? new AttentionCoordinator({\n source: options.attention,\n onRecap: (line) => this.manager.addAttentionRecap(line),\n })\n : undefined;\n this.lifecycle = new TuiChannelLifecycleCoordinator(\n {\n start: async () => this.attach(),\n stop: async () => {\n this.detach();\n this.attention?.unwire();\n this.onChange = null;\n this.manager.dispose();\n },\n beginShutdown: () => {\n this.detach();\n this.notice('Detached. The session keeps running.');\n },\n // Leaving never ends the host's session: it is shared, and it keeps running without us.\n shutdownSession: async () => undefined,\n },\n 0,\n );\n }\n\n get terminalHandoffController(): TerminalHandoffController | undefined {\n return this.options.terminalHandoff;\n }\n\n async start(): Promise<void> {\n await this.lifecycle.start();\n }\n\n /** Detaches from the session; the connection belongs to whoever opened it. */\n async stop(): Promise<void> {\n await this.lifecycle.stop();\n }\n\n async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {\n await this.lifecycle.shutdown(options);\n }\n\n subscribe(onChange: () => void): () => void {\n this.onChange = onChange;\n return () => {\n if (this.onChange === onChange) this.onChange = null;\n };\n }\n\n getSnapshot(): ITuiChannelSnapshot {\n const manager = this.manager;\n return {\n history: manager.history,\n streamingText: manager.streamingText,\n activeTools: manager.activeTools,\n isThinking: manager.isThinking,\n isAborting: manager.isAborting,\n lastErrorMessage: manager.lastErrorMessage,\n isStalled: manager.isStalled,\n sessionEventNotices: manager.sessionEventNotices,\n isShuttingDown: this.lifecycle.isShuttingDown,\n pendingPrompt: manager.pendingPrompt,\n pendingCount: manager.pendingPrompt === null ? 0 : Math.max(this.pendingCount, 1),\n executionWorkspaceSnapshot: manager.executionWorkspaceSnapshot,\n ...(manager.selectedExecutionEntryId !== undefined\n ? { selectedExecutionEntryId: manager.selectedExecutionEntryId }\n : {}),\n permissionRequest: this.permissions.current,\n pendingUserAction: this.userActions.current,\n contextState: manager.contextState,\n ...(this.hostSessions !== undefined ? { hostSessions: this.hostSessions } : {}),\n transcriptGeneration: this.transcriptGeneration,\n ...(this.readOnly ? { readOnly: true } : {}),\n attached: true,\n };\n }\n\n getCommandQueryPort(): ITuiCommandQueryPort {\n return this.commandQueryPort;\n }\n\n getSessionUiEventPort(): ITuiSessionUiEventPort {\n return this.uiEvents;\n }\n\n getRuntimeStatusSnapshot(fallbackPermissionMode: TPermissionMode): ITuiRuntimeStatusSnapshot {\n const status = this.status;\n if (status === undefined) return { permissionMode: fallbackPermissionMode, sessionId: '' };\n return {\n permissionMode: status.permissionMode,\n sessionId: status.sessionId,\n effort: status.effort,\n modelId: status.model,\n };\n }\n\n addEntry(entry: IHistoryEntry): void {\n this.manager.addEntry(entry);\n }\n\n async handleInput(input: string): Promise<void> {\n if (!input.startsWith('/')) {\n if (this.refuseReadOnly()) return;\n // The host answers with its queue once it has taken the prompt.\n this.send({ type: 'submit', prompt: input });\n return;\n }\n const { name, args } = parseSlashCommandInput(input);\n if (DETACH_COMMANDS.has(name)) {\n this.end('user');\n return;\n }\n const clientCommand = findTuiClientCommand(this.clientCommands?.set, name);\n if (clientCommand !== undefined) {\n await this.runClientCommand(clientCommand, args);\n return;\n }\n if (this.refuseReadOnly()) return;\n // Settles when the host's answer is on screen, so what reads the settings after a command\n // (the status line, the theme) reads them after the host changed them.\n this.commandSequence += 1;\n const requestId = `wire-tui-command-${this.commandSequence}`;\n await new Promise<void>((settle) => {\n this.pendingCommands.set(requestId, settle);\n if (!this.send({ type: 'command', name, args, requestId })) this.settleCommand(requestId);\n });\n }\n\n /**\n * Runs a terminal-owned command here and shows its answer as the in-process session would. It\n * settles once what the command wrote is written: the App reads the appearance again after it.\n */\n private async runClientCommand(command: ITuiClientCommand, args: string): Promise<void> {\n const commands = this.clientCommands;\n if (commands === undefined) return;\n try {\n const { result, uiIntents } = await runTuiClientCommand(\n command,\n args,\n commands.host,\n commands.set,\n );\n for (const intent of uiIntents) {\n this.uiEvents.emitUiIntent({ intent, requesterDriverId: OWNER_DRIVER_ID });\n }\n this.notice(result.message);\n } catch (error) {\n this.notice(`/${command.name} failed: ${errorMessage(error)}`);\n }\n }\n\n abort(): void {\n if (this.refuseReadOnly()) return;\n this.manager.setAborting(true);\n this.userActions.cancelAll();\n this.permissions.cancelAll();\n this.send({ type: 'abort' });\n }\n\n cancelQueue(): void {\n if (this.refuseReadOnly()) return;\n this.send({ type: 'cancel-queue' });\n this.userActions.cancelAll();\n this.permissions.cancelAll();\n this.pendingCount = 0;\n this.manager.setPendingPrompt(null);\n }\n\n /** Esc on an idle prompt: the host's session decides which waiting loop, if any, stops. */\n async stopWaitingSelfPacedLoop(): Promise<void> {\n if (this.refuseReadOnly()) return;\n this.loopStopSequence += 1;\n const requestId = `wire-tui-loop-stop-${this.loopStopSequence}`;\n const outcome = await new Promise<TWaitingLoopStopOutcome | undefined>((settle) => {\n this.pendingLoopStops.set(requestId, settle);\n if (!this.send({ type: 'stop-waiting-loop', requestId })) this.settleLoopStop(requestId);\n });\n const notice = outcome === undefined ? undefined : waitingLoopStopNotice(outcome);\n if (notice !== undefined) this.notice(notice);\n }\n\n selectExecutionWorkspaceEntry(entryId: string): void {\n this.manager.selectExecutionWorkspaceEntry(entryId);\n }\n\n /** A page of what a workspace entry recorded, read from the host. An observer may read it too. */\n async readExecutionWorkspaceDetail(entryId: string): Promise<IExecutionDetailPage> {\n this.detailSequence += 1;\n const requestId = `wire-tui-detail-${this.detailSequence}`;\n return new Promise<IExecutionDetailPage>((resolve, reject) => {\n this.pendingDetails.set(requestId, { resolve, reject });\n if (!this.send({ type: 'read-execution-detail', requestId, entryId })) {\n this.rejectDetail(requestId, 'Could not reach the session.');\n }\n });\n }\n\n /** Settles once the host says whether the task took the input; a refusal is shown as a notice. */\n async sendAgentJob(taskId: string, input: string): Promise<void> {\n if (this.refuseReadOnly()) return;\n const failure = await new Promise<string | undefined>((settle) => {\n const waiting = this.pendingTaskSends.get(taskId) ?? [];\n waiting.push(settle);\n this.pendingTaskSends.set(taskId, waiting);\n if (!this.send({ type: 'send-background-task', taskId, input: { prompt: input } })) {\n this.settleTaskSend(taskId);\n }\n });\n if (failure !== undefined) this.notice(`Could not send to background task ${taskId}: ${failure}`);\n }\n\n resolveUserAction(request: IActionRequest, response: TActionResponse): void {\n this.userActions.resolveCurrent(request, response);\n }\n\n /**\n * Ask the host to show another session; its `session_switched` resets this channel. Settles on the\n * host's answer, so the picker's switch is pending until the session changed or was refused.\n */\n async requestSessionSwitch(sessionId: string): Promise<void> {\n if (this.refuseReadOnly()) return;\n // The host answers a switch to the session already shown with nothing: there is nothing to wait for.\n if (sessionId === this.status?.sessionId) return;\n // An earlier switch still waiting is overtaken by this one.\n this.settleSessionChange();\n this.sessionChangeSequence += 1;\n const requestId = `wire-tui-session-change-${this.sessionChangeSequence}`;\n await new Promise<void>((settle) => {\n this.pendingSessionChange = { requestId, settle };\n if (!this.send({ type: 'switch-session', sessionId, requestId })) this.settleSessionChange();\n });\n }\n\n // ── Frames → render state ─────────────────────────────────────\n\n private attach(): void {\n const { connection } = this.options;\n if (this.options.driverId !== undefined) {\n this.own = { driverId: this.options.driverId, since: Date.now() };\n }\n this.unsubscribers.push(\n connection.subscribe((frame) => this.onFrame(frame)),\n connection.onClose(() => this.end('closed')),\n );\n this.attention?.wire();\n this.requestSnapshot();\n }\n\n private onFrame(frame: TServerMessage): void {\n if (this.detached) return;\n try {\n this.project(frame);\n } catch (error) {\n // A frame this terminal cannot show must not take the connection's reader down with it.\n this.notice(`Could not show the session's ${frame.type} update: ${errorMessage(error)}`);\n }\n }\n\n private project(frame: TServerMessage): void {\n const manager = this.manager;\n switch (frame.type) {\n case 'user_message': {\n const driverId = displayDriverId(frame.driverId, this.options.driverId) ?? null;\n this.hostHistory.echo(\n attributedUserEcho(frame.content, { getActiveDriverId: () => driverId }),\n frame.content,\n );\n return;\n }\n case 'text_delta':\n manager.onTextDelta(frame.delta);\n return;\n case 'tool_start':\n manager.onToolStart(frame.state);\n return;\n case 'tool_end':\n manager.onToolEnd(frame.state);\n return;\n case 'thinking':\n manager.onThinking(frame.isThinking);\n // A turn starting or ending is when the host's queue moves.\n this.send({ type: 'get-pending' });\n return;\n case 'executing':\n if (frame.executing !== manager.isThinking) manager.onThinking(frame.executing);\n return;\n case 'complete':\n manager.onComplete(frame.result);\n this.attention?.onComplete();\n // The answer is in the host's history now; only what came after the known entries is read.\n this.hostHistory.readTail();\n return;\n case 'interrupted':\n manager.onInterrupted();\n // The host kept the partial answer; the next turn's prompt must not take its place.\n this.hostHistory.readTail();\n return;\n case 'error':\n manager.onError(new Error(frame.message));\n this.attention?.onError();\n this.hostHistory.readTail();\n return;\n case 'history':\n this.hostHistory.onPage(frame);\n return;\n case 'history_changed':\n this.hostHistory.reload();\n return;\n case 'history_cleared':\n manager.clearHistory();\n this.hostHistory.reset();\n this.hostHistory.reload();\n return;\n case 'context':\n manager.onContextUpdate(frame.state);\n return;\n case 'turn_source':\n this.attention?.onTurnSource(frame.source);\n return;\n case 'pending':\n this.pendingCount = frame.pendingCount ?? (frame.pending === null ? 0 : 1);\n manager.setPendingPrompt(frame.pending);\n return;\n case 'execution_workspace_event':\n manager.syncExecutionWorkspaceSnapshot(frame.snapshot);\n this.attention?.onWorkspaceSnapshot(frame.snapshot);\n return;\n case 'plan_event':\n manager.addSessionEventNotice({ event: 'plan_event', payload: frame.event });\n return;\n case 'context_file_refreshed':\n manager.addSessionEventNotice({ event: 'context_file_refreshed', payload: frame.event });\n return;\n case 'branch_event':\n manager.addSessionEventNotice({ event: 'branch_event', payload: frame.event });\n return;\n default:\n this.projectSessionFrame(frame);\n }\n }\n\n /** Frames about the session rather than its turn: questions, commands, status, switching. */\n private projectSessionFrame(frame: TServerMessage): void {\n switch (frame.type) {\n case 'permission_request':\n this.askPermission(frame.event);\n return;\n case 'ask_request':\n this.askUser(frame.event);\n return;\n case 'prompt_resolved':\n this.dismissPrompt(frame.event.id);\n return;\n case 'ui_intent':\n this.showUiIntent(frame.event);\n return;\n case 'session_renamed':\n this.rename(frame.event.name);\n return;\n case 'session_status':\n this.applyStatus(frame.status);\n return;\n case 'commands':\n this.commandCatalog = toCommandCatalog(frame.commands, frame.skills);\n this.notify();\n return;\n case 'command_result':\n this.showCommandResult(frame);\n return;\n case 'sessions':\n if (frame.requestId !== this.sessionListRequestId) return;\n this.hostSessions = frame.listing.sessions;\n this.notify();\n this.openPendingPicker();\n return;\n case 'sessions_error':\n if (frame.requestId !== this.sessionListRequestId) return;\n // Asked for at attach too, where a host without a session list is nothing to report.\n if (this.pendingPicker !== undefined) {\n this.pendingPicker = undefined;\n this.notice(frame.message);\n }\n return;\n case 'session_switched':\n this.settleSessionChange();\n this.followSessionSwitch(frame.event.sessionId);\n return;\n case 'session_change_failed':\n // Only the client that asked is told. It answers the switch, never a command.\n if (frame.requestId === undefined || frame.requestId === this.pendingSessionChange?.requestId) {\n this.settleSessionChange();\n }\n this.notice(frame.message);\n return;\n case 'protocol_error':\n // A command the host could not run answers with this, naming the command's request. A host\n // older than `session_change_failed` refuses a switch with one that names no request.\n if (frame.requestId !== undefined) this.settleCommand(frame.requestId);\n else this.settleSessionChange();\n this.notice(frame.message);\n return;\n case 'resume_gap':\n // Frames were lost, perhaps the page a history read waits for: read everything again.\n this.hostHistory.restart();\n this.requestState();\n return;\n default:\n this.projectReply(frame);\n }\n }\n\n /** Answers to one request of this terminal's: a detail page, a loop stop, a task's input. */\n private projectReply(frame: TServerMessage): void {\n switch (frame.type) {\n case 'execution_detail': {\n const pending = this.pendingDetails.get(frame.requestId);\n this.pendingDetails.delete(frame.requestId);\n pending?.resolve(frame.page);\n return;\n }\n case 'execution_detail_error':\n this.rejectDetail(frame.requestId, frame.message);\n return;\n case 'waiting_loop_stop':\n this.settleLoopStop(frame.requestId, frame.outcome);\n return;\n case 'background_task_control_result':\n if (frame.action !== 'send') return;\n this.settleTaskSend(frame.taskId, frame.success ? undefined : (frame.message ?? 'refused'));\n return;\n default:\n // Other background-task and usage frames: the full TUI reads background work from the workspace.\n return;\n }\n }\n\n /** A question this terminal shows already is not shown twice (`get-prompts` sends it again). */\n private showsPrompt(id: string): boolean {\n if (this.shownPromptIds.has(id)) return true;\n this.shownPromptIds.add(id);\n return false;\n }\n\n private askPermission(event: IPermissionRequestEvent): void {\n if (this.showsPrompt(event.id)) return;\n this.attention?.onNeedsInput();\n // A peer turn's ask names the peer, printed only as a plain identifier.\n const requestedByPeer = event.requesterDriverId?.startsWith('peer:')\n ? printablePeerDriver(event.requesterDriverId)\n : undefined;\n const generation = this.promptGeneration;\n void this.permissions\n .enqueue(\n event.toolName,\n event.toolArgs,\n event.id,\n event.canPersistProjectPermission,\n requestedByPeer,\n )\n .then((result) =>\n this.answer(generation, event.id, { type: 'permission-response', id: event.id, result }),\n );\n }\n\n private askUser(event: IAskRequestEvent): void {\n if (this.showsPrompt(event.id)) return;\n this.attention?.onNeedsInput();\n const generation = this.promptGeneration;\n void this.userActions\n .enqueue(event.request, event.id)\n .then((response) =>\n this.answer(generation, event.id, { type: 'ask-response', id: event.id, response }),\n );\n }\n\n private answer(generation: number, id: string, message: TClientMessage): void {\n if (generation !== this.promptGeneration) return;\n this.shownPromptIds.delete(id);\n if (this.settledPromptIds.delete(id)) return;\n this.send(message);\n }\n\n /** Another client answered first: take the question off this screen without answering it. */\n private dismissPrompt(id: string): void {\n this.settledPromptIds.add(id);\n const dismissedAction = this.userActions.dismissById(id);\n const dismissedPermission = this.permissions.dismissById(id);\n if (!dismissedAction && !dismissedPermission) this.settledPromptIds.delete(id);\n }\n\n private showUiIntent(event: IUiIntentEvent): void {\n // An unattributed intent reaches every client; the App's own rule leaves it to its issuer.\n if (event.requesterDriverId === undefined) {\n this.uiEvents.emitUiIntent(event);\n return;\n }\n if (IN_PROCESS_SCREENS.has(event.intent.type)) {\n this.notice(`That screen is not available ${ATTACHED}.`);\n return;\n }\n // The host routes an attributed intent only to the client that asked: this terminal's operator.\n const own: IUiIntentEvent = { ...event, requesterDriverId: OWNER_DRIVER_ID };\n if (event.intent.type === 'show-session-picker') {\n this.pendingPicker = own;\n if (!this.requestSessionList()) this.pendingPicker = undefined;\n return;\n }\n this.uiEvents.emitUiIntent(own);\n }\n\n private openPendingPicker(): void {\n const picker = this.pendingPicker;\n if (picker === undefined) return;\n this.pendingPicker = undefined;\n this.uiEvents.emitUiIntent(picker);\n }\n\n private rename(name: string): void {\n this.sessionName = name;\n this.uiEvents.emitSessionRenamed({ name });\n this.notify();\n }\n\n /** The session is labelled by its name, or by its id until it has one: never by the host's label. */\n private applyStatus(status: ISessionStatusSnapshot): void {\n this.status = status;\n this.manager.onContextUpdate(status.context);\n const label =\n status.sessionName ??\n (status.sessionId !== '' ? shortSessionId(status.sessionId) : undefined);\n if (label !== undefined && label !== this.sessionName) this.rename(label);\n }\n\n private showCommandResult(frame: Extract<TServerMessage, { type: 'command_result' }>): void {\n for (const type of COMMAND_REFRESH_REQUESTS) this.send({ type });\n // A command that started a turn says nothing itself; the turn is its answer.\n if (frame.data?.['sessionExecution'] === true) {\n this.send({ type: 'get-pending' });\n } else {\n this.notice(frame.message);\n }\n if (frame.requestId !== undefined) this.settleCommand(frame.requestId);\n }\n\n private settleCommand(requestId: string): void {\n const settle = this.pendingCommands.get(requestId);\n this.pendingCommands.delete(requestId);\n settle?.();\n }\n\n private rejectDetail(requestId: string, message: string): void {\n const pending = this.pendingDetails.get(requestId);\n this.pendingDetails.delete(requestId);\n pending?.reject(new Error(message));\n }\n\n private settleLoopStop(requestId: string, outcome?: TWaitingLoopStopOutcome): void {\n const settle = this.pendingLoopStops.get(requestId);\n this.pendingLoopStops.delete(requestId);\n settle?.(outcome);\n }\n\n /** Settles the oldest input sent to the task; `failure` is why the host refused it. */\n private settleTaskSend(taskId: string, failure?: string): void {\n const waiting = this.pendingTaskSends.get(taskId);\n const settle = waiting?.shift();\n if (waiting !== undefined && waiting.length === 0) this.pendingTaskSends.delete(taskId);\n settle?.(failure);\n }\n\n /** An observer is told it cannot change the session; true when this terminal only observes. */\n private refuseReadOnly(): boolean {\n if (this.readOnly) this.notice(READ_ONLY_NOTICE);\n return this.readOnly;\n }\n\n private settleSessionChange(): void {\n const pending = this.pendingSessionChange;\n this.pendingSessionChange = undefined;\n pending?.settle();\n }\n\n /** The host now serves another session: nothing shown belongs to it, so start over from it. */\n private followSessionSwitch(sessionId: string): void {\n this.promptGeneration += 1;\n this.userActions.cancelAll();\n this.permissions.cancelAll();\n this.shownPromptIds.clear();\n this.settledPromptIds.clear();\n this.status = undefined;\n this.pendingCount = 0;\n this.hostHistory.reset();\n this.manager.dispose();\n this.manager = this.createManager();\n // A new transcript, printed from its start whatever this render also brings.\n this.transcriptGeneration += 1;\n this.notice(`Switched to session ${sessionId}.`);\n // Until the new session's status names it.\n this.rename(shortSessionId(sessionId));\n this.requestSnapshot();\n }\n\n // ── Requests ──────────────────────────────────────────────────\n\n private requestSnapshot(): void {\n this.hostHistory.reload();\n this.requestState();\n }\n\n /** Everything shown beside the history. */\n private requestState(): void {\n for (const type of this.readOnly ? SNAPSHOT_REQUESTS : DRIVER_SNAPSHOT_REQUESTS) {\n this.send({ type });\n }\n this.requestSessionList();\n }\n\n private requestSessionList(): boolean {\n this.sessionListSequence += 1;\n const id = `wire-tui-sessions-${this.sessionListSequence}`;\n this.sessionListRequestId = id;\n return this.send({ type: 'list-sessions', requestId: id });\n }\n\n private send(message: TClientMessage): boolean {\n if (this.detached) return false;\n // The host would refuse it: an observer sends only reads.\n if (this.readOnly && !isObserverMessageType(message.type)) return false;\n try {\n this.options.connection.send(message);\n return true;\n } catch (error) {\n this.notice(`Could not reach the session: ${errorMessage(error)}`);\n return false;\n }\n }\n\n // ── Leaving ───────────────────────────────────────────────────\n\n private end(reason: TAttachedSessionEnd): void {\n if (this.ended) return;\n this.ended = true;\n this.detach();\n this.notice(\n reason === 'closed'\n ? 'The connection to the session closed.'\n : 'Detached. The session keeps running.',\n );\n this.options.onEnd?.(reason);\n }\n\n /** Stop listening and sending. An open question is dropped here and stays open for others. */\n private detach(): void {\n if (this.detached) return;\n this.detached = true;\n this.pendingPicker = undefined;\n for (const unsubscribe of this.unsubscribers.splice(0)) unsubscribe();\n this.userActions.cancelAll();\n this.permissions.cancelAll();\n const pending = [...this.pendingCommands.values()];\n this.pendingCommands.clear();\n for (const settle of pending) settle();\n this.settleSessionChange();\n for (const requestId of [...this.pendingDetails.keys()]) {\n this.rejectDetail(requestId, 'Detached from the session.');\n }\n for (const requestId of [...this.pendingLoopStops.keys()]) this.settleLoopStop(requestId);\n const taskSends = [...this.pendingTaskSends.values()].flat();\n this.pendingTaskSends.clear();\n for (const settle of taskSends) settle();\n }\n\n // ── Render state ──────────────────────────────────────────────\n\n private createManager(): TuiStateManager {\n const manager = new TuiStateManager();\n manager.onChange = () => this.notify();\n return manager;\n }\n\n private notice(text: string): void {\n this.manager.addEntry(messageToHistoryEntry(createSystemMessage(text)));\n }\n\n private notify(): void {\n this.onChange?.();\n }\n}\n","/**\n * Ink render entry point.\n */\n\nimport chalk from 'chalk';\nimport { render } from 'ink';\nimport React from 'react';\n\nimport App from './App.js';\nimport { AttentionTracker } from './attention/attention-tracker.js';\nimport { FocusReportingStdin } from './attention/focus-input-filter.js';\nimport { KeybindingsProvider } from './keybindings/keybindings-context.js';\nimport { ProductDisplayNameProvider } from './product-display-name-context.js';\nimport { writeScreenReaderAnnouncement } from './screen-reader-announcement.js';\nimport { ScreenReaderProvider } from './screen-reader-context.js';\nimport { ScreenReaderPacingProvider } from './screen-reader-pacing-context.js';\nimport { awaitStartupQuietPeriod, resolvePacing } from './screen-reader-pacing.js';\nimport type { IScreenReaderPacingOverrides } from './screen-reader-pacing.js';\nimport { createParkedStdout, toPacingPort } from './screen-reader-stdout.js';\nimport { isInteractiveColorTerminal, supportsFocusReporting } from './terminal-capabilities.js';\nimport { TerminalCapabilitiesProvider } from './terminal-capabilities-context.js';\nimport type { ITerminalCapabilityOverrides } from './terminal-capabilities-context.js';\nimport { createFocusReportingWriter } from './terminal-focus-reporting.js';\nimport { TerminalHandoffController } from './terminal-handoff-controller.js';\nimport { TuiInteractionChannel } from './TuiInteractionChannel.js';\nimport { WireTuiChannel } from './wire-tui-channel.js';\n\nimport type {\n IAttachedSessionConnection,\n TAttachedSessionEnd,\n} from './attached-session-connection.js';\nimport type { IAttentionSource } from './attention/attention-tracker.js';\nimport type { IKeybindingsSource } from './keybindings/node-keybindings-source.js';\nimport type { TScreenReaderChannel } from './screen-reader-announcement.js';\nimport type { IThemeRegistry } from './theme/theme-registry.js';\nimport type { ITuiAppChannelPort } from './tui-app-channel-port.js';\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { ITuiInteractionChannelOptions } from './TuiInteractionChannel.js';\nimport type { ITuiClientCommands } from './wire-tui-client-commands.js';\nimport type {\n IAIProvider,\n IToolWithEventService,\n IProviderDefinition,\n} from '@robota-sdk/agent-core';\nimport type { TPermissionMode } from '@robota-sdk/agent-core';\nimport type {\n IBackgroundTaskRunner,\n ICommandHostAdapters,\n IOutputStylePrompt,\n ICommandModule,\n IRemoteCommandPolicy,\n TSubagentRunnerFactory,\n IAgentDefinition,\n TShellExecFn,\n CommandRegistry,\n IMemoryStore,\n IPromptHistoryOptions,\n IAutomaticMemoryConfig,\n IPerTurnRecallConfig,\n TWorkspaceProjectAccess,\n EditCheckpointStore,\n TInteractiveSessionOptions,\n IOrgPolicy,\n IProviderErrorGuidance,\n IProjectSettingsPath,\n IContributionSource,\n ISkillRootDescriptor,\n INodeHostSettingsSource,\n IToolCallHandoffPolicy,\n ICreateSessionOptions,\n ILivePromptTracePort,\n} from '@robota-sdk/agent-framework';\nimport type { TReducedMotionOverride } from '@robota-sdk/agent-interface-command';\nimport type {\n IInteractiveSessionStore,\n IPromptHistorySource,\n} from '@robota-sdk/agent-interface-session';\nimport type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';\n\nexport interface IRenderOptions {\n cwd: string;\n livePromptTrace?: ILivePromptTracePort;\n /** Product identity for terminal labels, title, and host-facing copy. */\n productDisplayName?: string;\n modelCommandToolPrefix?: string;\n subagentHookEnvironmentNames?: ICreateSessionOptions['subagentHookEnvironmentNames'];\n observerFailureWarningCode?: ICreateSessionOptions['observerFailureWarningCode'];\n commandHookShell?: string;\n /** Skip instruction files and plugin discovery (`--safe-mode`). */\n bare?: boolean;\n /** Run no hook the settings layers declare (`--safe-mode`). */\n skipConfiguredHooks?: boolean;\n promptFileReferenceTag?: string;\n provider: IAIProvider;\n providerErrorGuidance?: IProviderErrorGuidance;\n projectAccess?: TWorkspaceProjectAccess;\n projectSettingsPaths?: readonly IProjectSettingsPath[];\n baselinePermissionAllow?: readonly string[];\n /** Host-selected task-context root; absent means no framework task-directory scan. */\n taskContext?: { readonly enabled?: boolean; readonly dir?: string };\n contributionSources?: readonly IContributionSource[];\n skillRoots?: readonly ISkillRootDescriptor[];\n userSettingsSources?: readonly INodeHostSettingsSource[];\n /**\n * CLI-083 (issue #2287) — the org policy, forwarded to the session so `blockedCommands` is\n * enforced on the plain `robota` path as well as under `--serve`.\n *\n * DECLARED, not merely spread through. The shell forwards this with\n * `...(orgPolicy === null ? {} : { orgPolicy })`, and a spread bypasses TypeScript's\n * excess-property check — so before this field existed the value compiled, arrived, and was\n * dropped by `toChannelOptions` below, which copies field by field. The idiom that looked safest\n * is what disabled the one check that would have caught the missing declaration.\n */\n orgPolicy?: IOrgPolicy | undefined;\n /**\n * Builds the authority- and permission-backed edit checkpoint store for one session; absent, no\n * session keeps checkpoints. A factory, not a store: a session switch builds the next channel\n * while the old session may still be finishing a turn, and a store holds one turn in progress.\n */\n createEditCheckpointStore?: () => EditCheckpointStore;\n /** The host's way to build each external-event grant's verifier; absent, no grant opens. */\n externalEventVerifierFactory?: TInteractiveSessionOptions['externalEventVerifierFactory'];\n /**\n * #3189: the run's grant history, handed to every session this TUI builds so a switch replays no\n * spent token and resets no rate. DECLARED for the reason `orgPolicy` above is.\n */\n externalEventGrantHistory?: TInteractiveSessionOptions['externalEventGrantHistory'];\n providerOverride?: string | undefined;\n /**\n * #1844: forwarded to the session so `/provider switch` can construct the provider it switches TO.\n *\n * The session cannot discover these — they are assembled at the composition root from the provider\n * packages. Without them the hot-swap throws with an empty supported-list, which is the failure\n * this option exists to prevent rather than a nicety.\n */\n providerDefinitions?: readonly IProviderDefinition[];\n providerType?: string | undefined;\n modelId?: string;\n /** CLI-1988: resolved provider-neutral response style. */\n outputStyle?: IOutputStylePrompt;\n /** ARCH-013: resolved preset effort, forwarded to the session's `effort` seam. */\n effort?: ITuiInteractionChannelOptions['effort'];\n temperature?: number;\n maxOutputTokens?: number;\n /** Preset prompt seed, distinct from the replacing `systemPrompt` option. */\n presetSystemPrompt?: string;\n /** CLI-sourced additive prompt text, composed before the TUI is rendered. */\n appendSystemPrompt?: string;\n responseFormat?: ITuiInteractionChannelOptions['responseFormat'];\n language?: string;\n permissionMode?: TPermissionMode;\n maxTurns?: number;\n allowedTools?: readonly string[];\n deniedTools?: readonly string[];\n version?: string;\n sessionStore?: IInteractiveSessionStore;\n disableSessionLoops?: boolean;\n resolveDefaultLoopPrompt?: () => string;\n resumeSessionId?: string;\n showSessionPickerOnStart?: boolean;\n /** FLOW-2006: text a deep link prefilled into the composer. Never submitted on its own. */\n initialInput?: string;\n /** Where that text came from; `external-link` renders the provenance notice. */\n initialInputOrigin?: 'external-link';\n forkSession?: boolean;\n /** Issue #3081: the startup session is the target of a `/cd` from this directory. */\n workspaceMovedFrom?: string;\n sessionName?: string;\n backgroundTaskRunners?: IBackgroundTaskRunner[];\n /** MCP-004: the tool-call handoff policy the composition root computed for this runtime. */\n toolCallHandoff?: IToolCallHandoffPolicy;\n subagentRunnerFactory?: TSubagentRunnerFactory;\n /**\n * ARCH-005: subagent definitions contributed by the composition root (the capability packs\n * `assembleProduct` merged). Forwarded to the session's `agentDefinitions` seam; absent ⇒ unchanged.\n */\n agentDefinitions?: readonly IAgentDefinition[];\n agentDefinitionRoots?: readonly string[];\n pluginDirectories?: { readonly user?: string; readonly project?: string };\n /**\n * ARCH-006: tools contributed by the composition root (the capability packs `assembleProduct` merged)\n * and, when the profile hands the packs the whole tool surface, the suppressed framework default tier\n * (`defaultTools: []`). Forwarded to the session's tool-composition seam; absent ⇒ unchanged.\n */\n additionalTools?: IToolWithEventService[];\n defaultTools?: readonly IToolWithEventService[];\n /** The sandbox the shell tools run under, so the session can let a confined command skip the prompt. */\n sandboxClient?: ICreateSessionOptions['sandboxClient'];\n commandModules?: readonly ICommandModule[];\n commandHostAdapters?: ICommandHostAdapters;\n shellExec?: TShellExecFn;\n /** REMOTE-006: optional remote-command policy (allow-by-default; local == remote). */\n remoteCommandPolicy?: IRemoteCommandPolicy;\n startupUpdateNotice?: Promise<string | undefined>;\n transportRegistry?: ITransportRegistryView;\n bindTransports?: ITuiInteractionChannelOptions['bindTransports'];\n cliAdapter: ITuiCliAdapter;\n reloadPluginCommandSource?: (registry: CommandRegistry) => void;\n agentName?: string;\n /** Active preset id selected at startup (PRESET-011 runtime state). Defaults to 'default'. */\n activePresetId?: string;\n /** Preset persona block composed as a `source: 'persona'` system-prompt section (priority 5). */\n persona?: string;\n /** Preset execution capability: activate agent runtime + subagent/background dispatch. */\n enableParallelSubagents?: boolean;\n /** Preset execution capability: run a post-task self-verification step. */\n selfVerification?: boolean;\n /**\n * Called with each live channel (including session-switch re-creations). Lets the embedding\n * product wire process-level concerns (ERR-001 G1: error routing into the live session).\n */\n onChannelReady?: (channel: TuiInteractionChannel) => void;\n /**\n * SELFHOST-008 P6: optional durable-memory store injected by the surface (agent-cli). Forwarded to the\n * channel → `buildRuntimeSession`; absent ⇒ memory OFF (today's behavior). Enablement is surface-owned.\n */\n memoryStore?: IMemoryStore;\n /** SELFHOST-008 P6: optional automatic post-turn capture policy (absent ⇒ capture OFF). */\n automaticMemory?: IAutomaticMemoryConfig;\n /** SELFHOST-008 P6: optional per-turn recall policy (absent ⇒ recall OFF, startup-only injection). */\n recallMemory?: IPerTurnRecallConfig;\n /**\n * CLI-2004: the resolved screen-reader mode. DECLARED, not spread through — `toChannelOptions`\n * below copies field by field, so an undeclared field compiles, arrives and is silently dropped\n * (the ARCH-110 containment hazard the `orgPolicy` comment above records). Absent ⇒ mode OFF and\n * today's byte stream is unchanged.\n */\n screenReader?: boolean | undefined;\n /** Host-selected raw timing overrides; absent values use the renderer's defaults. */\n screenReaderPacing?: IScreenReaderPacingOverrides;\n /** Host-selected cursor and turn-mark overrides; absent values use terminal detection. */\n terminalCapabilities?: ITerminalCapabilityOverrides;\n /** CLI-2004: which input turned the mode on — printed in the confirmation line. */\n screenReaderChannel?: TScreenReaderChannel | undefined;\n /** CLI-2004: mode off, but the environment suggests a reader is running ⇒ one advisory line. */\n screenReaderHint?: boolean | undefined;\n /** BEHAVIOR-2003: one watched source shared with the optional `/keybindings` command. */\n keybindingsSource?: IKeybindingsSource;\n /**\n * SCREEN-2002: the theme catalogue this run renders from, shared with the optional `/theme`\n * command. DECLARED, not spread — see the `screenReader` note above. Absent ⇒ the built-ins.\n */\n themeRegistry?: IThemeRegistry;\n /** SCREEN-2002: reduced motion as the product shell resolved it (settings ← env ← flag). */\n reducedMotion?: boolean | undefined;\n /** SCREEN-2002: which tier decided it, when that was not the settings. */\n reducedMotionOverride?: TReducedMotionOverride | undefined;\n /**\n * SCREEN-1992: the focus-reporting override the product shell resolved from its own environment.\n * `true` forces DECSET 1004 on, `false` is the kill switch, absent ⇒ on for an interactive TTY.\n */\n focusReporting?: boolean | undefined;\n /**\n * SCREEN-1993: prompt history, resolved by the product shell. `promptHistory` is the session-side\n * writer (forwarded to the channel like `memoryStore`); `promptHistorySource` and\n * `promptHistoryProject` feed the input area's Ctrl+R search. DECLARED, not spread through — the\n * projection below copies field by field. Absent ⇒ nothing is written and `ctrl+r` is inert.\n */\n promptHistory?: IPromptHistoryOptions;\n promptHistorySource?: IPromptHistorySource;\n promptHistoryProject?: string;\n}\n\n/** Map render options to TuiInteractionChannel constructor options. */\nexport function toChannelOptions(\n options: IRenderOptions,\n resumeSessionId?: string,\n): ConstructorParameters<typeof TuiInteractionChannel>[0] {\n return {\n cwd: options.cwd,\n ...(options.livePromptTrace ? { livePromptTrace: options.livePromptTrace } : {}),\n provider: options.provider,\n ...(options.providerErrorGuidance !== undefined\n ? { providerErrorGuidance: options.providerErrorGuidance }\n : {}),\n ...(options.promptFileReferenceTag !== undefined\n ? { promptFileReferenceTag: options.promptFileReferenceTag }\n : {}),\n ...(options.modelCommandToolPrefix !== undefined\n ? { modelCommandToolPrefix: options.modelCommandToolPrefix }\n : {}),\n ...(options.subagentHookEnvironmentNames !== undefined\n ? { subagentHookEnvironmentNames: options.subagentHookEnvironmentNames }\n : {}),\n ...(options.observerFailureWarningCode !== undefined\n ? { observerFailureWarningCode: options.observerFailureWarningCode }\n : {}),\n ...(options.commandHookShell !== undefined\n ? { commandHookShell: options.commandHookShell }\n : {}),\n ...(options.bare === true ? { bare: true } : {}),\n ...(options.skipConfiguredHooks === true ? { skipConfiguredHooks: true } : {}),\n ...(options.projectAccess !== undefined ? { projectAccess: options.projectAccess } : {}),\n ...(options.projectSettingsPaths !== undefined\n ? { projectSettingsPaths: options.projectSettingsPaths }\n : {}),\n ...(options.baselinePermissionAllow !== undefined\n ? { baselinePermissionAllow: options.baselinePermissionAllow }\n : {}),\n ...(options.taskContext !== undefined ? { taskContext: options.taskContext } : {}),\n ...(options.contributionSources !== undefined\n ? { contributionSources: options.contributionSources }\n : {}),\n ...(options.skillRoots !== undefined ? { skillRoots: options.skillRoots } : {}),\n ...(options.userSettingsSources !== undefined\n ? { userSettingsSources: options.userSettingsSources }\n : {}),\n ...(options.orgPolicy !== undefined ? { orgPolicy: options.orgPolicy } : {}),\n ...(options.externalEventVerifierFactory !== undefined\n ? { externalEventVerifierFactory: options.externalEventVerifierFactory }\n : {}),\n ...(options.externalEventGrantHistory !== undefined\n ? { externalEventGrantHistory: options.externalEventGrantHistory }\n : {}),\n ...(options.providerDefinitions ? { providerDefinitions: options.providerDefinitions } : {}),\n // CLI-076: the display model id doubles as the session's model override so `--model` actually reaches\n // the provider chat call (header/status line == the model actually called).\n ...(options.modelId !== undefined ? { model: options.modelId } : {}),\n ...(options.effort !== undefined ? { effort: options.effort } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {}),\n ...(options.presetSystemPrompt !== undefined\n ? { presetSystemPrompt: options.presetSystemPrompt }\n : {}),\n ...(options.appendSystemPrompt !== undefined\n ? { appendSystemPrompt: options.appendSystemPrompt }\n : {}),\n ...(options.responseFormat !== undefined ? { responseFormat: options.responseFormat } : {}),\n ...(options.outputStyle !== undefined ? { outputStyle: options.outputStyle } : {}),\n permissionMode: options.permissionMode,\n maxTurns: options.maxTurns,\n allowedTools: options.allowedTools,\n deniedTools: options.deniedTools,\n sessionStore: options.sessionStore,\n disableSessionLoops: options.disableSessionLoops,\n resolveDefaultLoopPrompt: options.resolveDefaultLoopPrompt,\n resumeSessionId,\n forkSession: options.forkSession,\n sessionName: options.sessionName,\n backgroundTaskRunners: options.backgroundTaskRunners,\n ...(options.toolCallHandoff !== undefined ? { toolCallHandoff: options.toolCallHandoff } : {}),\n subagentRunnerFactory: options.subagentRunnerFactory,\n ...(options.agentDefinitions !== undefined\n ? { agentDefinitions: options.agentDefinitions }\n : {}),\n ...(options.agentDefinitionRoots !== undefined\n ? { agentDefinitionRoots: options.agentDefinitionRoots }\n : {}),\n ...(options.pluginDirectories !== undefined\n ? { pluginDirectories: options.pluginDirectories }\n : {}),\n ...(options.additionalTools !== undefined ? { additionalTools: options.additionalTools } : {}),\n ...(options.defaultTools !== undefined ? { defaultTools: options.defaultTools } : {}),\n ...(options.sandboxClient !== undefined ? { sandboxClient: options.sandboxClient } : {}),\n commandModules: options.commandModules,\n commandHostAdapters: options.commandHostAdapters,\n shellExec: options.shellExec,\n remoteCommandPolicy: options.remoteCommandPolicy,\n transportRegistry: options.transportRegistry,\n bindTransports: options.bindTransports,\n language: options.language,\n reloadPluginCommandSource: options.reloadPluginCommandSource,\n agentName: options.agentName,\n activePresetId: options.activePresetId,\n persona: options.persona,\n enableParallelSubagents: options.enableParallelSubagents,\n selfVerification: options.selfVerification,\n // SELFHOST-008 P6: forward the surface-resolved memory fields (absent ⇒ OFF).\n ...(options.memoryStore ? { memoryStore: options.memoryStore } : {}),\n ...(options.automaticMemory ? { automaticMemory: options.automaticMemory } : {}),\n ...(options.recallMemory ? { recallMemory: options.recallMemory } : {}),\n // CLI-2004: declared above AND projected here — the hand-maintained copy is the exact place an\n // undeclared option disappears, so both halves are asserted by one test.\n ...(options.screenReader !== undefined ? { screenReader: options.screenReader } : {}),\n // SCREEN-1993: the session-side writer (absent ⇒ nothing is recorded).\n promptHistory: options.promptHistory,\n };\n}\n\nexport async function waitForRenderAndStop<TResult>(\n waitUntilExit: () => Promise<TResult>,\n getActiveChannel: () => Pick<ITuiAppChannelPort, 'stop'> | undefined,\n): Promise<void> {\n let renderFailure: Error | undefined;\n try {\n await waitUntilExit();\n } catch (cause) {\n renderFailure = cause instanceof Error ? cause : new Error(String(cause));\n }\n let stopFailure: Error | undefined;\n try {\n await getActiveChannel()?.stop();\n } catch (cause) {\n stopFailure = cause instanceof Error ? cause : new Error(String(cause));\n }\n if (renderFailure !== undefined && stopFailure !== undefined) {\n throw new AggregateError([renderFailure, stopFailure], 'TUI render and teardown both failed.');\n }\n if (renderFailure !== undefined) throw renderFailure;\n if (stopFailure !== undefined) throw stopFailure;\n}\n\nexport async function renderApp(options: IRenderOptions): Promise<void> {\n // ERR-001 / Library Neutrality Rule: NO process-level error policy here — process survival\n // is the product assembly's boundary (agent-cli installs the guards via onChannelReady).\n try {\n await options.keybindingsSource?.start();\n await renderStartedApp(options, (services) => ({\n createChannel: createInProcessChannelFactory(options, services),\n }));\n } finally {\n options.keybindingsSource?.dispose();\n }\n}\n\n/**\n * What the full TUI needs to attach to a session a host runs (a daemon) instead of building one.\n * Only presentation options: everything else belongs to the host's session.\n */\nexport interface IRenderAttachedAppOptions extends Pick<\n IRenderOptions,\n | 'cwd'\n | 'productDisplayName'\n | 'version'\n | 'cliAdapter'\n | 'screenReader'\n | 'screenReaderChannel'\n | 'screenReaderHint'\n | 'screenReaderPacing'\n | 'terminalCapabilities'\n | 'keybindingsSource'\n | 'themeRegistry'\n | 'reducedMotion'\n | 'reducedMotionOverride'\n | 'focusReporting'\n | 'promptHistorySource'\n | 'promptHistoryProject'\n> {\n /** The session protocol connection; closing it belongs to the caller. */\n readonly connection: IAttachedSessionConnection;\n /** Shown as the session's label until the session reports its own name. */\n readonly sessionLabel: string;\n /** This terminal's driver id, from the attach handshake. */\n readonly driverId: string;\n /** Commands this terminal runs itself (`/shell`, `/theme`, ...); the host never sees them. */\n readonly clientCommands?: ITuiClientCommands;\n /**\n * How this terminal is on the session: `'drive'` (the default) sends prompts and answers questions;\n * `'observe'` only watches, and the host refuses anything else from it.\n */\n readonly mode?: 'drive' | 'observe';\n /** Print the screen-reader line; false when this process already printed it. Default true. */\n readonly announce?: boolean;\n}\n\n/**\n * Render the full TUI on a session the terminal is attached to over the session protocol. Leaving\n * only detaches: the session keeps running. Resolves `'user'` when the user leaves (`/exit`,\n * Ctrl-C) and `'closed'` when the connection closed.\n */\nexport async function renderAttachedApp(\n options: IRenderAttachedAppOptions,\n): Promise<TAttachedSessionEnd> {\n let end: TAttachedSessionEnd = 'user';\n let endApp: () => void = () => undefined;\n const ended = new Promise<void>((resolve) => {\n endApp = resolve;\n });\n try {\n await options.keybindingsSource?.start();\n await renderStartedApp(options, (services) => {\n let channel: WireTuiChannel | undefined;\n return {\n // One channel for the App's life: a session switch is the host's, and the channel follows it.\n createChannel: () =>\n (channel ??= new WireTuiChannel({\n connection: options.connection,\n driverId: options.driverId,\n sessionName: options.sessionLabel,\n role: options.mode ?? 'drive',\n terminalHandoff: services.terminalHandoff,\n attention: services.attention,\n ...(options.clientCommands !== undefined\n ? { clientCommands: options.clientCommands, cwd: options.cwd }\n : { clientCommands: undefined }),\n onEnd: (reason) => {\n end = reason;\n endApp();\n },\n })),\n requestSessionSwitch: async (sessionId) => channel?.requestSessionSwitch(sessionId),\n ended,\n };\n });\n } finally {\n options.keybindingsSource?.dispose();\n }\n return end;\n}\n\n/** Presentation options the App shell reads, whichever kind of channel it renders. */\ntype TAppShellOptions = Pick<\n IRenderOptions,\n | 'cwd'\n | 'productDisplayName'\n | 'modelCommandToolPrefix'\n | 'providerOverride'\n | 'providerType'\n | 'modelId'\n | 'permissionMode'\n | 'version'\n | 'sessionStore'\n | 'resumeSessionId'\n | 'showSessionPickerOnStart'\n | 'initialInput'\n | 'initialInputOrigin'\n | 'startupUpdateNotice'\n | 'transportRegistry'\n | 'commandHostAdapters'\n | 'cliAdapter'\n | 'promptHistorySource'\n | 'promptHistoryProject'\n | 'themeRegistry'\n | 'reducedMotion'\n | 'reducedMotionOverride'\n | 'screenReader'\n | 'screenReaderPacing'\n | 'terminalCapabilities'\n | 'screenReaderChannel'\n | 'screenReaderHint'\n | 'keybindingsSource'\n | 'focusReporting'\n> & {\n /** Print the screen-reader line. Default true; false when this process already printed it. */\n readonly announce?: boolean;\n};\n\n/** Terminal-wide services a channel is built with; they outlive every channel. */\nexport interface IChannelServices {\n readonly terminalHandoff: TerminalHandoffController;\n readonly attention: IAttentionSource;\n}\n\n/** Where the App's channels come from, and how a session switch and an app end are decided. */\ninterface IAppChannelComposition {\n readonly createChannel: (resumeSessionId?: string) => ITuiAppChannelPort;\n /** Present when the host switches sessions and the channel follows it. */\n readonly requestSessionSwitch?: (sessionId: string) => Promise<void>;\n /** Settles when the app ends without the user closing it (the connection closed). */\n readonly ended?: Promise<void>;\n}\n\n/**\n * The in-process factory: every channel builds its own session, and its own checkpoint store, from\n * the render options. A resumed session's store restores its active branch from the persisted pointer.\n */\nexport function createInProcessChannelFactory(\n options: IRenderOptions,\n services: IChannelServices,\n): (resumeSessionId?: string) => ITuiAppChannelPort {\n // Issue #3081: the move is announced by the FIRST session only — a later switch to another\n // session (a `/fork` attach) did not move anywhere.\n let pendingWorkspaceMovedFrom = options.workspaceMovedFrom;\n return (resumeSessionId) => {\n const workspaceMovedFrom = pendingWorkspaceMovedFrom;\n pendingWorkspaceMovedFrom = undefined;\n const channel = new TuiInteractionChannel({\n ...toChannelOptions(options, resumeSessionId),\n ...(options.createEditCheckpointStore !== undefined\n ? { editCheckpointStore: options.createEditCheckpointStore() }\n : {}),\n ...(workspaceMovedFrom !== undefined ? { workspaceMovedFrom } : {}),\n terminalHandoff: services.terminalHandoff,\n attention: services.attention,\n });\n // Expose each live channel (incl. session-switch re-creations) to the embedding product,\n // e.g. for process-level error routing (ERR-001 G1).\n options.onChannelReady?.(channel);\n return channel;\n };\n}\n\nasync function renderStartedApp(\n options: TAppShellOptions,\n compose: (services: IChannelServices) => IAppChannelComposition,\n): Promise<void> {\n // CLI-2004: one resolved boolean drives Ink's own screen-reader support AND the React context\n // every component reads. Both are set here so they can never disagree.\n const screenReader = options.screenReader === true;\n if (options.announce !== false) {\n writeScreenReaderAnnouncement({\n enabled: screenReader,\n channel: options.screenReaderChannel,\n hint: options.screenReaderHint,\n });\n }\n const pacing = resolvePacing({ enabled: screenReader, overrides: options.screenReaderPacing });\n // SCREEN-2670: the pre-write park. Constructed only when the mode is on AND the interval is\n // non-zero, so with the mode off `stdout` is not passed at all and Ink defaults to\n // `process.stdout` — the object it keys its instance map by — exactly as today.\n const parked =\n screenReader && pacing.preparkMs > 0\n ? createParkedStdout({ stdout: process.stdout, preparkMs: pacing.preparkMs })\n : undefined;\n\n // SCREEN-006: chalk (ink's styling engine) does not implement the NO_COLOR convention itself\n // (verified: chalk 5's vendored supports-color reads only FORCE_COLOR/TTY/TERM), so on a real\n // TTY `NO_COLOR=1` would still color every component. Sync chalk once with the package's single\n // color gate (`terminal-capabilities.ts` — the SSOT that DOES honor NO_COLOR) so gate-off means\n // zero SGR color output. Gate-on changes nothing: chalk's own level detection stays authoritative.\n if (!isInteractiveColorTerminal()) {\n chalk.level = 0;\n }\n\n // TERM-002: one terminal-handoff controller per process (one Ink instance / App). Shared across\n // channel re-creations (session switch) so the handoff capability survives a session swap.\n const handoffController = new TerminalHandoffController();\n\n // SCREEN-1992: focus reporting is negotiated once for the process; the tracker outlives channels.\n // Screen-reader mode keeps the recap (a plain notice line) — only the row countdown tick is off.\n const focusReportingSupported = supportsFocusReporting({ override: options.focusReporting });\n const focusReporting = createFocusReportingWriter({ supported: () => focusReportingSupported });\n const attention = new AttentionTracker({\n focusReporting: focusReportingSupported,\n now: Date.now,\n });\n handoffController.setTerminalModeHooks({\n preSuspend: async () => {\n focusReporting.disable();\n // SCREEN-2670: nothing parked may land on top of the child's output.\n await parked?.drain();\n },\n postResume: () => focusReporting.enable(),\n });\n\n // Concrete framework creation has one composition boundary. React receives only the bounded port;\n // App owns which narrowed channel is active, while each channel owns its own lifecycle.\n const composition = compose({ terminalHandoff: handoffController, attention });\n let activeChannel: ITuiAppChannelPort | undefined;\n const createChannel = (resumeSessionId?: string): ITuiAppChannelPort => {\n activeChannel = composition.createChannel(resumeSessionId);\n return activeChannel;\n };\n\n // The startup quiet period sits between the confirmation line and the first frame, so a reader\n // finishes announcing the mode before the prompt lands. A keypress ends it early; `0` skips it.\n await awaitStartupQuietPeriod(pacing.startupQuietMs, process.stdin);\n\n // The filtering proxy is attached only now: the quiet period consumed its own keystroke above, and\n // from here on every byte reaches Ink through the proxy, minus the focus sequences.\n const stdin = new FocusReportingStdin(process.stdin, {\n negotiated: () => focusReporting.negotiated,\n onFocusIn: () => attention.focusIn(),\n onFocusOut: () => attention.focusOut(),\n onKeystroke: () => attention.keystroke(),\n });\n process.stdin.resume();\n // Requested only now, so a focus event the terminal answers with lands in the proxy, never in\n // the quiet period's keypress wait; the tracker's idle source runs until the first one arrives.\n focusReporting.enable();\n attention.start();\n\n const pacingPort = parked === undefined ? undefined : toPacingPort(parked);\n const tree = (\n <ProductDisplayNameProvider\n name={options.productDisplayName}\n modelCommandToolPrefix={options.modelCommandToolPrefix}\n >\n <KeybindingsProvider source={options.keybindingsSource}>\n <ScreenReaderProvider enabled={screenReader}>\n <TerminalCapabilitiesProvider overrides={options.terminalCapabilities}>\n <App\n cwd={options.cwd}\n createChannel={createChannel}\n {...(composition.requestSessionSwitch !== undefined\n ? { requestSessionSwitch: composition.requestSessionSwitch }\n : {})}\n providerOverride={options.providerOverride}\n providerType={options.providerType}\n modelId={options.modelId}\n permissionMode={options.permissionMode}\n version={options.version}\n sessionStore={options.sessionStore}\n resumeSessionId={options.resumeSessionId}\n showSessionPickerOnStart={options.showSessionPickerOnStart}\n initialInput={options.initialInput}\n initialInputOrigin={options.initialInputOrigin}\n startupUpdateNotice={options.startupUpdateNotice}\n transportRegistry={options.transportRegistry}\n pluginAdapter={options.commandHostAdapters?.plugin}\n cliAdapter={options.cliAdapter}\n promptHistorySource={options.promptHistorySource}\n promptHistoryProject={options.promptHistoryProject}\n themeRegistry={options.themeRegistry}\n reducedMotion={options.reducedMotion}\n reducedMotionOverride={options.reducedMotionOverride}\n />\n </TerminalCapabilitiesProvider>\n </ScreenReaderProvider>\n </KeybindingsProvider>\n </ProductDisplayNameProvider>\n );\n const instance = render(\n pacingPort === undefined ? (\n tree\n ) : (\n <ScreenReaderPacingProvider port={pacingPort}>{tree}</ScreenReaderPacingProvider>\n ),\n {\n exitOnCtrlC: false,\n isScreenReaderEnabled: screenReader,\n stdin: stdin.asInkStdin(),\n ...(parked === undefined ? {} : { stdout: parked.asInkStdout() }),\n },\n );\n // The controller needs the Ink instance to clear the frame before a handoff.\n handoffController.setInkInstance(instance);\n // Unmounting resolves `waitUntilExit`, so an app the host ended leaves the way a user exit does.\n void composition.ended?.then(() => instance.unmount());\n try {\n await waitForRenderAndStop(\n () => instance.waitUntilExit(),\n () => activeChannel,\n );\n } finally {\n // The last frame of a session must not be the one the park loses: drain before restoring.\n await parked?.flush();\n // Leave the terminal as it was found: no focus sequences after exit, no reader on stdin.\n handoffController.setTerminalModeHooks(undefined);\n focusReporting.disable();\n attention.dispose();\n stdin.detach();\n process.stdin.pause();\n }\n}\n","import { Box, render, useApp, useInput, useStdout } from 'ink';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport stringWidth from 'string-width';\n\nimport { useNumberedSelection } from './hooks/useNumberedSelection.js';\nimport { formatNumberedSelectionPrompt, numberedRowPrefix } from './numbered-list.js';\nimport { Text } from './SafeText.js';\nimport {\n writeScreenReaderAnnouncement,\n type TScreenReaderChannel,\n} from './screen-reader-announcement.js';\nimport { ScreenReaderProvider, useScreenReader } from './screen-reader-context.js';\n\nexport interface ISupervisedViewRow {\n readonly id: string;\n readonly liveness: 'alive' | 'dead' | 'unknown';\n readonly control: 'available' | 'unavailable';\n readonly activity: 'working' | 'needs-input' | 'idle' | 'unknown';\n readonly nextLoopAt?: string;\n readonly name?: string;\n readonly cwd?: string;\n readonly pr?: {\n readonly url: string;\n readonly host: string;\n readonly number: number;\n readonly kind: 'pull' | 'merge-request';\n };\n readonly problem?: 'invalid-registration';\n /** The verified registration generation; a row without one never offers an action. Never rendered. */\n readonly generation?: string;\n}\n\n/** An attach the user confirmed in the view, bound to the process start the row showed. */\nexport interface ISupervisedAttachRequest {\n readonly id: string;\n readonly generation: string;\n readonly mode: 'drive' | 'observe';\n /** How the view was grouped, so it can reopen the same way after the attach. */\n readonly groupByDirectory: boolean;\n}\n\n/** How the view ended: closed, or handed to the host to attach and then come back. */\nexport type TSupervisedViewExit =\n | { readonly kind: 'closed' }\n | ({ readonly kind: 'attach' } & ISupervisedAttachRequest);\n\nconst ROLE: Readonly<Record<ISupervisedAttachRequest['mode'], string>> = {\n drive: 'drive (send prompts, answer its questions)',\n observe: 'observe (read only)',\n};\n\nexport interface ISupervisedSessionViewProps {\n readonly loadRows: (signal: AbortSignal) => Promise<readonly ISupervisedViewRow[]>;\n /** Called once the user confirmed; the view then closes so the host can attach this terminal. */\n readonly onAttach?: (request: ISupervisedAttachRequest) => void;\n /** Receives the generation the row displayed, so a session restarted under the same id is refused. */\n readonly onStop?: (id: string, generation: string) => Promise<void>;\n readonly onStart?: () => Promise<string>;\n readonly onOpenPr?: (id: string, url: string, generation: string) => Promise<void>;\n readonly filteredByCwd?: boolean;\n readonly filteredByName?: boolean;\n readonly filteredByPr?: boolean;\n readonly stateFilter?: TGroup;\n readonly refreshMs?: number;\n /** The row to select first, when it is still listed — the one an attach left from. */\n readonly initialSelectedId?: string;\n readonly initialGroupByDirectory?: boolean;\n}\n\nconst GROUP_ORDER = ['needs-input', 'working', 'idle', 'unknown', 'unverified', 'dead'] as const;\ntype TGroup = (typeof GROUP_ORDER)[number];\ntype TDisplayLine =\n | { readonly kind: 'group'; readonly label: string }\n | { readonly kind: 'row'; readonly row: ISupervisedViewRow; readonly index: number };\n\nfunction isControllable(\n row: ISupervisedViewRow | undefined,\n): row is ISupervisedViewRow & { readonly generation: string } {\n return (\n row?.liveness === 'alive' && row.control === 'available' && row.generation !== undefined\n );\n}\n\nfunction groupOf(row: ISupervisedViewRow): TGroup {\n if (row.liveness === 'dead') return 'dead';\n if (!isControllable(row)) return 'unverified';\n return row.activity;\n}\n\nfunction sortedRows(rows: readonly ISupervisedViewRow[]): readonly ISupervisedViewRow[] {\n return [...rows].sort(\n (a, b) =>\n GROUP_ORDER.indexOf(groupOf(a)) - GROUP_ORDER.indexOf(groupOf(b)) || a.id.localeCompare(b.id),\n );\n}\n\nfunction sortedDirectoryRows(rows: readonly ISupervisedViewRow[]): readonly ISupervisedViewRow[] {\n return [...rows].sort(\n (a, b) =>\n (a.cwd === undefined ? 1 : 0) - (b.cwd === undefined ? 1 : 0) ||\n (a.cwd ?? '').localeCompare(b.cwd ?? '') ||\n GROUP_ORDER.indexOf(groupOf(a)) - GROUP_ORDER.indexOf(groupOf(b)) ||\n a.id.localeCompare(b.id),\n );\n}\n\nfunction uniqueDirectorySuffix(\n cwd: string,\n directories: readonly string[],\n budget: number,\n): string {\n if (cwd === '/') return cwd;\n let longestSharedSuffix = 0;\n for (const other of directories) {\n if (other === cwd) continue;\n let shared = 0;\n while (\n shared < cwd.length &&\n shared < other.length &&\n cwd[cwd.length - shared - 1] === other[other.length - shared - 1]\n )\n shared++;\n longestSharedSuffix = Math.max(longestSharedSuffix, shared);\n }\n const basenameLength = cwd.split(/[\\\\/]/u).filter(Boolean).at(-1)?.length ?? cwd.length;\n const uniqueLength = Math.min(cwd.length, Math.max(basenameLength, longestSharedSuffix + 3));\n const suffixStart = cwd.length - uniqueLength;\n const boundary = Math.max(\n cwd.lastIndexOf('/', suffixStart - 1),\n cwd.lastIndexOf('\\\\', suffixStart - 1),\n );\n const componentSuffix = cwd.slice(boundary + 1);\n return stringWidth(displayPath(componentSuffix)) <= budget\n ? componentSuffix\n : cwd.slice(suffixStart);\n}\n\nfunction pathTokens(path: string): readonly string[] {\n return Array.from(path, (character) =>\n /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/u.test(character)\n ? `\\\\u{${character.codePointAt(0)!.toString(16)}}`\n : character,\n );\n}\n\nfunction displayPath(path: string): string {\n return pathTokens(path).join('');\n}\n\nfunction compactPath(path: string, budget: number): string {\n const safe = pathTokens(path);\n if (stringWidth(safe.join('')) <= budget) return safe.join('');\n const headBudget = Math.ceil((budget - 1) / 2);\n const tailBudget = budget - headBudget - 1;\n let head = '';\n for (const token of safe) {\n if (stringWidth(head) + stringWidth(token) > headBudget) break;\n head += token;\n }\n let tail = '';\n for (const token of [...safe].reverse()) {\n if (stringWidth(tail) + stringWidth(token) > tailBudget) break;\n tail = token + tail;\n }\n return `${head}…${tail}`;\n}\n\nfunction sameRows(a: readonly ISupervisedViewRow[], b: readonly ISupervisedViewRow[]): boolean {\n return (\n a.length === b.length &&\n a.every((row, index) => {\n const other = b[index];\n return (\n other !== undefined &&\n row.id === other.id &&\n row.liveness === other.liveness &&\n row.control === other.control &&\n row.activity === other.activity &&\n row.problem === other.problem &&\n row.nextLoopAt === other.nextLoopAt &&\n row.name === other.name &&\n row.cwd === other.cwd &&\n row.pr?.url === other.pr?.url &&\n row.generation === other.generation\n );\n })\n );\n}\n\nfunction loopWaitLabel(nextLoopAt: string, observedAtMs: number): string {\n const remaining = Date.parse(nextLoopAt) - observedAtMs;\n if (!Number.isFinite(remaining)) return '';\n if (remaining <= 0) return 'loop eligible now';\n return remaining < 60_000\n ? 'loop eligible in <1m'\n : `loop eligible in ${Math.ceil(remaining / 60_000)}m`;\n}\n\nexport default function SupervisedSessionView({\n loadRows,\n onStop,\n onStart,\n onOpenPr,\n onAttach,\n filteredByCwd = false,\n filteredByName = false,\n filteredByPr = false,\n stateFilter,\n refreshMs = 2_000,\n initialSelectedId,\n initialGroupByDirectory = false,\n}: ISupervisedSessionViewProps): React.ReactElement {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const columns = Math.max(12, stdout.columns ?? 80);\n const screenReader = useScreenReader();\n const [rows, setRows] = useState<readonly ISupervisedViewRow[]>([]);\n const [observedAtMs, setObservedAtMs] = useState(Date.now);\n const [status, setStatus] = useState<'loading' | 'ready' | 'unavailable'>('loading');\n const [chosenId, setSelectedId] = useState<string | undefined>(initialSelectedId);\n const [showHelp, setShowHelp] = useState(false);\n const [groupByDirectory, setGroupByDirectory] = useState(initialGroupByDirectory);\n // One confirmation at a time, for the one registration the row showed when the key was pressed.\n const [confirmStop, setConfirmStop] = useState<\n | {\n readonly id: string;\n readonly generation: string;\n readonly action: 'stop' | 'attach';\n readonly mode?: ISupervisedAttachRequest['mode'];\n }\n | undefined\n >();\n const confirmStopId = confirmStop?.id;\n const [attachStatus, setAttachStatus] = useState<'idle' | 'unavailable'>('idle');\n const [stopStatus, setStopStatus] = useState<\n 'idle' | 'unavailable' | 'stopping' | 'stopped' | 'failed'\n >('idle');\n const [lastStoppedId, setLastStoppedId] = useState<string | undefined>();\n const stoppingRef = useRef(false);\n const [startStatus, setStartStatus] = useState<'idle' | 'starting' | 'started' | 'failed'>(\n 'idle',\n );\n const [lastStartedId, setLastStartedId] = useState<string | undefined>();\n const startingRef = useRef(false);\n const [prOpenStatus, setPrOpenStatus] = useState<\n 'idle' | 'opening' | 'opened' | 'failed' | 'unavailable'\n >('idle');\n const openingPrRef = useRef(false);\n const mountedRef = useRef(true);\n const ordered = useMemo(() => {\n const filtered = rows.filter(\n (row) => stateFilter === undefined || groupOf(row) === stateFilter,\n );\n return groupByDirectory ? sortedDirectoryRows(filtered) : sortedRows(filtered);\n }, [rows, stateFilter, groupByDirectory]);\n const displayLines = useMemo((): readonly TDisplayLine[] => {\n const lines: TDisplayLine[] = [];\n const directories = [\n ...new Set(ordered.map((row) => row.cwd).filter((cwd): cwd is string => cwd !== undefined)),\n ];\n let previousGroup: string | undefined;\n let directoryNumber = 0;\n ordered.forEach((row, index) => {\n const group = groupByDirectory ? (row.cwd ?? 'unverified') : groupOf(row);\n if (group !== previousGroup) {\n const label = groupByDirectory\n ? row.cwd === undefined\n ? 'Directory: unverified'\n : (() => {\n const prefix = `Dir ${++directoryNumber}: `;\n const budget = Math.max(4, columns - prefix.length);\n const suffix = compactPath(\n uniqueDirectorySuffix(row.cwd, directories, budget),\n budget,\n );\n const full = `${prefix}${suffix} — ${displayPath(row.cwd)}`;\n return screenReader || stringWidth(full) <= columns ? full : `${prefix}${suffix}`;\n })()\n : `${group}:`;\n lines.push({ kind: 'group', label });\n }\n lines.push({ kind: 'row', row, index });\n previousGroup = group;\n });\n return lines;\n }, [ordered, groupByDirectory, columns, screenReader]);\n\n useEffect(() => {\n const controller = new AbortController();\n let mounted = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const poll = async (): Promise<void> => {\n try {\n const next = sortedRows(await loadRows(controller.signal));\n if (!mounted || controller.signal.aborted) return;\n const nowMs = Date.now();\n setRows((previous) => (sameRows(previous, next) ? previous : next));\n setObservedAtMs((previous) =>\n next.some(\n (row) =>\n row.nextLoopAt !== undefined &&\n loopWaitLabel(row.nextLoopAt, previous) !== loopWaitLabel(row.nextLoopAt, nowMs),\n )\n ? nowMs\n : previous,\n );\n setStatus('ready');\n } catch {\n if (mounted && !controller.signal.aborted) setStatus('unavailable');\n } finally {\n if (mounted && !controller.signal.aborted)\n timer = setTimeout(() => {\n void poll();\n }, refreshMs);\n }\n };\n void poll();\n return () => {\n mounted = false;\n if (timer !== undefined) clearTimeout(timer);\n controller.abort();\n };\n }, [loadRows, refreshMs]);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Derived during render, so no frame ever shows loaded rows without a selection. The effect then\n // records the fallback with a functional update, so it never overwrites a choice made meanwhile,\n // and the selected row stays selected when regrouping reorders the list.\n const selectedId =\n chosenId !== undefined && ordered.some((row) => row.id === chosenId)\n ? chosenId\n : ordered[0]?.id;\n useEffect(() => {\n // Before the first rows arrive there is nothing to fall back to; the choice waits for them.\n setSelectedId((current) =>\n ordered.length === 0 ||\n (current !== undefined && ordered.some((row) => row.id === current))\n ? current\n : ordered[0]?.id,\n );\n }, [ordered]);\n\n useInput((input, key) => {\n if (stoppingRef.current) return;\n if (\n openingPrRef.current &&\n input !== 'q' &&\n !(!screenReader && key.escape) &&\n !(key.ctrl && input === 'c')\n )\n return;\n if (\n startingRef.current &&\n input !== 'q' &&\n !(!screenReader && key.escape) &&\n !(key.ctrl && input === 'c')\n )\n return;\n if (confirmStopId !== undefined) {\n if (input === 'n' || key.escape) {\n setConfirmStop(undefined);\n return;\n }\n if (input === 'y' && confirmStop?.action === 'attach') {\n const target = confirmStop;\n const row = ordered.find((candidate) => candidate.id === target.id);\n setConfirmStop(undefined);\n if (\n status !== 'ready' ||\n !isControllable(row) ||\n row.generation !== target.generation ||\n target.mode === undefined ||\n onAttach === undefined\n ) {\n setAttachStatus('unavailable');\n return;\n }\n onAttach({ id: target.id, generation: target.generation, mode: target.mode, groupByDirectory });\n exit();\n return;\n }\n if (input === 'y') {\n const target = confirmStop;\n const row = ordered.find((candidate) => candidate.id === target?.id);\n setConfirmStop(undefined);\n // The confirmation names one registration; a restart under the same id is a different session.\n if (\n status !== 'ready' ||\n target === undefined ||\n !isControllable(row) ||\n row.generation !== target.generation ||\n onStop === undefined\n ) {\n setStopStatus('unavailable');\n return;\n }\n stoppingRef.current = true;\n setStopStatus('stopping');\n void onStop(target.id, target.generation)\n .then(() => {\n if (!mountedRef.current) return;\n setLastStoppedId(target.id);\n setStopStatus('stopped');\n })\n .catch(() => {\n if (mountedRef.current) setStopStatus('failed');\n })\n .finally(() => {\n stoppingRef.current = false;\n });\n }\n return;\n }\n if (input === 'q' || (!screenReader && key.escape) || (key.ctrl && input === 'c')) {\n exit();\n return;\n }\n if (input === '?') {\n setShowHelp((value) => !value);\n return;\n }\n if (input === 'g') {\n numbered.clear();\n setGroupByDirectory((value) => !value);\n return;\n }\n if (input === 'n' && onStart !== undefined) {\n startingRef.current = true;\n setStartStatus('starting');\n void Promise.resolve()\n .then(onStart)\n .then((id) => {\n if (!mountedRef.current) return;\n setLastStartedId(id);\n setStartStatus('started');\n })\n .catch(() => {\n if (mountedRef.current) setStartStatus('failed');\n })\n .finally(() => {\n startingRef.current = false;\n });\n return;\n }\n if ((input === 'a' || input === 'p') && onAttach !== undefined) {\n const row = ordered.find((candidate) => candidate.id === selectedId);\n if (status !== 'ready' || !isControllable(row)) {\n setAttachStatus('unavailable');\n } else {\n setAttachStatus('idle');\n setStopStatus('idle');\n setConfirmStop({\n id: row.id,\n generation: row.generation,\n action: 'attach',\n mode: input === 'a' ? 'drive' : 'observe',\n });\n }\n return;\n }\n if (input === 'o' && onOpenPr !== undefined) {\n const row = ordered.find((candidate) => candidate.id === selectedId);\n if (status !== 'ready' || !isControllable(row) || !row.pr) {\n setPrOpenStatus('unavailable');\n return;\n }\n openingPrRef.current = true;\n setPrOpenStatus('opening');\n void onOpenPr(row.id, row.pr.url, row.generation)\n .then(() => {\n if (mountedRef.current) setPrOpenStatus('opened');\n })\n .catch(() => {\n if (mountedRef.current) setPrOpenStatus('failed');\n })\n .finally(() => {\n openingPrRef.current = false;\n });\n return;\n }\n if (input === 's' && onStop !== undefined) {\n const row = ordered.find((candidate) => candidate.id === selectedId);\n if (status !== 'ready' || !isControllable(row)) {\n setStopStatus('unavailable');\n } else {\n setStopStatus('idle');\n setAttachStatus('idle');\n setConfirmStop({ id: row.id, generation: row.generation, action: 'stop' });\n }\n return;\n }\n if (screenReader || ordered.length === 0) return;\n if (key.upArrow || key.downArrow) {\n const index = Math.max(\n 0,\n ordered.findIndex((row) => row.id === selectedId),\n );\n const next = Math.max(0, Math.min(ordered.length - 1, index + (key.downArrow ? 1 : -1)));\n setSelectedId(ordered[next]?.id);\n }\n });\n\n const numbered = useNumberedSelection({\n enabled: screenReader && confirmStopId === undefined && stopStatus !== 'stopping',\n itemCount: ordered.length,\n cancellable: true,\n repeatable: true,\n onSelect: (index) => {\n setSelectedId(ordered[index]?.id);\n setStopStatus('idle');\n },\n onCancel: () => {\n if (!stoppingRef.current) exit();\n },\n });\n\n const selectedRow = ordered.find((row) => row.id === selectedId);\n const selectedName =\n status === 'ready' && isControllable(selectedRow) ? selectedRow.name : undefined;\n const selectedLoopStatus =\n status === 'ready' &&\n isControllable(selectedRow) &&\n selectedRow.activity === 'idle' &&\n selectedRow.nextLoopAt\n ? loopWaitLabel(selectedRow.nextLoopAt, observedAtMs)\n : '';\n const selectedPr = status === 'ready' && isControllable(selectedRow) ? selectedRow.pr : undefined;\n\n const height = Math.max(8, stdout.rows ?? 24);\n const helpLines = [\n 'Keys:',\n screenReader ? 'Number+Enter Select' : '↑/↓ Select',\n 's Request stop',\n 'g Group state/dir',\n ...(onStart === undefined ? [] : ['n New session']),\n ...(onAttach === undefined ? [] : ['a Attach (drive)', 'p Peek (read only)']),\n ...(onOpenPr === undefined ? [] : ['o Open linked PR']),\n 'y Confirm',\n 'n/Esc Cancel',\n 'q/Esc/Ctrl+C Close',\n '? Toggle help',\n 'Activity ≠ liveness',\n 'Idle ≠ attach-ready',\n 'Close keeps sessions',\n ];\n // Reserve all fixed chrome plus both possible overflow indicators before choosing row lines.\n const helpVisible = showHelp && confirmStopId === undefined && stopStatus !== 'stopping';\n const fixedLines =\n 2 +\n (stateFilter === undefined ? 0 : 1) +\n (status === 'loading' ||\n status === 'unavailable' ||\n (status === 'ready' && ordered.length === 0)\n ? 1\n : 0) +\n (selectedId === undefined ? 0 : 1) +\n (selectedName === undefined ? 0 : 1) +\n (selectedPr === undefined ? 0 : 2) +\n (selectedLoopStatus ? 1 : 0) +\n (prOpenStatus === 'idle' ? 0 : 1) +\n (startStatus === 'idle' ? 0 : 1) +\n (confirmStopId !== undefined ? (screenReader ? 1 : 2) : stopStatus !== 'idle' ? 1 : 0) +\n (confirmStopId === undefined && attachStatus !== 'idle' ? 1 : 0) +\n 1 +\n (helpVisible ? helpLines.length : 0) +\n 2;\n const viewport = Math.max(1, height - fixedLines);\n const selectedLine = Math.max(\n 0,\n displayLines.findIndex((line) => line.kind === 'row' && line.row.id === selectedId),\n );\n const start = Math.min(\n Math.max(0, selectedLine - Math.floor(viewport / 2)),\n Math.max(0, displayLines.length - viewport),\n );\n const visible = screenReader ? displayLines : displayLines.slice(start, start + viewport);\n const chromeWrap = screenReader ? {} : { wrap: 'truncate-end' as const };\n // Attach and peek are offered only for a row this terminal could actually attach to.\n const attachable = onAttach !== undefined && status === 'ready' && isControllable(selectedRow);\n const footer =\n stopStatus === 'stopping'\n ? 'Stop in progress; wait for result.'\n : confirmStopId !== undefined\n ? `Confirm ${confirmStop?.action === 'attach' ? 'attach' : 'stop'} or cancel before closing.`\n : screenReader\n ? `Type a number and Enter to select; s Stop;${attachable ? ' a Attach; p Peek;' : ''}${onStart ? ' n New;' : ''}${onOpenPr ? ' o Open PR;' : ''} g Group; Escape to close; ? Help.`\n : `↑↓ Navigate s Stop${attachable ? ' a Attach p Peek' : ''}${onStart ? ' n New' : ''}${onOpenPr ? ' o Open PR' : ''} g Group ? Help q/Esc Close`;\n const confirmQuestion = (id: string): string =>\n confirmStop?.action === 'attach' && confirmStop.mode !== undefined\n ? `${confirmStop.mode === 'drive' ? 'Attach to' : 'Peek at'} ${id} to ${ROLE[confirmStop.mode]}?`\n : `Stop ${id}?`;\n return (\n <Box flexDirection=\"column\" {...(screenReader ? {} : { height })}>\n <Text {...chromeWrap}>\n {filteredByCwd\n ? 'Background sessions in selected directory'\n : 'Background sessions across projects'}\n {filteredByName ? ' · name filter active' : ''}\n {filteredByPr ? ' · PR filter active' : ''}\n </Text>\n <Text {...chromeWrap}>\n {ordered.length} supervised session(s). Foreground peers and saved records are separate.\n </Text>\n {stateFilter !== undefined && <Text {...chromeWrap}>State: {stateFilter}</Text>}\n {status === 'loading' && <Text {...chromeWrap}>Loading supervised sessions...</Text>}\n {status === 'unavailable' && (\n <Text {...chromeWrap}>\n Supervised session discovery unavailable; last verified rows remain below.\n </Text>\n )}\n {status === 'ready' && ordered.length === 0 && (\n <Text {...chromeWrap}>No supervised sessions.</Text>\n )}\n {start > 0 && !screenReader && <Text>{start} more above</Text>}\n {visible.map((line) =>\n line.kind === 'group' ? (\n <Text key={`group-${line.label}`} {...chromeWrap}>\n {line.label}\n </Text>\n ) : (\n <Text\n key={`row-${line.row.id}`}\n {...(screenReader ? {} : { wrap: 'truncate-end' as const })}\n >\n {screenReader\n ? numberedRowPrefix(line.index)\n : line.row.id === selectedId\n ? '> '\n : ' '}\n {line.row.name && isControllable(line.row)\n ? screenReader\n ? `${line.row.name} (${line.row.id})`\n : `${Array.from(line.row.name).slice(0, 24).join('')}${Array.from(line.row.name).length > 24 ? '…' : ''} [${line.row.id.slice(-8)}]`\n : line.row.id}{' '}\n activity {line.row.activity} liveness {line.row.liveness} control {line.row.control}\n {status === 'ready' &&\n isControllable(line.row) &&\n line.row.activity === 'idle' &&\n line.row.nextLoopAt\n ? ` ${loopWaitLabel(line.row.nextLoopAt, observedAtMs)}`\n : ''}\n {line.row.problem ? ` ${line.row.problem}` : ''}\n {status === 'ready' && isControllable(line.row) && line.row.pr\n ? ` ${line.row.pr.host} ${line.row.pr.kind === 'pull' ? '#' : '!'}${line.row.pr.number}`\n : ''}\n </Text>\n ),\n )}\n {!screenReader && start + visible.length < displayLines.length && (\n <Text>{displayLines.length - start - visible.length} more below</Text>\n )}\n {selectedId !== undefined && <Text {...chromeWrap}>Selected {selectedId}</Text>}\n {selectedName !== undefined && <Text {...chromeWrap}>Name: {selectedName}</Text>}\n {selectedPr !== undefined && (\n <Text {...chromeWrap}>\n PR: {selectedPr.host} {selectedPr.kind === 'pull' ? '#' : '!'}\n {selectedPr.number}\n </Text>\n )}\n {selectedPr !== undefined && <Text {...chromeWrap}>URL: {selectedPr.url}</Text>}\n {selectedLoopStatus && <Text {...chromeWrap}>{selectedLoopStatus}</Text>}\n {prOpenStatus === 'opening' && <Text {...chromeWrap}>Opening linked PR...</Text>}\n {prOpenStatus === 'opened' && <Text {...chromeWrap}>Opened linked PR.</Text>}\n {prOpenStatus === 'failed' && (\n <Text {...chromeWrap}>PR link changed or could not be opened.</Text>\n )}\n {prOpenStatus === 'unavailable' && (\n <Text {...chromeWrap}>No verified PR link is available.</Text>\n )}\n {startStatus === 'starting' && <Text {...chromeWrap}>Starting a background session...</Text>}\n {startStatus === 'started' && (\n <Text {...chromeWrap}>\n Started {lastStartedId}\n {filteredByName || stateFilter !== undefined ? ' (may be hidden by filter)' : ''}\n </Text>\n )}\n {startStatus === 'failed' && (\n <Text {...chromeWrap}>\n Start failed; check workspace trust or run session start --background for details.\n </Text>\n )}\n {confirmStopId !== undefined &&\n (screenReader ? (\n <Text>{confirmQuestion(confirmStopId)} y Yes / n No</Text>\n ) : (\n <>\n <Text {...chromeWrap}>{confirmQuestion(`…${confirmStopId.slice(-8)}`)}</Text>\n <Text {...chromeWrap}>y Yes / n No</Text>\n </>\n ))}\n {confirmStopId === undefined && stopStatus === 'unavailable' && (\n <Text {...chromeWrap}>This session cannot be stopped from the view.</Text>\n )}\n {confirmStopId === undefined && attachStatus === 'unavailable' && (\n <Text {...chromeWrap}>This session cannot be attached to from the view.</Text>\n )}\n {stopStatus === 'stopping' && (\n <Text {...chromeWrap}>Stopping selected session; wait for confirmation.</Text>\n )}\n {stopStatus === 'stopped' && <Text {...chromeWrap}>Stopped {lastStoppedId}</Text>}\n {stopStatus === 'failed' && (\n <Text {...chromeWrap}>Stop failed; session remains listed until verified otherwise.</Text>\n )}\n {screenReader &&\n ordered.length > 0 &&\n confirmStopId === undefined &&\n stopStatus !== 'stopping' && (\n <Text>\n {formatNumberedSelectionPrompt(ordered.length, true)}\n {numbered.buffer ? ` ${numbered.buffer}` : ''}\n </Text>\n )}\n {screenReader && numbered.invalid && <Text>Selection out of range.</Text>}\n <Text {...chromeWrap}>{footer}</Text>\n {helpVisible &&\n helpLines.map((line) => (\n <Text key={line} {...chromeWrap}>\n {line}\n </Text>\n ))}\n </Box>\n );\n}\n\nexport async function renderSupervisedSessionView(\n options: ISupervisedSessionViewProps & {\n readonly screenReader: boolean;\n readonly screenReaderChannel?: TScreenReaderChannel;\n readonly screenReaderHint?: boolean;\n /** Print the screen-reader line; false when this process already printed it. */\n readonly announce?: boolean;\n },\n): Promise<TSupervisedViewExit> {\n if (options.announce !== false) {\n writeScreenReaderAnnouncement({\n enabled: options.screenReader,\n channel: options.screenReaderChannel,\n hint: options.screenReaderHint,\n });\n }\n let attach: ISupervisedAttachRequest | undefined;\n const instance = render(\n <ScreenReaderProvider enabled={options.screenReader}>\n <SupervisedSessionView\n loadRows={options.loadRows}\n onStop={options.onStop}\n onStart={options.onStart}\n onOpenPr={options.onOpenPr}\n onAttach={(request) => {\n attach = request;\n }}\n filteredByCwd={options.filteredByCwd}\n filteredByName={options.filteredByName}\n filteredByPr={options.filteredByPr}\n stateFilter={options.stateFilter}\n refreshMs={options.refreshMs}\n {...(options.initialSelectedId !== undefined ? { initialSelectedId: options.initialSelectedId } : {})}\n {...(options.initialGroupByDirectory !== undefined\n ? { initialGroupByDirectory: options.initialGroupByDirectory }\n : {})}\n />\n </ScreenReaderProvider>,\n { isScreenReaderEnabled: options.screenReader, exitOnCtrlC: false },\n );\n try {\n await instance.waitUntilExit();\n } finally {\n instance.unmount();\n }\n return attach === undefined ? { kind: 'closed' } : { kind: 'attach', ...attach };\n}\n","import { findProviderDefinition } from '@robota-sdk/agent-core';\nimport {\n applyActiveModelChange,\n createNodeHostSettingsStore,\n readSettings,\n resolveGitBranchFromNodeHost,\n} from '@robota-sdk/agent-framework';\n\nimport type { ITuiCliAdapter } from './tui-cli-adapter.js';\nimport type { IProviderDefinition } from '@robota-sdk/agent-core';\nimport type { CommandRegistry } from '@robota-sdk/agent-framework';\nimport type { TSettingsSource } from '@robota-sdk/agent-framework';\n\nexport interface IDefaultTuiCliAdapterOptions {\n providerDefinitions: readonly IProviderDefinition[];\n reloadPluginCommandSource: (registry: CommandRegistry) => void;\n userSettingsPath: string;\n settingsSources: readonly TSettingsSource[];\n formatResumeCommand?: (sessionId: string) => string;\n}\n\nexport function createDefaultTuiCliAdapter({\n providerDefinitions,\n reloadPluginCommandSource,\n userSettingsPath,\n settingsSources,\n formatResumeCommand,\n}: IDefaultTuiCliAdapterOptions): ITuiCliAdapter {\n return {\n getUserSettingsPath: () => userSettingsPath,\n readSettings: (path) => readSettings(path),\n reloadPluginCommandSource: (registry) => {\n reloadPluginCommandSource(registry);\n },\n applyActiveModelChange: (_cwd, modelId, options) => {\n const userStore = createNodeHostSettingsStore('user', userSettingsPath);\n applyActiveModelChange(settingsSources, [userStore], modelId, options);\n return { applied: true };\n },\n getGitBranch: (cwd) => resolveGitBranchFromNodeHost(cwd),\n getProviderDisplayName: (type) =>\n findProviderDefinition(providerDefinitions, type)?.displayName ?? type,\n ...(formatResumeCommand === undefined ? {} : { formatResumeCommand }),\n };\n}\n","import { watch, type FSWatcher } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\n\nimport {\n parseKeybindingsDocument,\n type IKeybindingDiagnostic,\n type IKeybindingSnapshot,\n} from './keybinding-registry.js';\n\nconst RELOAD_DEBOUNCE_MS = 10;\n\nexport interface IKeybindingsFilePort {\n ensureFile(): Promise<string>;\n}\n\nexport interface IKeybindingsSource extends IKeybindingsFilePort {\n readonly filePath: string;\n start(): Promise<void>;\n isStarted(): boolean;\n getSnapshot(): IKeybindingSnapshot;\n subscribe(listener: (snapshot: IKeybindingSnapshot) => void): () => void;\n dispose(): void;\n}\n\nexport interface INodeKeybindingsSourceOptions {\n readonly filePath: string;\n readonly schemaUrl: string;\n readonly onDiagnostic?: (diagnostic: IKeybindingDiagnostic) => void;\n}\n\nfunction defaultDocument(schemaUrl: string) {\n return { $schema: schemaUrl, version: 1 as const, bindings: {} };\n}\n\nfunction defaultSnapshot(filePath: string, schemaUrl: string): IKeybindingSnapshot {\n const parsed = parseKeybindingsDocument(JSON.stringify(defaultDocument(schemaUrl)), filePath);\n if (!parsed.ok) throw new Error(parsed.diagnostic.message);\n return parsed.snapshot;\n}\n\nclass NodeKeybindingsSource implements IKeybindingsSource {\n readonly filePath: string;\n readonly #schemaUrl: string;\n readonly #onDiagnostic: ((diagnostic: IKeybindingDiagnostic) => void) | undefined;\n readonly #listeners = new Set<(snapshot: IKeybindingSnapshot) => void>();\n #snapshot: IKeybindingSnapshot;\n #watcher: FSWatcher | undefined;\n #reloadTimer: ReturnType<typeof setTimeout> | undefined;\n #startPromise: Promise<void> | undefined;\n #disposed = false;\n\n constructor(options: INodeKeybindingsSourceOptions) {\n if (options.filePath.trim() === '') throw new Error('Keybindings file path must not be empty.');\n if (options.schemaUrl.trim() === '') throw new Error('Keybindings schema URL must not be empty.');\n this.filePath = options.filePath;\n this.#schemaUrl = options.schemaUrl;\n this.#onDiagnostic = options.onDiagnostic;\n this.#snapshot = defaultSnapshot(this.filePath, this.#schemaUrl);\n }\n\n start(): Promise<void> {\n if (this.#disposed) return Promise.reject(new Error('Keybindings source is disposed.'));\n if (this.#startPromise !== undefined) return this.#startPromise;\n this.#startPromise = this.#start().catch((cause) => {\n this.#watcher?.close();\n this.#watcher = undefined;\n this.#startPromise = undefined;\n throw cause;\n });\n return this.#startPromise;\n }\n\n async #start(): Promise<void> {\n const directory = dirname(this.filePath);\n await mkdir(directory, { recursive: true });\n await this.#load();\n if (this.#disposed) throw new Error('Keybindings source is disposed.');\n this.#watcher = watch(directory, { persistent: false }, (_event, filename) => {\n if (filename !== null && filename.toString() !== basename(this.filePath)) return;\n this.#scheduleReload();\n });\n }\n\n isStarted(): boolean {\n return this.#watcher !== undefined && !this.#disposed;\n }\n\n getSnapshot(): IKeybindingSnapshot {\n return this.#snapshot;\n }\n\n subscribe(listener: (snapshot: IKeybindingSnapshot) => void): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n async ensureFile(): Promise<string> {\n await mkdir(dirname(this.filePath), { recursive: true });\n try {\n await writeFile(this.filePath, `${JSON.stringify(defaultDocument(this.#schemaUrl), null, 2)}\\n`, {\n encoding: 'utf8',\n flag: 'wx',\n mode: 0o600,\n });\n } catch (cause) {\n if (!isAlreadyExists(cause as object)) throw cause;\n }\n return this.filePath;\n }\n\n dispose(): void {\n this.#disposed = true;\n if (this.#reloadTimer !== undefined) clearTimeout(this.#reloadTimer);\n this.#reloadTimer = undefined;\n this.#watcher?.close();\n this.#watcher = undefined;\n this.#listeners.clear();\n }\n\n #scheduleReload(): void {\n if (this.#disposed) return;\n if (this.#reloadTimer !== undefined) clearTimeout(this.#reloadTimer);\n this.#reloadTimer = setTimeout(() => {\n this.#reloadTimer = undefined;\n void this.#load();\n }, RELOAD_DEBOUNCE_MS);\n }\n\n async #load(): Promise<void> {\n let text: string;\n try {\n text = await readFile(this.filePath, 'utf8');\n } catch (cause) {\n if (isNotFound(cause as object)) return;\n this.#publishInvalid({\n file: this.filePath,\n path: '$',\n message: errorMessage(cause as object),\n });\n return;\n }\n const parsed = parseKeybindingsDocument(text, this.filePath);\n if (!parsed.ok) {\n this.#publishInvalid(parsed.diagnostic);\n return;\n }\n this.#publish(Object.freeze({ ...parsed.snapshot, generation: this.#snapshot.generation + 1 }));\n }\n\n #publishInvalid(diagnostic: IKeybindingDiagnostic): void {\n this.#onDiagnostic?.(diagnostic);\n this.#publish(\n Object.freeze({\n ...this.#snapshot,\n generation: this.#snapshot.generation + 1,\n diagnostic,\n }),\n );\n }\n\n #publish(snapshot: IKeybindingSnapshot): void {\n if (this.#disposed) return;\n this.#snapshot = snapshot;\n for (const listener of this.#listeners) listener(snapshot);\n }\n}\n\nfunction isNotFound(cause: object): boolean {\n return isNodeError(cause) && cause.code === 'ENOENT';\n}\n\nfunction isAlreadyExists(cause: object): boolean {\n return isNodeError(cause) && cause.code === 'EEXIST';\n}\n\nfunction isNodeError(cause: object): cause is NodeJS.ErrnoException {\n return cause instanceof Error && 'code' in cause;\n}\n\nfunction errorMessage(cause: object): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n\nexport function createNodeKeybindingsSource(\n options: INodeKeybindingsSourceOptions,\n): IKeybindingsSource {\n return new NodeKeybindingsSource(options);\n}\n"],"mappings":"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"}