@selesai/code 0.5.20 → 0.5.21
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/dist/extensions/handoff-new.test.ts +8 -9
- package/dist/extensions/handoff-new.ts +6 -8
- package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
- package/dist/extensions/pi-subagents/README.md +602 -220
- package/dist/extensions/pi-subagents/banner.png +0 -0
- package/dist/extensions/pi-subagents/index.ts +1 -0
- package/dist/extensions/pi-subagents/install.mjs +3 -3
- package/dist/extensions/pi-subagents/package-lock.json +3405 -0
- package/dist/extensions/pi-subagents/package.json +26 -14
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
- package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
- package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
- package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
- package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
- package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
- package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
- package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
- package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
- package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
- package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
- package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
- package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
- package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
- package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
- package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
- package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
- package/dist/extensions/question/batch.ts +39 -0
- package/dist/extensions/question/dialog-adapter.ts +23 -1
- package/dist/extensions/question/index.ts +237 -7
- package/dist/extensions/question/schemas.ts +28 -15
- package/dist/extensions/question/tests/batch.test.ts +26 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
- package/dist/extensions/question/tui-adapter.ts +2 -3
- package/dist/extensions/question/types.ts +37 -0
- package/dist/extensions/question/ui-protocol.ts +3 -3
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { describe, it, before, after, beforeEach, afterEach } from "node:test";
|
|
12
12
|
import assert from "node:assert/strict";
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
13
14
|
import * as fs from "node:fs";
|
|
14
15
|
import * as path from "node:path";
|
|
15
16
|
import type { MockPi } from "../support/helpers.ts";
|
|
@@ -40,6 +41,8 @@ interface TestSequentialStep {
|
|
|
40
41
|
progress?: boolean;
|
|
41
42
|
cwd?: string;
|
|
42
43
|
acceptance?: unknown;
|
|
44
|
+
agentContract?: { version: 1 };
|
|
45
|
+
gateOn?: "execution" | "acceptance";
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
interface TestParallelTask {
|
|
@@ -57,6 +60,8 @@ interface TestParallelTask {
|
|
|
57
60
|
progress?: boolean;
|
|
58
61
|
cwd?: string;
|
|
59
62
|
acceptance?: unknown;
|
|
63
|
+
agentContract?: { version: 1 };
|
|
64
|
+
gateOn?: "execution" | "acceptance";
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
type TestChainStep = TestSequentialStep | {
|
|
@@ -65,6 +70,8 @@ type TestChainStep = TestSequentialStep | {
|
|
|
65
70
|
failFast?: boolean;
|
|
66
71
|
worktree?: boolean;
|
|
67
72
|
cwd?: string;
|
|
73
|
+
agentContract?: { version: 1 };
|
|
74
|
+
gateOn?: "execution" | "acceptance";
|
|
68
75
|
} | {
|
|
69
76
|
expand: {
|
|
70
77
|
from: { output: string; path: string };
|
|
@@ -79,6 +86,8 @@ type TestChainStep = TestSequentialStep | {
|
|
|
79
86
|
failFast?: boolean;
|
|
80
87
|
label?: string;
|
|
81
88
|
acceptance?: unknown;
|
|
89
|
+
agentContract?: { version: 1 };
|
|
90
|
+
gateOn?: "execution" | "acceptance";
|
|
82
91
|
};
|
|
83
92
|
|
|
84
93
|
interface ChainResultItem {
|
|
@@ -108,6 +117,7 @@ interface ChainExecutionResult {
|
|
|
108
117
|
};
|
|
109
118
|
currentStepIndex?: number;
|
|
110
119
|
outputs?: Record<string, { text: string; structured?: unknown }>;
|
|
120
|
+
parallelHandoff?: { version: number; path: string; groupCount: number; childCount: number; cleanupState: string };
|
|
111
121
|
};
|
|
112
122
|
}
|
|
113
123
|
|
|
@@ -313,7 +323,7 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
313
323
|
|
|
314
324
|
it("passes file-only saved-output references through {previous}", async () => {
|
|
315
325
|
mockPi.onCall({ output: "full chain output\nwith details" });
|
|
316
|
-
const agents = [makeAgent("analyst"), makeAgent("reporter")];
|
|
326
|
+
const agents = [makeAgent("analyst", { tools: ["read", "grep", "find", "ls"] }), makeAgent("reporter")];
|
|
317
327
|
|
|
318
328
|
const result = await executeChain(
|
|
319
329
|
makeChainParams(
|
|
@@ -327,6 +337,13 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
327
337
|
);
|
|
328
338
|
|
|
329
339
|
assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
|
|
340
|
+
const firstTaskArg = readCallArgs(0).at(-1) ?? "";
|
|
341
|
+
assert.match(firstTaskArg, /Return the complete artifact in your final response\./);
|
|
342
|
+
assert.match(firstTaskArg, /runtime will persist it to exactly this path: .*analysis\.md/);
|
|
343
|
+
assert.match(firstTaskArg, /Do not call contact_supervisor merely because no write-capable tool is available\./);
|
|
344
|
+
assert.doesNotMatch(firstTaskArg, /\[Write to:|Write your findings to exactly this path/);
|
|
345
|
+
assert.ok(result.details.results[0]?.savedOutputPath);
|
|
346
|
+
assert.equal(fs.readFileSync(result.details.results[0].savedOutputPath, "utf-8"), "full chain output\nwith details");
|
|
330
347
|
assert.match(result.details.results[0]?.finalOutput ?? "", /Output saved to:/);
|
|
331
348
|
assert.doesNotMatch(result.details.results[0]?.finalOutput ?? "", /full chain output/);
|
|
332
349
|
const secondTaskArg = readCallArgs(1).at(-1) ?? "";
|
|
@@ -375,7 +392,6 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
375
392
|
JSON.stringify({
|
|
376
393
|
criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "patched" }],
|
|
377
394
|
changedFiles: ["src/file.ts"],
|
|
378
|
-
testsAddedOrUpdated: [],
|
|
379
395
|
commandsRun: [{ command: "npm test", result: "passed", summary: "passed" }],
|
|
380
396
|
residualRisks: [],
|
|
381
397
|
noStagedFiles: true,
|
|
@@ -661,7 +677,7 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
661
677
|
assert.deepEqual(dynamicNode?.children?.map((child) => child.itemKey), ["src/a.ts", "src/b.ts"]);
|
|
662
678
|
});
|
|
663
679
|
|
|
664
|
-
it("persists checked
|
|
680
|
+
it("persists checked child evidence and pending aggregate review for dynamic fanout", async () => {
|
|
665
681
|
mockPi.onCall({
|
|
666
682
|
output: "targets",
|
|
667
683
|
structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
|
|
@@ -688,8 +704,80 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
688
704
|
|
|
689
705
|
assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
|
|
690
706
|
const dynamicNode = result.details.workflowGraph?.nodes[1];
|
|
691
|
-
assert.equal(dynamicNode?.acceptanceStatus, "
|
|
707
|
+
assert.equal(dynamicNode?.acceptanceStatus, "review-required");
|
|
692
708
|
assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["checked", "checked"]);
|
|
709
|
+
assert.deepEqual(result.details.results.filter((child) => child.agent === "reviewer").map((child) => child.acceptance?.evidenceStatus), ["checked", "checked"]);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it("applies read-only acceptance roles to dynamic children and their aggregate group", async () => {
|
|
713
|
+
mockPi.onCall({
|
|
714
|
+
output: "targets",
|
|
715
|
+
structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
|
|
716
|
+
});
|
|
717
|
+
const readOnlyReport = acceptanceReport({
|
|
718
|
+
changedFiles: [],
|
|
719
|
+
testsAddedOrUpdated: [],
|
|
720
|
+
commandsRun: [],
|
|
721
|
+
validationOutput: [],
|
|
722
|
+
reviewFindings: ["No blocking findings"],
|
|
723
|
+
});
|
|
724
|
+
mockPi.onCall({ output: readOnlyReport, structuredOutput: { ok: "a" } });
|
|
725
|
+
mockPi.onCall({ output: readOnlyReport, structuredOutput: { ok: "b" } });
|
|
726
|
+
const agents = [makeAgent("scout"), makeAgent("explorer", { acceptanceRole: "read-only" })];
|
|
727
|
+
|
|
728
|
+
const result = await executeChain(
|
|
729
|
+
makeChainParams(
|
|
730
|
+
[
|
|
731
|
+
{ agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
|
|
732
|
+
{
|
|
733
|
+
expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
|
|
734
|
+
parallel: { agent: "explorer", task: "Explore {item.path}", outputSchema: { type: "object" } },
|
|
735
|
+
collect: { as: "reviews" },
|
|
736
|
+
concurrency: 1,
|
|
737
|
+
},
|
|
738
|
+
],
|
|
739
|
+
agents,
|
|
740
|
+
),
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
|
|
744
|
+
const explorerResults = result.details.results.filter((child) => child.agent === "explorer");
|
|
745
|
+
assert.deepEqual(explorerResults.map((child) => child.acceptance?.effectiveAcceptance.level), ["attested", "attested"]);
|
|
746
|
+
const dynamicNode = result.details.workflowGraph?.nodes[1];
|
|
747
|
+
assert.equal(dynamicNode?.acceptanceStatus, "attested");
|
|
748
|
+
assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["attested", "attested"]);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it("infers foreground dynamic acceptance after materializing item templates", async () => {
|
|
752
|
+
mockPi.onCall({
|
|
753
|
+
output: "targets",
|
|
754
|
+
structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
|
|
755
|
+
});
|
|
756
|
+
mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/a.ts"] }), structuredOutput: { ok: "a" } });
|
|
757
|
+
mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/b.ts"] }), structuredOutput: { ok: "b" } });
|
|
758
|
+
const agents = [makeAgent("scout"), makeAgent("explorer", { acceptanceRole: "read-only" })];
|
|
759
|
+
|
|
760
|
+
const result = await executeChain(
|
|
761
|
+
makeChainParams(
|
|
762
|
+
[
|
|
763
|
+
{ agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
|
|
764
|
+
{
|
|
765
|
+
expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
|
|
766
|
+
parallel: { agent: "explorer", task: "Patch {item.path}", outputSchema: { type: "object" } },
|
|
767
|
+
collect: { as: "reviews" },
|
|
768
|
+
concurrency: 1,
|
|
769
|
+
},
|
|
770
|
+
],
|
|
771
|
+
agents,
|
|
772
|
+
),
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
|
|
776
|
+
const explorerResults = result.details.results.filter((child) => child.agent === "explorer");
|
|
777
|
+
assert.deepEqual(explorerResults.map((child) => child.acceptance?.effectiveAcceptance.level), ["checked", "checked"]);
|
|
778
|
+
const dynamicNode = result.details.workflowGraph?.nodes[1];
|
|
779
|
+
assert.equal(dynamicNode?.acceptanceStatus, "rejected");
|
|
780
|
+
assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["rejected", "rejected"]);
|
|
693
781
|
});
|
|
694
782
|
|
|
695
783
|
it("does not expose collected dynamic output when a child fails", async () => {
|
|
@@ -992,6 +1080,50 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
|
|
|
992
1080
|
assert.equal(result.details.results[0].exitCode, 1);
|
|
993
1081
|
});
|
|
994
1082
|
|
|
1083
|
+
it("agent contract v1 chain defaults to execution gating after acceptance rejection", async () => {
|
|
1084
|
+
mockPi.onCall({ output: "Step 1 done\n```acceptance-report\n{\"criteriaSatisfied\":[{\"id\":\"criterion-1\",\"status\":\"not-satisfied\",\"evidence\":\"no proof\"}]}\n```" });
|
|
1085
|
+
mockPi.onCall({ output: "Step 2 ran" });
|
|
1086
|
+
const agents = [makeAgent("step1", { completionGuard: false }), makeAgent("step2", { completionGuard: false })];
|
|
1087
|
+
|
|
1088
|
+
const result = await executeChain(
|
|
1089
|
+
makeChainParams(
|
|
1090
|
+
[
|
|
1091
|
+
{ agent: "step1", task: "First", acceptance: { level: "checked", criteria: ["Return required proof"] } },
|
|
1092
|
+
{ agent: "step2", task: "Second receives {previous}" },
|
|
1093
|
+
],
|
|
1094
|
+
agents,
|
|
1095
|
+
{ task: "Original", agentContract: { version: 1 } },
|
|
1096
|
+
),
|
|
1097
|
+
);
|
|
1098
|
+
|
|
1099
|
+
assert.equal(result.isError, undefined);
|
|
1100
|
+
assert.equal(result.details.results.length, 2);
|
|
1101
|
+
assert.equal(result.details.results[0]?.acceptance?.status, "rejected");
|
|
1102
|
+
assert.equal(result.details.results[0]?.exitCode, 0);
|
|
1103
|
+
assert.match(result.details.results[1]?.finalOutput ?? "", /Step 2 ran/);
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it("agent contract v1 chain can gate progression on acceptance", async () => {
|
|
1107
|
+
mockPi.onCall({ output: "Step 1 done\n```acceptance-report\n{\"criteriaSatisfied\":[{\"id\":\"criterion-1\",\"status\":\"not-satisfied\",\"evidence\":\"no proof\"}]}\n```" });
|
|
1108
|
+
const agents = [makeAgent("step1", { completionGuard: false }), makeAgent("step2", { completionGuard: false })];
|
|
1109
|
+
|
|
1110
|
+
const result = await executeChain(
|
|
1111
|
+
makeChainParams(
|
|
1112
|
+
[
|
|
1113
|
+
{ agent: "step1", task: "First", acceptance: { level: "checked", criteria: ["Return required proof"] }, gateOn: "acceptance" },
|
|
1114
|
+
{ agent: "step2", task: "Should not run" },
|
|
1115
|
+
],
|
|
1116
|
+
agents,
|
|
1117
|
+
{ task: "Original", agentContract: { version: 1 } },
|
|
1118
|
+
),
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
assert.equal(result.isError, true);
|
|
1122
|
+
assert.equal(result.details.results.length, 1);
|
|
1123
|
+
assert.equal(result.details.results[0]?.acceptance?.status, "rejected");
|
|
1124
|
+
assert.equal(mockPi.callCount(), 1);
|
|
1125
|
+
});
|
|
1126
|
+
|
|
995
1127
|
it("runs a 3-step chain end-to-end", async () => {
|
|
996
1128
|
mockPi.onCall({ output: "Step output" });
|
|
997
1129
|
const agents = [makeAgent("scout"), makeAgent("planner"), makeAgent("executor")];
|
|
@@ -1183,6 +1315,12 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
|
|
|
1183
1315
|
};
|
|
1184
1316
|
}
|
|
1185
1317
|
|
|
1318
|
+
function git(args: string[]): string {
|
|
1319
|
+
const result = spawnSync("git", args, { cwd: tempDir, encoding: "utf-8" });
|
|
1320
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
1321
|
+
return result.stdout.trim();
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1186
1324
|
function readCallArgs(index: number): string[] {
|
|
1187
1325
|
const callFiles = fs.readdirSync(mockPi.dir)
|
|
1188
1326
|
.filter((name) => name.startsWith("call-") && name.endsWith(".json"))
|
|
@@ -1225,6 +1363,38 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
|
|
|
1225
1363
|
assert.equal(result.details.results.length, 2);
|
|
1226
1364
|
});
|
|
1227
1365
|
|
|
1366
|
+
it("aggregates worktree handoffs across foreground chain groups", { skip: process.platform === "win32" ? "worktree paths differ on Windows" : undefined }, async () => {
|
|
1367
|
+
git(["init"]);
|
|
1368
|
+
git(["config", "user.email", "test@example.com"]);
|
|
1369
|
+
git(["config", "user.name", "Test User"]);
|
|
1370
|
+
fs.writeFileSync(path.join(tempDir, "tracked.txt"), "base\n", "utf-8");
|
|
1371
|
+
git(["add", "tracked.txt"]);
|
|
1372
|
+
git(["commit", "-m", "initial"]);
|
|
1373
|
+
mockPi.onCall({ output: "Parallel task done" });
|
|
1374
|
+
const runId = "foreground-chain-handoff";
|
|
1375
|
+
const artifactsDir = path.join(tempDir, "artifacts");
|
|
1376
|
+
const result = await executeChain(
|
|
1377
|
+
makeChainParams(
|
|
1378
|
+
[{ parallel: [{ agent: "reviewer-a", task: "Review in isolation" }], worktree: true }],
|
|
1379
|
+
[makeAgent("reviewer-a")],
|
|
1380
|
+
{ runId, artifactsDir },
|
|
1381
|
+
),
|
|
1382
|
+
);
|
|
1383
|
+
|
|
1384
|
+
assert.equal(result.isError, undefined);
|
|
1385
|
+
assert.equal(result.details.parallelHandoff?.version, 1);
|
|
1386
|
+
assert.equal(result.details.parallelHandoff?.childCount, 1);
|
|
1387
|
+
assert.equal(result.details.parallelHandoff?.cleanupState, "complete");
|
|
1388
|
+
const handoff = JSON.parse(fs.readFileSync(result.details.parallelHandoff!.path, "utf-8")) as {
|
|
1389
|
+
groups: Array<{ stepIndex: number; children: Array<{ agent: string; patch: { path: string } }>; cleanup: { state: string } }>;
|
|
1390
|
+
};
|
|
1391
|
+
assert.equal(handoff.groups[0]!.stepIndex, 0);
|
|
1392
|
+
assert.equal(handoff.groups[0]!.children[0]!.agent, "reviewer-a");
|
|
1393
|
+
assert.equal(handoff.groups[0]!.cleanup.state, "complete");
|
|
1394
|
+
assert.equal(fs.existsSync(handoff.groups[0]!.children[0]!.patch.path), true);
|
|
1395
|
+
assert.match(handoff.groups[0]!.children[0]!.patch.path, /worktree-diffs\/foreground-chain-handoff\/step-0\//);
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1228
1398
|
it("aggregates parallel outputs for next sequential step", async () => {
|
|
1229
1399
|
mockPi.onCall({ output: "Review findings here" });
|
|
1230
1400
|
const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b"), makeAgent("synthesizer")];
|
|
@@ -1384,10 +1554,10 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
|
|
|
1384
1554
|
mockPi.onCall({
|
|
1385
1555
|
steps: [
|
|
1386
1556
|
{ jsonl: [events.toolStart("intercom", { action: "send", to: "orchestrator" })] },
|
|
1387
|
-
{ delay:
|
|
1557
|
+
{ delay: 25, jsonl: [events.assistantMessage("after handoff")] },
|
|
1388
1558
|
],
|
|
1389
1559
|
});
|
|
1390
|
-
mockPi.onCall({ output: "Other task done" });
|
|
1560
|
+
mockPi.onCall({ delay: 150, output: "Other task done" });
|
|
1391
1561
|
const agents = [
|
|
1392
1562
|
makeAgent("a", { systemPrompt: "Intercom orchestration channel:" }),
|
|
1393
1563
|
makeAgent("b", { systemPrompt: "Intercom orchestration channel:" }),
|
|
@@ -1420,9 +1590,9 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
|
|
|
1420
1590
|
|
|
1421
1591
|
assert.equal(result.isError, undefined);
|
|
1422
1592
|
assert.match(result.content[0]?.text ?? "", /Chain detached for intercom coordination/);
|
|
1423
|
-
assert.
|
|
1593
|
+
assert.match(result.content[0]?.text ?? "", /do not resume or launch a replacement/);
|
|
1424
1594
|
assert.equal(detachEmitted, true);
|
|
1425
|
-
assert.equal(result.details.results.some((entry) => entry.detached === true && entry.exitCode ===
|
|
1595
|
+
assert.equal(result.details.results.some((entry) => entry.detached === true && entry.exitCode === -2), true);
|
|
1426
1596
|
});
|
|
1427
1597
|
|
|
1428
1598
|
it("stops a sequential chain when a child detaches for intercom coordination", async () => {
|
|
@@ -1460,7 +1630,7 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
|
|
|
1460
1630
|
|
|
1461
1631
|
assert.equal(result.isError, undefined);
|
|
1462
1632
|
assert.match(result.content[0]?.text ?? "", /Chain detached for intercom coordination/);
|
|
1463
|
-
assert.
|
|
1633
|
+
assert.match(result.content[0]?.text ?? "", /do not resume or launch a replacement/);
|
|
1464
1634
|
assert.equal(detachEmitted, true);
|
|
1465
1635
|
assert.equal(mockPi.callCount(), 1);
|
|
1466
1636
|
});
|
|
@@ -69,6 +69,13 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
69
69
|
assert.equal(detectSubagentError(messages).hasError, false);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it("does not infer failure from successful bash output", () => {
|
|
73
|
+
const messages = [
|
|
74
|
+
toolResult("bash", "diff --git a/src/queue.ts b/src/queue.ts\n+ await new Promise((resolve) => setTimeout(resolve, 0))"),
|
|
75
|
+
];
|
|
76
|
+
assert.equal(detectSubagentError(messages).hasError, false);
|
|
77
|
+
});
|
|
78
|
+
|
|
72
79
|
it("detects isError tool result as failure (no assistant response)", () => {
|
|
73
80
|
const messages = [
|
|
74
81
|
toolResult("read", "file contents"),
|
|
@@ -80,18 +87,18 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
80
87
|
assert.match(result.details!, /EISDIR/);
|
|
81
88
|
});
|
|
82
89
|
|
|
83
|
-
it("detects bash
|
|
90
|
+
it("detects bash error (permission denied, no assistant response)", () => {
|
|
84
91
|
const messages = [
|
|
85
|
-
toolResult("bash", "ls: permission denied: /root/secret"),
|
|
92
|
+
toolResult("bash", "ls: permission denied: /root/secret", true),
|
|
86
93
|
];
|
|
87
94
|
const result = detectSubagentError(messages);
|
|
88
95
|
assert.equal(result.hasError, true);
|
|
89
96
|
assert.equal(result.errorType, "bash");
|
|
90
97
|
});
|
|
91
98
|
|
|
92
|
-
it("
|
|
99
|
+
it("extracts a bash exit code from error output", () => {
|
|
93
100
|
const messages = [
|
|
94
|
-
toolResult("bash", "error: process exited with code 127"),
|
|
101
|
+
toolResult("bash", "error: process exited with code 127", true),
|
|
95
102
|
];
|
|
96
103
|
const result = detectSubagentError(messages);
|
|
97
104
|
assert.equal(result.hasError, true);
|
|
@@ -129,9 +136,9 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
129
136
|
"agent produced substantive output after error — not a failure");
|
|
130
137
|
});
|
|
131
138
|
|
|
132
|
-
it("ignores bash
|
|
139
|
+
it("ignores bash error when agent responded after", () => {
|
|
133
140
|
const messages = [
|
|
134
|
-
toolResult("bash", "ls: permission denied: /root/secret"),
|
|
141
|
+
toolResult("bash", "ls: permission denied: /root/secret", true),
|
|
135
142
|
assistantMsg("I couldn't access /root/secret, but I found the data elsewhere."),
|
|
136
143
|
];
|
|
137
144
|
const result = detectSubagentError(messages);
|
|
@@ -145,7 +152,7 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
145
152
|
const messages = [
|
|
146
153
|
assistantMsg("Here is my analysis..."),
|
|
147
154
|
toolResult("bash", "rm -rf /important", false),
|
|
148
|
-
toolResult("bash", "error: process exited with code 1",
|
|
155
|
+
toolResult("bash", "error: process exited with code 1", true),
|
|
149
156
|
];
|
|
150
157
|
const result = detectSubagentError(messages);
|
|
151
158
|
assert.equal(result.hasError, true);
|
|
@@ -168,7 +175,7 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
168
175
|
it("flags error when no assistant messages at all", () => {
|
|
169
176
|
const messages = [
|
|
170
177
|
toolResult("read", "ok"),
|
|
171
|
-
toolResult("bash", "segmentation fault"),
|
|
178
|
+
toolResult("bash", "segmentation fault", true),
|
|
172
179
|
];
|
|
173
180
|
const result = detectSubagentError(messages);
|
|
174
181
|
assert.equal(result.hasError, true,
|
|
@@ -181,7 +188,7 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
|
|
|
181
188
|
// assistant message doesn't count as recovery. The final tool result is
|
|
182
189
|
// successful to ensure this test actually distinguishes correct behavior.
|
|
183
190
|
const messages = [
|
|
184
|
-
toolResult("bash", "permission denied: /etc/shadow"),
|
|
191
|
+
toolResult("bash", "permission denied: /etc/shadow", true),
|
|
185
192
|
assistantToolCall("bash"),
|
|
186
193
|
toolResult("bash", "command succeeded"),
|
|
187
194
|
];
|
|
@@ -49,13 +49,13 @@ describe("detectSubagentError", { skip: !detectSubagentError ? "utils not import
|
|
|
49
49
|
assert.equal(result.hasError, false);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
it("detects
|
|
52
|
+
it("detects bash error in last tool result", () => {
|
|
53
53
|
const messages = [
|
|
54
54
|
{ role: "assistant", content: [{ type: "text", text: "Running..." }] },
|
|
55
55
|
{
|
|
56
56
|
role: "toolResult",
|
|
57
57
|
toolName: "bash",
|
|
58
|
-
isError:
|
|
58
|
+
isError: true,
|
|
59
59
|
content: [{ type: "text", text: "command not found" }],
|
|
60
60
|
},
|
|
61
61
|
];
|
|
@@ -64,13 +64,13 @@ describe("detectSubagentError", { skip: !detectSubagentError ? "utils not import
|
|
|
64
64
|
assert.equal(result.errorType, "bash");
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it("
|
|
67
|
+
it("extracts a non-zero exit code from bash error output", () => {
|
|
68
68
|
const messages = [
|
|
69
69
|
{ role: "assistant", content: [{ type: "text", text: "Running..." }] },
|
|
70
70
|
{
|
|
71
71
|
role: "toolResult",
|
|
72
72
|
toolName: "bash",
|
|
73
|
-
isError:
|
|
73
|
+
isError: true,
|
|
74
74
|
content: [{ type: "text", text: "Error: process exited with code 127" }],
|
|
75
75
|
},
|
|
76
76
|
];
|
|
@@ -146,9 +146,10 @@ describe("runSync error handling", { skip: !piAvailable ? "pi packages not avail
|
|
|
146
146
|
it("detectSubagentError overrides exit 0 on hidden failure", async () => {
|
|
147
147
|
mockPi.onCall({
|
|
148
148
|
jsonl: [
|
|
149
|
+
events.assistantMessage("Starting deployment"),
|
|
149
150
|
events.toolStart("bash", { command: "deploy" }),
|
|
150
151
|
events.toolEnd("bash"),
|
|
151
|
-
events.toolResult("bash", "connection refused"),
|
|
152
|
+
events.toolResult("bash", "connection refused", true),
|
|
152
153
|
],
|
|
153
154
|
});
|
|
154
155
|
const agents = makeAgentConfigs(["deployer"]);
|