@namzu/cli 34.1.0 → 35.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +21 -7
  3. package/dist/commands/mcp.d.ts +3 -0
  4. package/dist/commands/mcp.d.ts.map +1 -1
  5. package/dist/commands/mcp.js +179 -6
  6. package/dist/commands/mcp.js.map +1 -1
  7. package/dist/doctor/checks/credentials.d.ts.map +1 -1
  8. package/dist/doctor/checks/credentials.js +19 -4
  9. package/dist/doctor/checks/credentials.js.map +1 -1
  10. package/dist/integrations/mcp/servers.d.ts.map +1 -1
  11. package/dist/integrations/mcp/servers.js +107 -15
  12. package/dist/integrations/mcp/servers.js.map +1 -1
  13. package/dist/integrations/providers/harness-credentials.d.ts +2 -0
  14. package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
  15. package/dist/integrations/providers/harness-credentials.js +8 -5
  16. package/dist/integrations/providers/harness-credentials.js.map +1 -1
  17. package/dist/integrations/providers/setup.d.ts.map +1 -1
  18. package/dist/integrations/providers/setup.js +3 -1
  19. package/dist/integrations/providers/setup.js.map +1 -1
  20. package/dist/integrations/resident/session-step.d.ts.map +1 -1
  21. package/dist/integrations/resident/session-step.js +13 -8
  22. package/dist/integrations/resident/session-step.js.map +1 -1
  23. package/dist/tui/App.d.ts.map +1 -1
  24. package/dist/tui/App.js +385 -95
  25. package/dist/tui/App.js.map +1 -1
  26. package/dist/tui/BackgroundJobsPanel.d.ts +26 -0
  27. package/dist/tui/BackgroundJobsPanel.d.ts.map +1 -0
  28. package/dist/tui/BackgroundJobsPanel.js +129 -0
  29. package/dist/tui/BackgroundJobsPanel.js.map +1 -0
  30. package/dist/tui/Composer.d.ts +7 -2
  31. package/dist/tui/Composer.d.ts.map +1 -1
  32. package/dist/tui/Composer.js +65 -34
  33. package/dist/tui/Composer.js.map +1 -1
  34. package/dist/tui/PendingInputPanel.d.ts +34 -0
  35. package/dist/tui/PendingInputPanel.d.ts.map +1 -0
  36. package/dist/tui/PendingInputPanel.js +108 -0
  37. package/dist/tui/PendingInputPanel.js.map +1 -0
  38. package/dist/tui/agent.d.ts +10 -3
  39. package/dist/tui/agent.d.ts.map +1 -1
  40. package/dist/tui/agent.js +78 -12
  41. package/dist/tui/agent.js.map +1 -1
  42. package/dist/tui/slashCommands.d.ts +5 -1
  43. package/dist/tui/slashCommands.d.ts.map +1 -1
  44. package/dist/tui/slashCommands.js +9 -8
  45. package/dist/tui/slashCommands.js.map +1 -1
  46. package/package.json +5 -5
