@selesai/code 0.8.4 → 0.8.6

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 (147) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +7 -1
  3. package/dist/core/slash-commands.js +1 -0
  4. package/dist/defaults/models.json +46 -55
  5. package/dist/defaults/settings.json +7 -8
  6. package/dist/extensions/agent-browser.test.ts +178 -2
  7. package/dist/extensions/agent-browser.ts +2 -0
  8. package/dist/extensions/copy-turn.test.ts +229 -0
  9. package/dist/extensions/copy-turn.ts +11 -0
  10. package/dist/extensions/grep-app/index.test.ts +356 -1
  11. package/dist/extensions/grep-app/index.ts +6 -1
  12. package/dist/extensions/handoff-new.test.ts +351 -161
  13. package/dist/extensions/handoff-new.ts +3 -0
  14. package/dist/extensions/inline-skills.test.ts +93 -0
  15. package/dist/extensions/inline-skills.ts +3 -0
  16. package/dist/extensions/pi-subagents/CHANGELOG.md +26 -3
  17. package/dist/extensions/pi-subagents/README.md +2 -0
  18. package/dist/extensions/pi-subagents/agents/builder.md +5 -2
  19. package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
  20. package/dist/extensions/pi-subagents/docs/configuration.md +54 -2
  21. package/dist/extensions/pi-subagents/docs/observability.md +1 -1
  22. package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -1
  23. package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
  24. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  25. package/dist/extensions/pi-subagents/package.json +1 -1
  26. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +1 -1
  27. package/dist/extensions/pi-subagents/src/agents/agents.ts +9 -3
  28. package/dist/extensions/pi-subagents/src/extension/config.ts +12 -0
  29. package/dist/extensions/pi-subagents/src/extension/doctor.ts +40 -0
  30. package/dist/extensions/pi-subagents/src/extension/index.ts +104 -14
  31. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +5 -0
  32. package/dist/extensions/pi-subagents/src/extension/rpc.ts +3 -9
  33. package/dist/extensions/pi-subagents/src/extension/schemas.ts +2 -2
  34. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -1
  35. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +4 -7
  36. package/dist/extensions/pi-subagents/src/missions/store.ts +4 -4
  37. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +6 -2
  38. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +374 -0
  39. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +9 -5
  40. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +117 -29
  41. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +12 -6
  42. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -1
  43. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +14 -5
  44. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +33 -15
  45. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
  46. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +104 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +17 -3
  48. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +19 -1
  49. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +2 -2
  50. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +5 -2
  51. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +12 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
  53. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +84 -36
  54. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
  55. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +37 -2
  56. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +135 -22
  57. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +12 -0
  58. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
  59. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +171 -0
  60. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +653 -196
  61. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +13 -4
  62. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +286 -0
  63. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +23 -0
  64. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  65. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +44 -1
  66. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +280 -0
  67. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +4 -2
  68. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +19 -3
  69. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +17 -5
  70. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
  71. package/dist/extensions/pi-subagents/src/shared/types.ts +108 -0
  72. package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -1
  73. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +7 -5
  74. package/dist/extensions/pi-subagents/src/tui/fleet.ts +226 -13
  75. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +22 -5
  76. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
  77. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +87 -0
  78. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +96 -6
  79. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +28 -5
  80. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +8 -5
  81. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +83 -2
  82. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +211 -15
  83. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +41 -3
  84. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +314 -10
  85. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +31 -1
  86. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +317 -0
  87. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +21 -2
  88. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +16 -1
  89. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +6 -1
  90. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +75 -4
  91. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +18 -0
  92. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +29 -1
  93. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +31 -5
  94. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +139 -1
  95. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  96. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
  97. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +22 -0
  98. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +2 -1
  99. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +171 -0
  100. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +30 -4
  101. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +14 -0
  102. package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +69 -0
  103. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +83 -0
  104. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +2 -2
  105. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +55 -2
  106. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +12 -1
  107. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +7 -7
  108. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +121 -0
  109. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +9 -0
  110. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -3
  111. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
  112. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +28 -0
  113. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +10 -1
  114. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +39 -0
  115. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +65 -0
  116. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
  117. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +61 -0
  118. package/dist/extensions/ponytail/index.js +11 -0
  119. package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
  120. package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
  121. package/dist/extensions/ponytail/test/extension.test.js +274 -140
  122. package/dist/extensions/ponytail/test/helpers.test.js +280 -92
  123. package/dist/extensions/question/index.ts +18 -0
  124. package/dist/extensions/question/question-list.ts +22 -0
  125. package/dist/extensions/question/tests/batch.test.ts +103 -65
  126. package/dist/extensions/question/tests/helpers.test.ts +68 -35
  127. package/dist/extensions/question/tests/question-list.test.ts +506 -204
  128. package/dist/extensions/question/tests/row-layout.test.ts +180 -114
  129. package/dist/extensions/question/tests/schemas.test.ts +42 -19
  130. package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
  131. package/dist/extensions/question/tests/wizard.test.ts +771 -50
  132. package/dist/extensions/rtk.test.ts +180 -1
  133. package/dist/extensions/tokenin-onboarding.ts +3 -0
  134. package/dist/extensions/undo.test.ts +720 -0
  135. package/dist/extensions/undo.ts +6 -0
  136. package/dist/extensions/web-agent-onboarding.test.ts +415 -0
  137. package/dist/extensions/workflow/extension.ts +3 -3
  138. package/dist/extensions/workflow/modes.ts +41 -19
  139. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  140. package/dist/modes/interactive/interactive-mode.js +48 -1
  141. package/dist/skills/pi-subagents/references/execution-controls.md +1 -1
  142. package/docs/plans/workflow-autoloop-reference.md +1 -2
  143. package/docs/plans/workflow-handoff-carryover.md +108 -0
  144. package/docs/quickstart.md +27 -43
  145. package/docs/settings.md +4 -0
  146. package/docs/usage.md +1 -0
  147. package/package.json +5 -1
