@astralyn/pi 0.84.5 → 0.84.7

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 (119) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +4 -0
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/index.d.ts +1 -1
  6. package/dist/core/extensions/index.d.ts.map +1 -1
  7. package/dist/core/extensions/index.js.map +1 -1
  8. package/dist/core/extensions/runner.d.ts +1 -0
  9. package/dist/core/extensions/runner.d.ts.map +1 -1
  10. package/dist/core/extensions/runner.js +6 -0
  11. package/dist/core/extensions/runner.js.map +1 -1
  12. package/dist/core/extensions/types.d.ts +11 -0
  13. package/dist/core/extensions/types.d.ts.map +1 -1
  14. package/dist/core/extensions/types.js.map +1 -1
  15. package/dist/core/tools/bash.d.ts +5 -0
  16. package/dist/core/tools/bash.d.ts.map +1 -1
  17. package/dist/core/tools/bash.js +1 -1
  18. package/dist/core/tools/bash.js.map +1 -1
  19. package/dist/extensions/background/index.d.ts +95 -20
  20. package/dist/extensions/background/index.d.ts.map +1 -1
  21. package/dist/extensions/background/index.js +470 -136
  22. package/dist/extensions/background/index.js.map +1 -1
  23. package/dist/extensions/background/manager.d.ts +14 -0
  24. package/dist/extensions/background/manager.d.ts.map +1 -1
  25. package/dist/extensions/background/manager.js +125 -24
  26. package/dist/extensions/background/manager.js.map +1 -1
  27. package/dist/extensions/background/registry.d.ts +82 -0
  28. package/dist/extensions/background/registry.d.ts.map +1 -1
  29. package/dist/extensions/background/registry.js +237 -6
  30. package/dist/extensions/background/registry.js.map +1 -1
  31. package/dist/extensions/background/render.d.ts +35 -11
  32. package/dist/extensions/background/render.d.ts.map +1 -1
  33. package/dist/extensions/background/render.js +180 -30
  34. package/dist/extensions/background/render.js.map +1 -1
  35. package/dist/extensions/router/dialog-host.d.ts +60 -0
  36. package/dist/extensions/router/dialog-host.d.ts.map +1 -0
  37. package/dist/extensions/router/dialog-host.js +196 -0
  38. package/dist/extensions/router/dialog-host.js.map +1 -0
  39. package/dist/extensions/router/probe.d.ts.map +1 -1
  40. package/dist/extensions/router/probe.js +2 -0
  41. package/dist/extensions/router/probe.js.map +1 -1
  42. package/dist/extensions/router/ui.d.ts.map +1 -1
  43. package/dist/extensions/router/ui.js +117 -67
  44. package/dist/extensions/router/ui.js.map +1 -1
  45. package/dist/extensions/subagent/activity.d.ts +3 -5
  46. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  47. package/dist/extensions/subagent/activity.js +8 -89
  48. package/dist/extensions/subagent/activity.js.map +1 -1
  49. package/dist/extensions/subagent/agents-command.d.ts.map +1 -1
  50. package/dist/extensions/subagent/agents-command.js +391 -161
  51. package/dist/extensions/subagent/agents-command.js.map +1 -1
  52. package/dist/extensions/subagent/agents.d.ts +2 -0
  53. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  54. package/dist/extensions/subagent/agents.js +7 -1
  55. package/dist/extensions/subagent/agents.js.map +1 -1
  56. package/dist/extensions/subagent/budget.d.ts +15 -0
  57. package/dist/extensions/subagent/budget.d.ts.map +1 -0
  58. package/dist/extensions/subagent/budget.js +96 -0
  59. package/dist/extensions/subagent/budget.js.map +1 -0
  60. package/dist/extensions/subagent/cancellation.d.ts +29 -0
  61. package/dist/extensions/subagent/cancellation.d.ts.map +1 -0
  62. package/dist/extensions/subagent/cancellation.js +73 -0
  63. package/dist/extensions/subagent/cancellation.js.map +1 -0
  64. package/dist/extensions/subagent/index.d.ts.map +1 -1
  65. package/dist/extensions/subagent/index.js +5 -22
  66. package/dist/extensions/subagent/index.js.map +1 -1
  67. package/dist/extensions/subagent/model-selection.d.ts +16 -0
  68. package/dist/extensions/subagent/model-selection.d.ts.map +1 -0
  69. package/dist/extensions/subagent/model-selection.js +51 -0
  70. package/dist/extensions/subagent/model-selection.js.map +1 -0
  71. package/dist/extensions/subagent/render.d.ts +6 -1
  72. package/dist/extensions/subagent/render.d.ts.map +1 -1
  73. package/dist/extensions/subagent/render.js +23 -12
  74. package/dist/extensions/subagent/render.js.map +1 -1
  75. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  76. package/dist/extensions/subagent/resolve.js +2 -21
  77. package/dist/extensions/subagent/resolve.js.map +1 -1
  78. package/dist/extensions/subagent/runner.d.ts +4 -5
  79. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  80. package/dist/extensions/subagent/runner.js +71 -264
  81. package/dist/extensions/subagent/runner.js.map +1 -1
  82. package/dist/extensions/subagent/sdk-runner.d.ts +13 -11
  83. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  84. package/dist/extensions/subagent/sdk-runner.js +167 -148
  85. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  86. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  87. package/dist/extensions/subagent/settings.js +11 -4
  88. package/dist/extensions/subagent/settings.js.map +1 -1
  89. package/dist/extensions/subagent/state.d.ts +89 -0
  90. package/dist/extensions/subagent/state.d.ts.map +1 -0
  91. package/dist/extensions/subagent/state.js +294 -0
  92. package/dist/extensions/subagent/state.js.map +1 -0
  93. package/dist/extensions/subagent/text.d.ts +4 -0
  94. package/dist/extensions/subagent/text.d.ts.map +1 -1
  95. package/dist/extensions/subagent/text.js +75 -0
  96. package/dist/extensions/subagent/text.js.map +1 -1
  97. package/dist/extensions/subagent/types.d.ts +3 -8
  98. package/dist/extensions/subagent/types.d.ts.map +1 -1
  99. package/dist/extensions/subagent/types.js.map +1 -1
  100. package/dist/extensions/subagent/ui/choices.d.ts +36 -0
  101. package/dist/extensions/subagent/ui/choices.d.ts.map +1 -0
  102. package/dist/extensions/subagent/ui/choices.js +54 -0
  103. package/dist/extensions/subagent/ui/choices.js.map +1 -0
  104. package/dist/extensions/subagent/ui/model-list.d.ts +1 -0
  105. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  106. package/dist/extensions/subagent/ui/model-list.js +15 -18
  107. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  108. package/dist/index.d.ts +1 -1
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js.map +1 -1
  111. package/docs/bundled/README.md +1 -1
  112. package/docs/bundled/extensions/background.md +55 -20
  113. package/docs/extensions.md +13 -0
  114. package/npm-shrinkwrap.json +2 -2
  115. package/package.json +1 -1
  116. package/dist/extensions/subagent/retry.d.ts +0 -17
  117. package/dist/extensions/subagent/retry.d.ts.map +0 -1
  118. package/dist/extensions/subagent/retry.js +0 -41
  119. package/dist/extensions/subagent/retry.js.map +0 -1
