pi-subagents 0.66.0 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +43 -15
- package/docs/configuration.md +67 -23
- package/docs/extension-api.md +38 -19
- package/docs/missions.md +10 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +22 -12
- package/docs/standalone-background.md +59 -0
- package/docs/tool-reference.md +33 -20
- package/docs/watchdog.md +39 -10
- package/docs/workflows.md +37 -13
- package/index.ts +5 -2
- package/inspector-runner.mjs +2 -2
- package/package.json +4 -2
- package/prompts/parallel-review.md +1 -1
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +32 -0
- package/skills/pi-subagents/SKILL.md +32 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +11 -9
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +190 -71
- package/src/agents/builtin-names.ts +1 -0
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +94 -59
- package/src/api/required-child-extensions.ts +6 -0
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +66 -4
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +62 -39
- package/src/extension/public-execution.ts +7 -5
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +81 -81
- package/src/extension/tool-description.ts +30 -82
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +44 -31
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +348 -176
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +17 -12
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +23 -10
- package/src/runs/background/binary-bootstrap.ts +38 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +105 -7
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +81 -33
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +12 -33
- package/src/runs/background/runner-child-launch.ts +6 -1
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +51 -18
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +441 -304
- package/src/runs/background/subagent-wait.ts +176 -28
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +134 -248
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +374 -178
- package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
- package/src/runs/shared/acceptance.ts +38 -11
- package/src/runs/shared/async-status-projection.ts +127 -33
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +28 -5
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +8 -1
- package/src/runs/shared/child-session.ts +127 -52
- package/src/runs/shared/child-tool-plan.ts +142 -11
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +45 -15
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +68 -8
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +74 -30
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -7
- package/src/slash/subagents-admin.ts +24 -13
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +168 -37
- package/src/watchdog/child-status.ts +28 -28
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +21 -1
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +10 -2
- package/src/watchdog/register-main.ts +39 -35
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +123 -74
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +100 -27
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +3 -0
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +5 -3
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +279 -10
- package/src/workflows/workflow-checklist.ts +2 -2
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/runner-server-preload.mjs +0 -13
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
|
@@ -2,33 +2,34 @@
|
|
|
2
2
|
* Async execution logic for subagent tool
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
5
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import
|
|
10
|
+
import * as nodeModule from "node:module";
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { discoverAgents, formatUnknownAgentError, unknownAgentDiagnosticContext, type AgentConfig, type UnknownAgentDiagnosticContext } from "../../agents/agents.ts";
|
|
13
|
-
import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.ts";
|
|
14
13
|
import { createAtomicJsonWriter, writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
14
|
+
import { childCacheRetentionEnv } from "../../shared/child-cache-retention.ts";
|
|
15
|
+
import { buildEffectiveSystemPrompt } from "../shared/effective-system-prompt.ts";
|
|
15
16
|
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
16
|
-
import { planChildLaunch, resolveStepBehavior, suppressProgressForReadOnlyTask, type ResolvedStepBehavior } from "../shared/child-launch-plan.ts";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { planChildLaunch, projectChainOutputSchemas, resolveStepBehavior, suppressProgressForReadOnlyTask, type ResolvedStepBehavior } from "../shared/child-launch-plan.ts";
|
|
18
|
+
import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.ts";
|
|
19
|
+
import { applyThinkingSuffix, getHostBuiltinToolNames, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/child-tool-plan.ts";
|
|
20
|
+
import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
19
21
|
import { applyWatchdogLaunchRules, sendRuleViolationWarning } from "../../watchdog/rules.ts";
|
|
20
22
|
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveExistingReadInstructionPaths, resolveExistingReadPaths, writeInitialProgressFile, type ChainStep, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
21
23
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
22
24
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
23
|
-
import { PI_CODING_AGENT_PACKAGE, resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
25
|
+
import { PI_CODING_AGENT_PACKAGE, resolveBunPiExecutable, resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
24
26
|
import { JITI_ALIAS_ENV, resolveHostPeerAliases } from "./runner-aliases.ts";
|
|
25
27
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
26
28
|
import { resolveNodeExecutable } from "../../shared/node-executable.ts";
|
|
27
29
|
import { backgroundProcessOptions } from "../shared/background-process-options.ts";
|
|
28
|
-
import {
|
|
29
|
-
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
30
|
+
import { normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
30
31
|
import { PI_CODING_AGENT_PACKAGE_ROOT_ENV, PROMPT_REDACTED, resolveChildCwd } from "../../shared/utils.ts";
|
|
31
|
-
import {
|
|
32
|
+
import { resolveEffectiveSubagentModel, resolveModelOrigin, resolveModelSelection, resolveSubagentModelOverride, type AvailableModelInfo, type ModelOrigin, type ParentModel } from "../shared/model-resolution.ts";
|
|
32
33
|
import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
33
34
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
34
35
|
import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
type ArtifactConfig,
|
|
49
50
|
type Details,
|
|
50
51
|
type IntercomBridgeConfig,
|
|
52
|
+
type HerdrMachineReference,
|
|
51
53
|
type JsonSchemaObject,
|
|
52
54
|
type MaxOutputConfig,
|
|
53
55
|
type NestedRouteInfo,
|
|
@@ -71,6 +73,7 @@ import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
|
|
|
71
73
|
import { childSessionFactoryModule } from "../shared/child-session.ts";
|
|
72
74
|
import { inheritedChildRuntime } from "../shared/child-launch.ts";
|
|
73
75
|
import { resultFilePath } from "./result-files.ts";
|
|
76
|
+
import { updateActiveRunIndex } from "./active-run-index.ts";
|
|
74
77
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
75
78
|
import { usageBudgetState } from "../shared/usage-budget.ts";
|
|
76
79
|
import type { ImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
@@ -80,13 +83,29 @@ import type { ActiveAsyncCapacityHandle } from "./active-async-capacity.ts";
|
|
|
80
83
|
import { statusStepDescription } from "./chain-append.ts";
|
|
81
84
|
import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.ts";
|
|
82
85
|
import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
83
|
-
import {
|
|
86
|
+
import { resolveLaunchBinding } from "../../shared/launch-contract.ts";
|
|
84
87
|
import { resolvePermissionRules, type PermissionConfig } from "../shared/permissions.ts";
|
|
85
88
|
import { normalizeExtensionBindings, omitExtensionBindingsEnv, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
86
89
|
import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.ts";
|
|
90
|
+
import { resolveRequiredChildExtensions, type RequiredChildExtensionSnapshot } from "../../shared/required-child-extensions.ts";
|
|
87
91
|
|
|
88
|
-
const require = createRequire(import.meta.url);
|
|
89
|
-
const piPackageRoot =
|
|
92
|
+
const require = nodeModule.createRequire(import.meta.url);
|
|
93
|
+
const piPackageRoot = resolveAsyncPiPackageRoot();
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The detached runner resolves the same host package the foreground
|
|
97
|
+
* `resolvePiCliScript` path resolves, so an explicit
|
|
98
|
+
* `PI_SUBAGENTS_PI_CODING_AGENT_PACKAGE_ROOT` override must be honored here
|
|
99
|
+
* too. Precedence mirrors the foreground resolver: argv-based discovery wins
|
|
100
|
+
* first (the foreground returns the argv script before any candidate), the
|
|
101
|
+
* environment override is consulted when that discovery cannot identify the
|
|
102
|
+
* host (wrapper installs, non-standard layouts), and the
|
|
103
|
+
* package-manager entry is last. Without the override, such hosts fail
|
|
104
|
+
* closed with "neither is available" while foreground children launch fine.
|
|
105
|
+
*/
|
|
106
|
+
function resolveAsyncPiPackageRoot(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
107
|
+
return resolvePiPackageRoot() || env[PI_CODING_AGENT_PACKAGE_ROOT_ENV]?.trim() || resolveInstalledPiPackageRoot();
|
|
108
|
+
}
|
|
90
109
|
|
|
91
110
|
function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {
|
|
92
111
|
if (!fs.existsSync(packageJsonPath)) return undefined;
|
|
@@ -110,12 +129,12 @@ function resolveJitiCliPath(): string | undefined {
|
|
|
110
129
|
const candidates: Array<() => string | undefined> = [
|
|
111
130
|
() => require.resolve("jiti/package.json"),
|
|
112
131
|
() => piPackageRoot
|
|
113
|
-
? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
|
|
132
|
+
? nodeModule.createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
|
|
114
133
|
: undefined,
|
|
115
134
|
() => {
|
|
116
135
|
if (!process.argv[1]) return undefined;
|
|
117
136
|
const piEntry = fs.realpathSync(process.argv[1]);
|
|
118
|
-
return createRequire(piEntry).resolve("jiti/package.json");
|
|
137
|
+
return nodeModule.createRequire(piEntry).resolve("jiti/package.json");
|
|
119
138
|
},
|
|
120
139
|
() => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
|
|
121
140
|
];
|
|
@@ -133,6 +152,17 @@ function resolveJitiCliPath(): string | undefined {
|
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
const jitiCliPath = resolveJitiCliPath();
|
|
155
|
+
const asyncRunnerSourcePath = path.join(
|
|
156
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
157
|
+
`subagent-runner${path.extname(fileURLToPath(import.meta.url))}`,
|
|
158
|
+
);
|
|
159
|
+
const sourceUnderNodeModules = asyncRunnerSourcePath.split(path.sep).some((segment) => segment.toLowerCase() === "node_modules");
|
|
160
|
+
function supportsNativeRunner(nodeExecutable: string): boolean {
|
|
161
|
+
return Boolean(process.features.typescript)
|
|
162
|
+
&& typeof nodeModule.registerHooks === "function"
|
|
163
|
+
&& nodeExecutable === process.execPath
|
|
164
|
+
&& !sourceUnderNodeModules;
|
|
165
|
+
}
|
|
136
166
|
|
|
137
167
|
interface AsyncExecutionContext {
|
|
138
168
|
pi: ExtensionAPI;
|
|
@@ -169,6 +199,9 @@ interface AsyncChainParams {
|
|
|
169
199
|
availableModels?: AvailableModelInfo[];
|
|
170
200
|
cwd?: string;
|
|
171
201
|
maxOutput?: MaxOutputConfig;
|
|
202
|
+
machine?: string;
|
|
203
|
+
/** Launch cwd as typed when a machine is set: a path on that machine, never resolved locally. */
|
|
204
|
+
machineCwd?: string;
|
|
172
205
|
artifactsDir?: string;
|
|
173
206
|
artifactConfig: ArtifactConfig;
|
|
174
207
|
shareEnabled: boolean;
|
|
@@ -224,9 +257,13 @@ interface AsyncSingleParams {
|
|
|
224
257
|
agentConfig: AgentConfig;
|
|
225
258
|
/** Agent contract before per-run bridge injection, used only for recovery persistence. */
|
|
226
259
|
recoveryAgentConfig?: AgentConfig;
|
|
260
|
+
requiredExtensions?: RequiredChildExtensionSnapshot;
|
|
227
261
|
ctx: AsyncExecutionContext;
|
|
228
262
|
cwd?: string;
|
|
229
263
|
requestedCwd?: string;
|
|
264
|
+
machine?: string;
|
|
265
|
+
/** Launch cwd as typed when a machine is set: a path on that machine, never resolved locally. */
|
|
266
|
+
machineCwd?: string;
|
|
230
267
|
maxOutput?: MaxOutputConfig;
|
|
231
268
|
artifactsDir?: string;
|
|
232
269
|
artifactConfig: ArtifactConfig;
|
|
@@ -270,6 +307,8 @@ interface AsyncSingleParams {
|
|
|
270
307
|
absoluteDeadlineAt?: number;
|
|
271
308
|
/** Optional per-call hard toolTimeoutMs override (highest precedence). */
|
|
272
309
|
toolTimeoutMs?: number;
|
|
310
|
+
/** Steer the child to checkpoint and stop this many ms before the run deadline (resolved call param ?? config). */
|
|
311
|
+
checkpointBeforeDeadlineMs?: number;
|
|
273
312
|
toolBudget?: ResolvedToolBudget | ToolBudgetConfig;
|
|
274
313
|
usageBudget?: UsageBudgetConfig;
|
|
275
314
|
configToolBudget?: ResolvedToolBudget;
|
|
@@ -313,6 +352,8 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
313
352
|
ctx: AsyncExecutionContext;
|
|
314
353
|
availableModels?: AvailableModelInfo[];
|
|
315
354
|
cwd?: string;
|
|
355
|
+
machine?: string;
|
|
356
|
+
machineCwd?: string;
|
|
316
357
|
chainSkills?: string[];
|
|
317
358
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
318
359
|
thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
|
|
@@ -369,11 +410,9 @@ export function formatAsyncStartedMessage(headline: string, interactive: boolean
|
|
|
369
410
|
return [headline, "", ...guidance].join("\n");
|
|
370
411
|
}
|
|
371
412
|
|
|
372
|
-
/**
|
|
373
|
-
* Check if jiti is available for async execution
|
|
374
|
-
*/
|
|
413
|
+
/** Check whether detached async execution has a supported runtime. */
|
|
375
414
|
export function isAsyncAvailable(): boolean {
|
|
376
|
-
return jitiCliPath !== undefined;
|
|
415
|
+
return resolveBunPiExecutable() !== undefined || supportsNativeRunner(resolveNodeExecutable()) || jitiCliPath !== undefined;
|
|
377
416
|
}
|
|
378
417
|
|
|
379
418
|
export function resolveAsyncRunnerLogPaths(cfg: object): { stdoutPath: string; stderrPath: string } | undefined {
|
|
@@ -400,24 +439,14 @@ function closeFd(fd: number | undefined): void {
|
|
|
400
439
|
* Spawn the async runner process
|
|
401
440
|
*/
|
|
402
441
|
const RUNNER_STARTUP_TIMEOUT_MS = 10_000;
|
|
403
|
-
|
|
404
|
-
const RUNNER_STARTUP_WAIT_VIEW = RUNNER_STARTUP_WAIT_BUFFER ? new Int32Array(RUNNER_STARTUP_WAIT_BUFFER) : undefined;
|
|
405
|
-
|
|
406
|
-
type RunnerStartupState = "ready" | "acknowledged";
|
|
442
|
+
type RunnerStartupState = "ready" | "acknowledged" | "confirmed";
|
|
407
443
|
|
|
408
444
|
type RunnerStartupWaitResult =
|
|
409
445
|
| { ok: true; token: string }
|
|
410
446
|
| { ok: false; error: string; startupDidNotProceed?: boolean };
|
|
411
447
|
|
|
412
|
-
function waitForStartupInterval(delayMs = 20): void {
|
|
413
|
-
|
|
414
|
-
Atomics.wait(RUNNER_STARTUP_WAIT_VIEW, 0, 0, delayMs);
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
const waitUntil = Date.now() + delayMs;
|
|
418
|
-
while (Date.now() < waitUntil) {
|
|
419
|
-
// Startup handshakes are synchronous so resume rejects before reporting a run as started.
|
|
420
|
-
}
|
|
448
|
+
function waitForStartupInterval(delayMs = 20): Promise<void> {
|
|
449
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
421
450
|
}
|
|
422
451
|
|
|
423
452
|
function readRunnerStartup(startupPath: string, expectedState: RunnerStartupState, expectedToken?: string): RunnerStartupWaitResult | undefined {
|
|
@@ -435,13 +464,32 @@ function readRunnerStartup(startupPath: string, expectedState: RunnerStartupStat
|
|
|
435
464
|
}
|
|
436
465
|
}
|
|
437
466
|
|
|
438
|
-
function waitForRunnerStartup(startupPath: string, expectedState: RunnerStartupState, timeoutMs: number, expectedToken?: string): RunnerStartupWaitResult {
|
|
467
|
+
async function waitForRunnerStartup(startupPath: string, expectedState: RunnerStartupState, timeoutMs: number, expectedToken?: string, processClosed?: Promise<{ exitCode: number | null; signal: NodeJS.Signals | null }>): Promise<RunnerStartupWaitResult> {
|
|
468
|
+
const confirmOpen = async (result: RunnerStartupWaitResult): Promise<RunnerStartupWaitResult> => {
|
|
469
|
+
if (!processClosed || result.ok === false) return result;
|
|
470
|
+
const outcome = await Promise.race([processClosed.then((exit) => ({ exit })), new Promise<undefined>((resolve) => setImmediate(() => resolve(undefined)))]);
|
|
471
|
+
if (!outcome) return result;
|
|
472
|
+
const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
473
|
+
if (finalResult?.ok === false) return finalResult;
|
|
474
|
+
const { exitCode, signal } = outcome.exit;
|
|
475
|
+
return { ok: false, error: `Async runner exited before startup state '${expectedState}' (exit code ${exitCode ?? "unknown"}, signal ${signal ?? "none"}).`, startupDidNotProceed: true };
|
|
476
|
+
};
|
|
439
477
|
const deadline = Date.now() + timeoutMs;
|
|
440
|
-
|
|
478
|
+
while (Date.now() <= deadline) {
|
|
441
479
|
const result = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
442
|
-
if (result) return result;
|
|
443
|
-
|
|
444
|
-
|
|
480
|
+
if (result) return await confirmOpen(result);
|
|
481
|
+
const delay = waitForStartupInterval(Math.min(20, Math.max(1, deadline - Date.now())));
|
|
482
|
+
if (processClosed) {
|
|
483
|
+
const outcome = await Promise.race([delay.then(() => undefined), processClosed.then((exit) => ({ exit }))]);
|
|
484
|
+
if (outcome) {
|
|
485
|
+
const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
486
|
+
if (finalResult?.ok === false) return finalResult;
|
|
487
|
+
const { exitCode, signal } = outcome.exit;
|
|
488
|
+
return { ok: false, error: `Async runner exited before startup state '${expectedState}' (exit code ${exitCode ?? "unknown"}, signal ${signal ?? "none"}).`, startupDidNotProceed: true };
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
await delay;
|
|
492
|
+
}
|
|
445
493
|
}
|
|
446
494
|
const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
447
495
|
if (finalResult) return finalResult;
|
|
@@ -450,7 +498,7 @@ function waitForRunnerStartup(startupPath: string, expectedState: RunnerStartupS
|
|
|
450
498
|
|
|
451
499
|
const writePrivateStartupControlJson = createAtomicJsonWriter({ mode: 0o600, ignoreCleanupErrorAfterSuccess: true });
|
|
452
500
|
|
|
453
|
-
function writeRunnerStartupControl(filePath: string, payload: { action: "ack" | "proceed"; token: string }): void {
|
|
501
|
+
function writeRunnerStartupControl(filePath: string, payload: { action: "ack" | "confirm" | "proceed"; token: string }): void {
|
|
454
502
|
// Delegate to the shared atomic JSON writer (temp file + rename, retrying
|
|
455
503
|
// transient Windows EPERM/EBUSY/EACCES locks and cleaning up the temp file
|
|
456
504
|
// on failure), so the startup handshake gets the same locking resilience as
|
|
@@ -468,6 +516,13 @@ function runnerIsAlive(pid: number): boolean {
|
|
|
468
516
|
}
|
|
469
517
|
}
|
|
470
518
|
|
|
519
|
+
function waitForStartupIntervalSync(delayMs = 20): void {
|
|
520
|
+
const waitUntil = Date.now() + delayMs;
|
|
521
|
+
while (Date.now() < waitUntil) {
|
|
522
|
+
// Pre-handshake failures happen before the runner can be observed asynchronously.
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
471
526
|
function terminateRunnerBeforeProceed(pid: number): boolean {
|
|
472
527
|
for (const signal of ["SIGTERM", "SIGKILL"] as const) {
|
|
473
528
|
if (!runnerIsAlive(pid)) return true;
|
|
@@ -477,11 +532,88 @@ function terminateRunnerBeforeProceed(pid: number): boolean {
|
|
|
477
532
|
if (!runnerIsAlive(pid)) return true;
|
|
478
533
|
}
|
|
479
534
|
const deadline = Date.now() + 1000;
|
|
480
|
-
while (runnerIsAlive(pid) && Date.now() < deadline)
|
|
535
|
+
while (runnerIsAlive(pid) && Date.now() < deadline) waitForStartupIntervalSync();
|
|
481
536
|
}
|
|
482
537
|
return !runnerIsAlive(pid);
|
|
483
538
|
}
|
|
484
539
|
|
|
540
|
+
async function terminateRunnerBeforeProceedAsync(proc: ChildProcess, processClosed: Promise<{ exitCode: number | null; signal: NodeJS.Signals | null }>): Promise<boolean> {
|
|
541
|
+
for (const signal of ["SIGTERM", "SIGKILL"] as const) {
|
|
542
|
+
if (proc.pid === undefined || !runnerIsAlive(proc.pid)) return true;
|
|
543
|
+
try { process.kill(proc.pid, signal); } catch {
|
|
544
|
+
if (proc.pid === undefined || !runnerIsAlive(proc.pid)) return true;
|
|
545
|
+
}
|
|
546
|
+
const exited = await Promise.race([processClosed.then(() => true), waitForStartupInterval(1000).then(() => false)]);
|
|
547
|
+
if (exited || proc.pid === undefined || !runnerIsAlive(proc.pid)) return true;
|
|
548
|
+
}
|
|
549
|
+
return proc.pid === undefined || !runnerIsAlive(proc.pid);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async function completeRunnerStartupHandshake(
|
|
553
|
+
startupPath: string,
|
|
554
|
+
startupAckPath: string,
|
|
555
|
+
startupProceedPath: string,
|
|
556
|
+
proc: ChildProcess,
|
|
557
|
+
processClosed: Promise<{ exitCode: number | null; signal: NodeJS.Signals | null }>,
|
|
558
|
+
getObservedProcessExit: () => { exitCode: number | null; signal: NodeJS.Signals | null } | undefined,
|
|
559
|
+
runnerProcessInstanceId: string,
|
|
560
|
+
persistStartupFailure: (message: string) => void,
|
|
561
|
+
): Promise<SpawnRunnerResult> {
|
|
562
|
+
try {
|
|
563
|
+
const ready = await waitForRunnerStartup(startupPath, "ready", RUNNER_STARTUP_TIMEOUT_MS, undefined, processClosed);
|
|
564
|
+
if (ready.ok === false) {
|
|
565
|
+
persistStartupFailure(ready.error);
|
|
566
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
567
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: ready.error, terminationObserved, startupDidNotProceed: true };
|
|
568
|
+
}
|
|
569
|
+
try { writeRunnerStartupControl(startupAckPath, { action: "ack", token: ready.token }); } catch (error) {
|
|
570
|
+
const message = `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
571
|
+
persistStartupFailure(message);
|
|
572
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
573
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
574
|
+
}
|
|
575
|
+
const acknowledged = await waitForRunnerStartup(startupPath, "acknowledged", RUNNER_STARTUP_TIMEOUT_MS, ready.token, processClosed);
|
|
576
|
+
if (acknowledged.ok === false) {
|
|
577
|
+
persistStartupFailure(acknowledged.error);
|
|
578
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
579
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: acknowledged.error, terminationObserved, startupDidNotProceed: true };
|
|
580
|
+
}
|
|
581
|
+
try { writeRunnerStartupControl(startupAckPath, { action: "confirm", token: ready.token }); } catch (error) {
|
|
582
|
+
const message = `Failed to confirm async runner startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
583
|
+
persistStartupFailure(message);
|
|
584
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
585
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
586
|
+
}
|
|
587
|
+
const confirmed = await waitForRunnerStartup(startupPath, "confirmed", RUNNER_STARTUP_TIMEOUT_MS, ready.token, processClosed);
|
|
588
|
+
if (confirmed.ok === false) {
|
|
589
|
+
persistStartupFailure(confirmed.error);
|
|
590
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
591
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: confirmed.error, terminationObserved, startupDidNotProceed: true };
|
|
592
|
+
}
|
|
593
|
+
const closedAtCommit = await Promise.race([
|
|
594
|
+
processClosed.then((exit) => exit),
|
|
595
|
+
new Promise<undefined>((resolve) => setImmediate(() => resolve(undefined))),
|
|
596
|
+
]);
|
|
597
|
+
const observedExit = closedAtCommit ?? getObservedProcessExit();
|
|
598
|
+
if (observedExit) {
|
|
599
|
+
const message = `Async runner exited after startup state 'acknowledged' before proceed (exit code ${observedExit.exitCode ?? "unknown"}, signal ${observedExit.signal ?? "none"}).`;
|
|
600
|
+
persistStartupFailure(message);
|
|
601
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
602
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
603
|
+
}
|
|
604
|
+
try { writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: ready.token }); } catch (error) {
|
|
605
|
+
const message = `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
606
|
+
persistStartupFailure(message);
|
|
607
|
+
const terminationObserved = await terminateRunnerBeforeProceedAsync(proc, processClosed);
|
|
608
|
+
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
609
|
+
}
|
|
610
|
+
try { fs.rmSync(startupPath, { force: true }); } catch {}
|
|
611
|
+
return { pid: proc.pid, runnerProcessInstanceId };
|
|
612
|
+
} finally {
|
|
613
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
485
617
|
function persistPreProceedStartupFailure(asyncDir: string, runId: string, runnerProcessInstanceId: string, sessionId: string | undefined, completionOwnerId: string | undefined, message: string): void {
|
|
486
618
|
const now = Date.now();
|
|
487
619
|
try {
|
|
@@ -490,6 +622,8 @@ function persistPreProceedStartupFailure(asyncDir: string, runId: string, runner
|
|
|
490
622
|
try {
|
|
491
623
|
status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as Partial<AsyncStatus>;
|
|
492
624
|
} catch {}
|
|
625
|
+
const existingProcessTerminal = status.processTerminal?.state === "observed" || status.processTerminal?.state === "unknown"
|
|
626
|
+
? status.processTerminal : undefined;
|
|
493
627
|
writePrivateAtomicJson(statusPath, {
|
|
494
628
|
...status,
|
|
495
629
|
runId,
|
|
@@ -498,7 +632,7 @@ function persistPreProceedStartupFailure(asyncDir: string, runId: string, runner
|
|
|
498
632
|
state: "failed",
|
|
499
633
|
lastUpdate: now,
|
|
500
634
|
error: message,
|
|
501
|
-
processTerminal: {
|
|
635
|
+
processTerminal: existingProcessTerminal ?? {
|
|
502
636
|
version: 1,
|
|
503
637
|
state: "not-started",
|
|
504
638
|
runId,
|
|
@@ -538,19 +672,34 @@ export function emitProcessTerminalEvent(ctx: AsyncExecutionContext, proof: unkn
|
|
|
538
672
|
}
|
|
539
673
|
}
|
|
540
674
|
|
|
541
|
-
function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Omit<AsyncStatus, "pid" | "processTerminal">, initialStatusPath: string, onProcessTerminal?: (proof: unknown) => void, onBeforeProceed?: (runnerProcessInstanceId: string) => void, requestedCwd = cwd): SpawnRunnerResult {
|
|
675
|
+
function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Omit<AsyncStatus, "pid" | "processTerminal">, initialStatusPath: string, onProcessTerminal?: (proof: unknown) => void, onBeforeProceed?: (runnerProcessInstanceId: string) => void, requestedCwd = cwd): SpawnRunnerResult | Promise<SpawnRunnerResult> {
|
|
542
676
|
const cwdError = preflightLaunchCwd(requestedCwd, cwd);
|
|
543
677
|
if (cwdError) return { error: cwdError };
|
|
544
678
|
|
|
545
|
-
|
|
546
|
-
|
|
679
|
+
// The compiled host exposes its SDK only through Pi's extension loader.
|
|
680
|
+
const binaryHost = resolveBunPiExecutable();
|
|
681
|
+
const nodeExecutable = resolveNodeExecutable();
|
|
682
|
+
const configuredExtensions = (cfg as { steps?: Array<{ extensions?: unknown[]; subagentOnlyExtensions?: unknown[]; requiredExtensions?: unknown[] }> }).steps?.some(
|
|
683
|
+
(step) => (step.extensions?.length ?? 0) > 0 || (step.subagentOnlyExtensions?.length ?? 0) > 0 || (step.requiredExtensions?.length ?? 0) > 0,
|
|
684
|
+
) ?? false;
|
|
685
|
+
// Keep provider/tool extensions in Jiti's host-alias boundary; the native preload
|
|
686
|
+
// only certifies the runner's imports, not a separately loaded extension graph.
|
|
687
|
+
const nativeRunnerSupported = supportsNativeRunner(nodeExecutable) && !configuredExtensions;
|
|
688
|
+
const runner = asyncRunnerSourcePath;
|
|
689
|
+
const runnerIsJavaScript = path.extname(runner) === ".js";
|
|
690
|
+
const bootstrap = path.join(path.dirname(runner), `binary-bootstrap${path.extname(fileURLToPath(import.meta.url))}`);
|
|
691
|
+
if (binaryHost && !fs.existsSync(bootstrap)) return { error: `Background runner bootstrap not found: ${bootstrap}` };
|
|
692
|
+
if (!binaryHost && !runnerIsJavaScript && !nativeRunnerSupported && !jitiCliPath) {
|
|
693
|
+
return { error: sourceUnderNodeModules
|
|
694
|
+
? "Background runner source is installed under node_modules, so upstream jiti is required for TypeScript execution."
|
|
695
|
+
: "Background runner requires enabled native TypeScript with synchronous module hooks, or installed upstream jiti for TypeScript execution." };
|
|
547
696
|
}
|
|
548
|
-
if (!piPackageRoot) {
|
|
549
|
-
return { error: `Background children require
|
|
697
|
+
if (!binaryHost && !piPackageRoot) {
|
|
698
|
+
return { error: `Background children require a supported standalone Pi host or the installed npm package (${PI_CODING_AGENT_PACKAGE}); neither is available.` };
|
|
550
699
|
}
|
|
551
|
-
const hostPeerAliases = resolveHostPeerAliases(piPackageRoot);
|
|
700
|
+
const hostPeerAliases = !binaryHost && piPackageRoot ? resolveHostPeerAliases(piPackageRoot) : { aliases: {}, missing: [] };
|
|
552
701
|
if (hostPeerAliases.missing.length > 0) {
|
|
553
|
-
return { error: `Background children require
|
|
702
|
+
return { error: `Background children require the host npm package (${PI_CODING_AGENT_PACKAGE}) with its dependencies; ${piPackageRoot} does not provide ${hostPeerAliases.missing.join(", ")}.` };
|
|
554
703
|
}
|
|
555
704
|
|
|
556
705
|
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
@@ -560,8 +709,7 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
560
709
|
const launchBarrierToken = hasRevivalLease ? undefined : runnerProcessInstanceId;
|
|
561
710
|
const launchConfig = { ...cfg, runnerProcessInstanceId, ...(launchBarrierToken ? { launchBarrierToken } : {}) };
|
|
562
711
|
writePrivateAtomicJson(cfgPath, launchConfig);
|
|
563
|
-
const
|
|
564
|
-
const nodeCommand = resolveNodeExecutable();
|
|
712
|
+
const command = binaryHost ?? nodeExecutable;
|
|
565
713
|
const launchForStartup = launchConfig as typeof launchConfig & { asyncDir?: unknown; id?: unknown; sessionId?: unknown; completionOwnerId?: unknown; revivalLease?: unknown };
|
|
566
714
|
const launchAsyncDir = typeof launchForStartup.asyncDir === "string" ? launchForStartup.asyncDir : undefined;
|
|
567
715
|
const launchRunId = typeof launchForStartup.id === "string" ? launchForStartup.id : suffix;
|
|
@@ -587,26 +735,49 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
587
735
|
stdoutFd = fs.openSync(logPaths.stdoutPath, "a");
|
|
588
736
|
stderrFd = fs.openSync(logPaths.stderrPath, "a");
|
|
589
737
|
}
|
|
590
|
-
const preload = hostPeerAliases.
|
|
591
|
-
? ["--import", new URL("../../../runner-
|
|
738
|
+
const preload = Object.keys(hostPeerAliases.aliases).length > 0
|
|
739
|
+
? ["--import", new URL("../../../runner-peer-preload.mjs", import.meta.url).href]
|
|
592
740
|
: [];
|
|
593
|
-
const
|
|
741
|
+
const args = binaryHost
|
|
742
|
+
? ["--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session", "--mode", "rpc", "--extension", bootstrap]
|
|
743
|
+
: runnerIsJavaScript
|
|
744
|
+
? [...preload, runner, cfgPath]
|
|
745
|
+
: nativeRunnerSupported
|
|
746
|
+
? [...preload, "--experimental-strip-types", runner, cfgPath]
|
|
747
|
+
: [...preload, jitiCliPath!, runner, cfgPath];
|
|
748
|
+
const proc = spawn(command, args, {
|
|
594
749
|
cwd,
|
|
595
750
|
...backgroundProcessOptions(),
|
|
596
751
|
stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
|
|
597
752
|
env: {
|
|
598
753
|
...omitExtensionBindingsEnv(process.env),
|
|
599
|
-
|
|
600
|
-
|
|
754
|
+
// Unset leaves the inherited parent value in place. See childCacheRetention.
|
|
755
|
+
...childCacheRetentionEnv(),
|
|
756
|
+
[PI_CODING_AGENT_PACKAGE_ROOT_ENV]: binaryHost ? undefined : piPackageRoot,
|
|
757
|
+
// npm must override inherited bundled layouts (#2071); binaries retain release assets.
|
|
758
|
+
PI_PACKAGE_DIR: binaryHost ? process.env.PI_PACKAGE_DIR : piPackageRoot,
|
|
759
|
+
[JITI_ALIAS_ENV]: binaryHost ? undefined : JSON.stringify(hostPeerAliases.aliases),
|
|
760
|
+
PI_ASYNC_NATIVE_RUNNER: !binaryHost && (runnerIsJavaScript || nativeRunnerSupported) ? "1" : "0",
|
|
761
|
+
PI_ASYNC_COMPILED_RUNNER: !binaryHost && runnerIsJavaScript ? "1" : "0",
|
|
762
|
+
PI_SUBAGENT_RUNNER_CONFIG: binaryHost ? cfgPath : undefined,
|
|
601
763
|
},
|
|
602
764
|
});
|
|
765
|
+
let observedProcessExit: { exitCode: number | null; signal: NodeJS.Signals | null } | undefined;
|
|
766
|
+
proc.once("exit", (exitCode, signal) => { observedProcessExit = { exitCode, signal }; });
|
|
767
|
+
const processClosed = new Promise<{ exitCode: number | null; signal: NodeJS.Signals | null }>((resolve) => {
|
|
768
|
+
proc.once("close", (exitCode, signal) => {
|
|
769
|
+
observedProcessExit ??= { exitCode, signal };
|
|
770
|
+
resolve({ exitCode, signal });
|
|
771
|
+
});
|
|
772
|
+
});
|
|
603
773
|
closeFd(stdoutFd);
|
|
604
774
|
closeFd(stderrFd);
|
|
605
775
|
proc.on("error", (error) => {
|
|
606
776
|
console.error(`[pi-subagents] async spawn failed: ${error.message}`);
|
|
607
777
|
});
|
|
608
778
|
proc.once("close", (exitCode, signal) => {
|
|
609
|
-
const
|
|
779
|
+
const finalize = () => {
|
|
780
|
+
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 }> } };
|
|
610
781
|
const asyncDir = launch.asyncDir;
|
|
611
782
|
const runId = launch.id;
|
|
612
783
|
if (typeof asyncDir !== "string" || typeof runId !== "string") return;
|
|
@@ -654,6 +825,9 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
654
825
|
}
|
|
655
826
|
}
|
|
656
827
|
onProcessTerminal?.(persisted);
|
|
828
|
+
};
|
|
829
|
+
if (startupPath) setImmediate(finalize);
|
|
830
|
+
else finalize();
|
|
657
831
|
});
|
|
658
832
|
if (typeof proc.pid !== "number") {
|
|
659
833
|
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
@@ -664,6 +838,8 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
664
838
|
pid: proc.pid,
|
|
665
839
|
processTerminal: { version: 1, state: "pending", runId: initialStatus.runId, runnerProcessInstanceId },
|
|
666
840
|
});
|
|
841
|
+
// Aggregate waits must see the launch before the runner's first status update.
|
|
842
|
+
updateActiveRunIndex(path.dirname(initialStatusPath), initialStatus.state, initialStatus.toolCallId);
|
|
667
843
|
} catch (error) {
|
|
668
844
|
const message = `Failed to persist initial async status: ${error instanceof Error ? error.message : String(error)}`;
|
|
669
845
|
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
|
|
@@ -702,39 +878,7 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
702
878
|
const persistStartupFailure = (message: string) => {
|
|
703
879
|
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
|
|
704
880
|
};
|
|
705
|
-
|
|
706
|
-
if (ready.ok === false) {
|
|
707
|
-
persistStartupFailure(ready.error);
|
|
708
|
-
const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
|
|
709
|
-
return { pid: proc.pid, runnerProcessInstanceId, error: ready.error, terminationObserved, startupDidNotProceed: ready.startupDidNotProceed };
|
|
710
|
-
}
|
|
711
|
-
try {
|
|
712
|
-
writeRunnerStartupControl(startupAckPath, { action: "ack", token: ready.token });
|
|
713
|
-
} catch (error) {
|
|
714
|
-
const message = `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
715
|
-
persistStartupFailure(message);
|
|
716
|
-
const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
|
|
717
|
-
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
718
|
-
}
|
|
719
|
-
const acknowledged = waitForRunnerStartup(startupPath, "acknowledged", RUNNER_STARTUP_TIMEOUT_MS, ready.token);
|
|
720
|
-
if (acknowledged.ok === false) {
|
|
721
|
-
persistStartupFailure(acknowledged.error);
|
|
722
|
-
const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
|
|
723
|
-
return { pid: proc.pid, runnerProcessInstanceId, error: acknowledged.error, terminationObserved, startupDidNotProceed: acknowledged.startupDidNotProceed };
|
|
724
|
-
}
|
|
725
|
-
try {
|
|
726
|
-
writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: ready.token });
|
|
727
|
-
} catch (error) {
|
|
728
|
-
const message = `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
729
|
-
persistStartupFailure(message);
|
|
730
|
-
const terminationObserved = terminateRunnerBeforeProceed(proc.pid);
|
|
731
|
-
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
732
|
-
}
|
|
733
|
-
try {
|
|
734
|
-
fs.rmSync(startupPath, { force: true });
|
|
735
|
-
} catch {
|
|
736
|
-
// Proceed is the commit point; handshake cleanup cannot turn a running revival into a start error.
|
|
737
|
-
}
|
|
881
|
+
return completeRunnerStartupHandshake(startupPath, startupAckPath, startupProceedPath, proc, processClosed, () => observedProcessExit ?? (proc.exitCode !== null || proc.signalCode !== null ? { exitCode: proc.exitCode, signal: proc.signalCode } : undefined), runnerProcessInstanceId, persistStartupFailure);
|
|
738
882
|
}
|
|
739
883
|
return { pid: proc.pid, runnerProcessInstanceId };
|
|
740
884
|
} catch (error) {
|
|
@@ -776,6 +920,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
776
920
|
const chainSkills = params.chainSkills ?? [];
|
|
777
921
|
const availableModels = params.availableModels;
|
|
778
922
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
923
|
+
const launchMachine = params.machine;
|
|
779
924
|
let managedWorktreeProvider: "native" | "worktrunk" | undefined;
|
|
780
925
|
try {
|
|
781
926
|
if (chain.some((step) => "worktree" in step && step.worktree === true)) {
|
|
@@ -810,8 +955,6 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
810
955
|
if (error instanceof ChainOutputValidationError) return { error: error.message };
|
|
811
956
|
throw error;
|
|
812
957
|
}
|
|
813
|
-
const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: graphChain });
|
|
814
|
-
|
|
815
958
|
const diagnosticContext = params.unknownAgentDiagnosticContext
|
|
816
959
|
?? unknownAgentDiagnosticContext(discoverAgents(path.resolve(runnerCwd), "both"));
|
|
817
960
|
for (const s of chain) {
|
|
@@ -824,6 +967,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
824
967
|
if (!agents.find((x) => x.name === agentName)) return { error: formatUnknownAgentError(agentName, diagnosticContext) };
|
|
825
968
|
}
|
|
826
969
|
}
|
|
970
|
+
const graphSteps = projectChainOutputSchemas(graphChain, agents) as ChainStep[];
|
|
971
|
+
const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: graphSteps });
|
|
827
972
|
|
|
828
973
|
let progressInstructionCreated = false;
|
|
829
974
|
const buildStepOverrides = (s: SequentialStep): StepOverrides => {
|
|
@@ -836,16 +981,32 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
836
981
|
...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
|
|
837
982
|
...(s.model !== undefined ? { model: s.model } : {}),
|
|
838
983
|
...(s.fast !== undefined ? { fast: s.fast } : {}),
|
|
984
|
+
...(s.outputSchema !== undefined ? { outputSchema: s.outputSchema } : {}),
|
|
839
985
|
};
|
|
840
986
|
};
|
|
841
987
|
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number, parallelOutputNamespace?: { stepIndex: number; taskIndex?: number }, runFanoutPath?: string) => {
|
|
842
988
|
const a = agents.find((x) => x.name === s.agent)!;
|
|
989
|
+
const effectiveBehavior = resolvedBehavior ?? suppressProgressForReadOnlyTask(resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
|
|
990
|
+
const requestedMachine = s.machine ?? launchMachine ?? a.machine;
|
|
843
991
|
const externalRunner = a.runner?.type === "external-cli" || a.runner?.type === "external-job";
|
|
844
992
|
const externalRunnerType = a.runner?.type;
|
|
993
|
+
const machineUnsupported = formatHerdrMachineRunnerUnsupported({ machine: requestedMachine, agentName: a.name, runnerType: a.runner?.type, adapter: a.runner?.type === "external-cli" ? a.runner.adapter : undefined, worktree: s.worktree });
|
|
994
|
+
if (machineUnsupported) throw new AsyncStartValidationError(machineUnsupported);
|
|
995
|
+
let machine: HerdrMachineReference | undefined;
|
|
996
|
+
let machineEnv: Record<string, string> | undefined;
|
|
997
|
+
if (requestedMachine) {
|
|
998
|
+
try {
|
|
999
|
+
const placement = resolveHerdrMachinePlacement({ machine: requestedMachine, cwd: runnerCwd, stepCwd: s.cwd ?? params.machineCwd });
|
|
1000
|
+
machine = placement.machine;
|
|
1001
|
+
machineEnv = placement.env;
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
845
1006
|
if (externalRunner) {
|
|
846
1007
|
const unsupported: string[] = [];
|
|
847
1008
|
if (s.model !== undefined) unsupported.push("model override");
|
|
848
|
-
if (
|
|
1009
|
+
if (effectiveBehavior.outputSchema !== undefined) unsupported.push("structured output");
|
|
849
1010
|
if (s.acceptance !== undefined || params.agentContract !== undefined || s.agentContract !== undefined) unsupported.push("acceptance/agent contract");
|
|
850
1011
|
if (s.toolBudget !== undefined || params.toolBudget !== undefined || a.toolBudget !== undefined || params.configToolBudget !== undefined) unsupported.push("tool budget");
|
|
851
1012
|
if ((s.fast ?? params.fast ?? a.fast) === true) unsupported.push("fast mode");
|
|
@@ -876,10 +1037,11 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
876
1037
|
runtimeCwd: ctx.cwd,
|
|
877
1038
|
stepCwdInput: s.cwd,
|
|
878
1039
|
behaviorCwd,
|
|
1040
|
+
...(machine ? { machineCwd: machine.cwd } : {}),
|
|
879
1041
|
chainSkills,
|
|
880
1042
|
outputBaseDir,
|
|
881
1043
|
parallelOutputNamespace,
|
|
882
|
-
resolvedBehavior,
|
|
1044
|
+
resolvedBehavior: effectiveBehavior,
|
|
883
1045
|
});
|
|
884
1046
|
const { stepCwd, instructionCwd, readExistenceCwd, behavior, namespaceOutputPath, outputPath, skillNames } = launchPlan;
|
|
885
1047
|
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
@@ -891,22 +1053,13 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
891
1053
|
);
|
|
892
1054
|
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
893
1055
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
const injection = buildSkillInjection(resolvedSkills);
|
|
897
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
898
|
-
}
|
|
899
|
-
const memoryInjection = buildAgentMemoryInjection(a, stepCwd);
|
|
900
|
-
if (memoryInjection) {
|
|
901
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
|
|
902
|
-
}
|
|
903
|
-
systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: stepCwd, agentName: a.name });
|
|
1056
|
+
// A namespaced parallel output is injected by the runner, not the prompt.
|
|
1057
|
+
const systemPrompt = buildEffectiveSystemPrompt({ agent: a, resolvedSkills, cwd: stepCwd, ...(!namespaceOutputPath && outputPath ? { outputPath } : {}) });
|
|
904
1058
|
|
|
905
1059
|
const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false, undefined, readExistenceCwd);
|
|
906
1060
|
const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
|
|
907
1061
|
if (behavior.progress) progressInstructionCreated = true;
|
|
908
1062
|
const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, progressDir, isFirstProgressAgent);
|
|
909
|
-
if (!namespaceOutputPath) systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, a);
|
|
910
1063
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
|
|
911
1064
|
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
912
1065
|
let taskTemplate = s.task ?? "{previous}";
|
|
@@ -944,43 +1097,46 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
944
1097
|
}
|
|
945
1098
|
const agentContract = s.agentContract ?? params.agentContract;
|
|
946
1099
|
const permissionRules = resolvePermissionRules(ctx.permissions, a.permissions);
|
|
947
|
-
let
|
|
1100
|
+
let selectedModel = model;
|
|
1101
|
+
let requestedModel: string | undefined;
|
|
948
1102
|
if (!externalRunner) {
|
|
949
1103
|
try {
|
|
950
|
-
|
|
1104
|
+
const modelEvidence = resolveModelSelection(primaryModel, availableModels, a.modelProvider ?? ctx.currentModelProvider, {
|
|
951
1105
|
scope: modelScopes,
|
|
952
1106
|
primaryModelFromParent,
|
|
953
1107
|
origin: modelOrigin,
|
|
954
|
-
}).flatMap((candidate) => {
|
|
955
|
-
const resolved = applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined);
|
|
956
|
-
return resolved ? [resolved] : [];
|
|
957
1108
|
});
|
|
958
|
-
|
|
1109
|
+
requestedModel = modelEvidence.requestedModel;
|
|
1110
|
+
selectedModel = applyThinkingSuffix(modelEvidence.model, effectiveThinking, thinkingOverride !== undefined);
|
|
1111
|
+
assertThinkingWithinCeiling({ model: selectedModel, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: a.name, runId: id });
|
|
959
1112
|
} catch (error) {
|
|
960
1113
|
throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
|
|
961
1114
|
}
|
|
962
1115
|
}
|
|
963
|
-
const launchRuleError = applyWatchdogLaunchRules({ cwd: stepCwd, agent: a.name, model:
|
|
1116
|
+
const launchRuleError = applyWatchdogLaunchRules({ cwd: machine ? runnerCwd : stepCwd, agent: a.name, model: selectedModel, warn: (violation) => sendRuleViolationWarning(ctx.pi, violation) });
|
|
964
1117
|
if (launchRuleError) throw new AsyncStartValidationError(launchRuleError);
|
|
965
1118
|
const fast = s.fast ?? params.fast ?? a.fast;
|
|
1119
|
+
const hostAvailableBuiltins = getHostBuiltinToolNames(ctx.pi);
|
|
1120
|
+
const requiredExtensions = externalRunner ? [] : ctx.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(ctx.parentSessionId ?? ctx.currentSessionId ?? undefined);
|
|
966
1121
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
967
1122
|
tools: a.tools,
|
|
968
1123
|
excludeTools: a.excludeTools,
|
|
969
1124
|
allowNestedSubagents: a.allowNestedSubagents,
|
|
970
1125
|
extensions: a.extensions,
|
|
971
1126
|
subagentOnlyExtensions: a.subagentOnlyExtensions,
|
|
1127
|
+
requiredExtensions,
|
|
972
1128
|
mcpDirectTools: a.mcpDirectTools,
|
|
973
1129
|
cwd: stepCwd,
|
|
974
1130
|
requireReadTool: Boolean(resolvedSkills.length),
|
|
975
|
-
structuredOutput: Boolean(
|
|
1131
|
+
structuredOutput: Boolean(behavior.outputSchema),
|
|
976
1132
|
fast,
|
|
977
|
-
model,
|
|
978
|
-
modelCandidates,
|
|
1133
|
+
model: selectedModel,
|
|
979
1134
|
capabilityCeiling: params.capabilityCeiling,
|
|
980
1135
|
inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
|
|
981
1136
|
agentName: a.name,
|
|
982
1137
|
permissionRules,
|
|
983
1138
|
runtimeSnapshotHost: ctx.pi,
|
|
1139
|
+
hostAvailableBuiltins,
|
|
984
1140
|
});
|
|
985
1141
|
const launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);
|
|
986
1142
|
if (externalRunner && permissionRules) {
|
|
@@ -1008,21 +1164,23 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1008
1164
|
agent: s.agent,
|
|
1009
1165
|
task,
|
|
1010
1166
|
...(a.runner ? { runner: a.runner } : {}),
|
|
1167
|
+
...(machine ? { machine } : {}),
|
|
1168
|
+
...(machineEnv ? { machineEnv } : {}),
|
|
1011
1169
|
...(params.contextForAgent ? { context: params.contextForAgent(s.agent) } : {}),
|
|
1012
1170
|
...(agentContract ? { agentContract } : {}),
|
|
1013
1171
|
phase: s.phase,
|
|
1014
1172
|
label: s.label,
|
|
1015
1173
|
outputName: s.as,
|
|
1016
|
-
structured: Boolean(
|
|
1174
|
+
structured: Boolean(behavior.outputSchema),
|
|
1017
1175
|
cwd: stepCwd,
|
|
1018
|
-
requestedCwd: s.cwd ?? stepCwd,
|
|
1019
|
-
model,
|
|
1176
|
+
requestedCwd: machine ? machine.cwd : s.cwd ?? stepCwd,
|
|
1177
|
+
model: selectedModel,
|
|
1020
1178
|
...(contextLimit !== undefined ? { contextLimit } : {}),
|
|
1021
1179
|
...(fast !== undefined ? { fast } : {}),
|
|
1022
|
-
thinking: resolveEffectiveThinking(
|
|
1180
|
+
thinking: resolveEffectiveThinking(selectedModel, effectiveThinking),
|
|
1023
1181
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
1024
1182
|
launchResolvedExtensions,
|
|
1025
|
-
|
|
1183
|
+
...(requestedModel ? { requestedModel } : {}),
|
|
1026
1184
|
...(primaryModelFromParent ? { skipPrimaryModelVerification: true } : {}),
|
|
1027
1185
|
...(availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {}),
|
|
1028
1186
|
...(ctx.modelResponseAliases ? { modelResponseAliases: ctx.modelResponseAliases } : {}),
|
|
@@ -1031,6 +1189,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1031
1189
|
allowNestedSubagents: a.allowNestedSubagents,
|
|
1032
1190
|
extensions: a.extensions,
|
|
1033
1191
|
subagentOnlyExtensions: a.subagentOnlyExtensions,
|
|
1192
|
+
...(!externalRunner ? { requiredExtensions } : {}),
|
|
1034
1193
|
mcpDirectTools: a.mcpDirectTools,
|
|
1035
1194
|
mutationTools: a.mutationTools,
|
|
1036
1195
|
completionGuard: a.completionGuard,
|
|
@@ -1062,8 +1221,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1062
1221
|
acceptanceInput: s.acceptance,
|
|
1063
1222
|
acceptanceRole: a.acceptanceRole,
|
|
1064
1223
|
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
1065
|
-
...(
|
|
1066
|
-
...(
|
|
1224
|
+
...(behavior.outputSchema ? { structuredOutputSchema: behavior.outputSchema } : {}),
|
|
1225
|
+
...(behavior.outputSchema ? { structuredOutput: createStructuredOutputRuntime(behavior.outputSchema, path.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(s.acceptance) }) } : {}),
|
|
1067
1226
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
1068
1227
|
...(s.worktree ? { worktree: true } : {}),
|
|
1069
1228
|
};
|
|
@@ -1107,7 +1266,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1107
1266
|
}
|
|
1108
1267
|
}
|
|
1109
1268
|
const staticStep = nextFlatStep();
|
|
1110
|
-
return buildSeqStep({ ...t, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn }, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex }, resultMode === "parallel" ? `tasks[${taskIndex}]` : `chain[${stepIndex}].parallel[${taskIndex}]`);
|
|
1269
|
+
return buildSeqStep({ ...t, machine: t.machine ?? s.machine, worktree: s.worktree, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn }, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex }, resultMode === "parallel" ? `tasks[${taskIndex}]` : `chain[${stepIndex}].parallel[${taskIndex}]`);
|
|
1111
1270
|
}),
|
|
1112
1271
|
concurrency: s.concurrency,
|
|
1113
1272
|
failFast: s.failFast,
|
|
@@ -1236,11 +1395,9 @@ export function executeAsyncChain(
|
|
|
1236
1395
|
} = params;
|
|
1237
1396
|
const resultMode = params.resultMode ?? "chain";
|
|
1238
1397
|
const acceptanceErrors = validateExecutionAcceptance({
|
|
1239
|
-
chain: chain
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
return { acceptance: step.acceptance, outputSchema: step.outputSchema };
|
|
1243
|
-
}),
|
|
1398
|
+
chain: projectChainOutputSchemas(chain, agents,
|
|
1399
|
+
(step, outputSchema) => ({ acceptance: step.acceptance, outputSchema }),
|
|
1400
|
+
(step, parallel) => Array.isArray(step.parallel) ? { parallel } : { acceptance: "acceptance" in step ? step.acceptance : undefined, parallel }),
|
|
1244
1401
|
});
|
|
1245
1402
|
if (acceptanceErrors.length > 0) return formatAsyncStartError(resultMode, acceptanceErrors.join(" "));
|
|
1246
1403
|
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
@@ -1274,6 +1431,8 @@ export function executeAsyncChain(
|
|
|
1274
1431
|
availableModels: params.availableModels,
|
|
1275
1432
|
cwd,
|
|
1276
1433
|
chainSkills: params.chainSkills,
|
|
1434
|
+
machine: params.machine,
|
|
1435
|
+
machineCwd: params.machineCwd,
|
|
1277
1436
|
sessionFilesByFlatIndex,
|
|
1278
1437
|
thinkingOverridesByFlatIndex,
|
|
1279
1438
|
contextForAgent: params.contextForAgent,
|
|
@@ -1377,6 +1536,7 @@ export function executeAsyncChain(
|
|
|
1377
1536
|
deadlineAt,
|
|
1378
1537
|
globalConcurrencyLimit: params.globalConcurrencyLimit,
|
|
1379
1538
|
runFanoutBudget,
|
|
1539
|
+
hostAvailableBuiltins: getHostBuiltinToolNames(ctx.pi),
|
|
1380
1540
|
workflowGraph,
|
|
1381
1541
|
...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),
|
|
1382
1542
|
...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),
|
|
@@ -1407,7 +1567,7 @@ export function executeAsyncChain(
|
|
|
1407
1567
|
path.join(asyncDir, "status.json"),
|
|
1408
1568
|
(proof) => emitProcessTerminalEvent(ctx, proof),
|
|
1409
1569
|
(runnerProcessInstanceId) => params.activeAsyncCapacity?.markStarted(runnerProcessInstanceId),
|
|
1410
|
-
);
|
|
1570
|
+
) as SpawnRunnerResult;
|
|
1411
1571
|
} catch (error) {
|
|
1412
1572
|
params.activeAsyncCapacity?.rollback();
|
|
1413
1573
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1547,7 +1707,7 @@ export function workflowAwaitedAsyncResultPath(asyncDir: string): string {
|
|
|
1547
1707
|
export function executeAsyncSingle(
|
|
1548
1708
|
id: string,
|
|
1549
1709
|
params: AsyncSingleParams,
|
|
1550
|
-
): AsyncExecutionResult {
|
|
1710
|
+
): AsyncExecutionResult | Promise<AsyncExecutionResult> {
|
|
1551
1711
|
const {
|
|
1552
1712
|
agent,
|
|
1553
1713
|
agentConfig,
|
|
@@ -1611,6 +1771,20 @@ export function executeAsyncSingle(
|
|
|
1611
1771
|
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1612
1772
|
}
|
|
1613
1773
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
1774
|
+
const requestedMachine = params.machine ?? agentConfig.machine;
|
|
1775
|
+
const machineUnsupported = formatHerdrMachineRunnerUnsupported({ machine: requestedMachine, agentName: agentConfig.name, runnerType: agentConfig.runner?.type, adapter: agentConfig.runner?.type === "external-cli" ? agentConfig.runner.adapter : undefined, worktree: params.worktree });
|
|
1776
|
+
if (machineUnsupported) return formatAsyncStartError("single", machineUnsupported);
|
|
1777
|
+
let machine: HerdrMachineReference | undefined;
|
|
1778
|
+
let machineEnv: Record<string, string> | undefined;
|
|
1779
|
+
if (requestedMachine) {
|
|
1780
|
+
try {
|
|
1781
|
+
const placement = resolveHerdrMachinePlacement({ machine: requestedMachine, cwd: runnerCwd, stepCwd: params.machineCwd });
|
|
1782
|
+
machine = placement.machine;
|
|
1783
|
+
machineEnv = placement.env;
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1614
1788
|
let managedWorktreeProvider: "native" | "worktrunk" | undefined;
|
|
1615
1789
|
if (params.worktree === true) {
|
|
1616
1790
|
try {
|
|
@@ -1624,7 +1798,7 @@ export function executeAsyncSingle(
|
|
|
1624
1798
|
? WORKTREE_AGENT_CWD_PLACEHOLDER
|
|
1625
1799
|
: params.worktree === true && managedWorktreeProvider === "native"
|
|
1626
1800
|
? resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s0`, 0, worktreeBaseDir)
|
|
1627
|
-
: runnerCwd;
|
|
1801
|
+
: machine?.cwd ?? runnerCwd;
|
|
1628
1802
|
const readExistenceCwd = params.worktree === true ? runnerCwd : instructionCwd;
|
|
1629
1803
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
1630
1804
|
const availableModels = params.availableModels;
|
|
@@ -1636,16 +1810,6 @@ export function executeAsyncSingle(
|
|
|
1636
1810
|
agentConfig.filePath ? path.dirname(agentConfig.filePath) : runnerCwd,
|
|
1637
1811
|
);
|
|
1638
1812
|
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
1639
|
-
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
1640
|
-
if (resolvedSkills.length > 0) {
|
|
1641
|
-
const injection = buildSkillInjection(resolvedSkills);
|
|
1642
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
1643
|
-
}
|
|
1644
|
-
const memoryInjection = buildAgentMemoryInjection(agentConfig, runnerCwd);
|
|
1645
|
-
if (memoryInjection) {
|
|
1646
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
|
|
1647
|
-
}
|
|
1648
|
-
systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: runnerCwd, agentName: agentConfig.name });
|
|
1649
1813
|
|
|
1650
1814
|
const inheritedNestedRoute = inheritedNestedRouteOf(ctx.childRuntime);
|
|
1651
1815
|
const nestedAddress = inheritedNestedRoute ? inheritedNestedParentAddressOf(ctx.childRuntime) : undefined;
|
|
@@ -1668,7 +1832,7 @@ export function executeAsyncSingle(
|
|
|
1668
1832
|
|
|
1669
1833
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
1670
1834
|
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
|
|
1671
|
-
systemPrompt =
|
|
1835
|
+
const systemPrompt = buildEffectiveSystemPrompt({ agent: agentConfig, resolvedSkills, cwd: runnerCwd, ...(outputPath ? { outputPath } : {}) });
|
|
1672
1836
|
const outputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
1673
1837
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
1674
1838
|
if (validationError) return formatAsyncStartError("single", validationError);
|
|
@@ -1677,7 +1841,11 @@ export function executeAsyncSingle(
|
|
|
1677
1841
|
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
1678
1842
|
const reads = params.reads !== undefined ? params.reads : agentConfig.defaultReads ?? false;
|
|
1679
1843
|
const readPaths = Array.isArray(reads)
|
|
1680
|
-
?
|
|
1844
|
+
? machine
|
|
1845
|
+
? externalRunner
|
|
1846
|
+
? reads.map((read) => read === "~" || read.startsWith("~/") || path.posix.isAbsolute(read) ? read : path.posix.resolve(instructionCwd, read))
|
|
1847
|
+
: []
|
|
1848
|
+
: managedWorktreeProvider === "worktrunk"
|
|
1681
1849
|
? resolveExistingReadInstructionPaths(reads, instructionCwd, readExistenceCwd)
|
|
1682
1850
|
: resolveExistingReadPaths(reads, readExistenceCwd)
|
|
1683
1851
|
: [];
|
|
@@ -1743,40 +1911,43 @@ export function executeAsyncSingle(
|
|
|
1743
1911
|
const structuredOutput = params.structuredOutputSchema
|
|
1744
1912
|
? createStructuredOutputRuntime(params.structuredOutputSchema, path.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) })
|
|
1745
1913
|
: undefined;
|
|
1746
|
-
let
|
|
1914
|
+
let selectedModel = model;
|
|
1915
|
+
let requestedModel: string | undefined;
|
|
1747
1916
|
if (!externalRunner) {
|
|
1748
1917
|
try {
|
|
1749
|
-
|
|
1918
|
+
const modelEvidence = resolveModelSelection(primaryModel, availableModels, agentConfig.modelProvider ?? ctx.currentModelProvider, {
|
|
1750
1919
|
scope: modelScopes,
|
|
1751
1920
|
primaryModelFromParent: modelOrigin === "inherited",
|
|
1752
1921
|
origin: modelOrigin,
|
|
1753
|
-
}).flatMap((candidate) => {
|
|
1754
|
-
const resolved = applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined);
|
|
1755
|
-
return resolved ? [resolved] : [];
|
|
1756
1922
|
});
|
|
1757
|
-
|
|
1923
|
+
requestedModel = modelEvidence.requestedModel;
|
|
1924
|
+
selectedModel = applyThinkingSuffix(modelEvidence.model, effectiveThinking, params.thinkingOverride !== undefined);
|
|
1925
|
+
assertThinkingWithinCeiling({ model: selectedModel, configThinking: effectiveThinking, ceiling: thinkingCeiling, agent: agentConfig.name, runId: id });
|
|
1758
1926
|
} catch (error) {
|
|
1759
1927
|
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1760
1928
|
}
|
|
1761
1929
|
}
|
|
1930
|
+
const hostAvailableBuiltins = getHostBuiltinToolNames(ctx.pi);
|
|
1931
|
+
const requiredExtensions = externalRunner ? [] : params.requiredExtensions ?? ctx.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(ctx.parentSessionId ?? ctx.currentSessionId ?? undefined);
|
|
1762
1932
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
1763
1933
|
tools: agentConfig.tools,
|
|
1764
1934
|
excludeTools: agentConfig.excludeTools,
|
|
1765
1935
|
allowNestedSubagents: agentConfig.allowNestedSubagents,
|
|
1766
1936
|
extensions: agentConfig.extensions,
|
|
1767
1937
|
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
1938
|
+
requiredExtensions,
|
|
1768
1939
|
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
1769
1940
|
cwd: runnerCwd,
|
|
1770
1941
|
requireReadTool: Boolean(resolvedSkills.length),
|
|
1771
1942
|
structuredOutput: Boolean(params.structuredOutputSchema),
|
|
1772
1943
|
fast: params.fast ?? agentConfig.fast,
|
|
1773
|
-
model,
|
|
1774
|
-
modelCandidates,
|
|
1944
|
+
model: selectedModel,
|
|
1775
1945
|
capabilityCeiling,
|
|
1776
1946
|
inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
|
|
1777
1947
|
agentName: agentConfig.name,
|
|
1778
1948
|
permissionRules: resolvePermissionRules(ctx.permissions, agentConfig.permissions),
|
|
1779
1949
|
runtimeSnapshotHost: ctx.pi,
|
|
1950
|
+
hostAvailableBuiltins,
|
|
1780
1951
|
});
|
|
1781
1952
|
const launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);
|
|
1782
1953
|
if (!externalRunner) {
|
|
@@ -1793,24 +1964,17 @@ export function executeAsyncSingle(
|
|
|
1793
1964
|
});
|
|
1794
1965
|
if (contractError) return formatAsyncStartError("single", contractError);
|
|
1795
1966
|
}
|
|
1796
|
-
const
|
|
1797
|
-
|
|
1967
|
+
const fast = params.fast ?? agentConfig.fast;
|
|
1968
|
+
const launchThinking = resolveEffectiveThinking(selectedModel, effectiveThinking);
|
|
1969
|
+
const { definitionDigest, launchContractDigest } = resolveLaunchBinding({
|
|
1970
|
+
agent: agentConfig,
|
|
1798
1971
|
task,
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
...(
|
|
1802
|
-
...(resolveEffectiveThinking(model, effectiveThinking) ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1803
|
-
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
1972
|
+
model: selectedModel,
|
|
1973
|
+
...(fast !== undefined ? { fast } : {}),
|
|
1974
|
+
...(launchThinking ? { thinking: launchThinking } : {}),
|
|
1804
1975
|
systemPrompt,
|
|
1805
|
-
systemPromptMode: agentConfig.systemPromptMode,
|
|
1806
|
-
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
1807
|
-
inheritGlobalContext: agentConfig.inheritGlobalContext,
|
|
1808
|
-
inheritSkills: agentConfig.inheritSkills,
|
|
1809
1976
|
skills: resolvedSkills.map((skill) => skill.name),
|
|
1810
|
-
|
|
1811
|
-
...(toolPlan.excludeTools.length > 0 ? { excludeTools: toolPlan.excludeTools } : {}),
|
|
1812
|
-
extensions: toolPlan.extensionArgs,
|
|
1813
|
-
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1977
|
+
toolPlan,
|
|
1814
1978
|
...(outputPath ? { outputPath } : {}),
|
|
1815
1979
|
outputMode,
|
|
1816
1980
|
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
@@ -1832,6 +1996,7 @@ export function executeAsyncSingle(
|
|
|
1832
1996
|
...(lane ? { lane } : {}),
|
|
1833
1997
|
launchContractDigest,
|
|
1834
1998
|
...(extensionBindings ? { extensionBindings } : {}),
|
|
1999
|
+
...(requiredExtensions.length > 0 ? { requiredExtensions } : {}),
|
|
1835
2000
|
runFanoutBudget,
|
|
1836
2001
|
sourceRunId: id,
|
|
1837
2002
|
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
@@ -1839,13 +2004,12 @@ export function executeAsyncSingle(
|
|
|
1839
2004
|
launchResolvedExtensions,
|
|
1840
2005
|
...(sessionFile ? { sessionFile } : {}),
|
|
1841
2006
|
cwd: runnerCwd,
|
|
1842
|
-
...(
|
|
2007
|
+
...(selectedModel ? { model: selectedModel } : {}),
|
|
1843
2008
|
...(params.fast ?? recoveryAgentConfig.fast ? { fast: params.fast ?? recoveryAgentConfig.fast } : {}),
|
|
1844
2009
|
...(recoveryAgentConfig.modelProvider ? { modelProvider: recoveryAgentConfig.modelProvider } : {}),
|
|
1845
2010
|
...(modelOrigin === "inherited" ? { modelOverrideFromParent: true } : {}),
|
|
1846
2011
|
modelOrigin,
|
|
1847
|
-
...(
|
|
1848
|
-
...(effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
2012
|
+
...(effectiveThinking ? { thinking: resolveEffectiveThinking(selectedModel, effectiveThinking) } : {}),
|
|
1849
2013
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
1850
2014
|
...(recoveryAgentConfig.tools ? { tools: [...recoveryAgentConfig.tools] } : {}),
|
|
1851
2015
|
...(recoveryAgentConfig.excludeTools ? { excludeTools: [...recoveryAgentConfig.excludeTools] } : {}),
|
|
@@ -1889,11 +2053,11 @@ export function executeAsyncSingle(
|
|
|
1889
2053
|
return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
|
|
1890
2054
|
}
|
|
1891
2055
|
}
|
|
1892
|
-
let
|
|
2056
|
+
let spawnResultOrPromise: SpawnRunnerResult | Promise<SpawnRunnerResult> = {};
|
|
1893
2057
|
const initialStatusAt = Date.now();
|
|
1894
2058
|
const initialCompletionOwnerId = ctx.completionOwnerId ?? currentCompletionOwnerId();
|
|
1895
2059
|
try {
|
|
1896
|
-
|
|
2060
|
+
spawnResultOrPromise = spawnRunner(
|
|
1897
2061
|
{
|
|
1898
2062
|
id,
|
|
1899
2063
|
steps: [
|
|
@@ -1904,16 +2068,19 @@ export function executeAsyncSingle(
|
|
|
1904
2068
|
agent,
|
|
1905
2069
|
task: taskText,
|
|
1906
2070
|
...(agentConfig.runner ? { runner: agentConfig.runner } : {}),
|
|
2071
|
+
...(machine ? { machine } : {}),
|
|
2072
|
+
...(!externalRunner && machine && params.reads !== undefined ? { remoteReads: params.reads } : {}),
|
|
2073
|
+
...(machineEnv ? { machineEnv } : {}),
|
|
1907
2074
|
...(params.externalJobFollowUp ? { externalJobFollowUp: params.externalJobFollowUp } : {}),
|
|
1908
2075
|
...(params.context ? { context: params.context } : {}),
|
|
1909
|
-
cwd: runnerCwd,
|
|
1910
|
-
requestedCwd: params.requestedCwd ?? runnerCwd,
|
|
1911
|
-
model,
|
|
2076
|
+
cwd: machine?.cwd ?? runnerCwd,
|
|
2077
|
+
requestedCwd: machine?.cwd ?? params.requestedCwd ?? runnerCwd,
|
|
2078
|
+
model: selectedModel,
|
|
1912
2079
|
...(contextLimit !== undefined ? { contextLimit } : {}),
|
|
1913
2080
|
...(params.fast ?? agentConfig.fast ? { fast: params.fast ?? agentConfig.fast } : {}),
|
|
1914
|
-
thinking: resolveEffectiveThinking(
|
|
2081
|
+
thinking: resolveEffectiveThinking(selectedModel, effectiveThinking),
|
|
1915
2082
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
1916
|
-
|
|
2083
|
+
...(requestedModel ? { requestedModel } : {}),
|
|
1917
2084
|
...(modelOrigin === "inherited" ? { skipPrimaryModelVerification: true } : {}),
|
|
1918
2085
|
...(availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {}),
|
|
1919
2086
|
...(ctx.modelResponseAliases ? { modelResponseAliases: ctx.modelResponseAliases } : {}),
|
|
@@ -1922,6 +2089,7 @@ export function executeAsyncSingle(
|
|
|
1922
2089
|
allowNestedSubagents: agentConfig.allowNestedSubagents,
|
|
1923
2090
|
extensions: agentConfig.extensions,
|
|
1924
2091
|
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
2092
|
+
...(!externalRunner ? { requiredExtensions } : {}),
|
|
1925
2093
|
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
1926
2094
|
mutationTools: agentConfig.mutationTools,
|
|
1927
2095
|
completionGuard: agentConfig.completionGuard,
|
|
@@ -1939,7 +2107,7 @@ export function executeAsyncSingle(
|
|
|
1939
2107
|
waitToolEnabled: params.waitToolEnabled,
|
|
1940
2108
|
waitToolDefaultTimeoutMs: params.waitToolDefaultTimeoutMs,
|
|
1941
2109
|
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
1942
|
-
definitionDigest
|
|
2110
|
+
definitionDigest,
|
|
1943
2111
|
launchBindingTask: task,
|
|
1944
2112
|
launchContractDigest,
|
|
1945
2113
|
...(extensionBindings ? { extensionBindings } : {}),
|
|
@@ -1980,6 +2148,7 @@ export function executeAsyncSingle(
|
|
|
1980
2148
|
timeoutMs,
|
|
1981
2149
|
deadlineAt,
|
|
1982
2150
|
toolTimeoutMs,
|
|
2151
|
+
checkpointBeforeDeadlineMs: params.checkpointBeforeDeadlineMs,
|
|
1983
2152
|
toolBudget: params.toolBudget,
|
|
1984
2153
|
usageBudget: params.usageBudget,
|
|
1985
2154
|
controlIntercomTarget,
|
|
@@ -1988,6 +2157,7 @@ export function executeAsyncSingle(
|
|
|
1988
2157
|
launchContractDigest,
|
|
1989
2158
|
launchResolvedExtensions,
|
|
1990
2159
|
runFanoutBudget,
|
|
2160
|
+
hostAvailableBuiltins,
|
|
1991
2161
|
...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),
|
|
1992
2162
|
...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),
|
|
1993
2163
|
...(lane ? { lane } : {}),
|
|
@@ -2026,7 +2196,7 @@ export function executeAsyncSingle(
|
|
|
2026
2196
|
const message = error instanceof Error ? error.message : String(error);
|
|
2027
2197
|
return formatAsyncStartError("single", `Failed to start async run '${id}': ${message}`);
|
|
2028
2198
|
}
|
|
2029
|
-
|
|
2199
|
+
const finishSpawnResult = (spawnResult: SpawnRunnerResult): AsyncExecutionResult => {
|
|
2030
2200
|
if (spawnResult.error) {
|
|
2031
2201
|
if (spawnResult.startupDidNotProceed) {
|
|
2032
2202
|
if (!spawnResult.runnerProcessInstanceId || params.activeAsyncCapacity?.rollbackBeforeRunnerProceed(spawnResult.runnerProcessInstanceId) !== true) params.activeAsyncCapacity?.rollback();
|
|
@@ -2103,4 +2273,6 @@ export function executeAsyncSingle(
|
|
|
2103
2273
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],
|
|
2104
2274
|
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, launchContractDigest, launchResolvedExtensions, ...(capabilityCeiling ? { capabilityCeiling } : {}), ...(params.context ? { context: params.context } : {}), ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}), ...(params.toolBudget ? { toolBudget: resolvedToolBudget.budget ?? params.toolBudget } : {}), ...(initialUsageBudget ? { usageBudget: initialUsageBudget } : {}) } as Details,
|
|
2105
2275
|
};
|
|
2276
|
+
};
|
|
2277
|
+
return spawnResultOrPromise instanceof Promise ? spawnResultOrPromise.then(finishSpawnResult) : finishSpawnResult(spawnResultOrPromise);
|
|
2106
2278
|
}
|