@@ -11,7 +11,7 @@ vi.mock("@selesai/code", async (importOriginal) => ({
11
11
  ensureTool: ensureToolMock,
12
12
  }));
13
13
 
14
- import rtkExtension from "./rtk.ts";
14
+ import rtkExtension, { parseSemver } from "./rtk.ts";
15
15
 
16
16
  // The fake-binary tests need a POSIX shell shim. Windows CI cannot spawn
17
17
  // .cmd shims without a shell, and the real flow uses the managed rtk.exe
@@ -138,4 +138,183 @@ posixOnly("rtk extension", () => {
138
138
  rmSync(wrongDir, { recursive: true, force: true });
139
139
  }
140
140
  });
141
+
142
+ it("parseSemver returns null for non-version strings", () => {
143
+ expect(parseSemver("no version here")).toBeNull();
144
+ expect(parseSemver("")).toBeNull();
145
+ });
146
+
147
+ it("parseSemver parses X.Y.Z from arbitrary text", () => {
148
+ expect(parseSemver("rtk 0.42.0")).toEqual([0, 42, 0]);
149
+ expect(parseSemver(" v1.2.3 ")).toEqual([1, 2, 3]);
150
+ });
151
+
152
+ it("disables the extension when --version fails", async () => {
153
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
154
+ const { pi, handlers } = makePi();
155
+ ensureToolMock.mockResolvedValue("rtk");
156
+ // Override exec so --version fails.
157
+ pi.exec = vi.fn(async () => ({ code: 1, stdout: "", stderr: "not found", killed: false }));
158
+ rtkExtension(pi);
159
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("failed --version")));
160
+ expect(handlers.has("tool_call")).toBe(false);
161
+ });
162
+
163
+ it("disables the extension when the version is unparseable", async () => {
164
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
165
+ const { pi, handlers } = makePi();
166
+ ensureToolMock.mockResolvedValue("rtk");
167
+ pi.exec = vi.fn(async () => ({ code: 0, stdout: "garbage output", stderr: "", killed: false }));
168
+ rtkExtension(pi);
169
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("could not parse version")));
170
+ expect(handlers.has("tool_call")).toBe(false);
171
+ });
172
+
173
+ it("disables the extension when the version output is empty", async () => {
174
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
175
+ const { pi, handlers } = makePi();
176
+ ensureToolMock.mockResolvedValue("rtk");
177
+ pi.exec = vi.fn(async () => ({ code: 0, stdout: " ", stderr: "", killed: false }));
178
+ rtkExtension(pi);
179
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("<empty output>")));
180
+ expect(handlers.has("tool_call")).toBe(false);
181
+ });
182
+
183
+ it("disables the extension when the version is too old", async () => {
184
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
185
+ const { pi, handlers } = makePi();
186
+ ensureToolMock.mockResolvedValue("rtk");
187
+ pi.exec = vi.fn(async () => ({ code: 0, stdout: "rtk 0.22.0", stderr: "", killed: false }));
188
+ rtkExtension(pi);
189
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("too old")));
190
+ expect(handlers.has("tool_call")).toBe(false);
191
+ });
192
+
193
+ it("warns and skips when ensureTool fails", async () => {
194
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
195
+ ensureToolMock.mockRejectedValue(new Error("download failed"));
196
+ const { pi, handlers } = makePi();
197
+ rtkExtension(pi);
198
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("managed installation failed")));
199
+ expect(handlers.has("tool_call")).toBe(false);
200
+ });
201
+
202
+ it("warns with the stringified error when ensureTool rejects a non-Error", async () => {
203
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
204
+ ensureToolMock.mockRejectedValue("plain string failure");
205
+ const { pi, handlers } = makePi();
206
+ rtkExtension(pi);
207
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("plain string failure")));
208
+ expect(handlers.has("tool_call")).toBe(false);
209
+ });
210
+
211
+ it("warns and skips when ensureTool returns nothing", async () => {
212
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
213
+ ensureToolMock.mockResolvedValue(undefined);
214
+ const { pi, handlers } = makePi();
215
+ rtkExtension(pi);
216
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("unavailable")));
217
+ expect(handlers.has("tool_call")).toBe(false);
218
+ });
219
+
220
+ it("warns and skips when probeRtk throws", async () => {
221
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
222
+ const { pi, handlers } = makePi();
223
+ ensureToolMock.mockResolvedValue("rtk");
224
+ pi.exec = vi.fn(async () => {
225
+ throw new Error("spawn ENOENT");
226
+ });
227
+ rtkExtension(pi);
228
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("verification failed")));
229
+ expect(handlers.has("tool_call")).toBe(false);
230
+ });
231
+
232
+ it("warns with the stringified error when probeRtk throws a non-Error", async () => {
233
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
234
+ const { pi, handlers } = makePi();
235
+ ensureToolMock.mockResolvedValue("rtk");
236
+ pi.exec = vi.fn(async () => {
237
+ throw "string failure";
238
+ });
239
+ rtkExtension(pi);
240
+ await vi.waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("string failure")));
241
+ expect(handlers.has("tool_call")).toBe(false);
242
+ });
243
+
244
+ it("ignores non-bash tool calls and empty commands", async () => {
245
+ const { pi, handlers } = makePi();
246
+ rtkExtension(pi);
247
+ const handler = await getToolCall(handlers);
248
+
249
+ const nonBash: any = { type: "tool_call", toolName: "edit", toolCallId: "c1", input: { path: "a.ts" } };
250
+ await handler(nonBash, { signal: undefined });
251
+ expect(nonBash.input.path).toBe("a.ts");
252
+
253
+ const nonString: any = { type: "tool_call", toolName: "bash", toolCallId: "c2", input: { command: 42 } };
254
+ await handler(nonString, { signal: undefined });
255
+ expect(nonString.input.command).toBe(42);
256
+
257
+ const empty: any = { type: "tool_call", toolName: "bash", toolCallId: "c3", input: { command: " " } };
258
+ await handler(empty, { signal: undefined });
259
+ expect(empty.input.command).toBe(" ");
260
+ });
261
+
262
+ it("passes through when RTK_DISABLED is set mid-run", async () => {
263
+ const { pi, handlers } = makePi();
264
+ rtkExtension(pi);
265
+ const handler = await getToolCall(handlers);
266
+
267
+ process.env.RTK_DISABLED = "1";
268
+ try {
269
+ const event: any = { type: "tool_call", toolName: "bash", toolCallId: "c1", input: { command: "git status" } };
270
+ await handler(event, { signal: undefined });
271
+ expect(event.input.command).toBe("git status");
272
+ } finally {
273
+ delete process.env.RTK_DISABLED;
274
+ }
275
+ });
276
+
277
+ it("passes through when rewrite is killed or returns an unexpected code", async () => {
278
+ const { pi, handlers } = makePi();
279
+ rtkExtension(pi);
280
+ const handler = await getToolCall(handlers);
281
+
282
+ // The probe (--version + gain) already ran against the real fake binary,
283
+ // so the mock's responses are consumed by the rewrite call only.
284
+ // Killed rewrite.
285
+ pi.exec = vi.fn().mockResolvedValueOnce({ code: 0, stdout: "", stderr: "", killed: true });
286
+ const event1: any = { type: "tool_call", toolName: "bash", toolCallId: "c1", input: { command: "git status" } };
287
+ await handler(event1, { signal: undefined });
288
+ expect(event1.input.command).toBe("git status");
289
+
290
+ // Unexpected exit code (2).
291
+ pi.exec = vi.fn().mockResolvedValueOnce({ code: 2, stdout: "", stderr: "err", killed: false });
292
+ const event2: any = { type: "tool_call", toolName: "bash", toolCallId: "c2", input: { command: "git status" } };
293
+ await handler(event2, { signal: undefined });
294
+ expect(event2.input.command).toBe("git status");
295
+ });
296
+
297
+ it("passes through when rewrite returns empty stdout", async () => {
298
+ const { pi, handlers } = makePi();
299
+ rtkExtension(pi);
300
+ const handler = await getToolCall(handlers);
301
+
302
+ pi.exec = vi.fn().mockResolvedValueOnce({ code: 0, stdout: " ", stderr: "", killed: false });
303
+ const event: any = { type: "tool_call", toolName: "bash", toolCallId: "c1", input: { command: "git status" } };
304
+ await handler(event, { signal: undefined });
305
+ expect(event.input.command).toBe("git status");
306
+ });
307
+
308
+ it("fails open when the tool_call handler throws", async () => {
309
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
310
+ const { pi, handlers } = makePi();
311
+ rtkExtension(pi);
312
+ const handler = await getToolCall(handlers);
313
+
314
+ pi.exec = vi.fn().mockRejectedValueOnce(new Error("boom"));
315
+ const event: any = { type: "tool_call", toolName: "bash", toolCallId: "c1", input: { command: "git status" } };
316
+ await handler(event, { signal: undefined });
317
+ expect(event.input.command).toBe("git status");
318
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining("unexpected error"), expect.any(Error));
319
+ });
141
320
  });
@@ -369,6 +369,9 @@ async function runOnboarding(pi: ExtensionAPI, ctx: ExtensionContext): Promise<v
369
369
  if (!storedKey && typeof legacyModelsKey === "string") {
370
370
  setStoredTokenInApiKey(ctx.modelRegistry.authStorage, legacyModelsKey);
371
371
  saveTokenInAccount(legacyModelsKey, { baseUrl: getTokenInBaseUrl(models), setActive: true });
372
+ // legacyModelsKey is only non-empty when models.json parsed successfully,
373
+ // so the file always exists here.
374
+ /* v8 ignore next 1 */
372
375
  if (existsSync(modelsPath)) writeModelsJson(removeTokenInApiKey(models), modelsPath);
373
376
  }
374
377
  markOnboardingComplete();