@@ -1,20 +1,63 @@
1
1
  /**
2
- * Transcript rendering for the background extension: tool call/result rows
3
- * and the completion-notification message. Style follows the built-in bash
4
- * presentation: a `$` prompt row with an `&` marker for the background call.
2
+ * Transcript rendering for the background extension: the single bg tool's
3
+ * call/result rows (dispatched on action) and the completion-notification
4
+ * message. Style follows the built-in bash presentation: a `$` prompt row
5
+ * with an `&` marker for the background call.
5
6
  */
6
7
  import { Container, Text, TruncatedText } from "@earendil-works/pi-tui";
8
+ import { formatSize } from "../../core/tools/truncate.js";
7
9
  import { highlightCode } from "../../modes/interactive/theme/theme.js";
8
- import { firstCommandLine, formatDuration } from "./registry.js";
10
+ import { BG_WAIT_DEFAULT_MS, BG_WAIT_MAX_MS, BG_WAIT_MIN_MS, firstCommandLine, formatDuration, } from "./registry.js";
9
11
  const COMMAND_PREVIEW_LIMIT = 120;
10
12
  const NOTIFY_TAIL_LIMIT = 4000;
13
+ /** Cap for expanded transcript views of tool-result text (already bounded at the source). */
14
+ const RESULT_EXPAND_LIMIT = 4000;
15
+ /** Live pending-wait line refresh cadence; the first settled render clears the timer. */
16
+ const WAIT_REFRESH_MS = 1000;
17
+ /**
18
+ * Arm a one-shot 1s refresh while a wait call is pending, clear it once
19
+ * settled. The timer fires → context.invalidate() → renderCall re-runs →
20
+ * re-arms, so at most one armed timer exists per tool row. Unref'd: it can
21
+ * never hold the process open.
22
+ */
23
+ export function scheduleWaitRefresh(context, pending) {
24
+ const state = context.state;
25
+ if (state === undefined)
26
+ return; // Standalone render without shell state: nothing to schedule.
27
+ if (pending) {
28
+ if (state.refreshTimer === undefined) {
29
+ state.refreshTimer = setTimeout(() => {
30
+ state.refreshTimer = undefined;
31
+ context.invalidate();
32
+ }, WAIT_REFRESH_MS);
33
+ state.refreshTimer.unref?.();
34
+ }
35
+ return;
36
+ }
37
+ if (state.refreshTimer !== undefined) {
38
+ clearTimeout(state.refreshTimer);
39
+ state.refreshTimer = undefined;
40
+ }
41
+ state.waitStartedAt = undefined;
42
+ state.waitBaselineBytes = undefined;
43
+ }
44
+ /** Basename of a path, slash-normalized — compact display of output files. */
45
+ export function fileNameOf(path) {
46
+ return path.replace(/\\/g, "/").split("/").at(-1) ?? path;
47
+ }
11
48
  function bgPrompt(theme) {
12
49
  return theme.fg("toolTitle", theme.bold("$ "));
13
50
  }
14
51
  function timeoutSuffix(timeout, theme) {
15
52
  return timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
16
53
  }
