@astralyn/pi 0.84.4 → 0.84.5

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 (234) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +11 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/auth-command.d.ts.map +1 -1
  7. package/dist/cli/auth-command.js +5 -4
  8. package/dist/cli/auth-command.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +6 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +7 -6
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/experimental.d.ts +4 -0
  14. package/dist/core/experimental.d.ts.map +1 -1
  15. package/dist/core/experimental.js +4 -0
  16. package/dist/core/experimental.js.map +1 -1
  17. package/dist/core/extensions/runner.d.ts.map +1 -1
  18. package/dist/core/extensions/runner.js +4 -0
  19. package/dist/core/extensions/runner.js.map +1 -1
  20. package/dist/core/extensions/types.d.ts +8 -1
  21. package/dist/core/extensions/types.d.ts.map +1 -1
  22. package/dist/core/extensions/types.js.map +1 -1
  23. package/dist/core/keybindings.d.ts +29 -0
  24. package/dist/core/keybindings.d.ts.map +1 -1
  25. package/dist/core/keybindings.js +4 -0
  26. package/dist/core/keybindings.js.map +1 -1
  27. package/dist/core/model-config.d.ts +5 -0
  28. package/dist/core/model-config.d.ts.map +1 -1
  29. package/dist/core/model-config.js +1 -0
  30. package/dist/core/model-config.js.map +1 -1
  31. package/dist/core/model-registry.d.ts +2 -0
  32. package/dist/core/model-registry.d.ts.map +1 -1
  33. package/dist/core/model-registry.js +4 -0
  34. package/dist/core/model-registry.js.map +1 -1
  35. package/dist/core/project-trust.js +2 -2
  36. package/dist/core/project-trust.js.map +1 -1
  37. package/dist/core/sdk.d.ts +5 -3
  38. package/dist/core/sdk.d.ts.map +1 -1
  39. package/dist/core/sdk.js +2 -1
  40. package/dist/core/sdk.js.map +1 -1
  41. package/dist/core/session-manager.js +2 -2
  42. package/dist/core/session-manager.js.map +1 -1
  43. package/dist/core/settings-manager.d.ts +6 -0
  44. package/dist/core/settings-manager.d.ts.map +1 -1
  45. package/dist/core/settings-manager.js +12 -0
  46. package/dist/core/settings-manager.js.map +1 -1
  47. package/dist/core/system-prompt.js +1 -1
  48. package/dist/core/system-prompt.js.map +1 -1
  49. package/dist/core/tools/bash.d.ts +2 -1
  50. package/dist/core/tools/bash.d.ts.map +1 -1
  51. package/dist/core/tools/bash.js +3 -1
  52. package/dist/core/tools/bash.js.map +1 -1
  53. package/dist/core/tools/edit.d.ts.map +1 -1
  54. package/dist/core/tools/edit.js +2 -0
  55. package/dist/core/tools/edit.js.map +1 -1
  56. package/dist/core/tools/find.js +1 -1
  57. package/dist/core/tools/find.js.map +1 -1
  58. package/dist/core/tools/grep.js +1 -1
  59. package/dist/core/tools/grep.js.map +1 -1
  60. package/dist/core/tools/read.d.ts.map +1 -1
  61. package/dist/core/tools/read.js +2 -0
  62. package/dist/core/tools/read.js.map +1 -1
  63. package/dist/core/tools/write.d.ts.map +1 -1
  64. package/dist/core/tools/write.js +2 -0
  65. package/dist/core/tools/write.js.map +1 -1
  66. package/dist/extensions/background/index.d.ts +74 -0
  67. package/dist/extensions/background/index.d.ts.map +1 -0
  68. package/dist/extensions/background/index.js +332 -0
  69. package/dist/extensions/background/index.js.map +1 -0
  70. package/dist/extensions/background/manager.d.ts +81 -0
  71. package/dist/extensions/background/manager.d.ts.map +1 -0
  72. package/dist/extensions/background/manager.js +320 -0
  73. package/dist/extensions/background/manager.js.map +1 -0
  74. package/dist/extensions/background/registry.d.ts +138 -0
  75. package/dist/extensions/background/registry.d.ts.map +1 -0
  76. package/dist/extensions/background/registry.js +358 -0
  77. package/dist/extensions/background/registry.js.map +1 -0
  78. package/dist/extensions/background/render.d.ts +26 -0
  79. package/dist/extensions/background/render.d.ts.map +1 -0
  80. package/dist/extensions/background/render.js +118 -0
  81. package/dist/extensions/background/render.js.map +1 -0
  82. package/dist/extensions/index.d.ts.map +1 -1
  83. package/dist/extensions/index.js +2 -0
  84. package/dist/extensions/index.js.map +1 -1
  85. package/dist/extensions/subagent/activity.d.ts +0 -1
  86. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  87. package/dist/extensions/subagent/activity.js +2 -5
  88. package/dist/extensions/subagent/activity.js.map +1 -1
  89. package/dist/extensions/subagent/agents-command.d.ts.map +1 -1
  90. package/dist/extensions/subagent/agents-command.js +308 -153
  91. package/dist/extensions/subagent/agents-command.js.map +1 -1
  92. package/dist/extensions/subagent/agents.d.ts +3 -6
  93. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  94. package/dist/extensions/subagent/agents.js +22 -192
  95. package/dist/extensions/subagent/agents.js.map +1 -1
  96. package/dist/extensions/subagent/constants.d.ts +4 -5
  97. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  98. package/dist/extensions/subagent/constants.js +11 -7
  99. package/dist/extensions/subagent/constants.js.map +1 -1
  100. package/dist/extensions/subagent/index.d.ts +0 -3
  101. package/dist/extensions/subagent/index.d.ts.map +1 -1
  102. package/dist/extensions/subagent/index.js +56 -188
  103. package/dist/extensions/subagent/index.js.map +1 -1
  104. package/dist/extensions/subagent/render.d.ts +1 -1
  105. package/dist/extensions/subagent/render.d.ts.map +1 -1
  106. package/dist/extensions/subagent/render.js +208 -576
  107. package/dist/extensions/subagent/render.js.map +1 -1
  108. package/dist/extensions/subagent/resolve.d.ts +2 -2
  109. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  110. package/dist/extensions/subagent/resolve.js +23 -15
  111. package/dist/extensions/subagent/resolve.js.map +1 -1
  112. package/dist/extensions/subagent/runner.d.ts +1 -2
  113. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  114. package/dist/extensions/subagent/runner.js +109 -131
  115. package/dist/extensions/subagent/runner.js.map +1 -1
  116. package/dist/extensions/subagent/schema.d.ts +4 -10
  117. package/dist/extensions/subagent/schema.d.ts.map +1 -1
  118. package/dist/extensions/subagent/schema.js +8 -13
  119. package/dist/extensions/subagent/schema.js.map +1 -1
  120. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  121. package/dist/extensions/subagent/sdk-runner.js +9 -20
  122. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  123. package/dist/extensions/subagent/settings.d.ts +2 -2
  124. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  125. package/dist/extensions/subagent/settings.js +27 -14
  126. package/dist/extensions/subagent/settings.js.map +1 -1
  127. package/dist/extensions/subagent/text.d.ts +3 -0
  128. package/dist/extensions/subagent/text.d.ts.map +1 -0
  129. package/dist/extensions/subagent/text.js +16 -0
  130. package/dist/extensions/subagent/text.js.map +1 -0
  131. package/dist/extensions/subagent/types.d.ts +13 -35
  132. package/dist/extensions/subagent/types.d.ts.map +1 -1
  133. package/dist/extensions/subagent/types.js.map +1 -1
  134. package/dist/extensions/subagent/ui/model-list.d.ts +4 -3
  135. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  136. package/dist/extensions/subagent/ui/model-list.js +10 -4
  137. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  138. package/dist/index.d.ts +1 -1
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js.map +1 -1
  141. package/dist/main.d.ts.map +1 -1
  142. package/dist/main.js +5 -1
  143. package/dist/main.js.map +1 -1
  144. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/model-selector.js +2 -1
  146. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  147. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  148. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/settings-selector.js +10 -0
  150. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  151. package/dist/modes/interactive/interactive-mode.d.ts +8 -2
  152. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  153. package/dist/modes/interactive/interactive-mode.js +77 -25
  154. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  155. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  156. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  157. package/dist/modes/interactive/model-catalog-refresh.js +36 -0
  158. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  159. package/dist/modes/interactive/theme/dark.json +2 -0
  160. package/dist/modes/interactive/theme/light.json +2 -0
  161. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  162. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  163. package/dist/modes/interactive/theme/theme-controller.js +23 -10
  164. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  165. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  166. package/dist/modes/interactive/theme/theme.d.ts +5 -3
  167. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  168. package/dist/modes/interactive/theme/theme.js +12 -2
  169. package/dist/modes/interactive/theme/theme.js.map +1 -1
  170. package/dist/modes/json-event.d.ts +4 -1
  171. package/dist/modes/json-event.d.ts.map +1 -1
  172. package/dist/modes/json-event.js +5 -2
  173. package/dist/modes/json-event.js.map +1 -1
  174. package/dist/package-manager-cli.js +1 -1
  175. package/dist/package-manager-cli.js.map +1 -1
  176. package/dist/server/create-harness.d.ts.map +1 -1
  177. package/dist/server/create-harness.js +2 -0
  178. package/dist/server/create-harness.js.map +1 -1
  179. package/dist/utils/tools-manager.d.ts +10 -1
  180. package/dist/utils/tools-manager.d.ts.map +1 -1
  181. package/dist/utils/tools-manager.js +14 -19
  182. package/dist/utils/tools-manager.js.map +1 -1
  183. package/docs/bundled/README.md +1 -0
  184. package/docs/bundled/extensions/background.md +64 -0
  185. package/docs/bundled/extensions/subagent.md +83 -71
  186. package/docs/compaction.md +6 -6
  187. package/docs/custom-provider.md +1 -1
  188. package/docs/docs.json +4 -0
  189. package/docs/environment-variables.md +8 -2
  190. package/docs/extensions.md +6 -2
  191. package/docs/json.md +6 -4
  192. package/docs/keybindings.md +9 -2
  193. package/docs/rpc.md +15 -4
  194. package/docs/sdk.md +9 -0
  195. package/docs/settings.md +31 -0
  196. package/docs/terminal-setup.md +23 -2
  197. package/docs/themes.md +24 -3
  198. package/docs/tui.md +2 -2
  199. package/docs/usage.md +4 -3
  200. package/examples/README.md +1 -1
  201. package/examples/extensions/README.md +0 -1
  202. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  203. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  204. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  205. package/examples/extensions/gondolin/package-lock.json +2 -2
  206. package/examples/extensions/gondolin/package.json +1 -1
  207. package/examples/extensions/sandbox/package-lock.json +2 -2
  208. package/examples/extensions/sandbox/package.json +1 -1
  209. package/examples/extensions/with-deps/package-lock.json +2 -2
  210. package/examples/extensions/with-deps/package.json +1 -1
  211. package/npm-shrinkwrap.json +170 -221
  212. package/package.json +6 -6
  213. package/dist/extensions/subagent/display-name.d.ts +0 -6
  214. package/dist/extensions/subagent/display-name.d.ts.map +0 -1
  215. package/dist/extensions/subagent/display-name.js +0 -13
  216. package/dist/extensions/subagent/display-name.js.map +0 -1
  217. package/dist/extensions/subagent/ui/profile-editor.d.ts +0 -54
  218. package/dist/extensions/subagent/ui/profile-editor.d.ts.map +0 -1
  219. package/dist/extensions/subagent/ui/profile-editor.js +0 -154
  220. package/dist/extensions/subagent/ui/profile-editor.js.map +0 -1
  221. package/dist/extensions/subagent/ui/profile-picker.d.ts +0 -32
  222. package/dist/extensions/subagent/ui/profile-picker.d.ts.map +0 -1
  223. package/dist/extensions/subagent/ui/profile-picker.js +0 -149
  224. package/dist/extensions/subagent/ui/profile-picker.js.map +0 -1
  225. package/examples/extensions/subagent/README.md +0 -175
  226. package/examples/extensions/subagent/agents/planner.md +0 -37
  227. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  228. package/examples/extensions/subagent/agents/scout.md +0 -50
  229. package/examples/extensions/subagent/agents/worker.md +0 -24
  230. package/examples/extensions/subagent/agents.ts +0 -126
  231. package/examples/extensions/subagent/index.ts +0 -1009
  232. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  233. package/examples/extensions/subagent/prompts/implement.md +0 -10
  234. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Background task registry: process lifecycle, output capture, completion
