@selesai/code 0.8.6 → 0.8.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.
- package/CHANGELOG.md +16 -0
- package/dist/core/agent-session.js +13 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/provider-composer.js +17 -4
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/system-prompt.js +2 -8
- package/dist/core/system-prompt.test.js +6 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/schema-prune.d.ts +16 -0
- package/dist/core/tools/schema-prune.js +39 -0
- package/dist/core/tools/schema-prune.test.d.ts +1 -0
- package/dist/core/tools/schema-prune.test.js +111 -0
- package/dist/defaults/settings.json +1 -0
- package/dist/extensions/model-prompt-injector/config.json +10 -0
- package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
- package/dist/extensions/model-prompt-injector/index.ts +151 -0
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
- package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
- package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/agents/worker.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +78 -19
- package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
- package/dist/extensions/pi-subagents/docs/missions.md +5 -3
- package/dist/extensions/pi-subagents/docs/models.md +1 -1
- package/dist/extensions/pi-subagents/docs/observability.md +42 -2
- package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
- package/dist/extensions/pi-subagents/docs/workflows.md +5 -5
- package/dist/extensions/pi-subagents/install.mjs +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
- package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
- package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
- package/dist/extensions/pi-subagents/src/extension/config.ts +53 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +67 -13
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
- package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
- package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +5 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +205 -41
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +30 -24
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +101 -4
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +333 -45
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
- package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
- package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +50 -4
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +128 -16
- package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
- package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
- package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
- package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
- package/dist/extensions/question/index.ts +21 -6
- package/dist/extensions/question/question-list.ts +7 -1
- package/dist/extensions/question/row-layout.ts +3 -0
- package/dist/extensions/question/tests/exp.test.ts +70 -0
- package/dist/extensions/question/tests/exp2.test.ts +59 -0
- package/dist/extensions/question/tests/exp3.test.ts +47 -0
- package/dist/extensions/question/tests/helpers.test.ts +7 -0
- package/dist/extensions/question/tests/question-list.test.ts +140 -0
- package/dist/extensions/question/tests/row-layout.test.ts +13 -0
- package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
- package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
- package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
- package/dist/skills/pi-subagents/SKILL.md +1 -1
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/docs/settings.md +6 -0
- package/package.json +3 -3
|
@@ -33,6 +33,7 @@ describe("async run status inspection", () => {
|
|
|
33
33
|
fs.writeFileSync(sessionFile, "", "utf-8");
|
|
34
34
|
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
35
35
|
runId: "run-stale",
|
|
36
|
+
sessionId: "session-current",
|
|
36
37
|
mode: "single",
|
|
37
38
|
state: "running",
|
|
38
39
|
pid: 12345,
|
|
@@ -206,6 +207,41 @@ describe("async run status inspection", () => {
|
|
|
206
207
|
}
|
|
207
208
|
});
|
|
208
209
|
|
|
210
|
+
it("does not advertise an output artifact that was never written", () => {
|
|
211
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-phantom-"));
|
|
212
|
+
try {
|
|
213
|
+
const asyncRoot = path.join(root, "runs");
|
|
214
|
+
const asyncDir = path.join(asyncRoot, "run-phantom-output");
|
|
215
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
216
|
+
// A workflow run orchestrates children elsewhere and never writes output-<index>.log itself.
|
|
217
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
218
|
+
runId: "run-phantom-output",
|
|
219
|
+
mode: "workflow",
|
|
220
|
+
state: "running",
|
|
221
|
+
startedAt: 100,
|
|
222
|
+
lastUpdate: 200,
|
|
223
|
+
currentStep: 0,
|
|
224
|
+
steps: [{ agent: "delegate", status: "running", startedAt: 100, recentOutput: ["CHILD_RECENT"] }],
|
|
225
|
+
}, null, 2), "utf-8");
|
|
226
|
+
|
|
227
|
+
const result = inspectSubagentStatus({ id: "run-phantom-output", view: "transcript" }, {
|
|
228
|
+
asyncDirRoot: asyncRoot,
|
|
229
|
+
resultsDir: path.join(root, "results"),
|
|
230
|
+
kill: () => true,
|
|
231
|
+
now: () => 250,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const text = textContent(result);
|
|
235
|
+
assert.equal(result.isError, undefined);
|
|
236
|
+
assert.doesNotMatch(text, /Output: .*output-0\.log/);
|
|
237
|
+
// The status.json fallback still supplies the transcript body.
|
|
238
|
+
assert.match(text, /Recent output from status\.json:/);
|
|
239
|
+
assert.match(text, /CHILD_RECENT/);
|
|
240
|
+
} finally {
|
|
241
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
209
245
|
it("refuses to tail status outputFile paths outside the async directory", () => {
|
|
210
246
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-escape-"));
|
|
211
247
|
try {
|
|
@@ -238,6 +274,69 @@ describe("async run status inspection", () => {
|
|
|
238
274
|
}
|
|
239
275
|
});
|
|
240
276
|
|
|
277
|
+
it("escapes terminal control sequences in async output transcripts", () => {
|
|
278
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-unsafe-"));
|
|
279
|
+
try {
|
|
280
|
+
const asyncRoot = path.join(root, "runs");
|
|
281
|
+
const asyncDir = path.join(asyncRoot, "run-unsafe-output");
|
|
282
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
283
|
+
fs.writeFileSync(path.join(asyncDir, "output-0.log"), "safe \u001b]8;;https://attacker.invalid\u0007link\u001b]8;;\u0007 bidi \u202e", "utf-8");
|
|
284
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
285
|
+
runId: "run-unsafe-output",
|
|
286
|
+
mode: "single",
|
|
287
|
+
state: "complete",
|
|
288
|
+
startedAt: 100,
|
|
289
|
+
lastUpdate: 200,
|
|
290
|
+
steps: [{ agent: "worker", status: "complete" }],
|
|
291
|
+
}, null, 2), "utf-8");
|
|
292
|
+
|
|
293
|
+
const result = inspectSubagentStatus({ id: "run-unsafe-output", view: "transcript" }, {
|
|
294
|
+
asyncDirRoot: asyncRoot,
|
|
295
|
+
resultsDir: path.join(root, "results"),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const text = textContent(result);
|
|
299
|
+
assert.doesNotMatch(text, /[\u001b\u0007\u202e]/u);
|
|
300
|
+
assert.match(text, /U\+001B/);
|
|
301
|
+
assert.match(text, /U\+202E/);
|
|
302
|
+
} finally {
|
|
303
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("keeps run metadata when child output contains binary content", () => {
|
|
308
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-binary-"));
|
|
309
|
+
try {
|
|
310
|
+
const asyncRoot = path.join(root, "runs");
|
|
311
|
+
const asyncDir = path.join(asyncRoot, "run-binary-output");
|
|
312
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
313
|
+
fs.writeFileSync(path.join(asyncDir, "output-0.log"), "useful line A\nuseful line B\n\u0000\n", "utf-8");
|
|
314
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
315
|
+
runId: "run-binary-output",
|
|
316
|
+
mode: "single",
|
|
317
|
+
state: "complete",
|
|
318
|
+
startedAt: 100,
|
|
319
|
+
lastUpdate: 200,
|
|
320
|
+
steps: [{ agent: "worker", status: "complete" }],
|
|
321
|
+
}, null, 2), "utf-8");
|
|
322
|
+
|
|
323
|
+
const result = inspectSubagentStatus({ id: "run-binary-output", view: "transcript" }, {
|
|
324
|
+
asyncDirRoot: asyncRoot,
|
|
325
|
+
resultsDir: path.join(root, "results"),
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// One malformed line must not erase run state, artifacts, or the safe lines.
|
|
329
|
+
const text = textContent(result);
|
|
330
|
+
assert.match(text, /Run: run-binary-output/);
|
|
331
|
+
assert.match(text, /State: complete/);
|
|
332
|
+
assert.match(text, /useful line A/);
|
|
333
|
+
assert.match(text, /useful line B/);
|
|
334
|
+
assert.match(text, /\[binary content omitted for safe display\]/);
|
|
335
|
+
} finally {
|
|
336
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
241
340
|
it("refuses symlink session transcript paths even under trusted roots", () => {
|
|
242
341
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-session-"));
|
|
243
342
|
try {
|
|
@@ -382,6 +481,40 @@ describe("async run status inspection", () => {
|
|
|
382
481
|
}
|
|
383
482
|
});
|
|
384
483
|
|
|
484
|
+
it("escapes terminal control sequences in remembered foreground transcripts", () => {
|
|
485
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-foreground-transcript-unsafe-"));
|
|
486
|
+
try {
|
|
487
|
+
const state = {
|
|
488
|
+
currentSessionId: "session-current",
|
|
489
|
+
asyncJobs: new Map(),
|
|
490
|
+
foregroundControls: new Map(),
|
|
491
|
+
foregroundRuns: new Map([["foreground-unsafe", {
|
|
492
|
+
runId: "foreground-unsafe",
|
|
493
|
+
mode: "single",
|
|
494
|
+
cwd: root,
|
|
495
|
+
sessionId: "session-current",
|
|
496
|
+
updatedAt: 100,
|
|
497
|
+
children: [{ agent: "worker", index: 0, status: "completed", finalOutput: "safe \u001b]8;;https://attacker.invalid\u0007link\u001b]8;;\u0007 bidi \u202e" }],
|
|
498
|
+
}]]),
|
|
499
|
+
} as unknown as SubagentState;
|
|
500
|
+
|
|
501
|
+
const result = inspectSubagentStatus({ id: "foreground-unsafe", view: "transcript" }, {
|
|
502
|
+
asyncDirRoot: path.join(root, "runs"),
|
|
503
|
+
resultsDir: path.join(root, "results"),
|
|
504
|
+
state,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const text = textContent(result);
|
|
508
|
+
assert.equal(result.isError, undefined);
|
|
509
|
+
assert.doesNotMatch(text, /[\u001b\u0007\u202e]/u);
|
|
510
|
+
assert.match(text, /U\+001B/);
|
|
511
|
+
assert.match(text, /U\+0007/);
|
|
512
|
+
assert.match(text, /U\+202E/);
|
|
513
|
+
} finally {
|
|
514
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
|
|
385
518
|
it("refuses transcript reads for async runs owned by another session", () => {
|
|
386
519
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-transcript-session-scope-"));
|
|
387
520
|
try {
|
|
@@ -571,6 +704,7 @@ describe("async run status inspection", () => {
|
|
|
571
704
|
}, null, 2), "utf-8");
|
|
572
705
|
fs.writeFileSync(path.join(nestedAsyncDir, "status.json"), JSON.stringify({
|
|
573
706
|
runId: "nested-stale",
|
|
707
|
+
sessionId: "session-nested",
|
|
574
708
|
mode: "single",
|
|
575
709
|
state: "running",
|
|
576
710
|
pid: 54321,
|
|
@@ -675,6 +809,48 @@ describe("async run status inspection", () => {
|
|
|
675
809
|
}
|
|
676
810
|
});
|
|
677
811
|
|
|
812
|
+
it("keeps safe nested session content parts around binary content", () => {
|
|
813
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-nested-session-binary-"));
|
|
814
|
+
const route = createNestedRoute("run-nested-session-binary-root");
|
|
815
|
+
try {
|
|
816
|
+
const sessionFile = path.join(root, "session.jsonl");
|
|
817
|
+
fs.writeFileSync(sessionFile, `${JSON.stringify({ message: { role: "assistant", content: [{ type: "text", text: "safe before" }, { type: "text", text: "\0" }, { type: "text", text: "safe after" }] } })}\n`, "utf-8");
|
|
818
|
+
writeNestedEvent(route, {
|
|
819
|
+
type: "subagent.nested.updated",
|
|
820
|
+
ts: 150,
|
|
821
|
+
parentRunId: "run-nested-session-binary-root",
|
|
822
|
+
parentStepIndex: 0,
|
|
823
|
+
child: {
|
|
824
|
+
id: "nested-session-binary-child",
|
|
825
|
+
parentRunId: "run-nested-session-binary-root",
|
|
826
|
+
parentStepIndex: 0,
|
|
827
|
+
depth: 1,
|
|
828
|
+
path: [{ runId: "run-nested-session-binary-root", stepIndex: 0, agent: "orchestrator" }],
|
|
829
|
+
state: "complete",
|
|
830
|
+
mode: "single",
|
|
831
|
+
agent: "worker",
|
|
832
|
+
sessionFile,
|
|
833
|
+
lastUpdate: 150,
|
|
834
|
+
},
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
const result = inspectSubagentStatus({ id: "nested-session-binary-child", view: "transcript" }, {
|
|
838
|
+
asyncDirRoot: path.join(root, "runs"),
|
|
839
|
+
resultsDir: path.join(root, "results"),
|
|
840
|
+
sessionRoots: [root],
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
const text = textContent(result);
|
|
844
|
+
assert.equal(result.isError, undefined);
|
|
845
|
+
assert.match(text, /safe before/);
|
|
846
|
+
assert.match(text, /\[binary content omitted for safe display\]/);
|
|
847
|
+
assert.match(text, /safe after/);
|
|
848
|
+
} finally {
|
|
849
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
850
|
+
fs.rmSync(path.dirname(route.eventSink), { recursive: true, force: true });
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
|
|
678
854
|
it("resolves exact nested run ids from the nested registry", () => {
|
|
679
855
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-nested-exact-"));
|
|
680
856
|
const route = createNestedRoute("run-nested-exact-root");
|
|
@@ -753,6 +929,77 @@ describe("async run status inspection", () => {
|
|
|
753
929
|
}
|
|
754
930
|
});
|
|
755
931
|
|
|
932
|
+
it("shows direct run-id recovery for workflow children", () => {
|
|
933
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-workflow-resume-"));
|
|
934
|
+
try {
|
|
935
|
+
const asyncRoot = path.join(root, "runs");
|
|
936
|
+
const asyncDir = path.join(asyncRoot, "workflow-parent");
|
|
937
|
+
const firstSession = path.join(root, "review.jsonl");
|
|
938
|
+
const secondSession = path.join(root, "writer.jsonl");
|
|
939
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
940
|
+
fs.writeFileSync(firstSession, "", "utf-8");
|
|
941
|
+
fs.writeFileSync(secondSession, "", "utf-8");
|
|
942
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
943
|
+
runId: "workflow-parent",
|
|
944
|
+
mode: "workflow",
|
|
945
|
+
state: "failed",
|
|
946
|
+
startedAt: 100,
|
|
947
|
+
lastUpdate: 200,
|
|
948
|
+
steps: [
|
|
949
|
+
{ agent: "reviewer", workflowKey: "review", runId: "child-review", status: "failed", sessionFile: firstSession },
|
|
950
|
+
{ agent: "worker", workflowKey: "write", runId: "child-write", status: "paused", sessionFile: secondSession },
|
|
951
|
+
],
|
|
952
|
+
}, null, 2), "utf-8");
|
|
953
|
+
|
|
954
|
+
const result = inspectSubagentStatus({ id: "workflow-parent" }, { asyncDirRoot: asyncRoot, resultsDir: path.join(root, "results") });
|
|
955
|
+
const text = textContent(result);
|
|
956
|
+
assert.match(text, /Revive workflow child 'review': subagent\(\{ action: "resume", id: "child-review", message: "\.\.\." \}\)/);
|
|
957
|
+
assert.match(text, /Revive workflow child 'write': subagent\(\{ action: "resume", id: "child-write", message: "\.\.\." \}\)/);
|
|
958
|
+
assert.doesNotMatch(text, /id: "workflow-parent", index:/);
|
|
959
|
+
} finally {
|
|
960
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
it("keeps supervisor-detached workflow children out of generic revive guidance", () => {
|
|
965
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-workflow-detached-"));
|
|
966
|
+
try {
|
|
967
|
+
const asyncRoot = path.join(root, "runs");
|
|
968
|
+
const asyncDir = path.join(asyncRoot, "workflow-detached");
|
|
969
|
+
const sessionFile = path.join(root, "detached.jsonl");
|
|
970
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
971
|
+
fs.writeFileSync(sessionFile, "", "utf-8");
|
|
972
|
+
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
973
|
+
runId: "workflow-detached",
|
|
974
|
+
mode: "workflow",
|
|
975
|
+
state: "paused",
|
|
976
|
+
activityState: "needs_attention",
|
|
977
|
+
error: "Run 'detaches' detached for intercom coordination. Reply to the supervisor request first, then wait with subagent_wait({ id: \"child-detached\" }). Use subagent({ action: \"status\", id: \"child-detached\" }) to recover the result; do not resume or launch a replacement while it remains detached.",
|
|
978
|
+
startedAt: 100,
|
|
979
|
+
lastUpdate: 200,
|
|
980
|
+
steps: [{
|
|
981
|
+
agent: "worker",
|
|
982
|
+
workflowKey: "detaches",
|
|
983
|
+
runId: "child-detached",
|
|
984
|
+
status: "paused",
|
|
985
|
+
activityState: "needs_attention",
|
|
986
|
+
sessionFile,
|
|
987
|
+
}],
|
|
988
|
+
}, null, 2), "utf-8");
|
|
989
|
+
|
|
990
|
+
const result = inspectSubagentStatus({ id: "workflow-detached" }, { asyncDirRoot: asyncRoot, resultsDir: path.join(root, "results") });
|
|
991
|
+
const text = textContent(result);
|
|
992
|
+
assert.match(text, /Reply to the supervisor request first/);
|
|
993
|
+
assert.match(text, /wait with subagent_wait\(\{ id: "child-detached" \}\)/);
|
|
994
|
+
assert.match(text, /do not resume or launch a replacement while it remains detached/);
|
|
995
|
+
assert.match(text, /Recovery workflow child 'detaches'/);
|
|
996
|
+
assert.doesNotMatch(text, /Revive workflow child 'detaches'/);
|
|
997
|
+
assert.doesNotMatch(text, /action: "resume", id: "child-detached"/);
|
|
998
|
+
} finally {
|
|
999
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
756
1003
|
it("uses original child indexes when result metadata contains invalid children", () => {
|
|
757
1004
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-original-index-"));
|
|
758
1005
|
try {
|
|
@@ -66,7 +66,7 @@ function context(cwd: string, sessionId = "session-a"): ExtensionContext {
|
|
|
66
66
|
} as unknown as ExtensionContext;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function harness(options: { cwd?: string; sessionId?: string; now?: number; config?: ExtensionConfig } = {}): Harness {
|
|
69
|
+
function harness(options: { cwd?: string; sessionId?: string; now?: number; config?: ExtensionConfig; randomId?: () => string } = {}): Harness {
|
|
70
70
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-schedule-test-"));
|
|
71
71
|
roots.push(root);
|
|
72
72
|
const project = options.cwd ?? path.join(root, "project");
|
|
@@ -80,7 +80,7 @@ function harness(options: { cwd?: string; sessionId?: string; now?: number; conf
|
|
|
80
80
|
config: options.config ?? { scheduledRuns: { enabled: true } },
|
|
81
81
|
storeRoot: path.join(root, "stores"),
|
|
82
82
|
now: () => clock.now,
|
|
83
|
-
randomId: () => `id-${++id}
|
|
83
|
+
randomId: options.randomId ?? (() => `id-${++id}`),
|
|
84
84
|
timers,
|
|
85
85
|
launch: (params, launchCtx) => new Promise((resolve) => launches.push({ params: params as Record<string, unknown>, ctx: launchCtx, resolve: resolve as Launch["resolve"] })) as never,
|
|
86
86
|
});
|
|
@@ -237,6 +237,41 @@ describe("project schedule management", () => {
|
|
|
237
237
|
assert.match(text(await h.manager.handleToolCall({ action: "schedule.list" }, h.ctx)), /No project schedules/);
|
|
238
238
|
});
|
|
239
239
|
|
|
240
|
+
it("ignores schedules deleted by another session while a timer is still armed", async () => {
|
|
241
|
+
const first = harness();
|
|
242
|
+
await first.manager.handleToolCall({ action: "schedule.create", id: "stale", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, first.ctx);
|
|
243
|
+
const secondTimers = new FakeTimers();
|
|
244
|
+
const second = createScheduledRunManager({
|
|
245
|
+
config: { scheduledRuns: { enabled: true } },
|
|
246
|
+
storeRoot: path.join(first.root, "stores"),
|
|
247
|
+
now: () => first.clock.now,
|
|
248
|
+
timers: secondTimers,
|
|
249
|
+
launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
|
|
250
|
+
});
|
|
251
|
+
const secondCtx = context(first.ctx.cwd, "session-b");
|
|
252
|
+
second.bindSession(secondCtx);
|
|
253
|
+
assert.equal(first.timers.values.size, 1);
|
|
254
|
+
assert.equal(secondTimers.values.size, 1);
|
|
255
|
+
|
|
256
|
+
assert.match(text(await second.handleToolCall({ action: "schedule.delete", id: "stale" }, secondCtx)), /Deleted/);
|
|
257
|
+
assert.equal(secondTimers.values.size, 0);
|
|
258
|
+
assert.equal(first.timers.values.size, 1);
|
|
259
|
+
first.clock.now += 3_600_000;
|
|
260
|
+
const warnings: string[] = [];
|
|
261
|
+
const originalWarn = console.warn;
|
|
262
|
+
console.warn = (message?: unknown) => void warnings.push(String(message));
|
|
263
|
+
try {
|
|
264
|
+
first.timers.fireAll();
|
|
265
|
+
await flush();
|
|
266
|
+
} finally {
|
|
267
|
+
console.warn = originalWarn;
|
|
268
|
+
}
|
|
269
|
+
assert.deepEqual(warnings, []);
|
|
270
|
+
assert.equal(first.launches.length, 0);
|
|
271
|
+
assert.equal(first.timers.values.size, 0);
|
|
272
|
+
assert.match(text(await first.manager.handleToolCall({ action: "schedule.list" }, first.ctx)), /No project schedules/);
|
|
273
|
+
});
|
|
274
|
+
|
|
240
275
|
it("reports corrupt project schedule records instead of dropping them", async () => {
|
|
241
276
|
const h = harness();
|
|
242
277
|
const root = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
|
|
@@ -294,6 +329,7 @@ describe("recurring schedule execution", () => {
|
|
|
294
329
|
assert.deepEqual(h.launches[0]?.params, { workflowScript: "return runs.run('main', { agent: 'worker', task: 'Maintain backlog' })", async: true, context: "fresh", cwd: h.ctx.cwd, mission: false });
|
|
295
330
|
h.launches[0]!.resolve({ content: [{ type: "text", text: "Async worker" }], details: { mode: "single", results: [], asyncId: "async-1", asyncDir: "/tmp/async-1" } });
|
|
296
331
|
await flush();
|
|
332
|
+
assert.deepEqual([...h.manager.observedCompletionRunIds()], ["async-1"]);
|
|
297
333
|
|
|
298
334
|
let history = await h.manager.handleToolCall({ action: "schedule.history", id: "hourly" }, h.ctx);
|
|
299
335
|
assert.match(text(history), /running.*async async-1/);
|
|
@@ -304,6 +340,7 @@ describe("recurring schedule execution", () => {
|
|
|
304
340
|
assert.equal(fs.readdirSync(path.join(dir, "runs")).length, 1);
|
|
305
341
|
|
|
306
342
|
h.manager.handleAsyncCompletion({ runId: "async-1", success: true, summary: "Done" });
|
|
343
|
+
assert.deepEqual([...h.manager.observedCompletionRunIds()], []);
|
|
307
344
|
history = await h.manager.handleToolCall({ action: "schedule.history", id: "hourly" }, h.ctx);
|
|
308
345
|
assert.match(text(history), /completed.*async async-1/);
|
|
309
346
|
assert.equal(fs.existsSync(path.join(dir, "active.lock")), false);
|
|
@@ -311,6 +348,74 @@ describe("recurring schedule execution", () => {
|
|
|
311
348
|
assert.match(text(shown), /2030-01-01T02:00:00.000Z/, "next occurrence advances from the planned time without completion drift");
|
|
312
349
|
});
|
|
313
350
|
|
|
351
|
+
it("delays retrying an overdue recurring schedule after an unexpected timer failure", async () => {
|
|
352
|
+
const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
|
|
353
|
+
await h.manager.handleToolCall({ action: "schedule.create", id: "hourly", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
|
|
354
|
+
h.clock.now += 3_600_000;
|
|
355
|
+
const warnings: string[] = [];
|
|
356
|
+
const originalWarn = console.warn;
|
|
357
|
+
console.warn = (message?: unknown) => void warnings.push(String(message));
|
|
358
|
+
try {
|
|
359
|
+
h.timers.fireAll();
|
|
360
|
+
await flush();
|
|
361
|
+
} finally {
|
|
362
|
+
console.warn = originalWarn;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
assert.equal(h.launches.length, 0);
|
|
366
|
+
assert.equal(h.timers.values.size, 1);
|
|
367
|
+
assert.equal([...h.timers.values.values()][0]?.delay, 3_600_000);
|
|
368
|
+
assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
|
|
369
|
+
const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "hourly" }, h.ctx);
|
|
370
|
+
assert.match(text(shown), /State: scheduled/);
|
|
371
|
+
assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("re-arms overdue catch-up-none schedules when missed-run recovery fails", async () => {
|
|
375
|
+
const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
|
|
376
|
+
await h.manager.handleToolCall({ action: "schedule.create", id: "none", every: "1h", catchUp: "none", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
|
|
377
|
+
h.clock.now += 2 * 3_600_000;
|
|
378
|
+
const warnings: string[] = [];
|
|
379
|
+
const originalWarn = console.warn;
|
|
380
|
+
console.warn = (message?: unknown) => void warnings.push(String(message));
|
|
381
|
+
try {
|
|
382
|
+
h.timers.fireAll();
|
|
383
|
+
await flush();
|
|
384
|
+
} finally {
|
|
385
|
+
console.warn = originalWarn;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
assert.equal(h.launches.length, 0);
|
|
389
|
+
assert.equal(h.timers.values.size, 1);
|
|
390
|
+
assert.equal([...h.timers.values.values()][0]?.delay, 3_600_000);
|
|
391
|
+
assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
|
|
392
|
+
assert.match(warnings[1] ?? "", /could not be restored after fire failure: persistent id failure/);
|
|
393
|
+
const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "none" }, h.ctx);
|
|
394
|
+
assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("does not immediately retry an overdue one-shot after an unexpected timer failure", async () => {
|
|
398
|
+
const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
|
|
399
|
+
await h.manager.handleToolCall({ action: "schedule.create", id: "once", at: "+1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
|
|
400
|
+
h.clock.now += 3_600_000;
|
|
401
|
+
const warnings: string[] = [];
|
|
402
|
+
const originalWarn = console.warn;
|
|
403
|
+
console.warn = (message?: unknown) => void warnings.push(String(message));
|
|
404
|
+
try {
|
|
405
|
+
h.timers.fireAll();
|
|
406
|
+
await flush();
|
|
407
|
+
} finally {
|
|
408
|
+
console.warn = originalWarn;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
assert.equal(h.launches.length, 0);
|
|
412
|
+
assert.equal(h.timers.values.size, 0);
|
|
413
|
+
assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
|
|
414
|
+
const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "once" }, h.ctx);
|
|
415
|
+
assert.match(text(shown), /State: scheduled/);
|
|
416
|
+
assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
|
|
417
|
+
});
|
|
418
|
+
|
|
314
419
|
it("run-due launches the latest missed occurrence while catchUp none records a miss", async () => {
|
|
315
420
|
const latest = harness();
|
|
316
421
|
await latest.manager.handleToolCall({ action: "schedule.create", id: "latest", every: "1h", catchUp: "latest", workflowScript: "return runs.run('main', { agent: 'worker' })" }, latest.ctx);
|
|
@@ -221,7 +221,7 @@ describe("SubagentParams schema", { skip: !schemasAvailable ? "typebox not avail
|
|
|
221
221
|
assert.equal(actionSchema.enum, undefined);
|
|
222
222
|
const description = String(actionSchema.description ?? "");
|
|
223
223
|
assert.match(description, /Optional management\/control action/);
|
|
224
|
-
assert.match(description, /Omit this field for workflowScript execution/);
|
|
224
|
+
assert.match(description, /Omit this field for structured single-child or workflowScript execution/);
|
|
225
225
|
assert.doesNotMatch(description, /\{agent, task\}/);
|
|
226
226
|
assert.match(description, /use it only for management\/control actions/);
|
|
227
227
|
assert.doesNotMatch(description, /orchestration\./);
|
|
@@ -236,14 +236,11 @@ describe("SubagentParams schema", { skip: !schemasAvailable ? "typebox not avail
|
|
|
236
236
|
assert.ok(maxRuntimeSchema, "maxRuntimeMs schema should exist");
|
|
237
237
|
assert.equal(timeoutSchema.minimum, 1);
|
|
238
238
|
assert.equal(maxRuntimeSchema.minimum, 1);
|
|
239
|
-
assert.match(String(timeoutSchema.description ?? ""), /foreground
|
|
240
|
-
assert.match(String(timeoutSchema.description ?? ""), /
|
|
241
|
-
assert.match(String(timeoutSchema.description ?? ""), /async composites have no default parent deadline/i);
|
|
239
|
+
assert.match(String(timeoutSchema.description ?? ""), /foreground\/single async runs/i);
|
|
240
|
+
assert.match(String(timeoutSchema.description ?? ""), /config timeoutMs or 30m/i);
|
|
242
241
|
assert.doesNotMatch(String(timeoutSchema.description ?? ""), /foreground-only/i);
|
|
243
242
|
assert.match(String(maxRuntimeSchema.description ?? ""), /timeoutMs/i);
|
|
244
|
-
assert.match(String(maxRuntimeSchema.description ?? ""), /
|
|
245
|
-
assert.match(String(maxRuntimeSchema.description ?? ""), /use config timeoutMs, else 30m/i);
|
|
246
|
-
assert.match(String(maxRuntimeSchema.description ?? ""), /async composites have no default parent deadline/i);
|
|
243
|
+
assert.match(String(maxRuntimeSchema.description ?? ""), /alias of timeoutMs/i);
|
|
247
244
|
assert.equal(turnBudgetSchema?.properties?.maxTurns?.minimum, 1);
|
|
248
245
|
assert.equal(turnBudgetSchema?.properties?.graceTurns?.minimum, 0);
|
|
249
246
|
assert.equal(toolBudgetSchema?.properties?.soft?.minimum, 1);
|
|
@@ -494,7 +491,7 @@ describe("SubagentParams schema", { skip: !schemasAvailable ? "typebox not avail
|
|
|
494
491
|
const reviewedRecoveryBranch = acceptanceStringBranches.find((branch) => Array.isArray(branch.enum) && branch.enum.includes("reviewed"));
|
|
495
492
|
assert.deepEqual(reviewedRecoveryBranch?.enum, ["reviewed"]);
|
|
496
493
|
assert.equal(reviewedRecoveryBranch?.deprecated, true);
|
|
497
|
-
assert.match(String(acceptanceSchema.description ?? ""), /
|
|
494
|
+
assert.match(String(acceptanceSchema.description ?? ""), /omit for read-only calls/i);
|
|
498
495
|
assert.match(String(acceptanceSchema.description ?? ""), /acceptance\.review\.required/);
|
|
499
496
|
const acceptanceObjectBranch = anyOfBranches(acceptanceSchema).find((branch) => branch.type === "object");
|
|
500
497
|
assert.ok(acceptanceObjectBranch, "acceptance should support object config");
|