@@ -0,0 +1,26 @@
1
+ import type { BackgroundJob, BackgroundJobOutput } from '@namzu/sdk';
2
+ export interface BackgroundJobsPanelProps {
3
+ readonly jobs: readonly BackgroundJob[];
4
+ /** The highlighted row in the list. An ended job remains selectable. */
5
+ readonly selectedJobId: string | null;
6
+ /** Null shows the list; an id shows that job's details and retained output. */
7
+ readonly detailJobId: string | null;
8
+ /** Physical output rows to stay above the live tail. Zero follows new output. */
9
+ readonly tailOffset: number;
10
+ /** Bound to the current session by the host. Reading must never start a job. */
11
+ readonly readJob: (id: string) => BackgroundJobOutput | undefined;
12
+ readonly rows: number;
13
+ readonly columns: number;
14
+ readonly now?: number;
15
+ readonly busyJobId?: string | null;
16
+ readonly notice?: string | null;
17
+ }
18
+ /** A list row per job, with room left for the composer and its footer. */
19
+ export declare function backgroundJobsPageSize(rows: number): number;
20
+ /** Detail output grows on taller terminals, while retaining the surrounding UI. */
21
+ export declare function backgroundJobOutputPageSize(rows: number): number;
22
+ /** The exact physical lines the output pager uses; App uses the same count for keys. */
23
+ export declare function backgroundJobOutputRows(output: BackgroundJobOutput | undefined, columns: number): readonly string[];
24
+ export declare function maxBackgroundJobTailOffset(output: BackgroundJobOutput | undefined, rows: number, columns: number): number;
25
+ export declare function BackgroundJobsPanel({ jobs, selectedJobId, detailJobId, tailOffset, readJob, rows, columns, now, busyJobId, notice, }: BackgroundJobsPanelProps): import("react/jsx-runtime").JSX.Element;
26
+ //# sourceMappingURL=BackgroundJobsPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackgroundJobsPanel.d.ts","sourceRoot":"","sources":["../../src/tui/BackgroundJobsPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAWpE,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAA;IACvC,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,iFAAiF;IACjF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,mBAAmB,GAAG,SAAS,CAAA;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAgBD,wFAAwF;AACxF,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,mBAAmB,GAAG,SAAS,EACvC,OAAO,EAAE,MAAM,GACb,SAAS,MAAM,EAAE,CAgBnB;AAED,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,mBAAmB,GAAG,SAAS,EACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACb,MAAM,CAER;AAuBD,wBAAgB,mBAAmB,CAAC,EACnC,IAAI,EACJ,aAAa,EACb,WAAW,EACX,UAAU,EACV,OAAO,EACP,IAAI,EACJ,OAAO,EACP,GAAgB,EAChB,SAAgB,EAChB,MAAa,GACb,EAAE,wBAAwB,2CAmH1B"}
@@ -0,0 +1,129 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { permissionReviewRows } from './permission-review.js';
4
+ import { terminalDisplayText } from './terminal-display.js';
5
+ import { choiceDisplayWidth, truncateChoiceText } from './terminal-choice-text.js';
6
+ import { theme } from './theme.js';
7
+ /** Keep a live log readable without projecting a whole retained 1 MiB on every repaint. */
8
+ const MAX_VISIBLE_OUTPUT_BYTES = 32 * 1024;
9
+ /** A list row per job, with room left for the composer and its footer. */
10
+ export function backgroundJobsPageSize(rows) {
11
+ return Math.max(1, Math.min(8, Math.floor(rows) - 11));
12
+ }
13
+ /** Detail output grows on taller terminals, while retaining the surrounding UI. */
14
+ export function backgroundJobOutputPageSize(rows) {
15
+ return Math.max(1, Math.min(18, Math.floor(rows) - 16));
16
+ }
17
+ function elapsed(job, now) {
18
+ const seconds = Math.max(0, Math.floor(((job.exitedAt ?? now) - job.startedAt) / 1000));
19
+ if (seconds < 60)
20
+ return `${seconds}s`;
21
+ const minutes = Math.floor(seconds / 60);
22
+ if (minutes < 60)
23
+ return `${minutes}m ${seconds % 60}s`;
24
+ return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
25
+ }
26
+ function jobState(job, now) {
27
+ if (job.status === 'running')
28
+ return `running · ${elapsed(job, now)}`;
29
+ if (job.status === 'killed')
30
+ return `stopped · ${elapsed(job, now)}`;
31
+ return `exited ${job.exitCode ?? '?'} · ${elapsed(job, now)}`;
32
+ }
33
+ /** The exact physical lines the output pager uses; App uses the same count for keys. */
34
+ export function backgroundJobOutputRows(output, columns) {
35
+ if (!output)
36
+ return ['Output unavailable.'];
37
+ const bytes = Buffer.from(output.chunk, 'utf8');
38
+ const omittedHere = Math.max(0, bytes.length - MAX_VISIBLE_OUTPUT_BYTES);
39
+ const latest = bytes.subarray(omittedHere).toString('utf8').replace(/\n$/u, '');
40
+ const lines = [
41
+ ...(output.droppedBytes > 0
42
+ ? [`Earlier output discarded by the job buffer (${output.droppedBytes} bytes).`]
43
+ : []),
44
+ ...(omittedHere > 0 ? [`Showing the latest ${MAX_VISIBLE_OUTPUT_BYTES} bytes.`] : []),
45
+ latest.length > 0 ? latest : 'No output yet.',
46
+ ];
47
+ // This wrapper preserves shell spacing and splits long lines before Ink can
48
+ // wrap them below the panel's row budget. It also makes terminal controls
49
+ // visible instead of letting process output move the cursor.
50
+ return permissionReviewRows(lines.join('\n').replace(/\t/gu, ' '), columns).map((row) => row.text);
51
+ }
52
+ export function maxBackgroundJobTailOffset(output, rows, columns) {
53
+ return Math.max(0, backgroundJobOutputRows(output, columns).length - backgroundJobOutputPageSize(rows));
54
+ }
55
+ function readOutput(readJob, id) {
56
+ try {
57
+ return readJob(id);
58
+ }
59
+ catch {
60
+ // A session can close between a status snapshot and a render. The host
61
+ // still owns the error and the panel stays navigable.
62
+ return undefined;
63
+ }
64
+ }
65
+ /** Keep the exit key readable when a narrow terminal cannot carry every hint. */
66
+ function fitClosingHint(candidates, columns) {
67
+ for (const candidate of candidates) {
68
+ if (choiceDisplayWidth(candidate) <= columns)
69
+ return candidate;
70
+ }
71
+ return truncateChoiceText(candidates.at(-1) ?? 'esc', columns);
72
+ }
73
+ export function BackgroundJobsPanel({ jobs, selectedJobId, detailJobId, tailOffset, readJob, rows, columns, now = Date.now(), busyJobId = null, notice = null, }) {
74
+ const running = jobs.filter((job) => job.status === 'running').length;
75
+ const selected = jobs.find((job) => job.id === selectedJobId) ?? jobs[0];
76
+ const detailed = detailJobId ? jobs.find((job) => job.id === detailJobId) : undefined;
77
+ const innerColumns = Math.max(1, columns - 6);
78
+ const narrow = columns < 56;
79
+ if (!detailJobId || !detailed) {
80
+ const pageSize = backgroundJobsPageSize(rows);
81
+ const selectedIndex = selected ? jobs.findIndex((job) => job.id === selected.id) : 0;
82
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(pageSize / 2), jobs.length - pageSize));
83
+ const shown = jobs.slice(start, start + pageSize);
84
+ const stop = selected?.status === 'running' && !busyJobId ? ' · x stop' : '';
85
+ const listHint = jobs.length === 0
86
+ ? 'esc close'
87
+ : fitClosingHint([
88
+ `↑↓ select · enter details${stop} · esc close`,
89
+ `↑↓ move · enter${stop} · esc`,
90
+ `enter${stop} · esc`,
91
+ `${stop ? 'x stop · ' : ''}esc`,
92
+ 'esc',
93
+ ], innerColumns);
94
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.border.default, paddingX: 1, children: [_jsx(Text, { bold: true, color: theme.accent.system, wrap: "truncate-end", children: truncateChoiceText(`Shell jobs · ${running} running`, innerColumns) }), jobs.length === 0 ? (_jsx(Text, { color: theme.text.secondary, children: "No shell jobs in this session." })) : (shown.map((job) => {
95
+ const focused = job.id === selected?.id;
96
+ const label = narrow
97
+ ? `${focused ? '>' : ' '} ${job.id} · ${job.status} · ${job.command}`
98
+ : `${focused ? '>' : ' '} ${job.id} · ${jobState(job, now)} · ${job.command}`;
99
+ return (_jsx(Text, { color: focused ? theme.accent.user : theme.text.secondary, wrap: "truncate-end", children: truncateChoiceText(label, innerColumns) }, job.id));
100
+ })), jobs.length > pageSize ? (_jsx(Text, { color: theme.text.muted, wrap: "truncate-end", children: `${start + 1}–${Math.min(start + pageSize, jobs.length)}/${jobs.length}` })) : null, notice ? _jsx(Text, { color: theme.status.warn, wrap: "truncate-end", children: truncateChoiceText(notice, innerColumns) }) : null, _jsx(Text, { color: theme.text.muted, wrap: "truncate-end", children: listHint })] }));
101
+ }
102
+ const output = readOutput(readJob, detailed.id);
103
+ const outputRows = backgroundJobOutputRows(output, columns);
104
+ const pageSize = backgroundJobOutputPageSize(rows);
105
+ const maxOffset = Math.max(0, outputRows.length - pageSize);
106
+ const offset = Math.max(0, Math.min(tailOffset, maxOffset));
107
+ const end = outputRows.length - offset;
108
+ const start = Math.max(0, end - pageSize);
109
+ const allCommandRows = permissionReviewRows(terminalDisplayText(detailed.command).replace(/\t/gu, ' '), columns);
110
+ const commandRows = allCommandRows.slice(0, 2);
111
+ const commandMore = allCommandRows.length > 2;
112
+ const shownOutput = outputRows.map((text, index) => ({ index, text })).slice(start, end);
113
+ const canStop = detailed.status === 'running' && !busyJobId;
114
+ const earlierOutputOmitted = (output?.droppedBytes ?? 0) > 0 ||
115
+ (output?.chunk !== undefined && Buffer.byteLength(output.chunk) > MAX_VISIBLE_OUTPUT_BYTES);
116
+ const pageLabel = `${start + 1}–${end}/${outputRows.length}`;
117
+ const stopHint = canStop ? ' · x stop' : '';
118
+ const footer = fitClosingHint([
119
+ `${pageLabel} · ↑↓ scroll · PgUp/PgDn · g/G${stopHint} · esc jobs · q close`,
120
+ `${pageLabel} · ↑↓ scroll${stopHint} · esc jobs`,
121
+ `↑↓ scroll${stopHint} · esc jobs`,
122
+ `↑↓${stopHint} · esc jobs`,
123
+ `${canStop ? 'x stop · ' : ''}esc jobs`,
124
+ 'esc jobs',
125
+ 'esc',
126
+ ], innerColumns);
127
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.border.focus, paddingX: 1, children: [_jsx(Text, { bold: true, color: theme.accent.system, wrap: "truncate-end", children: truncateChoiceText(`Shell details · ${detailed.id}`, innerColumns) }), _jsx(Text, { color: detailed.status === 'running' ? theme.status.ok : theme.text.secondary, wrap: "truncate-end", children: truncateChoiceText(busyJobId === detailed.id ? 'Stopping…' : jobState(detailed, now), innerColumns) }), _jsx(Text, { color: theme.text.muted, children: "Command" }), commandRows.map((row) => (_jsx(Text, { color: theme.text.primary, wrap: "truncate-end", children: row.text || ' ' }, row.index))), commandMore ? _jsx(Text, { color: theme.text.muted, children: "\u2026 command continues" }) : null, _jsx(Text, { color: theme.text.muted, wrap: "truncate-end", children: truncateChoiceText(earlierOutputOmitted ? 'Output · earlier bytes omitted' : 'Output', innerColumns) }), shownOutput.map((row) => (_jsx(Text, { color: theme.text.secondary, wrap: "truncate-end", children: row.text || ' ' }, row.index))), notice ? _jsx(Text, { color: theme.status.warn, wrap: "truncate-end", children: truncateChoiceText(notice, innerColumns) }) : null, _jsx(Text, { color: theme.text.muted, wrap: "truncate-end", children: footer })] }));
128
+ }
129
+ //# sourceMappingURL=BackgroundJobsPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackgroundJobsPanel.js","sourceRoot":"","sources":["../../src/tui/BackgroundJobsPanel.tsx"],"names":[],"mappings":"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"}
@@ -6,7 +6,8 @@
6
6
  * autocomplete dropdown is open), Tab completes that command or queues the
