@duetso/agent 0.1.81 → 0.1.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 (82) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/memory/observational.d.ts +7 -0
  3. package/dist/src/memory/observational.d.ts.map +1 -1
  4. package/dist/src/memory/observational.js +7 -1
  5. package/dist/src/memory/observational.js.map +1 -1
  6. package/dist/src/tui/app.d.ts +25 -35
  7. package/dist/src/tui/app.d.ts.map +1 -1
  8. package/dist/src/tui/app.js +195 -2308
  9. package/dist/src/tui/app.js.map +1 -1
  10. package/dist/src/tui/autocomplete-controller.d.ts +83 -0
  11. package/dist/src/tui/autocomplete-controller.d.ts.map +1 -0
  12. package/dist/src/tui/autocomplete-controller.js +308 -0
  13. package/dist/src/tui/autocomplete-controller.js.map +1 -0
  14. package/dist/src/tui/boot-screen.d.ts +64 -0
  15. package/dist/src/tui/boot-screen.d.ts.map +1 -0
  16. package/dist/src/tui/boot-screen.js +137 -0
  17. package/dist/src/tui/boot-screen.js.map +1 -0
  18. package/dist/src/tui/controllers.d.ts +39 -0
  19. package/dist/src/tui/controllers.d.ts.map +1 -0
  20. package/dist/src/tui/controllers.js +53 -0
  21. package/dist/src/tui/controllers.js.map +1 -0
  22. package/dist/src/tui/copy-controller.d.ts +75 -0
  23. package/dist/src/tui/copy-controller.d.ts.map +1 -0
  24. package/dist/src/tui/copy-controller.js +140 -0
  25. package/dist/src/tui/copy-controller.js.map +1 -0
  26. package/dist/src/tui/history-replay.d.ts +33 -0
  27. package/dist/src/tui/history-replay.d.ts.map +1 -0
  28. package/dist/src/tui/history-replay.js +47 -0
  29. package/dist/src/tui/history-replay.js.map +1 -0
  30. package/dist/src/tui/initial-prompt.d.ts +24 -0
  31. package/dist/src/tui/initial-prompt.d.ts.map +1 -0
  32. package/dist/src/tui/initial-prompt.js +29 -0
  33. package/dist/src/tui/initial-prompt.js.map +1 -0
  34. package/dist/src/tui/key-handlers.d.ts +49 -0
  35. package/dist/src/tui/key-handlers.d.ts.map +1 -0
  36. package/dist/src/tui/key-handlers.js +192 -0
  37. package/dist/src/tui/key-handlers.js.map +1 -0
  38. package/dist/src/tui/layout.d.ts +71 -0
  39. package/dist/src/tui/layout.d.ts.map +1 -0
  40. package/dist/src/tui/layout.js +245 -0
  41. package/dist/src/tui/layout.js.map +1 -0
  42. package/dist/src/tui/paste-controller.d.ts +90 -0
  43. package/dist/src/tui/paste-controller.d.ts.map +1 -0
  44. package/dist/src/tui/paste-controller.js +234 -0
  45. package/dist/src/tui/paste-controller.js.map +1 -0
  46. package/dist/src/tui/question-picker.d.ts +111 -0
  47. package/dist/src/tui/question-picker.d.ts.map +1 -0
  48. package/dist/src/tui/question-picker.js +308 -0
  49. package/dist/src/tui/question-picker.js.map +1 -0
  50. package/dist/src/tui/renderer-lifecycle.d.ts +20 -0
  51. package/dist/src/tui/renderer-lifecycle.d.ts.map +1 -0
  52. package/dist/src/tui/renderer-lifecycle.js +50 -0
  53. package/dist/src/tui/renderer-lifecycle.js.map +1 -0
  54. package/dist/src/tui/session-subscription.d.ts +30 -0
  55. package/dist/src/tui/session-subscription.d.ts.map +1 -0
  56. package/dist/src/tui/session-subscription.js +78 -0
  57. package/dist/src/tui/session-subscription.js.map +1 -0
  58. package/dist/src/tui/slash-commands.d.ts +23 -0
  59. package/dist/src/tui/slash-commands.d.ts.map +1 -0
  60. package/dist/src/tui/slash-commands.js +79 -0
  61. package/dist/src/tui/slash-commands.js.map +1 -0
  62. package/dist/src/tui/starter-section.d.ts +99 -0
  63. package/dist/src/tui/starter-section.d.ts.map +1 -0
  64. package/dist/src/tui/starter-section.js +254 -0
  65. package/dist/src/tui/starter-section.js.map +1 -0
  66. package/dist/src/tui/status-controller.d.ts +94 -0
  67. package/dist/src/tui/status-controller.d.ts.map +1 -0
  68. package/dist/src/tui/status-controller.js +186 -0
  69. package/dist/src/tui/status-controller.js.map +1 -0
  70. package/dist/src/tui/step-renderer.d.ts +55 -0
  71. package/dist/src/tui/step-renderer.d.ts.map +1 -0
  72. package/dist/src/tui/step-renderer.js +246 -0
  73. package/dist/src/tui/step-renderer.js.map +1 -0
  74. package/dist/src/tui/transcript-writer.d.ts +63 -0
  75. package/dist/src/tui/transcript-writer.d.ts.map +1 -0
  76. package/dist/src/tui/transcript-writer.js +130 -0
  77. package/dist/src/tui/transcript-writer.js.map +1 -0
  78. package/dist/src/turn-runner/turn-runner.d.ts +69 -3
  79. package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
  80. package/dist/src/turn-runner/turn-runner.js +123 -20
  81. package/dist/src/turn-runner/turn-runner.js.map +1 -1
  82. package/package.json +1 -1