3
+ * notification. Pure logic — no UI imports, dependencies injected for tests.
4
+ *
5
+ * Discipline (mirrors the reference implementation this extension distills):
6
+ * - Output streams straight to a file; memory holds only a byte counter.
7
+ * - Finalization is idempotent and strictly ordered: flush stream, set
8
+ * terminal state, then notify. A notification is only sent once the output
9
+ * file holds the complete truth.
10
+ * - Kill goes through AbortController; the shared bash operations kill the
11
+ * whole process tree cross-platform.
12
+ */
13
+ import { randomBytes } from "node:crypto";
14
+ import { createWriteStream, writeFileSync } from "node:fs";
15
+ import { open } from "node:fs/promises";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { formatSize } from "../../core/tools/truncate.js";
19
+ import { sanitizeBinaryOutput } from "../../utils/shell.js";
20
+ export const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;
21
+ export const DEFAULT_MAX_RUNNING_TASKS = 8;
22
+ export const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;
23
+ const SHUTDOWN_GRACE_MS = 2_000;
24
+ /**
25
+ * Read a bounded slice from the head or tail of a file via positioned reads —
26
+ * never the whole file. Tail reads skip UTF-8 continuation bytes so the text
27
+ * starts on a codepoint boundary.
28
+ */
29
+ export async function readOutputSlice(filePath, options) {
30
+ const file = await open(filePath, "r");
31
+ try {
32
+ const { size } = await file.stat();
33
+ let length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));
34
+ let position = options.mode === "tail" ? size - length : 0;
35
+ let startsMidLine = false;
36
+ if (options.mode === "tail" && position > 0 && length > 0) {
37
+ // Probe from one byte before the slice: probe[0] tells whether the
38
+ // slice starts at a line boundary, the rest lets us skip UTF-8
39
+ // continuation bytes (0b10xxxxxx) so we never split a codepoint.
40
+ const probeStart = position - 1;
41
+ const probe = Buffer.alloc(Math.min(5, size - probeStart));
42
+ await file.read(probe, 0, probe.length, probeStart);
43
+ let skip = 0;
44
+ while (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {
45
+ skip++;
46
+ }
47
+ position += skip;
48
+ length -= skip;
49
+ startsMidLine = probe[skip] !== 0x0a;
50
+ }
51
+ const buffer = Buffer.alloc(Math.max(0, length));
52
+ const bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;
53
+ return {
54
+ text: buffer.subarray(0, bytesRead).toString("utf8"),
55
+ sliceBytes: bytesRead,
56
+ totalBytes: size,
57
+ truncated: size > bytesRead,
58
+ startsMidLine,
59
+ };
60
+ }
61
+ finally {
62
+ await file.close();
63
+ }
64
+ }
65
+ export class BackgroundTaskRegistry {
66
+ constructor(options) {
67
+ this.tasks = new Map();
68
+ this.runtimes = new Map();
69
+ this.shuttingDown = false;
70
+ this.operations = options.operations;
71
+ this.outputDir = options.outputDir ?? tmpdir();
72
+ this.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
73
+ this.maxRunningTasks = options.maxRunningTasks ?? DEFAULT_MAX_RUNNING_TASKS;
74
+ this.notifyTailBytes = options.notifyTailBytes ?? DEFAULT_NOTIFY_TAIL_BYTES;
75
+ this.onNotify = options.onNotify;
76
+ this.onChange = options.onChange;
77
+ this.now = options.now ?? Date.now;
78
+ }
79
+ get isShuttingDown() {
80
+ return this.shuttingDown;
81
+ }
82
+ startTask(input) {
83
+ if (this.shuttingDown) {
84
+ throw new Error("Pi is shutting down; background task not started.");
85
+ }
86
+ const running = this.listTasks().filter((task) => task.status === "running");
87
+ if (running.length >= this.maxRunningTasks) {
88
+ const list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join("\n");
89
+ throw new Error(`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one with bg_kill first:\n${list}`);
90
+ }
91
+ const id = this.newTaskId();
92
+ const task = {
93
+ id,
94
+ command: input.command,
95
+ cwd: input.cwd,
96
+ status: "running",
97
+ startedAt: this.now(),
98
+ exitCode: undefined,
99
+ outputPath: join(this.outputDir, `pi-${id}.log`),
100
+ outputBytes: 0,
101
+ outputTruncated: false,
102
+ timeoutSeconds: input.timeoutSeconds,
103
+ notified: false,
104
+ };
105
+ let resolveDone;
106
+ const done = new Promise((resolve) => {
107
+ resolveDone = resolve;
108
+ });
109
+ // Create the file synchronously so bg_logs and the /bg viewer never see
110
+ // ENOENT between task start and the stream's async open.
111
+ writeFileSync(task.outputPath, "");
112
+ const runtime = {
113
+ controller: new AbortController(),
114
+ stream: createWriteStream(task.outputPath, { flags: "a" }),
115
+ killRequested: false,
116
+ overflow: false,
117
+ finalized: false,
118
+ done,
119
+ resolveDone,
120
+ };
121
+ runtime.stream.on("error", (error) => {
122
+ if (runtime.finalized)
123
+ return;
124
+ runtime.streamError = error.message;
125
+ runtime.controller.abort();
126
+ });
127
+ this.tasks.set(id, task);
128
+ this.runtimes.set(id, runtime);
129
+ void this.run(task, runtime, input.env);
130
+ this.onChange();
131
+ return task;
132
+ }
133
+ getTask(id) {
134
+ return this.tasks.get(id);
135
+ }
136
+ /** Running tasks first (oldest first), then finished tasks (newest first). */
137
+ listTasks() {
138
+ const all = [...this.tasks.values()];
139
+ const running = all.filter((task) => task.status === "running");
140
+ running.sort((a, b) => a.startedAt - b.startedAt);
141
+ const ended = all.filter((task) => task.status !== "running");
142
+ ended.sort((a, b) => (b.endedAt ?? b.startedAt) - (a.endedAt ?? a.startedAt));
143
+ return [...running, ...ended];
144
+ }
145
+ /** Resolve a task by id or unique id prefix; the "bg-" prefix may be omitted. */
146
+ resolveTask(idPrefix) {
147
+ const needle = idPrefix.trim();
148
+ if (!needle)
149
+ return { ok: false, reason: "not-found", candidates: [] };
150
+ const exact = this.tasks.get(needle) ?? this.tasks.get(`bg-${needle}`);
151
+ if (exact)
152
+ return { ok: true, task: exact };
153
+ const candidates = [...this.tasks.values()].filter((task) => task.id.startsWith(needle) || task.id.startsWith(`bg-${needle}`));
154
+ const first = candidates[0];
155
+ if (candidates.length === 1 && first)
156
+ return { ok: true, task: first };
157
+ if (candidates.length === 0)
158
+ return { ok: false, reason: "not-found", candidates: [] };
159
+ return { ok: false, reason: "ambiguous", candidates };
160
+ }
161
+ killTask(id) {
162
+ const task = this.tasks.get(id);
163
+ const runtime = this.runtimes.get(id);
164
+ if (!task || !runtime)
165
+ return { killed: false, reason: "not-found" };
166
+ if (task.status !== "running" || runtime.finalized)
167
+ return { killed: false, reason: "not-running" };
168
+ runtime.killRequested = true;
169
+ runtime.controller.abort();
170
+ return { killed: true };
171
+ }
172
+ counts() {
173
+ const counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0 };
174
+ for (const task of this.tasks.values()) {
175
+ counts[task.status]++;
176
+ counts.total++;
177
+ }
178
+ return counts;
179
+ }
180
+ /** Resolves once the task has finalized, including its notification attempt. */
181
+ async waitForTask(id) {
182
+ const task = this.tasks.get(id);
183
+ if (!task)
184
+ throw new Error(`Unknown background task: ${id}`);
185
+ await this.runtimes.get(id)?.done;
186
+ return task;
187
+ }
188
+ /**
189
+ * Mute notifications, refuse new tasks, abort every running task, and wait
190
+ * (bounded) for their finalization.
191
+ */
192
+ async shutdown() {
193
+ this.shuttingDown = true;
194
+ const pending = [];
195
+ for (const [id, runtime] of this.runtimes) {
196
+ const task = this.tasks.get(id);
197
+ if (task?.status === "running" && !runtime.finalized) {
198
+ runtime.killRequested = true;
199
+ runtime.controller.abort();
200
+ }
201
+ pending.push(runtime.done);
202
+ }
203
+ let timer;
204
+ const grace = new Promise((resolve) => {
205
+ timer = setTimeout(resolve, SHUTDOWN_GRACE_MS);
206
+ });
207
+ try {
208
+ await Promise.race([Promise.allSettled(pending).then(() => undefined), grace]);
209
+ }
210
+ finally {
211
+ if (timer)
212
+ clearTimeout(timer);
213
+ }
214
+ }
215
+ newTaskId() {
216
+ while (true) {
217
+ const id = `bg-${randomBytes(3).toString("hex")}`;
218
+ if (!this.tasks.has(id))
219
+ return id;
220
+ }
221
+ }
222
+ async run(task, runtime, env) {
223
+ let outcome;
224
+ try {
225
+ const result = await this.operations.exec(task.command, task.cwd, {
226
+ onData: (data) => this.handleData(task, runtime, data),
227
+ signal: runtime.controller.signal,
228
+ timeout: task.timeoutSeconds,
229
+ env,
230
+ });
231
+ outcome = { exitCode: result.exitCode };
232
+ }
233
+ catch (error) {
234
+ outcome = { error };
235
+ }
236
+ await this.finalize(task, runtime, outcome);
237
+ }
238
+ handleData(task, runtime, data) {
239
+ if (runtime.finalized || runtime.overflow || runtime.streamError !== undefined)
240
+ return;
241
+ const next = task.outputBytes + data.length;
242
+ if (next <= this.maxOutputBytes) {
243
+ runtime.stream.write(data);
244
+ task.outputBytes = next;
245
+ return;
246
+ }
247
+ const budget = this.maxOutputBytes - task.outputBytes;
248
+ if (budget > 0) {
249
+ runtime.stream.write(data.subarray(0, budget));
250
+ task.outputBytes = this.maxOutputBytes;
251
+ }
252
+ runtime.stream.write(`\n[output limit ${formatSize(this.maxOutputBytes)} exceeded; task killed]\n`);
253
+ task.outputTruncated = true;
254
+ runtime.overflow = true;
255
+ runtime.controller.abort();
256
+ }
257
+ async finalize(task, runtime, outcome) {
258
+ if (runtime.finalized)
259
+ return;
260
+ runtime.finalized = true;
261
+ // Flush before setting terminal state: the output file must hold the
262
+ // complete truth by the time anyone is told the task ended.
263
+ await new Promise((resolve) => {
264
+ const stream = runtime.stream;
265
+ if (stream.destroyed) {
266
+ resolve();
267
+ return;
268
+ }
269
+ stream.on("close", resolve);
270
+ stream.end(() => resolve());
271
+ });
272
+ const verdict = this.classifyOutcome(runtime, outcome);
273
+ task.status = verdict.status;
274
+ if (verdict.error !== undefined)
275
+ task.error = verdict.error;
276
+ task.exitCode = outcome.exitCode;
277
+ task.endedAt = this.now();
278
+ this.onChange();
279
+ await this.notifyCompletion(task);
280
+ runtime.resolveDone();
281
+ }
282
+ classifyOutcome(runtime, outcome) {
283
+ if (runtime.overflow) {
284
+ return {
285
+ status: "failed",
286
+ error: `Output exceeded the ${formatSize(this.maxOutputBytes)} limit; the task was killed.`,
287
+ };
288
+ }
289
+ if (runtime.streamError !== undefined) {
290
+ return { status: "failed", error: `Output file error: ${runtime.streamError}` };
291
+ }
292
+ if (runtime.killRequested)
293
+ return { status: "killed" };
294
+ const error = outcome.error;
295
+ if (error instanceof Error) {
296
+ if (error.message === "aborted")
297
+ return { status: "killed" };
298
+ if (error.message.startsWith("timeout:")) {
299
+ return { status: "timeout", error: `Timed out after ${error.message.slice("timeout:".length)}s.` };
300
+ }
301
+ return { status: "failed", error: error.message };
302
+ }
303
+ if (error !== undefined)
304
+ return { status: "failed", error: String(error) };
305
+ if (outcome.exitCode === 0)
306
+ return { status: "completed" };
307
+ if (outcome.exitCode === null)
308
+ return { status: "failed", error: "Command was terminated by a signal." };
309
+ return { status: "failed", error: `Command exited with code ${outcome.exitCode}` };
310
+ }
311
+ async notifyCompletion(task) {
312
+ if (this.shuttingDown || task.notified)
313
+ return;
314
+ task.notified = true;
315
+ let tailText = "";
316
+ let tailBytes = 0;
317
+ let totalBytes = task.outputBytes;
318
+ let tailTruncated = false;
319
+ let tailStartsMidLine = false;
320
+ let tailError;
321
+ try {
322
+ const slice = await readOutputSlice(task.outputPath, { mode: "tail", maxBytes: this.notifyTailBytes });
323
+ tailText = sanitizeBinaryOutput(slice.text);
324
+ tailBytes = slice.sliceBytes;
325
+ totalBytes = slice.totalBytes;
326
+ tailTruncated = slice.truncated;
327
+ tailStartsMidLine = slice.startsMidLine;
328
+ }
329
+ catch (error) {
330
+ tailError = error instanceof Error ? error.message : String(error);
331
+ }
332
+ try {
333
+ this.onNotify({ task, tailText, tailBytes, totalBytes, tailTruncated, tailStartsMidLine, tailError });
334
+ }
335
+ catch {
336
+ // The send failed synchronously (e.g. stale extension runtime after a
337
+ // session replacement); do not pretend the notification was delivered.
338
+ task.notified = false;
339
+ }
340
+ }
341
+ }
342
+ /** First non-empty line of a command, trimmed — for one-line task labels. */
343
+ export function firstCommandLine(command) {
344
+ const line = command.split(/\r?\n/).find((candidate) => candidate.trim().length > 0) ?? command;
345
+ return line.trim();
346
+ }
347
+ /** Compact duration like Pi's own timers: 12s, 3m05s, 1h02m. */
348
+ export function formatDuration(ms) {
349
+ const totalSeconds = Math.max(0, Math.round(ms / 1000));
350
+ if (totalSeconds < 60)
351
+ return `${totalSeconds}s`;
352
+ const minutes = Math.floor(totalSeconds / 60);
353
+ const seconds = totalSeconds % 60;
354
+ if (minutes < 60)
355
+ return `${minutes}m${String(seconds).padStart(2, "0")}s`;
356
+ return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, "0")}m`;
357
+ }
358
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Background task registry: process lifecycle, output capture, completion\n * notification. Pure logic — no UI imports, dependencies injected for tests.\n *\n * Discipline (mirrors the reference implementation this extension distills):\n * - Output streams straight to a file; memory holds only a byte counter.\n * - Finalization is idempotent and strictly ordered: flush stream, set\n * terminal state, then notify. A notification is only sent once the output\n * file holds the complete truth.\n * - Kill goes through AbortController; the shared bash operations kill the\n * whole process tree cross-platform.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream, writeFileSync } from \"node:fs\";\nimport { open } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BashOperations } from \"../../core/tools/bash.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\n\nexport const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;\nexport const DEFAULT_MAX_RUNNING_TASKS = 8;\nexport const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;\nconst SHUTDOWN_GRACE_MS = 2_000;\n\nexport type BgTaskStatus = \"running\" | \"completed\" | \"failed\" | \"killed\" | \"timeout\";\n\nexport interface BgTask {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\tstatus: BgTaskStatus;\n\tstartedAt: number;\n\tendedAt?: number;\n\t/** Process exit code; null when reaped by a signal, undefined while running or when spawn failed. */\n\texitCode: number | null | undefined;\n\toutputPath: string;\n\t/** Bytes accepted into the output file (excludes the overflow notice). */\n\toutputBytes: number;\n\t/** True when output hit the byte limit and the task was killed for it. */\n\toutputTruncated: boolean;\n\terror?: string;\n\ttimeoutSeconds?: number;\n\tnotified: boolean;\n}\n\nexport interface BgTaskNotification {\n\ttask: BgTask;\n\t/** Sanitized tail of the output file; empty when unavailable. */\n\ttailText: string;\n\ttailBytes: number;\n\ttotalBytes: number;\n\ttailTruncated: boolean;\n\ttailStartsMidLine: boolean;\n\t/** Set when the output file could not be read; the notification still fires. */\n\ttailError?: string;\n}\n\nexport interface OutputSlice {\n\ttext: string;\n\tsliceBytes: number;\n\ttotalBytes: number;\n\t/** True when the file holds more bytes than the slice. */\n\ttruncated: boolean;\n\t/** True in tail mode when the slice starts mid-line. */\n\tstartsMidLine: boolean;\n}\n\n/**\n * Read a bounded slice from the head or tail of a file via positioned reads —\n * never the whole file. Tail reads skip UTF-8 continuation bytes so the text\n * starts on a codepoint boundary.\n */\nexport async function readOutputSlice(\n\tfilePath: string,\n\toptions: { mode: \"head\" | \"tail\"; maxBytes: number },\n): Promise<OutputSlice> {\n\tconst file = await open(filePath, \"r\");\n\ttry {\n\t\tconst { size } = await file.stat();\n\t\tlet length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));\n\t\tlet position = options.mode === \"tail\" ? size - length : 0;\n\t\tlet startsMidLine = false;\n\n\t\tif (options.mode === \"tail\" && position > 0 && length > 0) {\n\t\t\t// Probe from one byte before the slice: probe[0] tells whether the\n\t\t\t// slice starts at a line boundary, the rest lets us skip UTF-8\n\t\t\t// continuation bytes (0b10xxxxxx) so we never split a codepoint.\n\t\t\tconst probeStart = position - 1;\n\t\t\tconst probe = Buffer.alloc(Math.min(5, size - probeStart));\n\t\t\tawait file.read(probe, 0, probe.length, probeStart);\n\t\t\tlet skip = 0;\n\t\t\twhile (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {\n\t\t\t\tskip++;\n\t\t\t}\n\t\t\tposition += skip;\n\t\t\tlength -= skip;\n\t\t\tstartsMidLine = probe[skip] !== 0x0a;\n\t\t}\n\n\t\tconst buffer = Buffer.alloc(Math.max(0, length));\n\t\tconst bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;\n\t\treturn {\n\t\t\ttext: buffer.subarray(0, bytesRead).toString(\"utf8\"),\n\t\t\tsliceBytes: bytesRead,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated: size > bytesRead,\n\t\t\tstartsMidLine,\n\t\t};\n\t} finally {\n\t\tawait file.close();\n\t}\n}\n\nexport interface BackgroundRegistryOptions {\n\toperations: BashOperations;\n\t/** Directory for task output files. Defaults to the system temp directory. */\n\toutputDir?: string;\n\tmaxOutputBytes?: number;\n\tmaxRunningTasks?: number;\n\tnotifyTailBytes?: number;\n\t/** Called once per finished task; a synchronous throw rolls back `notified`. */\n\tonNotify: (notification: BgTaskNotification) => void;\n\t/** Called whenever a task starts or reaches a terminal state. */\n\tonChange: () => void;\n\tnow?: () => number;\n}\n\ninterface TaskRuntime {\n\tcontroller: AbortController;\n\tstream: WriteStream;\n\tkillRequested: boolean;\n\toverflow: boolean;\n\tstreamError?: string;\n\tfinalized: boolean;\n\tdone: Promise<void>;\n\tresolveDone: () => void;\n}\n\nexport type ResolveTaskResult =\n\t| { ok: true; task: BgTask }\n\t| { ok: false; reason: \"not-found\" | \"ambiguous\"; candidates: BgTask[] };\n\nexport class BackgroundTaskRegistry {\n\tprivate readonly operations: BashOperations;\n\tprivate readonly outputDir: string;\n\tprivate readonly maxOutputBytes: number;\n\tprivate readonly maxRunningTasks: number;\n\tprivate readonly notifyTailBytes: number;\n\tprivate readonly onNotify: (notification: BgTaskNotification) => void;\n\tprivate readonly onChange: () => void;\n\tprivate readonly now: () => number;\n\n\tprivate readonly tasks = new Map<string, BgTask>();\n\tprivate readonly runtimes = new Map<string, TaskRuntime>();\n\tprivate shuttingDown = false;\n\n\tconstructor(options: BackgroundRegistryOptions) {\n\t\tthis.operations = options.operations;\n\t\tthis.outputDir = options.outputDir ?? tmpdir();\n\t\tthis.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n\t\tthis.maxRunningTasks = options.maxRunningTasks ?? DEFAULT_MAX_RUNNING_TASKS;\n\t\tthis.notifyTailBytes = options.notifyTailBytes ?? DEFAULT_NOTIFY_TAIL_BYTES;\n\t\tthis.onNotify = options.onNotify;\n\t\tthis.onChange = options.onChange;\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tget isShuttingDown(): boolean {\n\t\treturn this.shuttingDown;\n\t}\n\n\tstartTask(input: { command: string; cwd: string; timeoutSeconds?: number; env?: NodeJS.ProcessEnv }): BgTask {\n\t\tif (this.shuttingDown) {\n\t\t\tthrow new Error(\"Pi is shutting down; background task not started.\");\n\t\t}\n\t\tconst running = this.listTasks().filter((task) => task.status === \"running\");\n\t\tif (running.length >= this.maxRunningTasks) {\n\t\t\tconst list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join(\"\\n\");\n\t\t\tthrow new Error(\n\t\t\t\t`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one with bg_kill first:\\n${list}`,\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.newTaskId();\n\t\tconst task: BgTask = {\n\t\t\tid,\n\t\t\tcommand: input.command,\n\t\t\tcwd: input.cwd,\n\t\t\tstatus: \"running\",\n\t\t\tstartedAt: this.now(),\n\t\t\texitCode: undefined,\n\t\t\toutputPath: join(this.outputDir, `pi-${id}.log`),\n\t\t\toutputBytes: 0,\n\t\t\toutputTruncated: false,\n\t\t\ttimeoutSeconds: input.timeoutSeconds,\n\t\t\tnotified: false,\n\t\t};\n\n\t\tlet resolveDone!: () => void;\n\t\tconst done = new Promise<void>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\t\t// Create the file synchronously so bg_logs and the /bg viewer never see\n\t\t// ENOENT between task start and the stream's async open.\n\t\twriteFileSync(task.outputPath, \"\");\n\t\tconst runtime: TaskRuntime = {\n\t\t\tcontroller: new AbortController(),\n\t\t\tstream: createWriteStream(task.outputPath, { flags: \"a\" }),\n\t\t\tkillRequested: false,\n\t\t\toverflow: false,\n\t\t\tfinalized: false,\n\t\t\tdone,\n\t\t\tresolveDone,\n\t\t};\n\t\truntime.stream.on(\"error\", (error) => {\n\t\t\tif (runtime.finalized) return;\n\t\t\truntime.streamError = error.message;\n\t\t\truntime.controller.abort();\n\t\t});\n\n\t\tthis.tasks.set(id, task);\n\t\tthis.runtimes.set(id, runtime);\n\t\tvoid this.run(task, runtime, input.env);\n\t\tthis.onChange();\n\t\treturn task;\n\t}\n\n\tgetTask(id: string): BgTask | undefined {\n\t\treturn this.tasks.get(id);\n\t}\n\n\t/** Running tasks first (oldest first), then finished tasks (newest first). */\n\tlistTasks(): BgTask[] {\n\t\tconst all = [...this.tasks.values()];\n\t\tconst running = all.filter((task) => task.status === \"running\");\n\t\trunning.sort((a, b) => a.startedAt - b.startedAt);\n\t\tconst ended = all.filter((task) => task.status !== \"running\");\n\t\tended.sort((a, b) => (b.endedAt ?? b.startedAt) - (a.endedAt ?? a.startedAt));\n\t\treturn [...running, ...ended];\n\t}\n\n\t/** Resolve a task by id or unique id prefix; the \"bg-\" prefix may be omitted. */\n\tresolveTask(idPrefix: string): ResolveTaskResult {\n\t\tconst needle = idPrefix.trim();\n\t\tif (!needle) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\tconst exact = this.tasks.get(needle) ?? this.tasks.get(`bg-${needle}`);\n\t\tif (exact) return { ok: true, task: exact };\n\t\tconst candidates = [...this.tasks.values()].filter(\n\t\t\t(task) => task.id.startsWith(needle) || task.id.startsWith(`bg-${needle}`),\n\t\t);\n\t\tconst first = candidates[0];\n\t\tif (candidates.length === 1 && first) return { ok: true, task: first };\n\t\tif (candidates.length === 0) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\treturn { ok: false, reason: \"ambiguous\", candidates };\n\t}\n\n\tkillTask(id: string): { killed: true } | { killed: false; reason: \"not-found\" | \"not-running\" } {\n\t\tconst task = this.tasks.get(id);\n\t\tconst runtime = this.runtimes.get(id);\n\t\tif (!task || !runtime) return { killed: false, reason: \"not-found\" };\n\t\tif (task.status !== \"running\" || runtime.finalized) return { killed: false, reason: \"not-running\" };\n\t\truntime.killRequested = true;\n\t\truntime.controller.abort();\n\t\treturn { killed: true };\n\t}\n\n\tcounts(): Record<BgTaskStatus, number> & { total: number } {\n\t\tconst counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0 };\n\t\tfor (const task of this.tasks.values()) {\n\t\t\tcounts[task.status]++;\n\t\t\tcounts.total++;\n\t\t}\n\t\treturn counts;\n\t}\n\n\t/** Resolves once the task has finalized, including its notification attempt. */\n\tasync waitForTask(id: string): Promise<BgTask> {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) throw new Error(`Unknown background task: ${id}`);\n\t\tawait this.runtimes.get(id)?.done;\n\t\treturn task;\n\t}\n\n\t/**\n\t * Mute notifications, refuse new tasks, abort every running task, and wait\n\t * (bounded) for their finalization.\n\t */\n\tasync shutdown(): Promise<void> {\n\t\tthis.shuttingDown = true;\n\t\tconst pending: Promise<void>[] = [];\n\t\tfor (const [id, runtime] of this.runtimes) {\n\t\t\tconst task = this.tasks.get(id);\n\t\t\tif (task?.status === \"running\" && !runtime.finalized) {\n\t\t\t\truntime.killRequested = true;\n\t\t\t\truntime.controller.abort();\n\t\t\t}\n\t\t\tpending.push(runtime.done);\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst grace = new Promise<void>((resolve) => {\n\t\t\ttimer = setTimeout(resolve, SHUTDOWN_GRACE_MS);\n\t\t});\n\t\ttry {\n\t\t\tawait Promise.race([Promise.allSettled(pending).then(() => undefined), grace]);\n\t\t} finally {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t}\n\t}\n\n\tprivate newTaskId(): string {\n\t\twhile (true) {\n\t\t\tconst id = `bg-${randomBytes(3).toString(\"hex\")}`;\n\t\t\tif (!this.tasks.has(id)) return id;\n\t\t}\n\t}\n\n\tprivate async run(task: BgTask, runtime: TaskRuntime, env?: NodeJS.ProcessEnv): Promise<void> {\n\t\tlet outcome: { exitCode?: number | null; error?: unknown };\n\t\ttry {\n\t\t\tconst result = await this.operations.exec(task.command, task.cwd, {\n\t\t\t\tonData: (data) => this.handleData(task, runtime, data),\n\t\t\t\tsignal: runtime.controller.signal,\n\t\t\t\ttimeout: task.timeoutSeconds,\n\t\t\t\tenv,\n\t\t\t});\n\t\t\toutcome = { exitCode: result.exitCode };\n\t\t} catch (error) {\n\t\t\toutcome = { error };\n\t\t}\n\t\tawait this.finalize(task, runtime, outcome);\n\t}\n\n\tprivate handleData(task: BgTask, runtime: TaskRuntime, data: Buffer): void {\n\t\tif (runtime.finalized || runtime.overflow || runtime.streamError !== undefined) return;\n\t\tconst next = task.outputBytes + data.length;\n\t\tif (next <= this.maxOutputBytes) {\n\t\t\truntime.stream.write(data);\n\t\t\ttask.outputBytes = next;\n\t\t\treturn;\n\t\t}\n\t\tconst budget = this.maxOutputBytes - task.outputBytes;\n\t\tif (budget > 0) {\n\t\t\truntime.stream.write(data.subarray(0, budget));\n\t\t\ttask.outputBytes = this.maxOutputBytes;\n\t\t}\n\t\truntime.stream.write(`\\n[output limit ${formatSize(this.maxOutputBytes)} exceeded; task killed]\\n`);\n\t\ttask.outputTruncated = true;\n\t\truntime.overflow = true;\n\t\truntime.controller.abort();\n\t}\n\n\tprivate async finalize(\n\t\ttask: BgTask,\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): Promise<void> {\n\t\tif (runtime.finalized) return;\n\t\truntime.finalized = true;\n\n\t\t// Flush before setting terminal state: the output file must hold the\n\t\t// complete truth by the time anyone is told the task ended.\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tconst stream = runtime.stream;\n\t\t\tif (stream.destroyed) {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstream.on(\"close\", resolve);\n\t\t\tstream.end(() => resolve());\n\t\t});\n\n\t\tconst verdict = this.classifyOutcome(runtime, outcome);\n\t\ttask.status = verdict.status;\n\t\tif (verdict.error !== undefined) task.error = verdict.error;\n\t\ttask.exitCode = outcome.exitCode;\n\t\ttask.endedAt = this.now();\n\t\tthis.onChange();\n\t\tawait this.notifyCompletion(task);\n\t\truntime.resolveDone();\n\t}\n\n\tprivate classifyOutcome(\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): { status: BgTaskStatus; error?: string } {\n\t\tif (runtime.overflow) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Output exceeded the ${formatSize(this.maxOutputBytes)} limit; the task was killed.`,\n\t\t\t};\n\t\t}\n\t\tif (runtime.streamError !== undefined) {\n\t\t\treturn { status: \"failed\", error: `Output file error: ${runtime.streamError}` };\n\t\t}\n\t\tif (runtime.killRequested) return { status: \"killed\" };\n\t\tconst error = outcome.error;\n\t\tif (error instanceof Error) {\n\t\t\tif (error.message === \"aborted\") return { status: \"killed\" };\n\t\t\tif (error.message.startsWith(\"timeout:\")) {\n\t\t\t\treturn { status: \"timeout\", error: `Timed out after ${error.message.slice(\"timeout:\".length)}s.` };\n\t\t\t}\n\t\t\treturn { status: \"failed\", error: error.message };\n\t\t}\n\t\tif (error !== undefined) return { status: \"failed\", error: String(error) };\n\t\tif (outcome.exitCode === 0) return { status: \"completed\" };\n\t\tif (outcome.exitCode === null) return { status: \"failed\", error: \"Command was terminated by a signal.\" };\n\t\treturn { status: \"failed\", error: `Command exited with code ${outcome.exitCode}` };\n\t}\n\n\tprivate async notifyCompletion(task: BgTask): Promise<void> {\n\t\tif (this.shuttingDown || task.notified) return;\n\t\ttask.notified = true;\n\n\t\tlet tailText = \"\";\n\t\tlet tailBytes = 0;\n\t\tlet totalBytes = task.outputBytes;\n\t\tlet tailTruncated = false;\n\t\tlet tailStartsMidLine = false;\n\t\tlet tailError: string | undefined;\n\t\ttry {\n\t\t\tconst slice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.notifyTailBytes });\n\t\t\ttailText = sanitizeBinaryOutput(slice.text);\n\t\t\ttailBytes = slice.sliceBytes;\n\t\t\ttotalBytes = slice.totalBytes;\n\t\t\ttailTruncated = slice.truncated;\n\t\t\ttailStartsMidLine = slice.startsMidLine;\n\t\t} catch (error) {\n\t\t\ttailError = error instanceof Error ? error.message : String(error);\n\t\t}\n\n\t\ttry {\n\t\t\tthis.onNotify({ task, tailText, tailBytes, totalBytes, tailTruncated, tailStartsMidLine, tailError });\n\t\t} catch {\n\t\t\t// The send failed synchronously (e.g. stale extension runtime after a\n\t\t\t// session replacement); do not pretend the notification was delivered.\n\t\t\ttask.notified = false;\n\t\t}\n\t}\n}\n\n/** First non-empty line of a command, trimmed — for one-line task labels. */\nexport function firstCommandLine(command: string): string {\n\tconst line = command.split(/\\r?\\n/).find((candidate) => candidate.trim().length > 0) ?? command;\n\treturn line.trim();\n}\n\n/** Compact duration like Pi's own timers: 12s, 3m05s, 1h02m. */\nexport function formatDuration(ms: number): string {\n\tconst totalSeconds = Math.max(0, Math.round(ms / 1000));\n\tif (totalSeconds < 60) return `${totalSeconds}s`;\n\tconst minutes = Math.floor(totalSeconds / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (minutes < 60) return `${minutes}m${String(seconds).padStart(2, \"0\")}s`;\n\treturn `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, \"0\")}m`;\n}\n"]}
@@ -0,0 +1,26 @@
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.
5
+ */
6
+ import { type Component } from "@earendil-works/pi-tui";
7
+ import type { AgentToolResult, MessageRenderOptions, ToolRenderContext, ToolRenderResultOptions } from "../../core/extensions/types.ts";
8
+ import type { CustomMessage } from "../../core/messages.ts";
9
+ import { type Theme } from "../../modes/interactive/theme/theme.ts";
10
+ import type { BgBashDetails, BgBashInput, BgKillInput, BgLogsInput, BgNotificationDetails } from "./index.ts";
11
+ import { type BgTaskStatus } from "./registry.ts";
12
+ export declare function statusGlyph(status: BgTaskStatus): string;
13
+ export declare function statusColor(status: BgTaskStatus): "success" | "error" | "warning" | "accent";
14
+ /** First command line truncated to a visible-character budget with an ellipsis. */
15
+ export declare function commandLabel(command: string, width: number): string;
16
+ export declare function renderBgBashCall(args: BgBashInput, theme: Theme, context: ToolRenderContext): Component;
17
+ export declare function renderBgBashResult(result: AgentToolResult<BgBashDetails | undefined>, _options: ToolRenderResultOptions, theme: Theme, context: ToolRenderContext): Component;
18
+ export declare function renderBgLogsCall(args: BgLogsInput, theme: Theme): Component;
19
+ export declare function renderBgKillCall(args: BgKillInput, theme: Theme): Component;
20
+ /**
21
+ * Collapsed: status glyph, task id, command, and outcome on one line, output
22
+ * path below. Expanded adds the embedded output tail. Returns undefined for
23
+ * malformed details so the default custom-message rendering takes over.
24
+ */
25
+ export declare function renderBackgroundNotification(message: CustomMessage<BgNotificationDetails>, options: MessageRenderOptions, theme: Theme): Component | undefined;
26
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/render.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,wBAAwB,CAAC;AACxF,OAAO,KAAK,EACX,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC9G,OAAO,EAAE,KAAK,YAAY,EAAoC,MAAM,eAAe,CAAC;AAapF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAYxD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAY5F;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAcvG;AAED,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,eAAe,CAAC,aAAa,GAAG,SAAS,CAAC,EAClD,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,iBAAiB,GACxB,SAAS,CAYX;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAQ3E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAE3E;AAWD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC7C,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,KAAK,GACV,SAAS,GAAG,SAAS,CAyBvB"}
@@ -0,0 +1,118 @@
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.
5
+ */
6
+ import { Container, Text, TruncatedText } from "@earendil-works/pi-tui";
7
+ import { highlightCode } from "../../modes/interactive/theme/theme.js";
8
+ import { firstCommandLine, formatDuration } from "./registry.js";
9
+ const COMMAND_PREVIEW_LIMIT = 120;
10
+ const NOTIFY_TAIL_LIMIT = 4000;
11
+ function bgPrompt(theme) {
12
+ return theme.fg("toolTitle", theme.bold("$ "));
13
+ }
14
+ function timeoutSuffix(timeout, theme) {
15
+ return timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
16
+ }
17
+ export function statusGlyph(status) {
18
+ switch (status) {
19
+ case "completed":
20
+ return "✓";
21
+ case "failed":
22
+ case "timeout":
23
+ return "✗";
24
+ case "killed":
25
+ return "○";
26
+ default:
27
+ return "●";
28
+ }
29
+ }
30
+ export function statusColor(status) {
31
+ switch (status) {
32
+ case "completed":
33
+ return "success";
34
+ case "failed":
35
+ return "error";
36
+ case "timeout":
37
+ case "killed":
38
+ return "warning";
39
+ default:
40
+ return "accent";
41
+ }
42
+ }
43
+ /** First command line truncated to a visible-character budget with an ellipsis. */
44
+ export function commandLabel(command, width) {
45
+ const characters = [...firstCommandLine(command)];
46
+ return characters.length <= width ? characters.join("") : `${characters.slice(0, Math.max(0, width - 1)).join("")}…`;
47
+ }
48
+ export function renderBgBashCall(args, theme, context) {
49
+ const lines = args.command.split(/\r?\n/).filter((line) => line.trim().length > 0);
50
+ const suffix = theme.fg("muted", " &") + timeoutSuffix(args.timeout, theme);
51
+ if (context.expanded && lines.length > 1) {
52
+ const body = highlightCode(args.command, "bash").join("\n");
53
+ return new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);
54
+ }
55
+ const body = highlightCode(firstCommandLine(args.command), "bash").join("\n");
56
+ if (lines.length > 1) {
57
+ const hidden = lines.length - 1;
58
+ const more = theme.fg("muted", ` (+${hidden} line${hidden === 1 ? "" : "s"})`);
59
+ return new Text(`${bgPrompt(theme)}${body}${suffix}${more}`, 0, 0);
60
+ }
61
+ return new Text(`${bgPrompt(theme)}${body}${suffix}`, 0, 0);
62
+ }
63
+ export function renderBgBashResult(result, _options, theme, context) {
64
+ if (context.isError) {
65
+ const text = result.content.find((part) => part.type === "text")?.text ?? "";
66
+ return new Text(theme.fg("toolOutput", text.trimEnd()), 0, 0);
67
+ }
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);
78
+ }
79
+ export function renderBgKillCall(args, theme) {
80
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg kill "))}${theme.fg("accent", args.taskId)}`, 0, 0);
81
+ }
82
+ /** One-line summary of a finished task, shared by the notification renderer. */
83
+ function taskSummaryLine(details, theme) {
84
+ const runtime = formatDuration(details.runtimeMs);
85
+ const exit = details.exitCode !== undefined && details.exitCode !== null ? `, exit ${details.exitCode}` : "";
86
+ const outcome = theme.fg(statusColor(details.status), `${details.status}${exit} in ${runtime}`);
87
+ 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}`)}`;
89
+ }
90
+ /**
91
+ * Collapsed: status glyph, task id, command, and outcome on one line, output
92
+ * path below. Expanded adds the embedded output tail. Returns undefined for
93
+ * malformed details so the default custom-message rendering takes over.
94
+ */
95
+ export function renderBackgroundNotification(message, options, theme) {
96
+ const details = message.details;
97
+ if (!details || typeof details.taskId !== "string")
98
+ return undefined;
99
+ const container = new Container();
100
+ container.addChild(new TruncatedText(taskSummaryLine(details, theme), 1, 0));
101
+ container.addChild(new Text(theme.fg("muted", details.outputPath), 1, 0));
102
+ if (details.tailError) {
103
+ container.addChild(new Text(theme.fg("error", `Output unavailable: ${details.tailError}`), 1, 0));
104
+ }
105
+ else if (options.expanded && details.tailText) {
106
+ 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;
111
+ const truncatedNote = details.tailTruncated
112
+ ? `\n[showing tail of ${details.totalBytes} bytes; full output: ${details.outputPath}]`
113
+ : "";
114
+ container.addChild(new Text(`${theme.fg("toolOutput", tail.trimEnd())}${theme.fg("muted", truncatedNote)}`, 1, 0));
115
+ }
116
+ return container;
117
+ }
118
+ //# sourceMappingURL=render.js.map
@@ -0,0 +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 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AASnE,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAQ9C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAUnE,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAS9C,CAAC"}
@@ -1,3 +1,4 @@
1
+ import backgroundExtension from "./background/index.js";
1
2
  import deepwikiExtension from "./deepwiki/index.js";