7
7
  * draft, Esc clears / closes the dropdown, ↑/↓ navigate the dropdown when open
8
8
  * else browse history, Ctrl+R/Ctrl+S search history, Ctrl+W rubs out a word,
9
- * Ctrl+G edits the draft in VISUAL/EDITOR, Backspace deletes, Alt+W drops or
9
+ * Ctrl+G edits the draft in VISUAL/EDITOR, Alt+Up recalls the latest queued
10
+ * message into an empty composer, Backspace deletes, Alt+W drops or
10
11
  * arms the composer trigger nearest the cursor (`./triggers/`).
11
12
  */
12
13
  import type { MessageAttachment } from '@namzu/sdk';
@@ -78,6 +79,8 @@ export interface ComposerProps {
78
79
  * is the host's answer to give, not a value this key press can hold.
79
80
  */
80
81
  readonly onOpenAgentPanel?: () => void;
82
+ /** Recall the latest operator-authored next-turn message into an empty draft. */
83
+ readonly onRecallQueued?: () => Omit<ComposerDraft, 'token'> | null;
81
84
  /** Shift+Tab. Absent means the key does nothing, which the composer footer then does not advertise. */
82
85
  readonly onCycleMode?: () => void;
83
86
  /**
@@ -143,6 +146,8 @@ export interface ComposerDraft {
143
146
  readonly token: number;
144
147
  readonly text: string;
145
148
  readonly attachments?: readonly MessageAttachment[];
149
+ /** Keep a queued message's explicit trigger decisions when it is recalled. */
150
+ readonly triggers?: readonly TriggerId[];
146
151
  }
