@selesai/code 0.5.19 → 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-intercom/broker/spawn.test.ts +31 -0
- package/dist/extensions/pi-intercom/broker/spawn.ts +5 -3
- 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 +2 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import * as fs from "node:fs";
|
|
7
8
|
import * as os from "node:os";
|
|
8
9
|
import * as path from "node:path";
|
|
@@ -10,15 +11,17 @@ import { fileURLToPath } from "node:url";
|
|
|
10
11
|
import { createRequire } from "node:module";
|
|
11
12
|
import type { ExtensionAPI } from "@selesai/code";
|
|
12
13
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
13
|
-
import {
|
|
14
|
+
import { writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
15
|
+
import { applyThinkingSuffix, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
|
|
14
16
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
15
17
|
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
16
18
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
17
|
-
import {
|
|
19
|
+
import type { ContextMode } from "../shared/context-mode.ts";
|
|
20
|
+
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
18
21
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
19
22
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
20
23
|
import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV, resolveChildCwd } from "../../shared/utils.ts";
|
|
21
|
-
import { buildModelCandidates, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
24
|
+
import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
22
25
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
23
26
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
24
27
|
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
@@ -28,14 +31,17 @@ import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
|
28
31
|
import { resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
29
32
|
import {
|
|
30
33
|
type AcceptanceInput,
|
|
34
|
+
type AgentContract,
|
|
31
35
|
type ArtifactConfig,
|
|
32
36
|
type Details,
|
|
37
|
+
type JsonSchemaObject,
|
|
33
38
|
type MaxOutputConfig,
|
|
34
39
|
type NestedRouteInfo,
|
|
35
40
|
type ResolvedControlConfig,
|
|
36
41
|
type ResolvedTurnBudget,
|
|
37
42
|
type ResolvedToolBudget,
|
|
38
43
|
type SubagentRunMode,
|
|
44
|
+
type SteeringRecoveryDescriptor,
|
|
39
45
|
ASYNC_DIR,
|
|
40
46
|
RESULTS_DIR,
|
|
41
47
|
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
@@ -44,13 +50,18 @@ import {
|
|
|
44
50
|
getAsyncConfigPath,
|
|
45
51
|
resolveChildMaxSubagentDepth,
|
|
46
52
|
} from "../../shared/types.ts";
|
|
47
|
-
import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
48
|
-
import { initialTurnBudgetState } from "../shared/turn-budget.ts";
|
|
53
|
+
import { nestedResultsPath, nestedSummaryFromAsyncStatus, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
54
|
+
import { appendTurnBudgetSystemPrompt, initialTurnBudgetState } from "../shared/turn-budget.ts";
|
|
49
55
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
50
56
|
import type { ImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
57
|
+
import type { SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
58
|
+
import { finalizeProcessTerminal, readProcessTerminal } from "./process-terminal.ts";
|
|
59
|
+
import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.ts";
|
|
60
|
+
import { decodeSubagentCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
61
|
+
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
51
62
|
|
|
52
63
|
const require = createRequire(import.meta.url);
|
|
53
|
-
const
|
|
64
|
+
const piPackageRoot = resolvePiPackageRoot();
|
|
54
65
|
|
|
55
66
|
function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {
|
|
56
67
|
if (!fs.existsSync(packageJsonPath)) return undefined;
|
|
@@ -73,15 +84,15 @@ function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefi
|
|
|
73
84
|
function resolveJitiCliPath(): string | undefined {
|
|
74
85
|
const candidates: Array<() => string | undefined> = [
|
|
75
86
|
() => require.resolve("jiti/package.json"),
|
|
76
|
-
() =>
|
|
77
|
-
? createRequire(path.join(
|
|
87
|
+
() => piPackageRoot
|
|
88
|
+
? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
|
|
78
89
|
: undefined,
|
|
79
90
|
() => {
|
|
80
91
|
if (!process.argv[1]) return undefined;
|
|
81
92
|
const piEntry = fs.realpathSync(process.argv[1]);
|
|
82
93
|
return createRequire(piEntry).resolve("jiti/package.json");
|
|
83
94
|
},
|
|
84
|
-
() =>
|
|
95
|
+
() => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
|
|
85
96
|
];
|
|
86
97
|
for (const candidate of candidates) {
|
|
87
98
|
try {
|
|
@@ -108,11 +119,15 @@ interface AsyncExecutionContext {
|
|
|
108
119
|
currentModel?: ParentModel;
|
|
109
120
|
/** Optional model-scope enforcement resolved from subagent settings. */
|
|
110
121
|
modelScope?: ModelScopeConfig;
|
|
122
|
+
/** Whether the parent session has an interactive UI. */
|
|
123
|
+
interactive?: boolean;
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
interface AsyncChainParams {
|
|
114
127
|
chain: ChainStep[];
|
|
115
128
|
task?: string;
|
|
129
|
+
/** Raw caller-facing goal used only by the started event. */
|
|
130
|
+
goal?: string;
|
|
116
131
|
attachRoot?: ImportedAsyncRoot & { agent: string; outputName?: string; label?: string };
|
|
117
132
|
resultMode?: Exclude<SubagentRunMode, "single">;
|
|
118
133
|
agents: AgentConfig[];
|
|
@@ -124,12 +139,15 @@ interface AsyncChainParams {
|
|
|
124
139
|
artifactConfig: ArtifactConfig;
|
|
125
140
|
shareEnabled: boolean;
|
|
126
141
|
sessionRoot?: string;
|
|
142
|
+
agentContract?: AgentContract;
|
|
127
143
|
chainSkills?: string[];
|
|
128
144
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
129
145
|
thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
|
|
146
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
130
147
|
progressDir?: string;
|
|
131
148
|
dynamicFanoutMaxItems?: number;
|
|
132
149
|
maxSubagentDepth: number;
|
|
150
|
+
waitToolEnabled?: boolean;
|
|
133
151
|
worktreeSetupHook?: string;
|
|
134
152
|
worktreeSetupHookTimeoutMs?: number;
|
|
135
153
|
worktreeBaseDir?: string;
|
|
@@ -144,11 +162,14 @@ interface AsyncChainParams {
|
|
|
144
162
|
configToolBudget?: ResolvedToolBudget;
|
|
145
163
|
/** Global cap on simultaneously-running subagent tasks within the async run. */
|
|
146
164
|
globalConcurrencyLimit?: number;
|
|
165
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
147
166
|
}
|
|
148
167
|
|
|
149
168
|
interface AsyncSingleParams {
|
|
150
169
|
agent: string;
|
|
151
170
|
task?: string;
|
|
171
|
+
/** Raw caller-facing goal used only by the started event. */
|
|
172
|
+
goal?: string;
|
|
152
173
|
agentConfig: AgentConfig;
|
|
153
174
|
ctx: AsyncExecutionContext;
|
|
154
175
|
cwd?: string;
|
|
@@ -157,15 +178,21 @@ interface AsyncSingleParams {
|
|
|
157
178
|
artifactConfig: ArtifactConfig;
|
|
158
179
|
shareEnabled: boolean;
|
|
159
180
|
sessionRoot?: string;
|
|
181
|
+
sessionDir?: string;
|
|
160
182
|
sessionFile?: string;
|
|
183
|
+
revivalLease?: SessionLeaseRequest;
|
|
184
|
+
context?: ContextMode;
|
|
161
185
|
skills?: string[];
|
|
162
186
|
output?: string | boolean;
|
|
163
187
|
outputMode?: "inline" | "file-only";
|
|
164
188
|
outputBaseDir?: string;
|
|
189
|
+
agentContract?: AgentContract;
|
|
190
|
+
structuredOutputSchema?: JsonSchemaObject;
|
|
165
191
|
modelOverride?: string;
|
|
166
192
|
thinkingOverride?: AgentConfig["thinking"];
|
|
167
193
|
availableModels?: AvailableModelInfo[];
|
|
168
194
|
maxSubagentDepth: number;
|
|
195
|
+
waitToolEnabled?: boolean;
|
|
169
196
|
worktreeSetupHook?: string;
|
|
170
197
|
worktreeSetupHookTimeoutMs?: number;
|
|
171
198
|
worktreeBaseDir?: string;
|
|
@@ -175,9 +202,11 @@ interface AsyncSingleParams {
|
|
|
175
202
|
nestedRoute?: NestedRouteInfo;
|
|
176
203
|
acceptance?: AcceptanceInput;
|
|
177
204
|
timeoutMs?: number;
|
|
205
|
+
absoluteDeadlineAt?: number;
|
|
178
206
|
turnBudget?: ResolvedTurnBudget;
|
|
179
207
|
toolBudget?: ResolvedToolBudget;
|
|
180
208
|
configToolBudget?: ResolvedToolBudget;
|
|
209
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
181
210
|
}
|
|
182
211
|
|
|
183
212
|
interface AsyncExecutionResult {
|
|
@@ -198,15 +227,19 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
198
227
|
chainSkills?: string[];
|
|
199
228
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
200
229
|
thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
|
|
230
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
201
231
|
progressDir?: string;
|
|
232
|
+
agentContract?: AgentContract;
|
|
202
233
|
dynamicFanoutMaxItems?: number;
|
|
203
234
|
maxSubagentDepth: number;
|
|
235
|
+
waitToolEnabled?: boolean;
|
|
204
236
|
worktreeBaseDir?: string;
|
|
205
237
|
asyncDir: string;
|
|
206
238
|
outputBaseDir?: string;
|
|
207
239
|
validateOutputBindings?: boolean;
|
|
208
240
|
toolBudget?: ResolvedToolBudget;
|
|
209
241
|
configToolBudget?: ResolvedToolBudget;
|
|
242
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
210
243
|
}
|
|
211
244
|
|
|
212
245
|
export type AsyncRunnerStepBuildResult =
|
|
@@ -219,14 +252,20 @@ export type AsyncRunnerStepBuildResult =
|
|
|
219
252
|
}
|
|
220
253
|
| { error: string };
|
|
221
254
|
|
|
222
|
-
export function formatAsyncStartedMessage(headline: string): string {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
255
|
+
export function formatAsyncStartedMessage(headline: string, interactive: boolean): string {
|
|
256
|
+
const guidance = interactive
|
|
257
|
+
? [
|
|
258
|
+
"The async run is detached and running in the background.",
|
|
259
|
+
"You are in an interactive session. By default, return control to the user now; Pi will wake you on completion when the run finishes or needs attention. Do NOT call subagent_wait() merely to wait, and do not run sleep/polling loops to wait for it.",
|
|
260
|
+
"Override that default and call subagent_wait() before ending the turn only when the current request is run-to-completion — for example, the user asked you to report results back here before continuing, or a skill must finish in one turn. In that case, call subagent_wait() to block until the run completes so its results are delivered in this turn instead of deferred.",
|
|
261
|
+
"Otherwise, continue any independent work or return control to the user. Use subagent({ action: \"status\", id: \"...\" }) for a one-shot status/result or to inspect a blocked/stale run, never as a wait loop.",
|
|
262
|
+
]
|
|
263
|
+
: [
|
|
264
|
+
"The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
|
|
265
|
+
"This is a non-interactive run: Pi auto-drains current-session background work at agent_end so detached children are not abandoned; call subagent_wait() when this turn must receive the run's results before it ends, otherwise let the headless auto-drain finish the work.",
|
|
266
|
+
"Use subagent({ action: \"status\", id: \"...\" }) when you need a one-shot status/result or to inspect a blocked/stale run. To block until completion, use subagent_wait() — do not poll in a loop.",
|
|
267
|
+
];
|
|
268
|
+
return [headline, "", ...guidance].join("\n");
|
|
230
269
|
}
|
|
231
270
|
|
|
232
271
|
/**
|
|
@@ -280,7 +319,93 @@ function closeFd(fd: number | undefined): void {
|
|
|
280
319
|
/**
|
|
281
320
|
* Spawn the async runner process
|
|
282
321
|
*/
|
|
283
|
-
|
|
322
|
+
const RUNNER_STARTUP_TIMEOUT_MS = 10_000;
|
|
323
|
+
const RUNNER_STARTUP_WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
|
|
324
|
+
const RUNNER_STARTUP_WAIT_VIEW = RUNNER_STARTUP_WAIT_BUFFER ? new Int32Array(RUNNER_STARTUP_WAIT_BUFFER) : undefined;
|
|
325
|
+
|
|
326
|
+
type RunnerStartupState = "ready" | "acknowledged";
|
|
327
|
+
|
|
328
|
+
type RunnerStartupWaitResult =
|
|
329
|
+
| { ok: true; token: string }
|
|
330
|
+
| { ok: false; error: string };
|
|
331
|
+
|
|
332
|
+
function waitForStartupInterval(delayMs = 20): void {
|
|
333
|
+
if (RUNNER_STARTUP_WAIT_VIEW) {
|
|
334
|
+
Atomics.wait(RUNNER_STARTUP_WAIT_VIEW, 0, 0, delayMs);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const waitUntil = Date.now() + delayMs;
|
|
338
|
+
while (Date.now() < waitUntil) {
|
|
339
|
+
// Startup handshakes are synchronous so resume rejects before reporting a run as started.
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function readRunnerStartup(startupPath: string, expectedState: RunnerStartupState, expectedToken?: string): RunnerStartupWaitResult | undefined {
|
|
344
|
+
if (!fs.existsSync(startupPath)) return undefined;
|
|
345
|
+
try {
|
|
346
|
+
const payload = JSON.parse(fs.readFileSync(startupPath, "utf-8")) as { state?: unknown; token?: unknown; error?: unknown };
|
|
347
|
+
if (payload.state === "error" && typeof payload.error === "string") return { ok: false, error: payload.error };
|
|
348
|
+
if (payload.state !== expectedState) return undefined;
|
|
349
|
+
if (typeof payload.token !== "string" || (expectedToken !== undefined && payload.token !== expectedToken)) {
|
|
350
|
+
return { ok: false, error: `Async runner wrote an invalid ${expectedState} startup handshake: ${startupPath}` };
|
|
351
|
+
}
|
|
352
|
+
return { ok: true, token: payload.token };
|
|
353
|
+
} catch (error) {
|
|
354
|
+
return { ok: false, error: `Failed to read async runner startup handshake '${startupPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function waitForRunnerStartup(startupPath: string, expectedState: RunnerStartupState, timeoutMs: number, expectedToken?: string): RunnerStartupWaitResult {
|
|
359
|
+
const deadline = Date.now() + timeoutMs;
|
|
360
|
+
for (;;) {
|
|
361
|
+
const result = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
362
|
+
if (result) return result;
|
|
363
|
+
if (Date.now() >= deadline) break;
|
|
364
|
+
waitForStartupInterval(Math.min(20, Math.max(1, deadline - Date.now())));
|
|
365
|
+
}
|
|
366
|
+
const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
367
|
+
if (finalResult) return finalResult;
|
|
368
|
+
return { ok: false, error: `Timed out after ${timeoutMs}ms waiting for the async runner startup state '${expectedState}'.` };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function writeRunnerStartupControl(filePath: string, payload: { action: "ack" | "proceed"; token: string }): void {
|
|
372
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
373
|
+
try {
|
|
374
|
+
fs.writeFileSync(tempPath, JSON.stringify(payload), { encoding: "utf-8", mode: 0o600 });
|
|
375
|
+
fs.renameSync(tempPath, filePath);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
try {
|
|
378
|
+
fs.rmSync(tempPath, { force: true });
|
|
379
|
+
} catch {
|
|
380
|
+
// Preserve the startup-control write error.
|
|
381
|
+
}
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function runnerIsAlive(pid: number): boolean {
|
|
387
|
+
try {
|
|
388
|
+
process.kill(pid, 0);
|
|
389
|
+
return true;
|
|
390
|
+
} catch (error) {
|
|
391
|
+
return (error as NodeJS.ErrnoException).code === "EPERM";
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function terminateRunnerBeforeProceed(pid: number): void {
|
|
396
|
+
for (const signal of ["SIGTERM", "SIGKILL"] as const) {
|
|
397
|
+
if (!runnerIsAlive(pid)) return;
|
|
398
|
+
try {
|
|
399
|
+
process.kill(pid, signal);
|
|
400
|
+
} catch {
|
|
401
|
+
if (!runnerIsAlive(pid)) return;
|
|
402
|
+
}
|
|
403
|
+
const deadline = Date.now() + 1000;
|
|
404
|
+
while (runnerIsAlive(pid) && Date.now() < deadline) waitForStartupInterval();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function spawnRunner(cfg: object, suffix: string, cwd: string, onProcessTerminal?: (proof: unknown) => void): { pid?: number; error?: string } {
|
|
284
409
|
if (!jitiCliPath) {
|
|
285
410
|
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
286
411
|
}
|
|
@@ -296,11 +421,22 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
296
421
|
|
|
297
422
|
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
298
423
|
const cfgPath = getAsyncConfigPath(suffix);
|
|
299
|
-
|
|
424
|
+
const runnerProcessInstanceId = randomUUID();
|
|
425
|
+
const launchConfig = { ...cfg, runnerProcessInstanceId };
|
|
426
|
+
fs.writeFileSync(cfgPath, JSON.stringify(launchConfig));
|
|
300
427
|
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
301
428
|
const nodeCommand = resolveAsyncRunnerNodeCommand();
|
|
429
|
+
const startupPath = typeof (launchConfig as { revivalLease?: unknown; asyncDir?: unknown }).revivalLease === "object"
|
|
430
|
+
&& typeof (launchConfig as { asyncDir?: unknown }).asyncDir === "string"
|
|
431
|
+
? path.join((launchConfig as { asyncDir: string }).asyncDir, "runner-startup.json")
|
|
432
|
+
: undefined;
|
|
433
|
+
const startupAckPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-ack.json") : undefined;
|
|
434
|
+
const startupProceedPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-proceed.json") : undefined;
|
|
435
|
+
if (startupPath) fs.rmSync(startupPath, { force: true });
|
|
436
|
+
if (startupAckPath) fs.rmSync(startupAckPath, { force: true });
|
|
437
|
+
if (startupProceedPath) fs.rmSync(startupProceedPath, { force: true });
|
|
302
438
|
|
|
303
|
-
const logPaths = resolveAsyncRunnerLogPaths(
|
|
439
|
+
const logPaths = resolveAsyncRunnerLogPaths(launchConfig);
|
|
304
440
|
let stdoutFd: number | undefined;
|
|
305
441
|
let stderrFd: number | undefined;
|
|
306
442
|
try {
|
|
@@ -316,7 +452,7 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
316
452
|
windowsHide: true,
|
|
317
453
|
env: {
|
|
318
454
|
...process.env,
|
|
319
|
-
...(
|
|
455
|
+
...(piPackageRoot ? { [SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),
|
|
320
456
|
},
|
|
321
457
|
});
|
|
322
458
|
closeFd(stdoutFd);
|
|
@@ -324,10 +460,89 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
324
460
|
proc.on("error", (error) => {
|
|
325
461
|
console.error(`[pi-subagents] async spawn failed: ${error.message}`);
|
|
326
462
|
});
|
|
463
|
+
proc.once("close", (exitCode, signal) => {
|
|
464
|
+
const launch = launchConfig as { asyncDir?: unknown; id?: unknown; nestedRoute?: NestedRouteInfo; nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> } };
|
|
465
|
+
const asyncDir = launch.asyncDir;
|
|
466
|
+
const runId = launch.id;
|
|
467
|
+
if (typeof asyncDir !== "string" || typeof runId !== "string") return;
|
|
468
|
+
finalizeProcessTerminal(asyncDir, runId, {
|
|
469
|
+
processInstanceId: runnerProcessInstanceId,
|
|
470
|
+
closeObservedAt: Date.now(),
|
|
471
|
+
exitCode,
|
|
472
|
+
signal,
|
|
473
|
+
});
|
|
474
|
+
const persisted = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId });
|
|
475
|
+
if (!persisted) return;
|
|
476
|
+
if (launch.nestedRoute && launch.nestedSelf) {
|
|
477
|
+
try {
|
|
478
|
+
let status: import("../../shared/types.ts").AsyncStatus;
|
|
479
|
+
try {
|
|
480
|
+
status = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as import("../../shared/types.ts").AsyncStatus;
|
|
481
|
+
status.processTerminal = persisted;
|
|
482
|
+
} catch {
|
|
483
|
+
status = {
|
|
484
|
+
runId,
|
|
485
|
+
mode: "single",
|
|
486
|
+
state: persisted.state === "observed" ? "complete" : "failed",
|
|
487
|
+
startedAt: persisted.observedAt ?? Date.now(),
|
|
488
|
+
lastUpdate: Date.now(),
|
|
489
|
+
processTerminal: persisted,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
writeNestedEvent(launch.nestedRoute, {
|
|
493
|
+
type: "subagent.nested.completed",
|
|
494
|
+
ts: Date.now(),
|
|
495
|
+
parentRunId: launch.nestedSelf.parentRunId,
|
|
496
|
+
parentStepIndex: launch.nestedSelf.parentStepIndex,
|
|
497
|
+
child: nestedSummaryFromAsyncStatus(status, asyncDir, {
|
|
498
|
+
id: runId,
|
|
499
|
+
parentRunId: launch.nestedSelf.parentRunId,
|
|
500
|
+
parentStepIndex: launch.nestedSelf.parentStepIndex,
|
|
501
|
+
depth: launch.nestedSelf.depth,
|
|
502
|
+
path: launch.nestedSelf.path,
|
|
503
|
+
mode: status.mode,
|
|
504
|
+
ts: Date.now(),
|
|
505
|
+
}),
|
|
506
|
+
});
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error("Failed to emit final nested process-terminal status:", error);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
onProcessTerminal?.(persisted);
|
|
512
|
+
});
|
|
327
513
|
if (typeof proc.pid !== "number") {
|
|
328
514
|
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
329
515
|
}
|
|
330
516
|
proc.unref();
|
|
517
|
+
if (startupPath && startupAckPath && startupProceedPath) {
|
|
518
|
+
const ready = waitForRunnerStartup(startupPath, "ready", RUNNER_STARTUP_TIMEOUT_MS);
|
|
519
|
+
if (!ready.ok) {
|
|
520
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
521
|
+
return { error: ready.error };
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
writeRunnerStartupControl(startupAckPath, { action: "ack", token: ready.token });
|
|
525
|
+
} catch (error) {
|
|
526
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
527
|
+
return { error: `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}` };
|
|
528
|
+
}
|
|
529
|
+
const acknowledged = waitForRunnerStartup(startupPath, "acknowledged", RUNNER_STARTUP_TIMEOUT_MS, ready.token);
|
|
530
|
+
if (!acknowledged.ok) {
|
|
531
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
532
|
+
return { error: acknowledged.error };
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: ready.token });
|
|
536
|
+
} catch (error) {
|
|
537
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
538
|
+
return { error: `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}` };
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
fs.rmSync(startupPath, { force: true });
|
|
542
|
+
} catch {
|
|
543
|
+
// Proceed is the commit point; handshake cleanup cannot turn a running revival into a start error.
|
|
544
|
+
}
|
|
545
|
+
}
|
|
331
546
|
return { pid: proc.pid };
|
|
332
547
|
} catch (error) {
|
|
333
548
|
closeFd(stdoutFd);
|
|
@@ -415,19 +630,33 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
415
630
|
...(s.reads !== undefined ? { reads: s.reads } : {}),
|
|
416
631
|
...(s.progress !== undefined ? { progress: s.progress } : {}),
|
|
417
632
|
...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
|
|
418
|
-
...(s.model ? { model: s.model } : {}),
|
|
633
|
+
...(s.model !== undefined ? { model: s.model } : {}),
|
|
419
634
|
};
|
|
420
635
|
};
|
|
421
|
-
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number) => {
|
|
636
|
+
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number, parallelOutputNamespace?: { stepIndex: number; taskIndex?: number }) => {
|
|
422
637
|
const a = agents.find((x) => x.name === s.agent)!;
|
|
423
638
|
const toolBudgetInput = s.toolBudget ?? params.toolBudget ?? a.toolBudget ?? params.configToolBudget;
|
|
424
639
|
const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, s.toolBudget ? "toolBudget" : a.toolBudget ? "agent.toolBudget" : "config.toolBudget");
|
|
425
640
|
if (resolvedToolBudget.error) throw new AsyncStartValidationError(resolvedToolBudget.error);
|
|
426
641
|
const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
|
|
427
642
|
const instructionCwd = behaviorCwd ?? stepCwd;
|
|
428
|
-
|
|
643
|
+
let behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
|
|
644
|
+
const inheritedRelativeParallelOutput = parallelOutputNamespace && s.output === undefined && typeof behavior.output === "string" && !path.isAbsolute(behavior.output);
|
|
645
|
+
if (inheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex !== undefined) {
|
|
646
|
+
behavior = {
|
|
647
|
+
...behavior,
|
|
648
|
+
output: path.join(`parallel-${parallelOutputNamespace.stepIndex}`, `${parallelOutputNamespace.taskIndex}-${s.agent}`, behavior.output),
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
const namespaceOutputPath = Boolean(inheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex === undefined);
|
|
429
652
|
const skillNames = behavior.skills === false ? [] : behavior.skills;
|
|
430
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
653
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
654
|
+
skillNames,
|
|
655
|
+
stepCwd,
|
|
656
|
+
ctx.cwd,
|
|
657
|
+
a.skillPath,
|
|
658
|
+
a.filePath ? path.dirname(a.filePath) : stepCwd,
|
|
659
|
+
);
|
|
431
660
|
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
432
661
|
|
|
433
662
|
let systemPrompt = a.systemPrompt?.trim() ?? "";
|
|
@@ -445,23 +674,34 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
445
674
|
if (behavior.progress) progressInstructionCreated = true;
|
|
446
675
|
const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, progressDir, isFirstProgressAgent);
|
|
447
676
|
const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd, outputBaseDir);
|
|
448
|
-
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
|
|
677
|
+
if (!namespaceOutputPath) systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, a);
|
|
449
678
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
|
|
450
679
|
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
451
680
|
let taskTemplate = s.task ?? "{previous}";
|
|
452
681
|
taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
|
|
453
682
|
taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
|
|
454
|
-
const
|
|
683
|
+
const taskText = `${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`;
|
|
684
|
+
const task = namespaceOutputPath ? taskText : injectSingleOutputInstruction(taskText, outputPath, a);
|
|
455
685
|
|
|
456
|
-
const
|
|
457
|
-
|
|
686
|
+
const primaryModel = resolveEffectiveSubagentModel(
|
|
687
|
+
s.model,
|
|
688
|
+
a.model,
|
|
689
|
+
ctx.currentModel,
|
|
690
|
+
availableModels,
|
|
691
|
+
ctx.currentModelProvider,
|
|
692
|
+
{ scope: ctx.modelScope },
|
|
693
|
+
);
|
|
458
694
|
const thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];
|
|
459
695
|
const effectiveThinking = thinkingOverride ?? a.thinking;
|
|
460
696
|
const model = applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
|
|
697
|
+
const agentContract = s.agentContract ?? params.agentContract;
|
|
461
698
|
return {
|
|
462
699
|
parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
|
|
700
|
+
...(params.capabilityCeiling ? { capabilityCeiling: params.capabilityCeiling } : {}),
|
|
463
701
|
agent: s.agent,
|
|
464
702
|
task,
|
|
703
|
+
...(params.contextForAgent ? { context: params.contextForAgent(s.agent) } : {}),
|
|
704
|
+
...(agentContract ? { agentContract } : {}),
|
|
465
705
|
phase: s.phase,
|
|
466
706
|
label: s.label,
|
|
467
707
|
outputName: s.as,
|
|
@@ -483,17 +723,24 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
483
723
|
inheritSkills: a.inheritSkills,
|
|
484
724
|
skills: resolvedSkills.map((r) => r.name),
|
|
485
725
|
outputPath,
|
|
726
|
+
...(namespaceOutputPath ? { namespaceOutputPath: true } : {}),
|
|
486
727
|
outputMode: behavior.outputMode,
|
|
487
728
|
sessionFile,
|
|
488
729
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
|
|
730
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
489
731
|
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
490
732
|
explicit: s.acceptance,
|
|
491
733
|
agentName: s.agent,
|
|
492
|
-
|
|
734
|
+
acceptanceRole: a.acceptanceRole,
|
|
735
|
+
task,
|
|
493
736
|
mode: resultMode,
|
|
494
737
|
async: true,
|
|
495
738
|
dynamic: false,
|
|
739
|
+
agentContract,
|
|
496
740
|
}),
|
|
741
|
+
acceptanceInput: s.acceptance,
|
|
742
|
+
acceptanceRole: a.acceptanceRole,
|
|
743
|
+
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
497
744
|
...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
|
|
498
745
|
...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
|
|
499
746
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
@@ -536,7 +783,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
536
783
|
}
|
|
537
784
|
}
|
|
538
785
|
const staticStep = nextFlatStep();
|
|
539
|
-
return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index);
|
|
786
|
+
return buildSeqStep({ ...t, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn }, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex });
|
|
540
787
|
}),
|
|
541
788
|
concurrency: s.concurrency,
|
|
542
789
|
failFast: s.failFast,
|
|
@@ -553,9 +800,10 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
553
800
|
}
|
|
554
801
|
const maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;
|
|
555
802
|
const dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());
|
|
803
|
+
const parallel = buildSeqStep({ ...(s.parallel as SequentialStep), agentContract: s.parallel.agentContract ?? s.agentContract, gateOn: s.parallel.gateOn ?? s.gateOn }, undefined, undefined, progressPrecreated, behavior, undefined, { stepIndex });
|
|
556
804
|
return {
|
|
557
805
|
expand: s.expand,
|
|
558
|
-
parallel
|
|
806
|
+
parallel,
|
|
559
807
|
collect: s.collect,
|
|
560
808
|
concurrency: s.concurrency,
|
|
561
809
|
failFast: s.failFast,
|
|
@@ -566,11 +814,17 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
566
814
|
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
567
815
|
explicit: s.acceptance,
|
|
568
816
|
agentName: s.parallel.agent,
|
|
569
|
-
|
|
817
|
+
acceptanceRole: agent.acceptanceRole,
|
|
818
|
+
task: parallel.task,
|
|
570
819
|
mode: resultMode,
|
|
571
820
|
async: true,
|
|
572
821
|
dynamicGroup: true,
|
|
822
|
+
agentContract: s.agentContract ?? params.agentContract,
|
|
573
823
|
}),
|
|
824
|
+
acceptanceInput: s.acceptance,
|
|
825
|
+
acceptanceRole: agent.acceptanceRole,
|
|
826
|
+
...(s.agentContract ?? params.agentContract ? { agentContract: s.agentContract ?? params.agentContract } : {}),
|
|
827
|
+
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
574
828
|
};
|
|
575
829
|
}
|
|
576
830
|
const staticStep = nextFlatStep();
|
|
@@ -592,6 +846,19 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
592
846
|
inheritSkills: false,
|
|
593
847
|
}, ...builtSteps]
|
|
594
848
|
: builtSteps;
|
|
849
|
+
for (const step of steps) {
|
|
850
|
+
if (!("parallel" in step) || !Array.isArray(step.parallel)) continue;
|
|
851
|
+
const seen = new Map<string, { index: number; agent: string }>();
|
|
852
|
+
for (let index = 0; index < step.parallel.length; index++) {
|
|
853
|
+
const task = step.parallel[index]!;
|
|
854
|
+
if (!task.outputPath) continue;
|
|
855
|
+
const previous = seen.get(task.outputPath);
|
|
856
|
+
if (previous) {
|
|
857
|
+
throw new AsyncStartValidationError(`Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${task.outputPath}. Use distinct output paths.`);
|
|
858
|
+
}
|
|
859
|
+
seen.set(task.outputPath, { index, agent: task.agent });
|
|
860
|
+
}
|
|
861
|
+
}
|
|
595
862
|
return { steps, runnerCwd, workflowGraph, eventChain: graphChain, ...(originalTask !== undefined ? { originalTask } : {}) };
|
|
596
863
|
} catch (error) {
|
|
597
864
|
if (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError) return { error: error.message };
|
|
@@ -628,6 +895,7 @@ export function executeAsyncChain(
|
|
|
628
895
|
nestedRoute,
|
|
629
896
|
} = params;
|
|
630
897
|
const resultMode = params.resultMode ?? "chain";
|
|
898
|
+
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
631
899
|
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
632
900
|
const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
633
901
|
const asyncDir = inheritedNestedRoute
|
|
@@ -656,14 +924,18 @@ export function executeAsyncChain(
|
|
|
656
924
|
chainSkills: params.chainSkills,
|
|
657
925
|
sessionFilesByFlatIndex,
|
|
658
926
|
thinkingOverridesByFlatIndex,
|
|
927
|
+
contextForAgent: params.contextForAgent,
|
|
659
928
|
progressDir: params.progressDir ?? (artifactsDir ? path.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path.join(asyncDir, "progress") : undefined),
|
|
929
|
+
agentContract: params.agentContract,
|
|
660
930
|
outputBaseDir: artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined,
|
|
661
931
|
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
662
932
|
maxSubagentDepth,
|
|
933
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
663
934
|
worktreeBaseDir,
|
|
664
935
|
asyncDir,
|
|
665
936
|
toolBudget: params.toolBudget,
|
|
666
937
|
configToolBudget: params.configToolBudget,
|
|
938
|
+
capabilityCeiling,
|
|
667
939
|
});
|
|
668
940
|
if ("error" in built) {
|
|
669
941
|
try {
|
|
@@ -708,7 +980,8 @@ export function executeAsyncChain(
|
|
|
708
980
|
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
709
981
|
asyncDir,
|
|
710
982
|
sessionId: ctx.currentSessionId,
|
|
711
|
-
|
|
983
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
984
|
+
piPackageRoot,
|
|
712
985
|
piArgv1: process.argv[1],
|
|
713
986
|
worktreeSetupHook,
|
|
714
987
|
worktreeSetupHookTimeoutMs,
|
|
@@ -734,6 +1007,7 @@ export function executeAsyncChain(
|
|
|
734
1007
|
},
|
|
735
1008
|
id,
|
|
736
1009
|
runnerCwd,
|
|
1010
|
+
(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),
|
|
737
1011
|
);
|
|
738
1012
|
} catch (error) {
|
|
739
1013
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -751,6 +1025,12 @@ export function executeAsyncChain(
|
|
|
751
1025
|
: isDynamicParallelStep(eventFirstStep)
|
|
752
1026
|
? [eventFirstStep.parallel.agent]
|
|
753
1027
|
: [(eventFirstStep as SequentialStep).agent];
|
|
1028
|
+
const firstTask = isParallelStep(eventFirstStep)
|
|
1029
|
+
? eventFirstStep.parallel[0]?.task
|
|
1030
|
+
: isDynamicParallelStep(eventFirstStep)
|
|
1031
|
+
? eventFirstStep.parallel.task
|
|
1032
|
+
: (eventFirstStep as SequentialStep).task;
|
|
1033
|
+
const workflowGoal = params.goal ?? (params.task?.trim() || firstTask);
|
|
754
1034
|
const parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];
|
|
755
1035
|
const flatAgents: string[] = [];
|
|
756
1036
|
let flatStepStart = 0;
|
|
@@ -799,6 +1079,7 @@ export function executeAsyncChain(
|
|
|
799
1079
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
800
1080
|
startedAt: now,
|
|
801
1081
|
lastUpdate: now,
|
|
1082
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
802
1083
|
},
|
|
803
1084
|
});
|
|
804
1085
|
} catch (error) {
|
|
@@ -813,11 +1094,8 @@ export function executeAsyncChain(
|
|
|
813
1094
|
mode: resultMode,
|
|
814
1095
|
agent: firstAgents[0],
|
|
815
1096
|
agents: flatAgents,
|
|
816
|
-
task:
|
|
817
|
-
|
|
818
|
-
: isDynamicParallelStep(eventFirstStep)
|
|
819
|
-
? eventFirstStep.parallel.task?.slice(0, 50)
|
|
820
|
-
: (eventFirstStep as SequentialStep).task?.slice(0, 50),
|
|
1097
|
+
task: firstTask?.slice(0, 50),
|
|
1098
|
+
goal: workflowGoal?.slice(0, 120),
|
|
821
1099
|
chain: eventChain.map((s) =>
|
|
822
1100
|
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
|
|
823
1101
|
),
|
|
@@ -828,6 +1106,7 @@ export function executeAsyncChain(
|
|
|
828
1106
|
asyncDir,
|
|
829
1107
|
...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
|
|
830
1108
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1109
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
831
1110
|
nestedRoute,
|
|
832
1111
|
});
|
|
833
1112
|
}
|
|
@@ -839,8 +1118,8 @@ export function executeAsyncChain(
|
|
|
839
1118
|
.join(" -> ");
|
|
840
1119
|
|
|
841
1120
|
return {
|
|
842
|
-
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]
|
|
843
|
-
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1121
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`, ctx.interactive === true) }],
|
|
1122
|
+
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(capabilityCeiling ? { capabilityCeiling } : {}), ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
844
1123
|
};
|
|
845
1124
|
}
|
|
846
1125
|
|
|
@@ -872,10 +1151,17 @@ export function executeAsyncSingle(
|
|
|
872
1151
|
nestedRoute,
|
|
873
1152
|
} = params;
|
|
874
1153
|
const task = params.task ?? "";
|
|
1154
|
+
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
875
1155
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
876
1156
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
877
1157
|
const availableModels = params.availableModels;
|
|
878
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
1158
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
1159
|
+
skillNames,
|
|
1160
|
+
runnerCwd,
|
|
1161
|
+
ctx.cwd,
|
|
1162
|
+
agentConfig.skillPath,
|
|
1163
|
+
agentConfig.filePath ? path.dirname(agentConfig.filePath) : runnerCwd,
|
|
1164
|
+
);
|
|
879
1165
|
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
880
1166
|
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
881
1167
|
if (resolvedSkills.length > 0) {
|
|
@@ -905,11 +1191,11 @@ export function executeAsyncSingle(
|
|
|
905
1191
|
|
|
906
1192
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
907
1193
|
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
|
|
908
|
-
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
|
|
1194
|
+
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, agentConfig);
|
|
909
1195
|
const outputMode = params.outputMode ?? "inline";
|
|
910
1196
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
911
1197
|
if (validationError) return formatAsyncStartError("single", validationError);
|
|
912
|
-
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
|
|
1198
|
+
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
913
1199
|
const primaryModel = resolveSubagentModelOverride(
|
|
914
1200
|
params.modelOverride ?? agentConfig.model,
|
|
915
1201
|
ctx.currentModel,
|
|
@@ -921,8 +1207,103 @@ export function executeAsyncSingle(
|
|
|
921
1207
|
const toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;
|
|
922
1208
|
const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, params.toolBudget ? "toolBudget" : agentConfig.toolBudget ? "agent.toolBudget" : "config.toolBudget");
|
|
923
1209
|
if (resolvedToolBudget.error) return formatAsyncStartError("single", resolvedToolBudget.error);
|
|
924
|
-
const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
|
|
1210
|
+
const deadlineAt = params.absoluteDeadlineAt ?? (params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined);
|
|
1211
|
+
const timeoutMs = params.absoluteDeadlineAt !== undefined && deadlineAt !== undefined
|
|
1212
|
+
? deadlineAt - Date.now()
|
|
1213
|
+
: params.timeoutMs;
|
|
1214
|
+
if (timeoutMs !== undefined && timeoutMs <= 0) return formatAsyncStartError("single", "The source run's absolute deadline expired before recovery could launch.");
|
|
925
1215
|
const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
|
|
1216
|
+
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined);
|
|
1217
|
+
const structuredOutput = params.structuredOutputSchema
|
|
1218
|
+
? createStructuredOutputRuntime(params.structuredOutputSchema, path.join(asyncDir, "structured-output"))
|
|
1219
|
+
: undefined;
|
|
1220
|
+
const modelCandidates = buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) =>
|
|
1221
|
+
applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined),
|
|
1222
|
+
);
|
|
1223
|
+
const effectiveSystemPrompt = appendTurnBudgetSystemPrompt(systemPrompt, params.turnBudget);
|
|
1224
|
+
const toolPlan = resolvePiLaunchToolPlan({
|
|
1225
|
+
tools: agentConfig.tools,
|
|
1226
|
+
extensions: agentConfig.extensions,
|
|
1227
|
+
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
1228
|
+
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
1229
|
+
cwd: runnerCwd,
|
|
1230
|
+
requireReadTool: Boolean(resolvedSkills.length),
|
|
1231
|
+
structuredOutput: Boolean(params.structuredOutputSchema),
|
|
1232
|
+
capabilityCeiling,
|
|
1233
|
+
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
1234
|
+
});
|
|
1235
|
+
const launchContractDigest = launchBindingDigest({
|
|
1236
|
+
definitionDigest: agentDefinitionDigest(agentConfig),
|
|
1237
|
+
task,
|
|
1238
|
+
...(model ? { model } : {}),
|
|
1239
|
+
modelCandidates,
|
|
1240
|
+
...(resolveEffectiveThinking(model, effectiveThinking) ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1241
|
+
systemPrompt: effectiveSystemPrompt,
|
|
1242
|
+
systemPromptMode: agentConfig.systemPromptMode,
|
|
1243
|
+
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
1244
|
+
inheritSkills: agentConfig.inheritSkills,
|
|
1245
|
+
skills: resolvedSkills.map((skill) => skill.name),
|
|
1246
|
+
tools: toolPlan.effectiveToolAllowlist,
|
|
1247
|
+
extensions: toolPlan.extensionArgs,
|
|
1248
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1249
|
+
...(outputPath ? { outputPath } : {}),
|
|
1250
|
+
outputMode,
|
|
1251
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1252
|
+
});
|
|
1253
|
+
const resolvedAcceptance = resolveEffectiveAcceptance({
|
|
1254
|
+
explicit: params.acceptance,
|
|
1255
|
+
agentName: agent,
|
|
1256
|
+
acceptanceRole: agentConfig.acceptanceRole,
|
|
1257
|
+
task,
|
|
1258
|
+
mode: "single",
|
|
1259
|
+
async: true,
|
|
1260
|
+
agentContract: params.agentContract,
|
|
1261
|
+
});
|
|
1262
|
+
const recoveryDescriptor: SteeringRecoveryDescriptor = {
|
|
1263
|
+
version: 1,
|
|
1264
|
+
launchContractDigest,
|
|
1265
|
+
sourceRunId: id,
|
|
1266
|
+
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
1267
|
+
agent,
|
|
1268
|
+
...(sessionFile ? { sessionFile } : {}),
|
|
1269
|
+
cwd: runnerCwd,
|
|
1270
|
+
...(model ? { model } : {}),
|
|
1271
|
+
...(agentConfig.fallbackModels ? { fallbackModels: [...agentConfig.fallbackModels] } : {}),
|
|
1272
|
+
...(effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1273
|
+
...(agentConfig.tools ? { tools: [...agentConfig.tools] } : {}),
|
|
1274
|
+
...(agentConfig.extensions ? { extensions: [...agentConfig.extensions] } : {}),
|
|
1275
|
+
...(agentConfig.subagentOnlyExtensions ? { subagentOnlyExtensions: [...agentConfig.subagentOnlyExtensions] } : {}),
|
|
1276
|
+
...(agentConfig.mcpDirectTools ? { mcpDirectTools: [...agentConfig.mcpDirectTools] } : {}),
|
|
1277
|
+
...(agentConfig.systemPrompt ? { systemPrompt: agentConfig.systemPrompt } : {}),
|
|
1278
|
+
systemPromptMode: agentConfig.systemPromptMode,
|
|
1279
|
+
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
1280
|
+
inheritSkills: agentConfig.inheritSkills,
|
|
1281
|
+
...(resolvedSkills.length ? { skills: resolvedSkills.map((skill) => skill.name) } : {}),
|
|
1282
|
+
...(agentConfig.skillPath ? { skillPath: [...agentConfig.skillPath] } : {}),
|
|
1283
|
+
...(agentConfig.filePath ? { agentFilePath: agentConfig.filePath } : {}),
|
|
1284
|
+
...(agentConfig.completionGuard !== undefined ? { completionGuard: agentConfig.completionGuard } : {}),
|
|
1285
|
+
...(agentConfig.memory ? { memory: { ...agentConfig.memory } } : {}),
|
|
1286
|
+
...(outputPath ? { outputPath } : {}),
|
|
1287
|
+
outputMode,
|
|
1288
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1289
|
+
...(params.acceptance !== undefined ? { acceptance: params.acceptance } : {}),
|
|
1290
|
+
...(controlConfig ? { controlConfig } : {}),
|
|
1291
|
+
...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),
|
|
1292
|
+
...(params.turnBudget ? { initialTurnBudget: params.turnBudget } : {}),
|
|
1293
|
+
...(resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {}),
|
|
1294
|
+
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
1295
|
+
...(maxOutput ? { maxOutput } : {}),
|
|
1296
|
+
share: shareEnabled,
|
|
1297
|
+
...(resolvedSessionDir ? { sessionDir: resolvedSessionDir } : {}),
|
|
1298
|
+
...(artifactsDir ? { artifactsDir } : {}),
|
|
1299
|
+
artifactConfig,
|
|
1300
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1301
|
+
};
|
|
1302
|
+
try {
|
|
1303
|
+
writePrivateAtomicJson(path.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
|
|
1306
|
+
}
|
|
926
1307
|
let spawnResult: { pid?: number; error?: string } = {};
|
|
927
1308
|
try {
|
|
928
1309
|
spawnResult = spawnRunner(
|
|
@@ -931,14 +1312,14 @@ export function executeAsyncSingle(
|
|
|
931
1312
|
steps: [
|
|
932
1313
|
{
|
|
933
1314
|
parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
|
|
1315
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
934
1316
|
agent,
|
|
935
1317
|
task: taskWithOutputInstruction,
|
|
1318
|
+
...(params.context ? { context: params.context } : {}),
|
|
936
1319
|
cwd: runnerCwd,
|
|
937
1320
|
model,
|
|
938
1321
|
thinking: resolveEffectiveThinking(model, effectiveThinking),
|
|
939
|
-
modelCandidates
|
|
940
|
-
applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined),
|
|
941
|
-
),
|
|
1322
|
+
modelCandidates,
|
|
942
1323
|
tools: agentConfig.tools,
|
|
943
1324
|
extensions: agentConfig.extensions,
|
|
944
1325
|
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
@@ -953,13 +1334,14 @@ export function executeAsyncSingle(
|
|
|
953
1334
|
outputMode,
|
|
954
1335
|
sessionFile,
|
|
955
1336
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}),
|
|
1337
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
1338
|
+
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
1339
|
+
definitionDigest: agentDefinitionDigest(agentConfig),
|
|
1340
|
+
launchBindingTask: task,
|
|
1341
|
+
launchContractDigest,
|
|
1342
|
+
effectiveAcceptance: resolvedAcceptance,
|
|
1343
|
+
...(structuredOutput ? { structuredOutput } : {}),
|
|
1344
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
963
1345
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
964
1346
|
},
|
|
965
1347
|
],
|
|
@@ -970,22 +1352,25 @@ export function executeAsyncSingle(
|
|
|
970
1352
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
971
1353
|
artifactConfig,
|
|
972
1354
|
share: shareEnabled,
|
|
973
|
-
sessionDir:
|
|
1355
|
+
sessionDir: resolvedSessionDir,
|
|
974
1356
|
asyncDir,
|
|
975
1357
|
sessionId: ctx.currentSessionId,
|
|
976
|
-
|
|
1358
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1359
|
+
piPackageRoot,
|
|
977
1360
|
piArgv1: process.argv[1],
|
|
978
1361
|
worktreeSetupHook,
|
|
979
1362
|
worktreeSetupHookTimeoutMs,
|
|
980
1363
|
worktreeBaseDir,
|
|
981
1364
|
controlConfig,
|
|
982
|
-
timeoutMs
|
|
1365
|
+
timeoutMs,
|
|
983
1366
|
deadlineAt,
|
|
984
1367
|
turnBudget: params.turnBudget,
|
|
985
1368
|
toolBudget: params.toolBudget,
|
|
986
1369
|
controlIntercomTarget,
|
|
987
1370
|
childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
|
|
988
1371
|
resultMode: "single",
|
|
1372
|
+
launchContractDigest,
|
|
1373
|
+
...(params.revivalLease ? { revivalLease: params.revivalLease } : {}),
|
|
989
1374
|
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
990
1375
|
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
991
1376
|
parentRunId: nestedAddress.parentRunId,
|
|
@@ -996,6 +1381,7 @@ export function executeAsyncSingle(
|
|
|
996
1381
|
},
|
|
997
1382
|
id,
|
|
998
1383
|
runnerCwd,
|
|
1384
|
+
(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),
|
|
999
1385
|
);
|
|
1000
1386
|
} catch (error) {
|
|
1001
1387
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1032,10 +1418,11 @@ export function executeAsyncSingle(
|
|
|
1032
1418
|
agent,
|
|
1033
1419
|
agents: [agent],
|
|
1034
1420
|
chainStepCount: 1,
|
|
1035
|
-
...(
|
|
1421
|
+
...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),
|
|
1036
1422
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1037
1423
|
startedAt: now,
|
|
1038
1424
|
lastUpdate: now,
|
|
1425
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1039
1426
|
},
|
|
1040
1427
|
});
|
|
1041
1428
|
} catch (error) {
|
|
@@ -1050,16 +1437,19 @@ export function executeAsyncSingle(
|
|
|
1050
1437
|
mode: "single",
|
|
1051
1438
|
agent,
|
|
1052
1439
|
task: task?.slice(0, 50),
|
|
1440
|
+
goal: (params.goal ?? task).slice(0, 120),
|
|
1053
1441
|
cwd: runnerCwd,
|
|
1054
1442
|
asyncDir,
|
|
1055
|
-
|
|
1443
|
+
launchContractDigest,
|
|
1444
|
+
...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),
|
|
1056
1445
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1446
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1057
1447
|
nestedRoute,
|
|
1058
1448
|
});
|
|
1059
1449
|
}
|
|
1060
1450
|
|
|
1061
1451
|
return {
|
|
1062
|
-
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]
|
|
1063
|
-
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...(params.timeoutMs !== undefined ? { timeoutMs
|
|
1452
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],
|
|
1453
|
+
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, launchContractDigest, ...(capabilityCeiling ? { capabilityCeiling } : {}), ...(params.context ? { context: params.context } : {}), ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1064
1454
|
};
|
|
1065
1455
|
}
|