@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
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Acceptance report sourcing through the real execution drivers: the report the
|
|
3
|
+
* child wrote to its configured output file (recovered from its write tool
|
|
4
|
+
* calls) versus the assistant text, ordered by outputMode, including parallel
|
|
5
|
+
* children with the distinct configured paths required after #420. Runs the
|
|
6
|
+
* full spawn → stream-parse → acceptance pipeline against the mock pi CLI.
|
|
7
|
+
*/
|
|
8
|
+
import { after, afterEach, before, beforeEach, describe, it } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import type { MockPi } from "../support/helpers.ts";
|
|
13
|
+
import { createEventBus, createMockPi, createTempDir, events, makeAgent, makeAgentConfigs, makeMinimalCtx, removeTempDir, tryImport } from "../support/helpers.ts";
|
|
14
|
+
|
|
15
|
+
interface AcceptanceSummary {
|
|
16
|
+
status?: string;
|
|
17
|
+
childReport?: { criteriaSatisfied?: Array<{ id?: string; status?: string; evidence?: string }> };
|
|
18
|
+
runtimeChecks?: Array<{ id?: string; status?: string; message?: string }>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface AcceptanceArtifactPaths {
|
|
22
|
+
outputPath: string;
|
|
23
|
+
metadataPath: string;
|
|
24
|
+
transcriptPath: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ExecutionModule {
|
|
28
|
+
runSync(
|
|
29
|
+
runtimeCwd: string,
|
|
30
|
+
agents: ReturnType<typeof makeAgentConfigs>,
|
|
31
|
+
agentName: string,
|
|
32
|
+
task: string,
|
|
33
|
+
options: Record<string, unknown>,
|
|
34
|
+
): Promise<{
|
|
35
|
+
exitCode: number;
|
|
36
|
+
error?: string;
|
|
37
|
+
finalOutput?: string;
|
|
38
|
+
savedOutputPath?: string;
|
|
39
|
+
acceptance?: AcceptanceSummary;
|
|
40
|
+
artifactPaths?: AcceptanceArtifactPaths;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface AsyncExecutionModule {
|
|
45
|
+
isAsyncAvailable(): boolean;
|
|
46
|
+
executeAsyncSingle(id: string, params: Record<string, unknown>): unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface TypesModule {
|
|
50
|
+
ASYNC_DIR: string;
|
|
51
|
+
RESULTS_DIR: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface ExecutorModule {
|
|
55
|
+
createSubagentExecutor?: (...args: unknown[]) => {
|
|
56
|
+
execute: (...args: unknown[]) => Promise<{ content: Array<{ text?: string }>; isError?: boolean; details?: { asyncId?: string } }>;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface AsyncResultPayload {
|
|
61
|
+
success: boolean;
|
|
62
|
+
results: Array<{ output?: string; error?: string; acceptance?: AcceptanceSummary; artifactPaths?: AcceptanceArtifactPaths }>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const execution = await tryImport<ExecutionModule>("./src/runs/foreground/execution.ts");
|
|
66
|
+
const asyncMod = await tryImport<AsyncExecutionModule>("./src/runs/background/async-execution.ts");
|
|
67
|
+
const typesMod = await tryImport<TypesModule>("./src/shared/types.ts");
|
|
68
|
+
const executorMod = await tryImport<ExecutorModule>("./src/runs/foreground/subagent-executor.ts");
|
|
69
|
+
|
|
70
|
+
const runSync = execution?.runSync;
|
|
71
|
+
const isAsyncAvailable = asyncMod?.isAsyncAvailable;
|
|
72
|
+
const executeAsyncSingle = asyncMod?.executeAsyncSingle;
|
|
73
|
+
const ASYNC_DIR = typesMod?.ASYNC_DIR;
|
|
74
|
+
const RESULTS_DIR = typesMod?.RESULTS_DIR;
|
|
75
|
+
const createSubagentExecutor = executorMod?.createSubagentExecutor;
|
|
76
|
+
|
|
77
|
+
const DISABLED_ARTIFACTS = {
|
|
78
|
+
enabled: false,
|
|
79
|
+
includeInput: false,
|
|
80
|
+
includeOutput: false,
|
|
81
|
+
includeJsonl: false,
|
|
82
|
+
includeMetadata: false,
|
|
83
|
+
cleanupDays: 7,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const ACCEPTANCE_ARTIFACTS = {
|
|
87
|
+
enabled: true,
|
|
88
|
+
includeInput: false,
|
|
89
|
+
includeOutput: true,
|
|
90
|
+
includeJsonl: false,
|
|
91
|
+
includeTranscript: true,
|
|
92
|
+
includeMetadata: true,
|
|
93
|
+
cleanupDays: 7,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function acceptanceReport(criterionStatus: "satisfied" | "not-satisfied", evidence: string): string {
|
|
97
|
+
return [
|
|
98
|
+
"```acceptance-report",
|
|
99
|
+
JSON.stringify({
|
|
100
|
+
criteriaSatisfied: [{ id: "criterion-1", status: criterionStatus, evidence }],
|
|
101
|
+
changedFiles: ["src/module.ts"],
|
|
102
|
+
testsAddedOrUpdated: ["test/module.test.ts"],
|
|
103
|
+
commandsRun: [{ command: "npm test", result: "passed", summary: "passed" }],
|
|
104
|
+
validationOutput: ["passed"],
|
|
105
|
+
residualRisks: [],
|
|
106
|
+
noStagedFiles: true,
|
|
107
|
+
notes: evidence,
|
|
108
|
+
}),
|
|
109
|
+
"```",
|
|
110
|
+
].join("\n");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function waitForAsyncResult(id: string, timeoutMs = 15_000): Promise<AsyncResultPayload> {
|
|
114
|
+
const resultPath = path.join(RESULTS_DIR!, `${id}.json`);
|
|
115
|
+
const deadline = Date.now() + timeoutMs;
|
|
116
|
+
while (!fs.existsSync(resultPath)) {
|
|
117
|
+
if (Date.now() > deadline) assert.fail(`Timed out waiting for async result file: ${resultPath}`);
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
119
|
+
}
|
|
120
|
+
return JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
describe("acceptance file reports", { skip: !runSync ? "pi packages not available" : undefined }, () => {
|
|
124
|
+
let tempDir: string;
|
|
125
|
+
let mockPi: MockPi;
|
|
126
|
+
|
|
127
|
+
before(() => {
|
|
128
|
+
mockPi = createMockPi();
|
|
129
|
+
mockPi.install();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
after(() => {
|
|
133
|
+
mockPi.uninstall();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
tempDir = createTempDir();
|
|
138
|
+
mockPi.reset();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
afterEach(() => {
|
|
142
|
+
removeTempDir(tempDir);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function conflictingReportsCall(outputPath: string, fileStatus: "satisfied" | "not-satisfied", textStatus: "satisfied" | "not-satisfied") {
|
|
146
|
+
const fileReport = `# Findings\n${acceptanceReport(fileStatus, "from child-written file")}`;
|
|
147
|
+
mockPi.onCall({
|
|
148
|
+
jsonl: [
|
|
149
|
+
...events.completedWrite(outputPath, fileReport),
|
|
150
|
+
events.assistantMessage(`Report written to the output file.\n${acceptanceReport(textStatus, "from assistant text")}`),
|
|
151
|
+
],
|
|
152
|
+
writeFiles: [{ path: outputPath, content: fileReport }],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
describe("foreground runSync", () => {
|
|
157
|
+
it("file-only mode accepts from the child-written file when the text report fails", async () => {
|
|
158
|
+
const outputPath = path.join(tempDir, "report.md");
|
|
159
|
+
conflictingReportsCall(outputPath, "satisfied", "not-satisfied");
|
|
160
|
+
|
|
161
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
162
|
+
runId: "acceptance-file-only",
|
|
163
|
+
outputPath,
|
|
164
|
+
outputMode: "file-only",
|
|
165
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
assert.equal(result.acceptance?.status, "checked");
|
|
169
|
+
assert.equal(result.exitCode, 0);
|
|
170
|
+
assert.equal(result.savedOutputPath, outputPath);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("file-only mode persists acceptance metadata when final assistant text is only a receipt", async () => {
|
|
174
|
+
const outputPath = path.join(tempDir, "saved-review.md");
|
|
175
|
+
const artifactsDir = path.join(tempDir, "artifacts");
|
|
176
|
+
const fileReport = `# Review\n${acceptanceReport("satisfied", "foreground saved verdict")}`;
|
|
177
|
+
mockPi.onCall({
|
|
178
|
+
jsonl: [...events.completedWrite(outputPath, fileReport), events.assistantMessage("Output saved to the configured file.")],
|
|
179
|
+
writeFiles: [{ path: outputPath, content: fileReport }],
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
183
|
+
runId: "acceptance-foreground-saved-receipt",
|
|
184
|
+
outputPath,
|
|
185
|
+
outputMode: "file-only",
|
|
186
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
187
|
+
artifactsDir,
|
|
188
|
+
artifactConfig: ACCEPTANCE_ARTIFACTS,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
assert.equal(result.acceptance?.status, "checked");
|
|
192
|
+
assert.equal(result.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "foreground saved verdict");
|
|
193
|
+
assert.equal(result.exitCode, 0);
|
|
194
|
+
assert.ok(result.artifactPaths);
|
|
195
|
+
const metadata = JSON.parse(fs.readFileSync(result.artifactPaths.metadataPath, "utf-8")) as { exitCode?: number; acceptance?: AcceptanceSummary };
|
|
196
|
+
assert.equal(metadata.exitCode, 0);
|
|
197
|
+
assert.equal(metadata.acceptance?.status, "checked");
|
|
198
|
+
assert.equal(metadata.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "foreground saved verdict");
|
|
199
|
+
assert.doesNotMatch(fs.readFileSync(result.artifactPaths.outputPath, "utf-8"), /```acceptance-report/);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("persists a report-only child response in metadata while normal output stays clean", async () => {
|
|
203
|
+
const artifactsDir = path.join(tempDir, "report-only-artifacts");
|
|
204
|
+
mockPi.onCall({ output: acceptanceReport("satisfied", "report-only evidence") });
|
|
205
|
+
|
|
206
|
+
const result = await runSync!(tempDir, [makeAgent("worker", { completionGuard: false })], "worker", "Implement and report the fix.", {
|
|
207
|
+
runId: "acceptance-report-only",
|
|
208
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
209
|
+
artifactsDir,
|
|
210
|
+
artifactConfig: ACCEPTANCE_ARTIFACTS,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
assert.equal(result.exitCode, 0);
|
|
214
|
+
assert.equal(result.finalOutput, "");
|
|
215
|
+
assert.ok(result.artifactPaths);
|
|
216
|
+
assert.equal(fs.readFileSync(result.artifactPaths.outputPath, "utf-8"), "");
|
|
217
|
+
const metadata = JSON.parse(fs.readFileSync(result.artifactPaths.metadataPath, "utf-8")) as { acceptance?: AcceptanceSummary };
|
|
218
|
+
assert.equal(metadata.acceptance?.status, "checked");
|
|
219
|
+
assert.equal(metadata.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "report-only evidence");
|
|
220
|
+
assert.match(fs.readFileSync(result.artifactPaths.transcriptPath, "utf-8"), /```acceptance-report/);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("inline mode persists final rejection metadata when the text report fails", async () => {
|
|
224
|
+
const outputPath = path.join(tempDir, "report.md");
|
|
225
|
+
const artifactsDir = path.join(tempDir, "rejected-artifacts");
|
|
226
|
+
conflictingReportsCall(outputPath, "satisfied", "not-satisfied");
|
|
227
|
+
|
|
228
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
229
|
+
runId: "acceptance-inline-text-first",
|
|
230
|
+
outputPath,
|
|
231
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
232
|
+
artifactsDir,
|
|
233
|
+
artifactConfig: ACCEPTANCE_ARTIFACTS,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
assert.equal(result.acceptance?.status, "rejected");
|
|
237
|
+
assert.equal(result.exitCode, 1);
|
|
238
|
+
assert.match(result.error ?? "", /Acceptance rejected: Required criterion 'criterion-1' was reported as not-satisfied\./);
|
|
239
|
+
assert.ok(result.artifactPaths);
|
|
240
|
+
const metadata = JSON.parse(fs.readFileSync(result.artifactPaths.metadataPath, "utf-8")) as { exitCode?: number; error?: string; acceptance?: AcceptanceSummary };
|
|
241
|
+
assert.equal(metadata.exitCode, 1);
|
|
242
|
+
assert.match(metadata.error ?? "", /Acceptance rejected/);
|
|
243
|
+
assert.equal(metadata.acceptance?.status, "rejected");
|
|
244
|
+
assert.equal(metadata.acceptance?.runtimeChecks?.find((check) => check.id === "criterion:criterion-1")?.status, "failed");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("inline mode accepts on the text report regardless of a failing file report", async () => {
|
|
248
|
+
const outputPath = path.join(tempDir, "report.md");
|
|
249
|
+
conflictingReportsCall(outputPath, "not-satisfied", "satisfied");
|
|
250
|
+
|
|
251
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
252
|
+
runId: "acceptance-inline-file-fallback-only",
|
|
253
|
+
outputPath,
|
|
254
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
assert.equal(result.acceptance?.status, "checked");
|
|
258
|
+
assert.equal(result.exitCode, 0);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("does not credit a failed write as the file report", async () => {
|
|
262
|
+
const outputPath = path.join(tempDir, "report.md");
|
|
263
|
+
const fileReport = `# Findings\n${acceptanceReport("satisfied", "from a write that failed")}`;
|
|
264
|
+
mockPi.onCall({
|
|
265
|
+
jsonl: [
|
|
266
|
+
{
|
|
267
|
+
type: "message_end",
|
|
268
|
+
message: {
|
|
269
|
+
role: "assistant",
|
|
270
|
+
content: [{ type: "toolCall", id: "w-failed", name: "write", arguments: { path: outputPath, content: fileReport } }],
|
|
271
|
+
model: "mock/test-model",
|
|
272
|
+
stopReason: "toolUse",
|
|
273
|
+
usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, cost: { total: 0.001 } },
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
type: "tool_result_end",
|
|
278
|
+
message: { role: "toolResult", toolCallId: "w-failed", toolName: "write", isError: true, content: [{ type: "text", text: "disk full" }] },
|
|
279
|
+
},
|
|
280
|
+
events.assistantMessage(`The write failed.\n${acceptanceReport("not-satisfied", "from assistant text")}`),
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
285
|
+
runId: "acceptance-failed-write",
|
|
286
|
+
outputPath,
|
|
287
|
+
outputMode: "file-only",
|
|
288
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
assert.equal(result.acceptance?.status, "rejected");
|
|
292
|
+
assert.equal(result.exitCode, 1);
|
|
293
|
+
assert.match(result.error ?? "", /reported as not-satisfied/);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("rejects a malformed child-written file report instead of accepting the text report", async () => {
|
|
297
|
+
const outputPath = path.join(tempDir, "report.md");
|
|
298
|
+
const malformedReport = "```acceptance-report\n{ not json";
|
|
299
|
+
mockPi.onCall({
|
|
300
|
+
jsonl: [
|
|
301
|
+
...events.completedWrite(outputPath, malformedReport),
|
|
302
|
+
events.assistantMessage(acceptanceReport("satisfied", "from assistant text")),
|
|
303
|
+
],
|
|
304
|
+
writeFiles: [{ path: outputPath, content: malformedReport }],
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const result = await runSync!(tempDir, makeAgentConfigs(["worker"]), "worker", "Write the findings report.", {
|
|
308
|
+
runId: "acceptance-malformed-file-report",
|
|
309
|
+
outputPath,
|
|
310
|
+
outputMode: "file-only",
|
|
311
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
assert.equal(result.acceptance?.status, "rejected");
|
|
315
|
+
assert.equal(result.exitCode, 1);
|
|
316
|
+
assert.match(result.error ?? "", /Empty or unterminated acceptance-report fence.*configured output/);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe("background runner", { skip: isAsyncAvailable && !isAsyncAvailable() ? "jiti not available" : undefined }, () => {
|
|
321
|
+
function runAsyncSingle(id: string, outputPath: string, outputMode: "inline" | "file-only", artifactConfig = DISABLED_ARTIFACTS) {
|
|
322
|
+
executeAsyncSingle!(id, {
|
|
323
|
+
agent: "worker",
|
|
324
|
+
task: "Write the findings report.",
|
|
325
|
+
agentConfig: makeAgent("worker", { completionGuard: false }),
|
|
326
|
+
ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-file-report" },
|
|
327
|
+
artifactConfig,
|
|
328
|
+
artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
|
|
329
|
+
shareEnabled: false,
|
|
330
|
+
maxSubagentDepth: 2,
|
|
331
|
+
output: outputPath,
|
|
332
|
+
outputMode,
|
|
333
|
+
acceptance: { level: "checked", criteria: ["Report the findings"] },
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
it("file-only mode accepts from the child-written file when the text report fails", async () => {
|
|
338
|
+
const outputPath = path.join(tempDir, "async-report.md");
|
|
339
|
+
conflictingReportsCall(outputPath, "satisfied", "not-satisfied");
|
|
340
|
+
const id = `acceptance-file-report-${Date.now().toString(36)}`;
|
|
341
|
+
runAsyncSingle(id, outputPath, "file-only");
|
|
342
|
+
|
|
343
|
+
const payload = await waitForAsyncResult(id);
|
|
344
|
+
assert.equal(payload.success, true);
|
|
345
|
+
assert.equal(payload.results[0]?.acceptance?.status, "checked");
|
|
346
|
+
assert.match(payload.results[0]?.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence ?? "", /from child-written file/);
|
|
347
|
+
const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR!, id, "status.json"), "utf-8")) as { steps?: Array<{ acceptance?: { status?: string } }> };
|
|
348
|
+
assert.equal(status.steps?.[0]?.acceptance?.status, "checked");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("file-only mode persists async acceptance metadata when final assistant text is only a receipt", async () => {
|
|
352
|
+
const outputPath = path.join(tempDir, "saved-review.md");
|
|
353
|
+
const fileReport = `# Review\n${acceptanceReport("satisfied", "saved reviewer verdict")}`;
|
|
354
|
+
mockPi.onCall({
|
|
355
|
+
jsonl: [...events.completedWrite(outputPath, fileReport), events.assistantMessage("Output saved to the configured file.")],
|
|
356
|
+
writeFiles: [{ path: outputPath, content: fileReport }],
|
|
357
|
+
});
|
|
358
|
+
const id = `acceptance-saved-receipt-${Date.now().toString(36)}`;
|
|
359
|
+
runAsyncSingle(id, outputPath, "file-only", ACCEPTANCE_ARTIFACTS);
|
|
360
|
+
|
|
361
|
+
const payload = await waitForAsyncResult(id);
|
|
362
|
+
assert.equal(payload.success, true);
|
|
363
|
+
assert.equal(payload.results[0]?.acceptance?.status, "checked");
|
|
364
|
+
assert.equal(payload.results[0]?.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "saved reviewer verdict");
|
|
365
|
+
const metadataPath = payload.results[0]?.artifactPaths?.metadataPath;
|
|
366
|
+
assert.ok(metadataPath);
|
|
367
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8")) as { acceptance?: AcceptanceSummary };
|
|
368
|
+
assert.equal(metadata.acceptance?.status, "checked");
|
|
369
|
+
assert.equal(metadata.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "saved reviewer verdict");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("inline mode accepts from the text report and strips fences from the resolved file output", async () => {
|
|
373
|
+
const outputPath = path.join(tempDir, "async-report.md");
|
|
374
|
+
conflictingReportsCall(outputPath, "not-satisfied", "satisfied");
|
|
375
|
+
const id = `acceptance-inline-strip-${Date.now().toString(36)}`;
|
|
376
|
+
runAsyncSingle(id, outputPath, "inline");
|
|
377
|
+
|
|
378
|
+
const payload = await waitForAsyncResult(id);
|
|
379
|
+
assert.equal(payload.success, true);
|
|
380
|
+
assert.equal(payload.results[0]?.acceptance?.status, "checked");
|
|
381
|
+
assert.match(payload.results[0]?.output ?? "", /# Findings/);
|
|
382
|
+
assert.doesNotMatch(payload.results[0]?.output ?? "", /```acceptance-report/);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("parallel children keep acceptance tied to their distinct configured outputs", { skip: !createSubagentExecutor ? "executor not available" : undefined }, async () => {
|
|
386
|
+
const alphaPath = path.join(tempDir, "alpha-report.md");
|
|
387
|
+
const betaPath = path.join(tempDir, "beta-report.md");
|
|
388
|
+
const alphaReport = `# Alpha findings\n${acceptanceReport("satisfied", "alpha evidence")}`;
|
|
389
|
+
const betaReport = `# Beta findings\n${acceptanceReport("not-satisfied", "beta evidence")}`;
|
|
390
|
+
mockPi.onCall({
|
|
391
|
+
matchArgIncludes: "Alpha task",
|
|
392
|
+
jsonl: [...events.completedWrite(alphaPath, alphaReport), events.assistantMessage("Alpha wrote its report.")],
|
|
393
|
+
writeFiles: [{ path: alphaPath, content: alphaReport }],
|
|
394
|
+
});
|
|
395
|
+
mockPi.onCall({
|
|
396
|
+
matchArgIncludes: "Beta task",
|
|
397
|
+
jsonl: [...events.completedWrite(betaPath, betaReport), events.assistantMessage("Beta wrote its report.")],
|
|
398
|
+
writeFiles: [{ path: betaPath, content: betaReport }],
|
|
399
|
+
});
|
|
400
|
+
const executor = createSubagentExecutor!({
|
|
401
|
+
pi: { events: createEventBus(), getSessionName: () => undefined },
|
|
402
|
+
state: { baseCwd: tempDir, currentSessionId: null, asyncJobs: new Map(), foregroundControls: new Map(), lastForegroundControlId: null },
|
|
403
|
+
config: {},
|
|
404
|
+
asyncByDefault: false,
|
|
405
|
+
tempArtifactsDir: tempDir,
|
|
406
|
+
getSubagentSessionRoot: () => tempDir,
|
|
407
|
+
expandTilde: (p: string) => p,
|
|
408
|
+
discoverAgents: () => ({ agents: [makeAgent("worker", { completionGuard: false })] }),
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const acceptance = { level: "checked", criteria: ["Report the findings"] };
|
|
412
|
+
const result = await executor.execute(
|
|
413
|
+
"acceptance-parallel-files",
|
|
414
|
+
{
|
|
415
|
+
tasks: [
|
|
416
|
+
{ agent: "worker", task: "Alpha task: write the report.", output: alphaPath, outputMode: "file-only", acceptance },
|
|
417
|
+
{ agent: "worker", task: "Beta task: write the report.", output: betaPath, outputMode: "file-only", acceptance },
|
|
418
|
+
],
|
|
419
|
+
async: true,
|
|
420
|
+
clarify: false,
|
|
421
|
+
},
|
|
422
|
+
new AbortController().signal,
|
|
423
|
+
undefined,
|
|
424
|
+
makeMinimalCtx(tempDir),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
const asyncId = result.details?.asyncId;
|
|
428
|
+
assert.ok(asyncId, "expected asyncId");
|
|
429
|
+
const payload = await waitForAsyncResult(asyncId);
|
|
430
|
+
assert.equal(payload.results.length, 2);
|
|
431
|
+
const alpha = payload.results[0];
|
|
432
|
+
const beta = payload.results[1];
|
|
433
|
+
assert.equal(alpha?.acceptance?.status, "checked");
|
|
434
|
+
assert.equal(alpha?.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "alpha evidence");
|
|
435
|
+
assert.equal(beta?.acceptance?.status, "rejected");
|
|
436
|
+
assert.equal(beta?.acceptance?.childReport?.criteriaSatisfied?.[0]?.evidence, "beta evidence");
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
});
|