147
152
  /**
148
153
  * Visible rows in a completion menu for this terminal height.
@@ -162,5 +167,5 @@ export declare function suggestionWindowSize(rows: number | undefined): number;
162
167
  * operators already bring to this key.
163
168
  */
164
169
  export declare function deletePreviousWord(source: string): string;
165
- export declare function Composer({ disabled, reasoningEffortLevels, onSubmit, history, userCommands, builtins, mentionCandidates, hidden, escapeInterrupts, onNotice, onStepReasoningEffort, onExternalEdit, onEditPrevious, draftToRestore, onDraftRestored, onDraftPresenceChange, onOpenAgentPanel, onCycleMode, triggers, turnActive, }: ComposerProps): import("react/jsx-runtime").JSX.Element | null;
170
+ export declare function Composer({ disabled, reasoningEffortLevels, onSubmit, history, userCommands, builtins, mentionCandidates, hidden, escapeInterrupts, onNotice, onStepReasoningEffort, onExternalEdit, onEditPrevious, draftToRestore, onDraftRestored, onDraftPresenceChange, onOpenAgentPanel, onRecallQueued, onCycleMode, triggers, turnActive, }: ComposerProps): import("react/jsx-runtime").JSX.Element | null;
166
171
  //# sourceMappingURL=Composer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.d.ts","sourceRoot":"","sources":["../../src/tui/Composer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Composer.d.ts","sourceRoot":"","sources":["../../src/tui/Composer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAKnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAG5D,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAI1E,OAAO,EAGN,KAAK,cAAc,EAGnB,MAAM,sBAAsB,CAAA;AAW7B,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExE,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClD,QAAQ,CAAC,QAAQ,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,SAAS,iBAAiB,EAAE,GAAG,SAAS,EACrD,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,UAAU,KACZ,IAAI,CAAA;IACT,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IAC9C,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,KAAK,IAAI,CAAA;IACvE,yEAAyE;IACzE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3D,yEAAyE;IACzE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC9C,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC5D;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;IACtC,iFAAiF;IACjF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACnE,uGAAuG;IACvG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACjC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,uBAAuB,CAAA;IAC3C,kGAAkG;IAClG,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAClC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;IAChC,kGAAkG;IAClG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,mGAAmG;IACnG,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,wFAAwF;AACxF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GACrB,UAAU,GACV,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,YAAY,CAAA;AAEf;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACpB;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAA;IAC1C;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAC7B,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;CAAE,CAAA;AAEzD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACnD,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;CACxC;AA4FD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMrE;AA8HD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAmDD,wBAAgB,QAAQ,CAAC,EACxB,QAAgB,EAChB,qBAAqB,EACrB,QAAQ,EACR,OAAO,EACP,YAAiB,EACjB,QAAQ,EACR,iBAAsB,EACtB,MAAc,EACd,gBAAwB,EACxB,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,cAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,QAAQ,EACR,UAAkB,GAClB,EAAE,aAAa,kDA22Bf"}