@@ -0,0 +1,308 @@
1
+ import { fg, t, } from "@opentui/core";
2
+ import { AUTOCOMPLETE_LIMITS, commitActiveAnswer, formatQuestionOptionDescription, moveQuestionHighlight, NO_HIGHLIGHT, restoreSavedAnswer, } from "./autocomplete.js";
3
+ import { COLORS } from "./theme.js";
4
+ const QUESTION_OPTION_LIMIT = AUTOCOMPLETE_LIMITS.questionOption;
5
+ /**
6
+ * Owns the question picker: an ask terminal hands it a list of
7
+ * `TurnQuestion`s and the picker walks the user through them, accumulating
8
+ * selected option labels until the user either finishes the last question
9
+ * (dispatches an `answer`), types a free-form prompt over the top
10
+ * (`flushWithMessage`), or escapes out (host suppresses next escape so the
11
+ * TUI doesn't also exit).
12
+ *
13
+ * Single-select questions live-record the highlighted option as the answer
14
+ * so arrow navigation alone is enough; multi-select keeps the highlight
15
+ * separate from a checked-set toggled via Space/Enter and grows a synthetic
16
+ * "Done" row that advances to the next question.
17
+ */
18
+ export class QuestionPicker {
19
+ opts;
20
+ pendingQuestions = [];
21
+ activeIndex = 0;
22
+ // `NO_HIGHLIGHT` (-1) means no row is highlighted yet — the user must press
23
+ // Up/Down to land on a concrete row. Single-select live-records the
24
+ // highlight as the answer; multi-select uses highlight purely for
25
+ // navigation and toggles the checked set on Space/Enter.
26
+ optionSelectedIndex = NO_HIGHLIGHT;
27
+ // Per-question checked indices for the active multi-select question. Reset
28
+ // when the picker advances to the next question; single-select questions
29
+ // simply ignore this set and use `optionSelectedIndex` instead.
30
+ multiSelectChecked = new Set();
31
+ // Answers collected while walking the picker, keyed by question text. We
32
+ // dispatch the full map once the user finishes the last question, or flush
33
+ // it early when they decide to type a free-form prompt instead.
34
+ accumulatedAnswers = {};
35
+ constructor(opts) {
36
+ this.opts = opts;
37
+ }
38
+ isOpen() {
39
+ const question = this.pendingQuestions[this.activeIndex];
40
+ return Boolean(question && question.options.length > 0);
41
+ }
42
+ show(questions) {
43
+ this.pendingQuestions = questions;
44
+ this.activeIndex = 0;
45
+ this.optionSelectedIndex = NO_HIGHLIGHT;
46
+ this.multiSelectChecked = new Set();
47
+ this.accumulatedAnswers = {};
48
+ this.render();
49
+ }
50
+ hide() {
51
+ this.pendingQuestions = [];
52
+ this.activeIndex = 0;
53
+ this.optionSelectedIndex = NO_HIGHLIGHT;
54
+ this.multiSelectChecked = new Set();
55
+ this.accumulatedAnswers = {};
56
+ this.opts.questionPanel.visible = false;
57
+ for (const row of this.opts.questionRows) {
58
+ row.visible = false;
59
+ row.content = "";
60
+ }
61
+ }
62
+ /**
63
+ * Confirm whatever is currently highlighted. Multi-select on a regular row
64
+ * toggles; multi-select on the Done row advances. Single-select always
65
+ * advances (highlight = answer is already live-recorded by Up/Down). No-op
66
+ * when nothing is highlighted yet so the user is forced to make an
67
+ * explicit choice (or skip via Right-arrow).
68
+ */
69
+ confirmSelection() {
70
+ const question = this.activeQuestion();
71
+ if (!question)
72
+ return false;
73
+ if (this.optionSelectedIndex === NO_HIGHLIGHT)
74
+ return false;
75
+ if (question.multiSelect && this.optionSelectedIndex !== this.activeQuestionDoneIndex()) {
76
+ this.toggleActiveMultiSelectOption();
77
+ return true;
78
+ }
79
+ return this.advanceOrSubmit();
80
+ }
81
+ /**
82
+ * Dispatch the partially-collected answers together with a free-form
83
+ * `message` (and any pending image attachments) as a single
84
+ * `session.answer({ ..., message, images })` turn, then hide the picker.
85
+ * Used when the user types over the top of an open picker — without this
86
+ * the partial selections would be discarded when the prompt fires.
87
+ */
88
+ flushWithMessage(message, images) {
89
+ void this.opts.session
90
+ .answer({
91
+ questions: this.pendingQuestions,
92
+ answers: this.accumulatedAnswers,
93
+ behavior: "follow_up",
94
+ message,
95
+ images,
96
+ })
97
+ .catch(this.opts.reportError);
98
+ this.hide();
99
+ if (!this.opts.isRunning())
100
+ this.opts.markRunning();
101
+ }
102
+ /**
103
+ * Handle a keystroke while the picker is open. Returns `true` if the key
104
+ * was claimed (and `key.preventDefault()` already called); the caller
105
+ * should stop further dispatch in that case. Returns `false` when the
106
+ * picker is closed or the key isn't one this controller handles.
107
+ */
108
+ handleKey(key) {
109
+ if (!this.isOpen())
110
+ return false;
111
+ if (key.name === "up") {
112
+ this.moveActiveHighlight(-1);
113
+ key.preventDefault();
114
+ return true;
115
+ }
116
+ if (key.name === "down") {
117
+ this.moveActiveHighlight(1);
118
+ key.preventDefault();
119
+ return true;
120
+ }
121
+ // Space confirms the active selection only when the composer is empty
122
+ // so users can still type a free-form prompt that includes spaces.
123
+ // Match either the named form (most terminals) or the literal-char
124
+ // form some kitty-keyboard parsers emit so the binding is robust
125
+ // regardless of how the host reports an unmodified Space.
126
+ if ((key.name === "space" || key.name === " ") && this.opts.inputField.plainText.length === 0) {
127
+ key.preventDefault();
128
+ this.confirmSelection();
129
+ return true;
130
+ }
131
+ // Left/Right navigate between questions, but only when the composer is
132
+ // empty so editing a typed prompt with arrow keys still works.
133
+ if ((key.name === "left" || key.name === "right") &&
134
+ this.opts.inputField.plainText.length === 0 &&
135
+ this.pendingQuestions.length > 1) {
136
+ const direction = key.name === "left" ? -1 : 1;
137
+ if (this.navigateActive(direction)) {
138
+ key.preventDefault();
139
+ return true;
140
+ }
141
+ }
142
+ if (key.name === "escape") {
143
+ key.preventDefault();
144
+ this.opts.onEscapeClose();
145
+ this.hide();
146
+ return true;
147
+ }
148
+ return false;
149
+ }
150
+ // ---- internal -----------------------------------------------------------
151
+ activeQuestion() {
152
+ return this.pendingQuestions[this.activeIndex];
153
+ }
154
+ /**
155
+ * Total navigable rows for the active question. The Up/Down handler clamps
156
+ * navigation to what is actually rendered on screen so a user cannot land
157
+ * the highlight on a row they cannot see.
158
+ */
159
+ activeRowCount() {
160
+ const question = this.activeQuestion();
161
+ if (!question)
162
+ return 0;
163
+ const optionLimit = question.multiSelect ? QUESTION_OPTION_LIMIT - 1 : QUESTION_OPTION_LIMIT;
164
+ const visibleOptionCount = Math.min(question.options.length, optionLimit);
165
+ return visibleOptionCount + (question.multiSelect ? 1 : 0);
166
+ }
167
+ /**
168
+ * Row index of the synthetic Done row when the active question is
169
+ * multi-select; `undefined` otherwise so callers don't compare against a
170
+ * sentinel value. The Done row sits one past the last visible option,
171
+ * clamped to the same limit `render()` uses.
172
+ */
173
+ activeQuestionDoneIndex() {
174
+ const question = this.activeQuestion();
175
+ if (!question?.multiSelect)
176
+ return undefined;
177
+ const optionLimit = QUESTION_OPTION_LIMIT - 1;
178
+ return Math.min(question.options.length, optionLimit);
179
+ }
180
+ render() {
181
+ const question = this.activeQuestion();
182
+ if (!question || question.options.length === 0) {
183
+ this.hide();
184
+ return;
185
+ }
186
+ const { questionPanel, questionTitle, questionRows } = this.opts;
187
+ questionPanel.visible = true;
188
+ const baseTitle = question.header
189
+ ? `${question.header}: ${question.question}`
190
+ : question.question;
191
+ const positionPrefix = this.pendingQuestions.length > 1
192
+ ? `(${this.activeIndex + 1}/${this.pendingQuestions.length}) `
193
+ : "";
194
+ const navHint = this.pendingQuestions.length > 1 ? " [←/→ navigate]" : "";
195
+ questionTitle.content = `${positionPrefix}${baseTitle}${navHint}`;
196
+ const optionLimit = question.multiSelect ? QUESTION_OPTION_LIMIT - 1 : QUESTION_OPTION_LIMIT;
197
+ const visibleOptions = question.options.slice(0, optionLimit);
198
+ const doneIndex = this.activeQuestionDoneIndex();
199
+ for (const [index, row] of questionRows.entries()) {
200
+ if (index < visibleOptions.length) {
201
+ const option = visibleOptions[index];
202
+ const highlighted = index === this.optionSelectedIndex;
203
+ const checkbox = question.multiSelect
204
+ ? this.multiSelectChecked.has(index)
205
+ ? "[x] "
206
+ : "[ ] "
207
+ : "";
208
+ const labelColor = highlighted ? COLORS.status : COLORS.user;
209
+ const description = formatQuestionOptionDescription(option.description);
210
+ const labelLine = `${checkbox}${option.label}`;
211
+ row.content = description
212
+ ? t `${fg(labelColor)(labelLine)}\n${description}`
213
+ : t `${fg(labelColor)(labelLine)}`;
214
+ row.fg = highlighted ? COLORS.agent : COLORS.hint;
215
+ row.visible = true;
216
+ continue;
217
+ }
218
+ if (question.multiSelect && index === visibleOptions.length) {
219
+ // Synthetic Done row carries no checkbox prefix and self-documents
220
+ // its purpose so users discover how to advance from a multi-select.
221
+ const highlighted = doneIndex === this.optionSelectedIndex;
222
+ const labelColor = highlighted ? COLORS.status : COLORS.user;
223
+ const description = formatQuestionOptionDescription("Advance to next question");
224
+ row.content = t `${fg(labelColor)("Done")}\n${description}`;
225
+ row.fg = highlighted ? COLORS.agent : COLORS.hint;
226
+ row.visible = true;
227
+ continue;
228
+ }
229
+ row.visible = false;
230
+ row.content = "";
231
+ }
232
+ }
233
+ moveActiveHighlight(direction) {
234
+ const question = this.activeQuestion();
235
+ if (!question)
236
+ return;
237
+ this.optionSelectedIndex = moveQuestionHighlight(this.optionSelectedIndex, this.activeRowCount(), direction);
238
+ // Single-select live-records the highlight as the answer so a
239
+ // prompt-flush or arrow-nav captures it without requiring Space/Enter.
240
+ // Multi-select keeps highlight separate from the toggled set.
241
+ if (!question.multiSelect) {
242
+ this.accumulatedAnswers = commitActiveAnswer(question, this.optionSelectedIndex, this.multiSelectChecked, this.accumulatedAnswers);
243
+ }
244
+ this.render();
245
+ }
246
+ toggleActiveMultiSelectOption() {
247
+ const question = this.activeQuestion();
248
+ if (!question?.multiSelect)
249
+ return;
250
+ if (this.multiSelectChecked.has(this.optionSelectedIndex)) {
251
+ this.multiSelectChecked.delete(this.optionSelectedIndex);
252
+ }
253
+ else {
254
+ this.multiSelectChecked.add(this.optionSelectedIndex);
255
+ }
256
+ this.accumulatedAnswers = commitActiveAnswer(question, this.optionSelectedIndex, this.multiSelectChecked, this.accumulatedAnswers);
257
+ this.render();
258
+ }
259
+ navigateActive(direction) {
260
+ if (this.pendingQuestions.length <= 1)
261
+ return false;
262
+ const nextIndex = this.activeIndex + direction;
263
+ if (nextIndex < 0 || nextIndex >= this.pendingQuestions.length)
264
+ return false;
265
+ this.accumulatedAnswers = commitActiveAnswer(this.activeQuestion(), this.optionSelectedIndex, this.multiSelectChecked, this.accumulatedAnswers);
266
+ this.activeIndex = nextIndex;
267
+ const restored = restoreSavedAnswer(this.activeQuestion(), this.accumulatedAnswers);
268
+ this.optionSelectedIndex = restored.selectedIndex;
269
+ this.multiSelectChecked = restored.checked;
270
+ this.render();
271
+ return true;
272
+ }
273
+ describeAnswerLabels(question, labels) {
274
+ if (labels.length === 0)
275
+ return question.multiSelect ? "(no selection)" : "";
276
+ return labels.join(", ");
277
+ }
278
+ advanceOrSubmit() {
279
+ const question = this.activeQuestion();
280
+ if (!question)
281
+ return false;
282
+ const accumulatedForActive = this.accumulatedAnswers[question.question] ?? [];
283
+ const transcriptText = this.describeAnswerLabels(question, accumulatedForActive);
284
+ if (transcriptText) {
285
+ this.opts.recordTranscriptEntry("user", transcriptText);
286
+ this.opts.appendBlock("you:", transcriptText, COLORS.user);
287
+ }
288
+ if (this.activeIndex < this.pendingQuestions.length - 1) {
289
+ this.activeIndex += 1;
290
+ const restored = restoreSavedAnswer(this.activeQuestion(), this.accumulatedAnswers);
291
+ this.optionSelectedIndex = restored.selectedIndex;
292
+ this.multiSelectChecked = restored.checked;
293
+ this.render();
294
+ return true;
295
+ }
296
+ void this.opts.session
297
+ .answer({
298
+ questions: this.pendingQuestions,
299
+ answers: this.accumulatedAnswers,
300
+ behavior: "follow_up",
301
+ })
302
+ .catch(this.opts.reportError);
303
+ this.hide();
304
+ this.opts.markRunning();
305
+ return true;
306
+ }
307
+ }
308
+ //# sourceMappingURL=question-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-picker.js","sourceRoot":"","sources":["../../../src/tui/question-picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type CliRenderer } from "@opentui/core";
2
+ /**
3
+ * useMouse: true so the scroll wheel reaches the transcript and OpenTUI
4
+ * receives drag events for in-app text selection. Bare Ctrl+C is the
5
+ * always-exit keystroke (via exitOnCtrlC) so the convention every other
6
+ * interactive Linux/Windows terminal app follows still works here.
7
+ *
8
+ * Tests inject a `createTestRenderer` instance via `input.renderer`; in
9
+ * that mode we skip the production renderer construction and the
10
+ * `globalThis.window` restore that wraps it (the test renderer never
11
+ * installs the shim).
12
+ */
13
+ export declare function acquireRenderer(injected?: CliRenderer): Promise<CliRenderer>;
14
+ /**
15
+ * Resolves when the renderer emits its `destroy` event, running the supplied
16
+ * teardown hook synchronously inside the event listener so chrome writers can
17
+ * flip into a stopped state before the rest of `runTui` returns.
18
+ */
19
+ export declare function waitForRendererDestroy(renderer: CliRenderer, onDestroy: () => void): Promise<void>;
20
+ //# sourceMappingURL=renderer-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/tui/renderer-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,eAAe,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAWlF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,CAOf"}
@@ -0,0 +1,50 @@
1
+ import { createCliRenderer } from "@opentui/core";
2
+ /**
3
+ * useMouse: true so the scroll wheel reaches the transcript and OpenTUI
4
+ * receives drag events for in-app text selection. Bare Ctrl+C is the
5
+ * always-exit keystroke (via exitOnCtrlC) so the convention every other
6
+ * interactive Linux/Windows terminal app follows still works here.
7
+ *
8
+ * Tests inject a `createTestRenderer` instance via `input.renderer`; in
9
+ * that mode we skip the production renderer construction and the
10
+ * `globalThis.window` restore that wraps it (the test renderer never
11
+ * installs the shim).
12
+ */
13
+ export async function acquireRenderer(injected) {
14
+ if (injected)
15
+ return injected;
16
+ const previousWindow = Object.getOwnPropertyDescriptor(globalThis, "window");
17
+ const renderer = await createCliRenderer({
18
+ exitOnCtrlC: true,
19
+ useMouse: true,
20
+ useKittyKeyboard: {},
21
+ targetFps: 60,
22
+ });
23
+ restoreWindowGlobal(previousWindow);
24
+ return renderer;
25
+ }
26
+ /**
27
+ * Resolves when the renderer emits its `destroy` event, running the supplied
28
+ * teardown hook synchronously inside the event listener so chrome writers can
29
+ * flip into a stopped state before the rest of `runTui` returns.
30
+ */
31
+ export function waitForRendererDestroy(renderer, onDestroy) {
32
+ return new Promise((resolve) => {
33
+ renderer.once("destroy", () => {
34
+ onDestroy();
35
+ resolve();
36
+ });
37
+ });
38
+ }
39
+ function restoreWindowGlobal(previousWindow) {
40
+ // OpenTUI installs `window.requestAnimationFrame` for browser-style
41
+ // animation compatibility. In Bun, the presence of `window` can send fetch
42
+ // internals down browser-only paths, while `global.requestAnimationFrame`
43
+ // remains enough for OpenTUI after initialization.
44
+ if (previousWindow) {
45
+ Object.defineProperty(globalThis, "window", previousWindow);
46
+ return;
47
+ }
48
+ delete globalThis.window;
49
+ }
50
+ //# sourceMappingURL=renderer-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer-lifecycle.js","sourceRoot":"","sources":["../../../src/tui/renderer-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAsB;IAC1D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,cAAc,GAAG,MAAM,CAAC,wBAAwB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC;QACvC,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,IAAI;QACd,gBAAgB,EAAE,EAAE;QACpB,SAAS,EAAE,EAAE;KACd,CAAC,CAAC;IACH,mBAAmB,CAAC,cAAc,CAAC,CAAC;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAqB,EACrB,SAAqB;IAErB,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;YAC5B,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,mBAAmB,CAAC,cAA8C;IACzE,oEAAoE;IACpE,2EAA2E;IAC3E,0EAA0E;IAC1E,mDAAmD;IACnD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IACD,OAAQ,UAAuD,CAAC,MAAM,CAAC;AACzE,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { Session } from "../session/session.js";
2
+ import type { QuestionPicker } from "./question-picker.js";
3
+ import type { Sidebar } from "./sidebar.js";
4
+ import type { StatusController } from "./status-controller.js";
5
+ import type { StepRenderer } from "./step-renderer.js";
6
+ export interface SessionSubscriptionDeps {
7
+ session: Session;
8
+ sidebar: Sidebar;
9
+ stepRenderer: StepRenderer;
10
+ statusController: StatusController;
11
+ questionPicker: QuestionPicker;
12
+ appendLine(content: string, fg: string): void;
13
+ appendBlock(label: string | null, body: string, fg: string): void;
14
+ }
15
+ /**
16
+ * Re-paints the sidebar from the session's current state snapshot. Used both
17
+ * inside the event subscription and after the initial boot screen renders.
18
+ */
19
+ export declare function refreshSidebarFromSession(deps: {
20
+ session: Session;
21
+ sidebar: Sidebar;
22
+ }): void;
23
+ /**
24
+ * Subscribes the chrome (transcript, sidebar, status, question picker) to the
25
+ * session's event stream and returns the unsubscribe handle. Every event the
26
+ * runner emits funnels through this one router so the visual layer stays a
27
+ * thin projection of the session state.
28
+ */
29
+ export declare function bindSessionToUi(deps: SessionSubscriptionDeps): () => void;
30
+ //# sourceMappingURL=session-subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-subscription.d.ts","sourceRoot":"","sources":["../../../src/tui/session-subscription.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAkB5F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,GAAG,MAAM,IAAI,CAgDzE"}
@@ -0,0 +1,78 @@
1
+ import { COLORS } from "./theme.js";
2
+ /**
3
+ * Re-paints the sidebar from the session's current state snapshot. Used both
4
+ * inside the event subscription and after the initial boot screen renders.
5
+ */
6
+ export function refreshSidebarFromSession(deps) {
7
+ const { session, sidebar } = deps;
8
+ const state = session.getState();
9
+ sidebar.setTodos(state?.todos ?? []);
10
+ sidebar.setFollowUpQueue(state?.followUpQueue ?? []);
11
+ sidebar.setStateMachine(state?.stateMachine);
12
+ const snap = session.getLastUsage();
13
+ sidebar.setUsage(snap
14
+ ? {
15
+ type: "usage",
16
+ usage: snap.usage,
17
+ effectiveContextWindow: snap.effectiveContextWindow,
18
+ contextWindowUsage: snap.contextWindowUsage,
19
+ }
20
+ : undefined);
21
+ sidebar.setSessionCost(session.getSessionCostUsd());
22
+ }
23
+ /**
24
+ * Subscribes the chrome (transcript, sidebar, status, question picker) to the
25
+ * session's event stream and returns the unsubscribe handle. Every event the
26
+ * runner emits funnels through this one router so the visual layer stays a
27
+ * thin projection of the session state.
28
+ */
29
+ export function bindSessionToUi(deps) {
30
+ const { session, sidebar, stepRenderer, statusController, questionPicker, appendLine, appendBlock, } = deps;
31
+ const refreshSidebar = () => refreshSidebarFromSession({ session, sidebar });
32
+ return session.subscribe((event) => {
33
+ refreshSidebar();
34
+ if (event.type === "step") {
35
+ stepRenderer.renderStep(event.step);
36
+ }
37
+ else if (event.type === "follow_up_queue") {
38
+ // Mirror the count into the working-status line so the user can see
39
+ // queued prompts at a glance without scrolling the sidebar.
40
+ statusController.setQueuedFollowUps(event.prompts.length);
41
+ }
42
+ else if (event.type === "memory") {
43
+ stepRenderer.renderMemoryStatus(event);
44
+ }
45
+ else if (event.type === "system") {
46
+ appendBlock("[system]", event.message, COLORS.system);
47
+ if (event.level === "error")
48
+ statusController.markIdle();
49
+ }
50
+ else if (event.type === "ask") {
51
+ appendBlock("[question]", event.questions.map((q) => q.question).join("\n"), COLORS.system);
52
+ questionPicker.show(event.questions);
53
+ stepRenderer.renderUsage(event.usage);
54
+ stepRenderer.renderTurnElapsed();
55
+ statusController.markIdle(event);
56
+ }
57
+ else if (event.type === "complete") {
58
+ if (event.error) {
59
+ appendBlock("[error]", event.error, COLORS.error);
60
+ }
61
+ stepRenderer.renderUsage(event.usage);
62
+ stepRenderer.renderTurnElapsed();
63
+ statusController.markIdle(event);
64
+ }
65
+ else if (event.type === "interrupted") {
66
+ appendLine("[interrupted]", COLORS.system);
67
+ stepRenderer.renderUsage(event.usage);
68
+ stepRenderer.renderTurnElapsed();
69
+ statusController.markIdle(event);
70
+ }
71
+ else if (event.type === "sleep") {
72
+ stepRenderer.renderUsage(event.usage);
73
+ stepRenderer.renderSleeping(event.wakeAt);
74
+ statusController.markIdle(event);
75
+ }
76
+ });
77
+ }
78
+ //# sourceMappingURL=session-subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-subscription.js","sourceRoot":"","sources":["../../../src/tui/session-subscription.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAYpC;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAA4C;IACpF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CACd,IAAI;QACF,CAAC,CAAC;YACE,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,sBAAsB,EAAE,IAAI,CAAC,sBAAsB;YACnD,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;SAC5C;QACH,CAAC,CAAC,SAAS,CACd,CAAC;IACF,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAA6B;IAC3D,MAAM,EACJ,OAAO,EACP,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,WAAW,GACZ,GAAG,IAAI,CAAC;IACT,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,yBAAyB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7E,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,KAAgB,EAAE,EAAE;QAC5C,cAAc,EAAE,CAAC;QACjB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC5C,oEAAoE;YACpE,4DAA4D;YAC5D,gBAAgB,CAAC,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,YAAY,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO;gBAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAC3D,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAChC,WAAW,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5F,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACrC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,YAAY,CAAC,iBAAiB,EAAE,CAAC;YACjC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACpD,CAAC;YACD,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,YAAY,CAAC,iBAAiB,EAAE,CAAC;YACjC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACxC,UAAU,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3C,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,YAAY,CAAC,iBAAiB,EAAE,CAAC;YACjC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAClC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1C,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { CopyController } from "./copy-controller.js";
2
+ import type { PasteController } from "./paste-controller.js";
3
+ import type { TranscriptWriter } from "./transcript-writer.js";
4
+ /**
5
+ * Surface used by the slash-command dispatcher. Every handler in this module
6
+ * is stateless and reaches its mutable state through the controllers wired up
7
+ * by `runTui`. The dispatcher returns true when the message was claimed so
8
+ * `submit()` can short-circuit before the message reaches `session.prompt()`.
9
+ */
10
+ export interface SlashCommandContext {
11
+ pasteController: PasteController;
12
+ copyController: CopyController;
13
+ transcriptWriter: TranscriptWriter;
14
+ appendBlock(label: string | null, body: string, fg: string): void;
15
+ }
16
+ /**
17
+ * Routes the message to a local handler when it begins with a slash command
18
+ * recognized by the TUI. Returns true when handled (caller must not forward
19
+ * the message to the runner) and false when the message should fall through
20
+ * to the normal prompt path.
21
+ */
22
+ export declare function tryDispatchSlashCommand(message: string, ctx: SlashCommandContext): boolean;
23
+ //# sourceMappingURL=slash-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../../src/tui/slash-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CA2B1F"}
@@ -0,0 +1,79 @@
1
+ import { submitDuetFeedback } from "../lib/feedback.js";
2
+ import { COLORS } from "./theme.js";
3
+ /**
4
+ * Routes the message to a local handler when it begins with a slash command
5
+ * recognized by the TUI. Returns true when handled (caller must not forward
6
+ * the message to the runner) and false when the message should fall through
7
+ * to the normal prompt path.
8
+ */
9
+ export function tryDispatchSlashCommand(message, ctx) {
10
+ if (message.startsWith("/image ") || message === "/image") {
11
+ void handleImageSlashCommand(message, ctx);
12
+ return true;
13
+ }
14
+ if (message === "/paste") {
15
+ void ctx.pasteController.triggerClipboardProbe("slash");
16
+ return true;
17
+ }
18
+ if (message === "/clear-images") {
19
+ ctx.pasteController.clearPendingImages();
20
+ ctx.appendBlock("[paste]", "cleared pending image attachments", COLORS.system);
21
+ return true;
22
+ }
23
+ if (message === "/copy" || message.startsWith("/copy ")) {
24
+ void ctx.copyController.handleCopySlashCommand(message);
25
+ return true;
26
+ }
27
+ if (message === "/diag" || message.startsWith("/diag ")) {
28
+ handleDiagSlashCommand(message, ctx);
29
+ return true;
30
+ }
31
+ if (message === "/feedback" || message.startsWith("/feedback ")) {
32
+ void handleFeedbackSlashCommand(message, ctx);
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ /**
38
+ * `/diag` toggles a key + selection event log so users can show us exactly
39
+ * what their terminal forwards when something silently fails (a keystroke
40
+ * that never reaches the handler, a selection event firing with empty text).
41
+ * Kept as a flag rather than a one-shot capture so we can layer additional
42
+ * diagnostic facets on the same surface without inventing new commands.
43
+ */
44
+ function handleDiagSlashCommand(raw, ctx) {
45
+ const argument = raw === "/diag" ? "" : raw.slice("/diag ".length).trim();
46
+ if (argument === "" || argument === "keys") {
47
+ const enabled = !ctx.transcriptWriter.isKeyDiagnosticsEnabled();
48
+ ctx.transcriptWriter.setKeyDiagnosticsEnabled(enabled);
49
+ ctx.appendBlock("[diag]", enabled
50
+ ? "key + selection event logging ON. Run /diag again to stop."
51
+ : "key + selection event logging OFF.", COLORS.system);
52
+ return;
53
+ }
54
+ ctx.appendBlock("[diag]", "Usage: /diag (or /diag keys) — toggles key + selection event logging", COLORS.system);
55
+ }
56
+ async function handleFeedbackSlashCommand(raw, ctx) {
57
+ const content = raw.slice("/feedback".length).trim();
58
+ if (!content) {
59
+ ctx.appendBlock("[feedback]", "Usage: /feedback <message> — send free-form feedback to the Duet team", COLORS.system);
60
+ return;
61
+ }
62
+ ctx.appendBlock("[feedback]", "sending…", COLORS.system);
63
+ try {
64
+ const { baseUrl } = await submitDuetFeedback({ content });
65
+ ctx.appendBlock("[feedback]", `Thanks! Feedback sent to ${baseUrl}.`, COLORS.system);
66
+ }
67
+ catch (error) {
68
+ ctx.appendBlock("[feedback]", error instanceof Error ? error.message : String(error), COLORS.error);
69
+ }
70
+ }
71
+ async function handleImageSlashCommand(raw, ctx) {
72
+ const rest = raw.slice("/image".length).trim();
73
+ if (!rest) {
74
+ ctx.appendBlock("[paste]", "Usage: /image <path> — attach a PNG/JPEG/GIF/WebP from disk", COLORS.system);
75
+ return;
76
+ }
77
+ await ctx.pasteController.attachImageFromPath(rest);
78
+ }
79
+ //# sourceMappingURL=slash-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../../src/tui/slash-commands.ts"],"names":[],"mappings":"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"}