17
- export function statusGlyph(status) {
54
+ /** Keep the END of oversized text — that is where the outcome lives. */
55
+ function capForTranscript(text, limit) {
56
+ return text.length > limit ? `…${text.slice(-limit)}` : text;
57
+ }
58
+ export function statusGlyph(status, stalled) {
59
+ if (stalled)
60
+ return "…";
18
61
  switch (status) {
19
62
  case "completed":
20
63
  return "✓";
@@ -27,7 +70,9 @@ export function statusGlyph(status) {
27
70
  return "●";
28
71
  }
29
72
  }
30
- export function statusColor(status) {
73
+ export function statusColor(status, stalled) {
74
+ if (stalled)
75
+ return "warning";
31
76
  switch (status) {
32
77
  case "completed":
33
78
  return "success";
@@ -45,9 +90,72 @@ export function commandLabel(command, width) {
45
90
  const characters = [...firstCommandLine(command)];
46
91
  return characters.length <= width ? characters.join("") : `${characters.slice(0, Math.max(0, width - 1)).join("")}…`;
47
92
  }
48
- export function renderBgBashCall(args, theme, context) {
93
+ // ── tool call ─────────────────────────────────────────────────────────────
94
+ export function renderBgCall(args, theme, context, getTaskLive) {
95
+ switch (args.action) {
96
+ case "create":
97
+ return typeof args.command === "string"
98
+ ? renderCreateCall(args, theme, context)
99
+ : new Text(theme.fg("toolTitle", theme.bold("bg create")), 0, 0);
100
+ case "read": {
101
+ const mode = args.mode ?? "tail";
102
+ const size = args.bytes !== undefined ? ` ${formatSize(args.bytes)}` : "";
103
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg read "))}${theme.fg("accent", args.taskId ?? "")}${theme.fg("muted", ` ${mode}${size}`)}`, 0, 0);
104
+ }
105
+ case "wait":
106
+ return renderWaitCall(args, theme, context, getTaskLive);
107
+ case "kill":
108
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg kill "))}${theme.fg("accent", args.taskId ?? "")}`, 0, 0);
109
+ case "list":
110
+ return new Text(theme.fg("toolTitle", theme.bold("bg list")), 0, 0);
111
+ default: {
112
+ // Tool arguments arrive incrementally while the model is still emitting
113
+ // JSON. Keep the call row renderable until `action` becomes valid.
114
+ const action = typeof args.action === "string" ? ` ${theme.fg("dim", args.action)}` : "";
115
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg"))}${action}`, 0, 0);
116
+ }
117
+ }
118
+ }
119
+ /** Mirror the execution-side clamp so the shown window matches the real one. */
120
+ export function clampWaitMs(waitMs) {
121
+ return Math.min(BG_WAIT_MAX_MS, Math.max(BG_WAIT_MIN_MS, Math.floor(waitMs ?? BG_WAIT_DEFAULT_MS)));
122
+ }
123
+ /**
124
+ * Pending wait call: elapsed/wait-window and the new-output delta since the
125
+ * wait began, refreshed once per second by scheduleWaitRefresh until the
126
+ * result settles and takes over the row.
127
+ */
128
+ function renderWaitCall(args, theme, context, getTaskLive) {
129
+ const taskId = typeof args.taskId === "string" ? args.taskId.trim() : "";
130
+ const live = getTaskLive !== undefined && taskId ? getTaskLive(taskId) : undefined;
131
+ // The live display needs shell-owned state; without it (standalone renders)
132
+ // fall back to the static line.
133
+ const pending = context.isPartial === true && context.state !== undefined && taskId.length > 0;
134
+ scheduleWaitRefresh(context, pending);
135
+ if (!pending) {
136
+ const ms = args.waitMs !== undefined ? ` ${formatDuration(clampWaitMs(args.waitMs))}` : "";
137
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg wait "))}${theme.fg("accent", args.taskId ?? "")}${theme.fg("muted", ms)}`, 0, 0);
138
+ }
139
+ const state = context.state;
140
+ if (state.waitStartedAt === undefined)
141
+ state.waitStartedAt = Date.now();
142
+ const startedAt = state.waitStartedAt;
143
+ let liveBase;
144
+ if (live) {
145
+ if (state.waitBaselineBytes === undefined)
146
+ state.waitBaselineBytes = live.outputBytes;
147
+ liveBase = state.waitBaselineBytes;
148
+ }
149
+ const parts = [`waiting ${formatDuration(Date.now() - startedAt)}/${formatDuration(clampWaitMs(args.waitMs))}`];
150
+ if (live && live.status === "running" && liveBase !== undefined && live.outputBytes > liveBase) {
151
+ parts.push(`+${formatSize(live.outputBytes - liveBase)} new output`);
152
+ }
153
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg wait "))}${theme.fg("accent", taskId)} ${theme.fg("muted", parts.join(" · "))}`, 0, 0);
154
+ }
155
+ function renderCreateCall(args, theme, context) {
49
156
  const lines = args.command.split(/\r?\n/).filter((line) => line.trim().length > 0);
50
- const suffix = theme.fg("muted", " &") + timeoutSuffix(args.timeout, theme);
157
+ const label = args.description ? theme.fg("muted", ` · ${args.description}`) : "";
158
+ const suffix = theme.fg("muted", " &") + timeoutSuffix(args.timeout, theme) + label;
51
159
  if (context.expanded && lines.length > 1) {
52
160
  const body = highlightCode(args.command, "bash").join("\n");
53
161
  return new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);
@@ -60,35 +168,74 @@ export function renderBgBashCall(args, theme, context) {
60
168
  }
61
169
  return new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);
62
170
  }
63
- export function renderBgBashResult(result, _options, theme, context) {
64
- if (context.isError) {
171
+ // ── tool result ───────────────────────────────────────────────────────────
172
+ export function renderBgResult(result, options, theme, context) {
173
+ // bg results are always settled today; clear defensively so no live-refresh
174
+ // timer can outlive its row if a host ever streams partial results.
175
+ if (context.args?.action === "wait" && !options.isPartial)
176
+ scheduleWaitRefresh(context, false);
177
+ const details = result.details;
178
+ if (context.isError || !details) {
65
179
  const text = result.content.find((part) => part.type === "text")?.text ?? "";
66
180
  return new Text(theme.fg("toolOutput", text.trimEnd()), 0, 0);
67
181
  }
68
- const details = result.details;
69
- if (!details)
70
- return new Text(theme.fg("toolOutput", "Background task started."), 0, 0);
71
- const line = `${theme.fg("muted", "→ task ")}${theme.fg("accent", details.taskId)}${theme.fg("muted", ` started · ${details.outputPath}`)}`;
72
- return new Text(line, 0, 0);
73
- }
74
- export function renderBgLogsCall(args, theme) {
75
- const mode = args.mode ?? "tail";
76
- const size = args.bytes !== undefined ? ` ${args.bytes}B` : "";
77
- return new Text(`${theme.fg("toolTitle", theme.bold("bg logs "))}${theme.fg("accent", args.taskId)}${theme.fg("muted", ` ${mode}${size}`)}`, 0, 0);
182
+ const container = new Container();
183
+ container.addChild(new TruncatedText(resultSummaryLine(details, theme), 1, 0));
184
+ if (options.expanded) {
185
+ const text = result.content.find((part) => part.type === "text")?.text ?? "";
186
+ container.addChild(new Text("", 0, 0));
187
+ container.addChild(new Text(theme.fg("toolOutput", capForTranscript(text, RESULT_EXPAND_LIMIT).trimEnd()), 1, 0));
188
+ }
189
+ return container;
78
190
  }
79
- export function renderBgKillCall(args, theme) {
80
- return new Text(`${theme.fg("toolTitle", theme.bold("bg kill "))}${theme.fg("accent", args.taskId)}`, 0, 0);
191
+ function resultSummaryLine(details, theme) {
192
+ switch (details.action) {
193
+ case "create": {
194
+ const label = details.description ? ` (${details.description})` : "";
195
+ return `${theme.fg("muted", "→ task ")}${theme.fg("accent", `${details.taskId}${label}`)}${theme.fg("muted", ` started · ${details.outputPath}`)}`;
196
+ }
197
+ case "read": {
198
+ const size = details.sliceBytes !== details.totalBytes
199
+ ? `${details.mode} ${formatSize(details.sliceBytes)} of ${formatSize(details.totalBytes)}`
200
+ : formatSize(details.totalBytes);
201
+ return `${theme.fg("muted", "→ ")}${theme.fg("accent", details.taskId)}${theme.fg("muted", ` ${size} · ${details.outputPath}`)}`;
202
+ }
203
+ case "wait": {
204
+ if (details.timedOut) {
205
+ return `${theme.fg(statusColor(details.status), statusGlyph(details.status))} ${theme.fg("accent", details.taskId)}${theme.fg("muted", ` still running · waited ${formatDuration(details.waitedMs)} · ${formatSize(details.totalBytes)}`)}`;
206
+ }
207
+ const exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : "";
208
+ return `${theme.fg(statusColor(details.status), statusGlyph(details.status))} ${theme.fg("accent", details.taskId)}${theme.fg("muted", ` ${details.status}${exit} · waited ${formatDuration(details.waitedMs)} · +${formatSize(details.deltaBytes)}`)}`;
209
+ }
210
+ case "kill":
211
+ return `${theme.fg(statusColor("killed"), statusGlyph("killed"))} ${theme.fg("accent", details.taskId)}${theme.fg("muted", " stopped")}`;
212
+ case "list": {
213
+ const hidden = details.hidden > 0 ? ` · ${details.hidden} more finished` : "";
214
+ return theme.fg("muted", `${details.running} running · ${details.finished} finished${hidden}`);
215
+ }
216
+ }
81
217
  }
218
+ // ── completion notification ───────────────────────────────────────────────
82
219
  /** One-line summary of a finished task, shared by the notification renderer. */
83
220
  function taskSummaryLine(details, theme) {
84
221
  const runtime = formatDuration(details.runtimeMs);
85
222
  const exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : "";
223
+ if (details.stalled) {
224
+ const glyph = theme.fg("warning", "…");
225
+ const label = details.description
226
+ ? `${details.description} (${commandLabel(details.command, 40)})`
227
+ : commandLabel(details.command, COMMAND_PREVIEW_LIMIT);
228
+ return `${glyph} ${theme.fg("accent", details.taskId)} ${label} ${theme.fg("muted", `— waiting for input (${runtime})`)}`;
229
+ }
86
230
  const outcome = theme.fg(statusColor(details.status), `${details.status}${exit} in ${runtime}`);
87
231
  const glyph = theme.fg(statusColor(details.status), statusGlyph(details.status));
88
- return `${glyph} ${theme.fg("accent", details.taskId)} ${commandLabel(details.command, COMMAND_PREVIEW_LIMIT)} ${theme.fg("muted", `— ${outcome}`)}`;
232
+ const label = details.description
233
+ ? `${details.description} (${commandLabel(details.command, 40)})`
234
+ : commandLabel(details.command, COMMAND_PREVIEW_LIMIT);
235
+ return `${glyph} ${theme.fg("accent", details.taskId)} ${label} ${theme.fg("muted", `— ${outcome}`)}`;
89
236
  }
90
237
  /**
91
- * Collapsed: status glyph, task id, command, and outcome on one line, output
238
+ * Collapsed: status glyph, task id, label, and outcome on one line, output
92
239
  * path below. Expanded adds the embedded output tail. Returns undefined for
93
240
  * malformed details so the default custom-message rendering takes over.
94
241
  */
@@ -98,21 +245,24 @@ export function renderBackgroundNotification(message, options, theme) {
98
245
  return undefined;
99
246
  const container = new Container();
100
247
  container.addChild(new TruncatedText(taskSummaryLine(details, theme), 1, 0));
101
- container.addChild(new Text(theme.fg("muted", details.outputPath), 1, 0));
248
+ // Collapsed keeps the row compact with the file name; the full path
249
+ // (model-relevant, human-rarely) shows when expanded.
250
+ const shownPath = options.expanded ? details.outputPath : fileNameOf(details.outputPath);
251
+ container.addChild(new Text(theme.fg("muted", shownPath), 1, 0));
102
252
  if (details.tailError) {
103
253
  container.addChild(new Text(theme.fg("error", `Output unavailable: ${details.tailError}`), 1, 0));
104
254
  }
105
255
  else if (options.expanded && details.tailText) {
106
256
  container.addChild(new Text("", 0, 0));
107
- // Keep the END of the tail when over budget — that is where the outcome lives.
108
- const tail = details.tailText.length > NOTIFY_TAIL_LIMIT
109
- ? `…${details.tailText.slice(-NOTIFY_TAIL_LIMIT)}`
110
- : details.tailText;
257
+ const tail = capForTranscript(details.tailText, NOTIFY_TAIL_LIMIT);
111
258
  const truncatedNote = details.tailTruncated
112
- ? `\n[showing tail of ${details.totalBytes} bytes; full output: ${details.outputPath}]`
259
+ ? `\n[showing tail of ${formatSize(details.totalBytes)} bytes; full output: ${details.outputPath}]`
113
260
  : "";
114
261
  container.addChild(new Text(`${theme.fg("toolOutput", tail.trimEnd())}${theme.fg("muted", truncatedNote)}`, 1, 0));
115
262
  }
263
+ if (details.stalled && options.expanded) {
264
+ container.addChild(new Text(theme.fg("warning", "Blocked on interactive input — kill (bg action kill) and re-run with piped input or a non-interactive flag."), 1, 0));
265
+ }
116
266
  return container;
117
267
  }
118
268
  //# sourceMappingURL=render.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/extensions/background/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Transcript rendering for the background extension: tool call/result rows\n * and the completion-notification message. Style follows the built-in bash\n * presentation: a `$` prompt row with an `&` marker for the background call.\n */\n\nimport { type Component, Container, Text, TruncatedText } from \"@earendil-works/pi-tui\";\nimport type {\n\tAgentToolResult,\n\tMessageRenderOptions,\n\tToolRenderContext,\n\tToolRenderResultOptions,\n} from \"../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../core/messages.ts\";\nimport { highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { BgBashDetails, BgBashInput, BgKillInput, BgLogsInput, BgNotificationDetails } from \"./index.ts\";\nimport { type BgTaskStatus, firstCommandLine, formatDuration } from \"./registry.ts\";\n\nconst COMMAND_PREVIEW_LIMIT = 120;\nconst NOTIFY_TAIL_LIMIT = 4000;\n\nfunction bgPrompt(theme: Theme): string {\n\treturn theme.fg(\"toolTitle\", theme.bold(\"$ \"));\n}\n\nfunction timeoutSuffix(timeout: number | undefined, theme: Theme): string {\n\treturn timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n}\n\nexport function statusGlyph(status: BgTaskStatus): string {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"✓\";\n\t\tcase \"failed\":\n\t\tcase \"timeout\":\n\t\t\treturn \"✗\";\n\t\tcase \"killed\":\n\t\t\treturn \"○\";\n\t\tdefault:\n\t\t\treturn \"●\";\n\t}\n}\n\nexport function statusColor(status: BgTaskStatus): \"success\" | \"error\" | \"warning\" | \"accent\" {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tcase \"timeout\":\n\t\tcase \"killed\":\n\t\t\treturn \"warning\";\n\t\tdefault:\n\t\t\treturn \"accent\";\n\t}\n}\n\n/** First command line truncated to a visible-character budget with an ellipsis. */\nexport function commandLabel(command: string, width: number): string {\n\tconst characters = [...firstCommandLine(command)];\n\treturn characters.length <= width ? characters.join(\"\") : `${characters.slice(0, Math.max(0, width - 1)).join(\"\")}…`;\n}\n\nexport function renderBgBashCall(args: BgBashInput, theme: Theme, context: ToolRenderContext): Component {\n\tconst lines = args.command.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n\tconst suffix = theme.fg(\"muted\", \" &\") + timeoutSuffix(args.timeout, theme);\n\tif (context.expanded && lines.length > 1) {\n\t\tconst body = highlightCode(args.command, \"bash\").join(\"\\n\");\n\t\treturn new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);\n\t}\n\tconst body = highlightCode(firstCommandLine(args.command), \"bash\").join(\"\\n\");\n\tif (lines.length > 1) {\n\t\tconst hidden = lines.length - 1;\n\t\tconst more = theme.fg(\"muted\", ` (+${hidden} line${hidden === 1 ? \"\" : \"s\"})`);\n\t\treturn new Text(`${bgPrompt(theme)}${body}${suffix}${more}`, 0, 0);\n\t}\n\treturn new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);\n}\n\nexport function renderBgBashResult(\n\tresult: AgentToolResult<BgBashDetails | undefined>,\n\t_options: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcontext: ToolRenderContext,\n): Component {\n\tif (context.isError) {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text ?? \"\";\n\t\treturn new Text(theme.fg(\"toolOutput\", text.trimEnd()), 0, 0);\n\t}\n\tconst details = result.details;\n\tif (!details) return new Text(theme.fg(\"toolOutput\", \"Background task started.\"), 0, 0);\n\tconst line = `${theme.fg(\"muted\", \"→ task \")}${theme.fg(\"accent\", details.taskId)}${theme.fg(\n\t\t\"muted\",\n\t\t` started · ${details.outputPath}`,\n\t)}`;\n\treturn new Text(line, 0, 0);\n}\n\nexport function renderBgLogsCall(args: BgLogsInput, theme: Theme): Component {\n\tconst mode = args.mode ?? \"tail\";\n\tconst size = args.bytes !== undefined ? ` ${args.bytes}B` : \"\";\n\treturn new Text(\n\t\t`${theme.fg(\"toolTitle\", theme.bold(\"bg logs \"))}${theme.fg(\"accent\", args.taskId)}${theme.fg(\"muted\", ` ${mode}${size}`)}`,\n\t\t0,\n\t\t0,\n\t);\n}\n\nexport function renderBgKillCall(args: BgKillInput, theme: Theme): Component {\n\treturn new Text(`${theme.fg(\"toolTitle\", theme.bold(\"bg kill \"))}${theme.fg(\"accent\", args.taskId)}`, 0, 0);\n}\n\n/** One-line summary of a finished task, shared by the notification renderer. */\nfunction taskSummaryLine(details: BgNotificationDetails, theme: Theme): string {\n\tconst runtime = formatDuration(details.runtimeMs);\n\tconst exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : \"\";\n\tconst outcome = theme.fg(statusColor(details.status), `${details.status}${exit} in ${runtime}`);\n\tconst glyph = theme.fg(statusColor(details.status), statusGlyph(details.status));\n\treturn `${glyph} ${theme.fg(\"accent\", details.taskId)} ${commandLabel(details.command, COMMAND_PREVIEW_LIMIT)} ${theme.fg(\"muted\", `— ${outcome}`)}`;\n}\n\n/**\n * Collapsed: status glyph, task id, command, and outcome on one line, output\n * path below. Expanded adds the embedded output tail. Returns undefined for\n * malformed details so the default custom-message rendering takes over.\n */\nexport function renderBackgroundNotification(\n\tmessage: CustomMessage<BgNotificationDetails>,\n\toptions: MessageRenderOptions,\n\ttheme: Theme,\n): Component | undefined {\n\tconst details = message.details;\n\tif (!details || typeof details.taskId !== \"string\") return undefined;\n\n\tconst container = new Container();\n\tcontainer.addChild(new TruncatedText(taskSummaryLine(details, theme), 1, 0));\n\tcontainer.addChild(new Text(theme.fg(\"muted\", details.outputPath), 1, 0));\n\n\tif (details.tailError) {\n\t\tcontainer.addChild(new Text(theme.fg(\"error\", `Output unavailable: ${details.tailError}`), 1, 0));\n\t} else if (options.expanded && details.tailText) {\n\t\tcontainer.addChild(new Text(\"\", 0, 0));\n\t\t// Keep the END of the tail when over budget — that is where the outcome lives.\n\t\tconst tail =\n\t\t\tdetails.tailText.length > NOTIFY_TAIL_LIMIT\n\t\t\t\t? `…${details.tailText.slice(-NOTIFY_TAIL_LIMIT)}`\n\t\t\t\t: details.tailText;\n\t\tconst truncatedNote = details.tailTruncated\n\t\t\t? `\\n[showing tail of ${details.totalBytes} bytes; full output: ${details.outputPath}]`\n\t\t\t: \"\";\n\t\tcontainer.addChild(\n\t\t\tnew Text(`${theme.fg(\"toolOutput\", tail.trimEnd())}${theme.fg(\"muted\", truncatedNote)}`, 1, 0),\n\t\t);\n\t}\n\treturn container;\n}\n"]}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/extensions/background/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Transcript rendering for the background extension: the single bg tool's\n * call/result rows (dispatched on action) and the completion-notification\n * message. Style follows the built-in bash presentation: a `$` prompt row\n * with an `&` marker for the background call.\n */\n\nimport { type Component, Container, Text, TruncatedText } from \"@earendil-works/pi-tui\";\nimport type {\n\tAgentToolResult,\n\tMessageRenderOptions,\n\tToolRenderContext,\n\tToolRenderResultOptions,\n} from \"../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../core/messages.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { BgCreateInput, BgDetails, BgInput, BgNotificationDetails } from \"./index.ts\";\nimport {\n\tBG_WAIT_DEFAULT_MS,\n\tBG_WAIT_MAX_MS,\n\tBG_WAIT_MIN_MS,\n\ttype BgTaskStatus,\n\tfirstCommandLine,\n\tformatDuration,\n} from \"./registry.ts\";\n\nconst COMMAND_PREVIEW_LIMIT = 120;\nconst NOTIFY_TAIL_LIMIT = 4000;\n/** Cap for expanded transcript views of tool-result text (already bounded at the source). */\nconst RESULT_EXPAND_LIMIT = 4000;\n/** Live pending-wait line refresh cadence; the first settled render clears the timer. */\nconst WAIT_REFRESH_MS = 1000;\n\n/** Live peek at a waited-on task, fed from the registry by the call renderer. */\nexport interface BgTaskLive {\n\tstatus: BgTaskStatus;\n\toutputBytes: number;\n}\n\nexport type WaitLiveProbe = (taskId: string) => BgTaskLive | undefined;\n\n/** Per-call live-refresh state owned by the shell's render context. */\nexport interface BgRenderState {\n\trefreshTimer?: ReturnType<typeof setTimeout>;\n\t/** First pending render of a wait call; anchors the elapsed display. */\n\twaitStartedAt?: number;\n\t/** outputBytes snapshot at wait start; anchors the \"+new output\" display. */\n\twaitBaselineBytes?: number;\n}\n\n/**\n * Arm a one-shot 1s refresh while a wait call is pending, clear it once\n * settled. The timer fires → context.invalidate() → renderCall re-runs →\n * re-arms, so at most one armed timer exists per tool row. Unref'd: it can\n * never hold the process open.\n */\nexport function scheduleWaitRefresh(context: ToolRenderContext<BgRenderState>, pending: boolean): void {\n\tconst state = context.state;\n\tif (state === undefined) return; // Standalone render without shell state: nothing to schedule.\n\tif (pending) {\n\t\tif (state.refreshTimer === undefined) {\n\t\t\tstate.refreshTimer = setTimeout(() => {\n\t\t\t\tstate.refreshTimer = undefined;\n\t\t\t\tcontext.invalidate();\n\t\t\t}, WAIT_REFRESH_MS);\n\t\t\tstate.refreshTimer.unref?.();\n\t\t}\n\t\treturn;\n\t}\n\tif (state.refreshTimer !== undefined) {\n\t\tclearTimeout(state.refreshTimer);\n\t\tstate.refreshTimer = undefined;\n\t}\n\tstate.waitStartedAt = undefined;\n\tstate.waitBaselineBytes = undefined;\n}\n\n/** Basename of a path, slash-normalized — compact display of output files. */\nexport function fileNameOf(path: string): string {\n\treturn path.replace(/\\\\/g, \"/\").split(\"/\").at(-1) ?? path;\n}\n\nfunction bgPrompt(theme: Theme): string {\n\treturn theme.fg(\"toolTitle\", theme.bold(\"$ \"));\n}\n\nfunction timeoutSuffix(timeout: number | undefined, theme: Theme): string {\n\treturn timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n}\n\n/** Keep the END of oversized text — that is where the outcome lives. */\nfunction capForTranscript(text: string, limit: number): string {\n\treturn text.length > limit ? `…${text.slice(-limit)}` : text;\n}\n\nexport function statusGlyph(status: BgTaskStatus, stalled?: boolean): string {\n\tif (stalled) return \"…\";\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"✓\";\n\t\tcase \"failed\":\n\t\tcase \"timeout\":\n\t\t\treturn \"✗\";\n\t\tcase \"killed\":\n\t\t\treturn \"○\";\n\t\tdefault:\n\t\t\treturn \"●\";\n\t}\n}\n\nexport function statusColor(status: BgTaskStatus, stalled?: boolean): \"success\" | \"error\" | \"warning\" | \"accent\" {\n\tif (stalled) return \"warning\";\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tcase \"timeout\":\n\t\tcase \"killed\":\n\t\t\treturn \"warning\";\n\t\tdefault:\n\t\t\treturn \"accent\";\n\t}\n}\n\n/** First command line truncated to a visible-character budget with an ellipsis. */\nexport function commandLabel(command: string, width: number): string {\n\tconst characters = [...firstCommandLine(command)];\n\treturn characters.length <= width ? characters.join(\"\") : `${characters.slice(0, Math.max(0, width - 1)).join(\"\")}…`;\n}\n\n// ── tool call ─────────────────────────────────────────────────────────────\n\nexport function renderBgCall(\n\targs: BgInput,\n\ttheme: Theme,\n\tcontext: ToolRenderContext<BgRenderState>,\n\tgetTaskLive?: WaitLiveProbe,\n): Component {\n\tswitch (args.action) {\n\t\tcase \"create\":\n\t\t\treturn typeof args.command === \"string\"\n\t\t\t\t? renderCreateCall(args as BgCreateInput, theme, context)\n\t\t\t\t: new Text(theme.fg(\"toolTitle\", theme.bold(\"bg create\")), 0, 0);\n\t\tcase \"read\": {\n\t\t\tconst mode = args.mode ?? \"tail\";\n\t\t\tconst size = args.bytes !== undefined ? ` ${formatSize(args.bytes)}` : \"\";\n\t\t\treturn new Text(\n\t\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"bg read \"))}${theme.fg(\"accent\", args.taskId ?? \"\")}${theme.fg(\"muted\", ` ${mode}${size}`)}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t}\n\t\tcase \"wait\":\n\t\t\treturn renderWaitCall(args, theme, context, getTaskLive);\n\t\tcase \"kill\":\n\t\t\treturn new Text(\n\t\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"bg kill \"))}${theme.fg(\"accent\", args.taskId ?? \"\")}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\tcase \"list\":\n\t\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(\"bg list\")), 0, 0);\n\t\tdefault: {\n\t\t\t// Tool arguments arrive incrementally while the model is still emitting\n\t\t\t// JSON. Keep the call row renderable until `action` becomes valid.\n\t\t\tconst action = typeof args.action === \"string\" ? ` ${theme.fg(\"dim\", args.action)}` : \"\";\n\t\t\treturn new Text(`${theme.fg(\"toolTitle\", theme.bold(\"bg\"))}${action}`, 0, 0);\n\t\t}\n\t}\n}\n\n/** Mirror the execution-side clamp so the shown window matches the real one. */\nexport function clampWaitMs(waitMs: number | undefined): number {\n\treturn Math.min(BG_WAIT_MAX_MS, Math.max(BG_WAIT_MIN_MS, Math.floor(waitMs ?? BG_WAIT_DEFAULT_MS)));\n}\n\n/**\n * Pending wait call: elapsed/wait-window and the new-output delta since the\n * wait began, refreshed once per second by scheduleWaitRefresh until the\n * result settles and takes over the row.\n */\nfunction renderWaitCall(\n\targs: BgInput,\n\ttheme: Theme,\n\tcontext: ToolRenderContext<BgRenderState>,\n\tgetTaskLive: WaitLiveProbe | undefined,\n): Component {\n\tconst taskId = typeof args.taskId === \"string\" ? args.taskId.trim() : \"\";\n\tconst live = getTaskLive !== undefined && taskId ? getTaskLive(taskId) : undefined;\n\t// The live display needs shell-owned state; without it (standalone renders)\n\t// fall back to the static line.\n\tconst pending = context.isPartial === true && context.state !== undefined && taskId.length > 0;\n\tscheduleWaitRefresh(context, pending);\n\tif (!pending) {\n\t\tconst ms = args.waitMs !== undefined ? ` ${formatDuration(clampWaitMs(args.waitMs))}` : \"\";\n\t\treturn new Text(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"bg wait \"))}${theme.fg(\"accent\", args.taskId ?? \"\")}${theme.fg(\"muted\", ms)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t}\n\tconst state = context.state;\n\tif (state.waitStartedAt === undefined) state.waitStartedAt = Date.now();\n\tconst startedAt = state.waitStartedAt;\n\tlet liveBase: number | undefined;\n\tif (live) {\n\t\tif (state.waitBaselineBytes === undefined) state.waitBaselineBytes = live.outputBytes;\n\t\tliveBase = state.waitBaselineBytes;\n\t}\n\tconst parts = [`waiting ${formatDuration(Date.now() - startedAt)}/${formatDuration(clampWaitMs(args.waitMs))}`];\n\tif (live && live.status === \"running\" && liveBase !== undefined && live.outputBytes > liveBase) {\n\t\tparts.push(`+${formatSize(live.outputBytes - liveBase)} new output`);\n\t}\n\treturn new Text(\n\t\t`${theme.fg(\"toolTitle\", theme.bold(\"bg wait \"))}${theme.fg(\"accent\", taskId)} ${theme.fg(\"muted\", parts.join(\" · \"))}`,\n\t\t0,\n\t\t0,\n\t);\n}\n\nfunction renderCreateCall(args: BgCreateInput, theme: Theme, context: ToolRenderContext): Component {\n\tconst lines = args.command.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n\tconst label = args.description ? theme.fg(\"muted\", ` · ${args.description}`) : \"\";\n\tconst suffix = theme.fg(\"muted\", \" &\") + timeoutSuffix(args.timeout, theme) + label;\n\tif (context.expanded && lines.length > 1) {\n\t\tconst body = highlightCode(args.command, \"bash\").join(\"\\n\");\n\t\treturn new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);\n\t}\n\tconst body = highlightCode(firstCommandLine(args.command), \"bash\").join(\"\\n\");\n\tif (lines.length > 1) {\n\t\tconst hidden = lines.length - 1;\n\t\tconst more = theme.fg(\"muted\", ` (+${hidden} line${hidden === 1 ? \"\" : \"s\"})`);\n\t\treturn new Text(`${bgPrompt(theme)}${body}${suffix}${more}`, 0, 0);\n\t}\n\treturn new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);\n}\n\n// ── tool result ───────────────────────────────────────────────────────────\n\nexport function renderBgResult(\n\tresult: AgentToolResult<BgDetails | undefined>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcontext: ToolRenderContext<BgRenderState>,\n): Component {\n\t// bg results are always settled today; clear defensively so no live-refresh\n\t// timer can outlive its row if a host ever streams partial results.\n\tif (context.args?.action === \"wait\" && !options.isPartial) scheduleWaitRefresh(context, false);\n\tconst details = result.details;\n\tif (context.isError || !details) {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text ?? \"\";\n\t\treturn new Text(theme.fg(\"toolOutput\", text.trimEnd()), 0, 0);\n\t}\n\n\tconst container = new Container();\n\tcontainer.addChild(new TruncatedText(resultSummaryLine(details, theme), 1, 0));\n\tif (options.expanded) {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text ?? \"\";\n\t\tcontainer.addChild(new Text(\"\", 0, 0));\n\t\tcontainer.addChild(new Text(theme.fg(\"toolOutput\", capForTranscript(text, RESULT_EXPAND_LIMIT).trimEnd()), 1, 0));\n\t}\n\treturn container;\n}\n\nfunction resultSummaryLine(details: BgDetails, theme: Theme): string {\n\tswitch (details.action) {\n\t\tcase \"create\": {\n\t\t\tconst label = details.description ? ` (${details.description})` : \"\";\n\t\t\treturn `${theme.fg(\"muted\", \"→ task \")}${theme.fg(\"accent\", `${details.taskId}${label}`)}${theme.fg(\"muted\", ` started · ${details.outputPath}`)}`;\n\t\t}\n\t\tcase \"read\": {\n\t\t\tconst size =\n\t\t\t\tdetails.sliceBytes !== details.totalBytes\n\t\t\t\t\t? `${details.mode} ${formatSize(details.sliceBytes)} of ${formatSize(details.totalBytes)}`\n\t\t\t\t\t: formatSize(details.totalBytes);\n\t\t\treturn `${theme.fg(\"muted\", \"→ \")}${theme.fg(\"accent\", details.taskId)}${theme.fg(\"muted\", ` ${size} · ${details.outputPath}`)}`;\n\t\t}\n\t\tcase \"wait\": {\n\t\t\tif (details.timedOut) {\n\t\t\t\treturn `${theme.fg(statusColor(details.status), statusGlyph(details.status))} ${theme.fg(\"accent\", details.taskId)}${theme.fg(\"muted\", ` still running · waited ${formatDuration(details.waitedMs)} · ${formatSize(details.totalBytes)}`)}`;\n\t\t\t}\n\t\t\tconst exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : \"\";\n\t\t\treturn `${theme.fg(statusColor(details.status), statusGlyph(details.status))} ${theme.fg(\"accent\", details.taskId)}${theme.fg(\"muted\", ` ${details.status}${exit} · waited ${formatDuration(details.waitedMs)} · +${formatSize(details.deltaBytes)}`)}`;\n\t\t}\n\t\tcase \"kill\":\n\t\t\treturn `${theme.fg(statusColor(\"killed\"), statusGlyph(\"killed\"))} ${theme.fg(\"accent\", details.taskId)}${theme.fg(\"muted\", \" stopped\")}`;\n\t\tcase \"list\": {\n\t\t\tconst hidden = details.hidden > 0 ? ` · ${details.hidden} more finished` : \"\";\n\t\t\treturn theme.fg(\"muted\", `${details.running} running · ${details.finished} finished${hidden}`);\n\t\t}\n\t}\n}\n\n// ── completion notification ───────────────────────────────────────────────\n\n/** One-line summary of a finished task, shared by the notification renderer. */\nfunction taskSummaryLine(details: BgNotificationDetails, theme: Theme): string {\n\tconst runtime = formatDuration(details.runtimeMs);\n\tconst exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : \"\";\n\tif (details.stalled) {\n\t\tconst glyph = theme.fg(\"warning\", \"…\");\n\t\tconst label = details.description\n\t\t\t? `${details.description} (${commandLabel(details.command, 40)})`\n\t\t\t: commandLabel(details.command, COMMAND_PREVIEW_LIMIT);\n\t\treturn `${glyph} ${theme.fg(\"accent\", details.taskId)} ${label} ${theme.fg(\"muted\", `— waiting for input (${runtime})`)}`;\n\t}\n\tconst outcome = theme.fg(statusColor(details.status), `${details.status}${exit} in ${runtime}`);\n\tconst glyph = theme.fg(statusColor(details.status), statusGlyph(details.status));\n\tconst label = details.description\n\t\t? `${details.description} (${commandLabel(details.command, 40)})`\n\t\t: commandLabel(details.command, COMMAND_PREVIEW_LIMIT);\n\treturn `${glyph} ${theme.fg(\"accent\", details.taskId)} ${label} ${theme.fg(\"muted\", `— ${outcome}`)}`;\n}\n\n/**\n * Collapsed: status glyph, task id, label, and outcome on one line, output\n * path below. Expanded adds the embedded output tail. Returns undefined for\n * malformed details so the default custom-message rendering takes over.\n */\nexport function renderBackgroundNotification(\n\tmessage: CustomMessage<BgNotificationDetails>,\n\toptions: MessageRenderOptions,\n\ttheme: Theme,\n): Component | undefined {\n\tconst details = message.details;\n\tif (!details || typeof details.taskId !== \"string\") return undefined;\n\n\tconst container = new Container();\n\tcontainer.addChild(new TruncatedText(taskSummaryLine(details, theme), 1, 0));\n\t// Collapsed keeps the row compact with the file name; the full path\n\t// (model-relevant, human-rarely) shows when expanded.\n\tconst shownPath = options.expanded ? details.outputPath : fileNameOf(details.outputPath);\n\tcontainer.addChild(new Text(theme.fg(\"muted\", shownPath), 1, 0));\n\n\tif (details.tailError) {\n\t\tcontainer.addChild(new Text(theme.fg(\"error\", `Output unavailable: ${details.tailError}`), 1, 0));\n\t} else if (options.expanded && details.tailText) {\n\t\tcontainer.addChild(new Text(\"\", 0, 0));\n\t\tconst tail = capForTranscript(details.tailText, NOTIFY_TAIL_LIMIT);\n\t\tconst truncatedNote = details.tailTruncated\n\t\t\t? `\\n[showing tail of ${formatSize(details.totalBytes)} bytes; full output: ${details.outputPath}]`\n\t\t\t: \"\";\n\t\tcontainer.addChild(\n\t\t\tnew Text(`${theme.fg(\"toolOutput\", tail.trimEnd())}${theme.fg(\"muted\", truncatedNote)}`, 1, 0),\n\t\t);\n\t}\n\tif (details.stalled && options.expanded) {\n\t\tcontainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"warning\",\n\t\t\t\t\t\"Blocked on interactive input — kill (bg action kill) and re-run with piped input or a non-interactive flag.\",\n\t\t\t\t),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t}\n\treturn container;\n}\n"]}
@@ -0,0 +1,60 @@
1
+ /** Extension-owned dialog host that keeps every /router TUI page in one modal lifecycle. */
2
+ import { type Component, Container, type Focusable, type TUI } from "@earendil-works/pi-tui";
3
+ import type { ExtensionCommandContext } from "../../core/extensions/types.ts";
4
+ import type { KeybindingsManager } from "../../core/keybindings.ts";
5
+ import type { Theme } from "../../modes/interactive/theme/theme.ts";
6
+ export type RouterComponent = Component & {
7
+ dispose?(): void;
8
+ };
9
+ export type RouterComponentFactory<T> = (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T) => void) => RouterComponent | Promise<RouterComponent>;
10
+ interface RouterDialogBase {
11
+ input(title: string, placeholder?: string): Promise<string | undefined>;
12
+ confirm(title: string, message: string): Promise<boolean>;
13
+ }
14
+ export interface RouterNativeDialogs extends RouterDialogBase {
15
+ readonly kind: "native";
16
+ select(title: string, options: string[]): Promise<string | undefined>;
17
+ }
18
+ export interface RouterTuiDialogs extends RouterDialogBase {
19
+ readonly kind: "tui";
20
+ show<T>(factory: RouterComponentFactory<T>): Promise<T>;
21
+ }
22
+ export type RouterDialogs = RouterNativeDialogs | RouterTuiDialogs;
23
+ export declare class RouterTuiSessionClosedError extends Error {
24
+ constructor();
25
+ }
26
+ export declare function createNativeRouterDialogs(ctx: ExtensionCommandContext): RouterNativeDialogs;
27
+ /**
28
+ * Persistent root mounted by one ctx.ui.custom() call. Child dialogs resolve
29
+ * without closing this root, so async flow continuations can replace them
30
+ * without exposing the main editor between pages.
31
+ */
32
+ export declare class RouterTuiSession extends Container implements Focusable, RouterTuiDialogs {
33
+ readonly kind: "tui";
34
+ private readonly tui;
35
+ private readonly theme;
36
+ private readonly keybindings;
37
+ private readonly onTransitionClose;
38
+ private activeScreen;
39
+ private pendingReject;
40
+ private transitionTimer;
41
+ private acceptsInput;
42
+ private closeRequested;
43
+ private disposed;
44
+ private _focused;
45
+ constructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, onTransitionClose: () => void);
46
+ get focused(): boolean;
47
+ set focused(value: boolean);
48
+ show<T>(factory: RouterComponentFactory<T>): Promise<T>;
49
+ input(title: string, placeholder?: string): Promise<string | undefined>;
50
+ confirm(title: string, message: string): Promise<boolean>;
51
+ handleInput(data: string): void;
52
+ dispose(): void;
53
+ private beginTransition;
54
+ private clearTransitionTimer;
55
+ private setActiveScreen;
56
+ private disposeActiveScreen;
57
+ private disposeScreen;
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=dialog-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-host.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/dialog-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,196 @@
1
+ /** Extension-owned dialog host that keeps every /router TUI page in one modal lifecycle. */
2
+ import { Container, isFocusable, Spacer, Text } from "@earendil-works/pi-tui";
3
+ import { DynamicBorder } from "../../modes/interactive/components/dynamic-border.js";
4
+ import { ExtensionInputComponent } from "../../modes/interactive/components/extension-input.js";
5
+ import { ExtensionSelectorComponent } from "../../modes/interactive/components/extension-selector.js";
6
+ import { keyLabel } from "../../modes/interactive/components/keybinding-hints.js";
7
+ export class RouterTuiSessionClosedError extends Error {
8
+ constructor() {
9
+ super("Router TUI session is closed.");
10
+ this.name = "RouterTuiSessionClosedError";
11
+ }
12
+ }
13
+ export function createNativeRouterDialogs(ctx) {
14
+ return {
15
+ kind: "native",
16
+ select: (title, options) => ctx.ui.select(title, options),
17
+ input: (title, placeholder) => ctx.ui.input(title, placeholder),
18
+ confirm: (title, message) => ctx.ui.confirm(title, message),
19
+ };
20
+ }
21
+ /**
22
+ * Persistent root mounted by one ctx.ui.custom() call. Child dialogs resolve
23
+ * without closing this root, so async flow continuations can replace them
24
+ * without exposing the main editor between pages.
25
+ */
26
+ export class RouterTuiSession extends Container {
27
+ constructor(tui, theme, keybindings, onTransitionClose) {
28
+ super();
29
+ this.kind = "tui";
30
+ this.acceptsInput = false;
31
+ this.closeRequested = false;
32
+ this.disposed = false;
33
+ this._focused = false;
34
+ this.tui = tui;
35
+ this.theme = theme;
36
+ this.keybindings = keybindings;
37
+ this.onTransitionClose = onTransitionClose;
38
+ const loading = new Container();
39
+ loading.addChild(new DynamicBorder((text) => theme.fg("border", text)));
40
+ loading.addChild(new Spacer(1));
41
+ loading.addChild(new Text(theme.fg("muted", "Loading router…"), 1, 0));
42
+ loading.addChild(new Spacer(1));
43
+ loading.addChild(new DynamicBorder((text) => theme.fg("border", text)));
44
+ this.activeScreen = loading;
45
+ this.addChild(loading);
46
+ }
47
+ get focused() {
48
+ return this._focused;
49
+ }
50
+ set focused(value) {
51
+ this._focused = value;
52
+ if (this.activeScreen && isFocusable(this.activeScreen)) {
53
+ this.activeScreen.focused = value && this.acceptsInput;
54
+ }
55
+ }
56
+ show(factory) {
57
+ this.clearTransitionTimer();
58
+ if (this.disposed || this.closeRequested)
59
+ return Promise.reject(new RouterTuiSessionClosedError());
60
+ if (this.pendingReject)
61
+ return Promise.reject(new Error("Router TUI session already has an active dialog."));
62
+ return new Promise((resolve, reject) => {
63
+ let settled = false;
64
+ const fail = (error) => {
65
+ if (settled || this.disposed)
66
+ return;
67
+ settled = true;
68
+ this.acceptsInput = false;
69
+ if (this.pendingReject === reject)
70
+ this.pendingReject = undefined;
71
+ reject(error instanceof Error ? error : new Error(String(error)));
72
+ };
73
+ const done = (result) => {
74
+ if (settled || this.disposed)
75
+ return;
76
+ settled = true;
77
+ this.acceptsInput = false;
78
+ if (this.activeScreen && isFocusable(this.activeScreen))
79
+ this.activeScreen.focused = false;
80
+ if (this.pendingReject === reject)
81
+ this.pendingReject = undefined;
82
+ this.tui.setFocus(this);
83
+ this.beginTransition();
84
+ resolve(result);
85
+ };
86
+ this.pendingReject = reject;
87
+ let pending;
88
+ try {
89
+ pending = factory(this.tui, this.theme, this.keybindings, done);
90
+ }
91
+ catch (error) {
92
+ fail(error);
93
+ return;
94
+ }
95
+ void Promise.resolve(pending).then((component) => {
96
+ if (settled || this.disposed) {
97
+ this.disposeScreen(component);
98
+ return;
99
+ }
100
+ try {
101
+ this.setActiveScreen(component);
102
+ }
103
+ catch (error) {
104
+ this.disposeScreen(component);
105
+ fail(error);
106
+ }
107
+ }, fail);
108
+ });
109
+ }
110
+ input(title, placeholder) {
111
+ return this.show((tui, _theme, _keybindings, done) => new ExtensionInputComponent(title, placeholder, done, () => done(undefined), { tui }));
112
+ }
113
+ async confirm(title, message) {
114
+ const selected = await this.show((tui, _theme, _keybindings, done) => new ExtensionSelectorComponent(title, ["Yes", "No"], done, () => done(undefined), {
115
+ tui,
116
+ subtitle: message,
117
+ }));
118
+ return selected === "Yes";
119
+ }
120
+ handleInput(data) {
121
+ if (this.acceptsInput) {
122
+ this.activeScreen?.handleInput?.(data);
123
+ return;
124
+ }
125
+ if (this.closeRequested || !this.keybindings.matches(data, "tui.select.cancel"))
126
+ return;
127
+ this.closeRequested = true;
128
+ this.onTransitionClose();
129
+ }
130
+ dispose() {
131
+ if (this.disposed)
132
+ return;
133
+ this.disposed = true;
134
+ this.acceptsInput = false;
135
+ this.clearTransitionTimer();
136
+ const reject = this.pendingReject;
137
+ this.pendingReject = undefined;
138
+ this.disposeActiveScreen();
139
+ this.clear();
140
+ reject?.(new RouterTuiSessionClosedError());
141
+ }
142
+ beginTransition() {
143
+ this.clearTransitionTimer();
144
+ this.transitionTimer = setTimeout(() => {
145
+ this.transitionTimer = undefined;
146
+ if (this.disposed || this.closeRequested || this.acceptsInput)
147
+ return;
148
+ const working = new Container();
149
+ working.addChild(new DynamicBorder((text) => this.theme.fg("border", text)));
150
+ working.addChild(new Spacer(1));
151
+ const cancelKey = keyLabel("tui.select.cancel", { keybindings: this.keybindings });
152
+ const closeHint = cancelKey ? `${cancelKey} closes /router` : "Close /router to continue";
153
+ working.addChild(new Text(this.theme.fg("muted", `Working… ${closeHint}`), 1, 0));
154
+ working.addChild(new Spacer(1));
155
+ working.addChild(new DynamicBorder((text) => this.theme.fg("border", text)));
156
+ this.disposeActiveScreen();
157
+ this.clear();
158
+ this.activeScreen = working;
159
+ this.addChild(working);
160
+ this.tui.requestRender();
161
+ }, 150);
162
+ }
163
+ clearTransitionTimer() {
164
+ if (this.transitionTimer === undefined)
165
+ return;
166
+ clearTimeout(this.transitionTimer);
167
+ this.transitionTimer = undefined;
168
+ }
169
+ setActiveScreen(component) {
170
+ this.disposeActiveScreen();
171
+ this.clear();
172
+ this.activeScreen = component;
173
+ this.addChild(component);
174
+ this.acceptsInput = true;
175
+ this.tui.setFocus(component);
176
+ this.tui.requestRender();
177
+ }
178
+ disposeActiveScreen() {
179
+ const screen = this.activeScreen;
180
+ this.activeScreen = undefined;
181
+ if (screen)
182
+ this.disposeScreen(screen);
183
+ }
184
+ disposeScreen(screen) {
185
+ const dispose = screen.dispose;
186
+ if (isFocusable(screen))
187
+ screen.focused = false;
188
+ try {
189
+ dispose?.call(screen);
190
+ }
191
+ catch {
192
+ // A child cleanup failure must not strand the outer custom lifecycle.
193
+ }
194
+ }
195
+ }
196
+ //# sourceMappingURL=dialog-host.js.map