@@ -232,7 +232,7 @@ function deleteNextWordAt(source, cursor) {
232
232
  const end = nextWordBoundary(source, cursor);
233
233
  return { value: source.slice(0, cursor) + source.slice(end), cursor };
234
234
  }
235
- export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, history, userCommands = [], builtins, mentionCandidates = [], hidden = false, escapeInterrupts = false, onNotice, onStepReasoningEffort, onExternalEdit, onEditPrevious, draftToRestore = null, onDraftRestored, onDraftPresenceChange, onOpenAgentPanel, onCycleMode, triggers, turnActive = false, }) {
235
+ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, history, userCommands = [], builtins, mentionCandidates = [], hidden = false, escapeInterrupts = false, onNotice, onStepReasoningEffort, onExternalEdit, onEditPrevious, draftToRestore = null, onDraftRestored, onDraftPresenceChange, onOpenAgentPanel, onRecallQueued, onCycleMode, triggers, turnActive = false, }) {
236
236
  const terminal = useWindowSize();
237
237
  const [value, setValueState] = useState('');
238
238
  const valueRef = useRef('');
@@ -256,9 +256,21 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
256
256
  // Large pastes are held as attachments (shown as chips) instead of being
257
257
  // dumped into the input, then folded into the message on submit.
258
258
  const [pastes, setPastes] = useState([]);
259
+ const pastesRef = useRef([]);
260
+ const replacePastes = useCallback((next) => {
261
+ const resolved = typeof next === 'function' ? next(pastesRef.current) : next;
262
+ pastesRef.current = resolved;
263
+ setPastes(resolved);
264
+ }, []);
259
265
  // Pasted images and restored durable attachments, shown as chips and sent
260
266
  // back in the exact SDK union on submit.
261
267
  const [attachments, setAttachments] = useState([]);
268
+ const attachmentsRef = useRef([]);
269
+ const replaceAttachments = useCallback((next) => {
270
+ const resolved = typeof next === 'function' ? next(attachmentsRef.current) : next;
271
+ attachmentsRef.current = resolved;
272
+ setAttachments(resolved);
273
+ }, []);
262
274
  const [editPreviousArmed, setEditPreviousArmed] = useState(false);
263
275
  const restoredTokenRef = useRef(null);
264
276
  const draftPresenceRef = useRef(false);
@@ -366,32 +378,40 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
366
378
  setBuffer('', 0, 'typed');
367
379
  setHistoryIndex(-1);
368
380
  setSelectedIndex(0);
369
- setPastes([]);
370
- setAttachments([]);
381
+ replacePastes([]);
382
+ replaceAttachments([]);
371
383
  setEditPreviousArmed(false);
372
- }, [setBuffer, setHistoryIndex, setHistorySearch, setSelectedIndex]);
373
- useEffect(() => {
374
- if (!draftToRestore || restoredTokenRef.current === draftToRestore.token)
375
- return;
376
- restoredTokenRef.current = draftToRestore.token;
377
- setBuffer(draftToRestore.text, draftToRestore.text.length, 'restored');
384
+ }, [replaceAttachments, replacePastes, setBuffer, setHistoryIndex, setHistorySearch, setSelectedIndex]);
385
+ /** Restore synchronously when Alt+Up is pressed, before the next typed key. */
386
+ const restoreDraft = useCallback((draft) => {
387
+ setBuffer(draft.text, draft.text.length, 'restored');
388
+ if (triggers && draft.triggers?.length) {
389
+ const selected = new Set(draft.triggers);
390
+ const hits = detectTriggers(draft.text, triggers.registry, triggers.context, {
391
+ nonTyped: spansRef.current,
392
+ suggest: true,
393
+ }).hits;
394
+ overridesRef.current = hits
395
+ .filter((hit) => selected.has(hit.id))
396
+ .map((hit) => ({ id: hit.id, start: hit.start, end: hit.end, state: 'armed' }));
397
+ setTriggerVersion((version) => version + 1);
398
+ }
378
399
  setHistoryIndex(-1);
379
400
  setSelectedIndex(0);
380
401
  verticalColumnRef.current = null;
381
402
  historyDraftRef.current = null;
382
403
  setHistorySearch(null);
383
- setPastes([]);
384
- setAttachments(draftToRestore.attachments ? [...draftToRestore.attachments] : []);
404
+ replacePastes([]);
405
+ replaceAttachments(draft.attachments ? [...draft.attachments] : []);
385
406
  setEditPreviousArmed(false);
407
+ }, [replaceAttachments, replacePastes, setBuffer, setHistoryIndex, setHistorySearch, setSelectedIndex, triggers]);
408
+ useEffect(() => {
409
+ if (!draftToRestore || restoredTokenRef.current === draftToRestore.token)
410
+ return;
411
+ restoredTokenRef.current = draftToRestore.token;
412
+ restoreDraft(draftToRestore);
386
413
  onDraftRestored?.(draftToRestore.token);
387
- }, [
388
- draftToRestore,
389
- onDraftRestored,
390
- setBuffer,
391
- setHistoryIndex,
392
- setHistorySearch,
393
- setSelectedIndex,
394
- ]);
414
+ }, [draftToRestore, onDraftRestored, restoreDraft]);
395
415
  // Bracketed paste is one event even when the terminal splits its bytes.