2
3
  import llamaExtension from "./llama/index.js";
3
4
  import questionExtension from "./question/index.js";
@@ -7,6 +8,7 @@ import subagentExtension from "./subagent/index.js";
7
8
  import todoExtension from "./todo/index.js";
8
9
  export const builtInExtensions = [
9
10
  { name: "llama.cpp", factory: llamaExtension, hidden: true },
11
+ { name: "background", factory: backgroundExtension, hidden: true },
10
12
  { name: "deepwiki", factory: deepwikiExtension, hidden: true },
11
13
  { name: "question", factory: questionExtension, hidden: true },
12
14
  { name: "router", factory: routerExtension, hidden: true },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AACA,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IACnD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE;IAC5D,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE;IAC1D,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE;IAClE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE;CACtD,CAAC","sourcesContent":["import type { InlineExtension } from \"../core/extensions/types.ts\";\nimport deepwikiExtension from \"./deepwiki/index.ts\";\nimport llamaExtension from \"./llama/index.ts\";\nimport questionExtension from \"./question/index.ts\";\nimport routerExtension from \"./router/index.ts\";\nimport statuslineExtension from \"./statusline/index.ts\";\nimport subagentExtension from \"./subagent/index.ts\";\nimport todoExtension from \"./todo/index.ts\";\n\nexport const builtInExtensions: InlineExtension[] = [\n\t{ name: \"llama.cpp\", factory: llamaExtension, hidden: true },\n\t{ name: \"deepwiki\", factory: deepwikiExtension, hidden: true },\n\t{ name: \"question\", factory: questionExtension, hidden: true },\n\t{ name: \"router\", factory: routerExtension, hidden: true },\n\t{ name: \"statusline\", factory: statuslineExtension, hidden: true },\n\t{ name: \"subagent\", factory: subagentExtension, hidden: true },\n\t{ name: \"todo\", factory: todoExtension, hidden: true },\n];\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AACA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IACnD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE;IAC5D,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE;IAClE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE;IAC1D,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE;IAClE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE;IAC9D,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE;CACtD,CAAC","sourcesContent":["import type { InlineExtension } from \"../core/extensions/types.ts\";\nimport backgroundExtension from \"./background/index.ts\";\nimport deepwikiExtension from \"./deepwiki/index.ts\";\nimport llamaExtension from \"./llama/index.ts\";\nimport questionExtension from \"./question/index.ts\";\nimport routerExtension from \"./router/index.ts\";\nimport statuslineExtension from \"./statusline/index.ts\";\nimport subagentExtension from \"./subagent/index.ts\";\nimport todoExtension from \"./todo/index.ts\";\n\nexport const builtInExtensions: InlineExtension[] = [\n\t{ name: \"llama.cpp\", factory: llamaExtension, hidden: true },\n\t{ name: \"background\", factory: backgroundExtension, hidden: true },\n\t{ name: \"deepwiki\", factory: deepwikiExtension, hidden: true },\n\t{ name: \"question\", factory: questionExtension, hidden: true },\n\t{ name: \"router\", factory: routerExtension, hidden: true },\n\t{ name: \"statusline\", factory: statuslineExtension, hidden: true },\n\t{ name: \"subagent\", factory: subagentExtension, hidden: true },\n\t{ name: \"todo\", factory: todoExtension, hidden: true },\n];\n"]}