396
416
  // Its newlines are content, never Enter/shortcut events.
397
417
  usePaste((text) => {
@@ -401,7 +421,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
401
421
  if (normalized.length === 0)
402
422
  return;
403
423
  if (normalized.includes('\n') || normalized.length > PASTE_THRESHOLD) {
404
- setPastes((previous) => [...previous, normalized]);
424
+ replacePastes((previous) => [...previous, normalized]);
405
425
  return;
406
426
  }
407
427
  const position = cursorRef.current;
@@ -448,6 +468,16 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
448
468
  }
449
469
  return false;
450
470
  };
471
+ if (key.meta && key.upArrow && onRecallQueued) {
472
+ if (valueRef.current.length > 0 || pastesRef.current.length > 0 || attachmentsRef.current.length > 0) {
473
+ onNotice?.('Send or clear the current draft before recalling a queued message.');
474
+ return;
475
+ }
476
+ const recalled = onRecallQueued();
477
+ if (recalled)
478
+ restoreDraft(recalled);
479
+ return;
480
+ }
451
481
  if (!hasLiveSuggestions &&
452
482
  onStepReasoningEffort &&
453
483
  ((key.shift && key.downArrow) || (key.meta && input === ','))) {
@@ -463,13 +493,14 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
463
493
  if (!key.escape && editPreviousArmed)
464
494
  setEditPreviousArmed(false);
465
495
  const submit = (mode) => {
466
- const message = [valueRef.current, ...pastes]
496
+ const message = [valueRef.current, ...pastesRef.current]
467
497
  .map((s) => s.trim())
468
498
  .filter(Boolean)
469
499
  .join('\n\n');
470
- if (message.length === 0 && attachments.length === 0)
500
+ const currentAttachments = attachmentsRef.current;
501
+ if (message.length === 0 && currentAttachments.length === 0)
471
502
  return false;
472
- const submittedAttachments = attachments.length > 0 ? attachments : undefined;
503
+ const submittedAttachments = currentAttachments.length > 0 ? currentAttachments : undefined;
473
504
  // Read again from what the last key left, not from the last render.
474
505
  const detection = detect(valueRef.current);
475
506
  // The message is the draft trimmed: offsets move by what the trim took.
@@ -480,7 +511,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
480
511
  onSubmit(message, submittedAttachments, mode, {
481
512
  source: 'composer',
482
513
  triggers: armed,
483
- onlyTriggers: armed.length > 0 && detection.onlyTriggers && pastes.length === 0 && attachments.length === 0,
514
+ onlyTriggers: armed.length > 0 && detection.onlyTriggers && pastesRef.current.length === 0 && currentAttachments.length === 0,
484
515
  });
485
516
  reset();
486
517
  return true;
@@ -561,7 +592,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
561
592
  setHistorySearch(null);
562
593
  return;
563
594
  }
564
- const empty = valueRef.current.length === 0 && pastes.length === 0 && attachments.length === 0;
595
+ const empty = valueRef.current.length === 0 && pastesRef.current.length === 0 && attachmentsRef.current.length === 0;
565
596
  if (empty && onEditPrevious) {
566
597
  if (editPreviousArmed) {
567
598
  setEditPreviousArmed(false);
@@ -587,12 +618,12 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
587
618
  if (key.backspace) {
588
619
  // Backspace on an empty line removes the last durable attachment first,
589
620
  // then pasted text.
590
- if (valueRef.current.length === 0 && attachments.length > 0) {
591
- setAttachments((p) => p.slice(0, -1));
621
+ if (valueRef.current.length === 0 && attachmentsRef.current.length > 0) {
622
+ replaceAttachments((p) => p.slice(0, -1));
592
623
  return;
593
624
  }
594
- if (valueRef.current.length === 0 && pastes.length > 0) {
595
- setPastes((p) => p.slice(0, -1));
625
+ if (valueRef.current.length === 0 && pastesRef.current.length > 0) {
626
+ replacePastes((p) => p.slice(0, -1));
596
627
  return;
597
628
  }
598
629
  const position = cursorRef.current;
@@ -707,7 +738,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
707
738
  }
708
739
  verticalColumnRef.current = null;
709
740
  if (historyIndexRef.current < 0) {
710
- if (valueRef.current.length === 0 && pastes.length === 0 && attachments.length === 0) {
741
+ if (valueRef.current.length === 0 && pastesRef.current.length === 0 && attachmentsRef.current.length === 0) {
711
742
  onOpenAgentPanel?.();
712
743
  }
713
744
  return;
@@ -729,14 +760,14 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
729
760
  onNotice?.('External editor support is unavailable in this terminal.');
730
761
  return;
731
762
  }
732
- const seed = [valueRef.current, ...pastes]
763
+ const seed = [valueRef.current, ...pastesRef.current]
733
764
  .map((part) => part.trim())
734
765
  .filter(Boolean)
735
766
  .join('\n\n');
736
767
  void onExternalEdit(seed)
737
768
  .then((edited) => {
738
769
  const cleaned = edited.trimEnd();
739
- setPastes([]);
770
+ replacePastes([]);
740
771
  setHistoryIndex(-1);
741
772
  setHistorySearch(null);
742
773
  setSelectedIndex(0);
@@ -758,7 +789,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
758
789
  if ((key.ctrl || key.meta) && input === 'v') {
759
790
  const read = readClipboardImage();
760
791
  if (read.kind === 'image') {
761
- setAttachments((p) => [...p, read.image]);
792
+ replaceAttachments((p) => [...p, read.image]);
762
793
  }
763
794
  else if (read.kind === 'empty') {
764
795
  onNotice?.('No image on the clipboard. Copy one, then press Ctrl+V.');
@@ -831,7 +862,7 @@ export function Composer({ disabled = false, reasoningEffortLevels, onSubmit, hi
831
862
  // the model received. A real paste reaches `usePaste` above, where a
832
863
  // long one is still a chip.
833
864
  if (input.includes('\n')) {
834
- setPastes((p) => [...p, input]);
865
+ replacePastes((p) => [...p, input]);
835
866
  return;
836
867
  }
837
868
  setSelectedIndex(0);