pi-subagents 0.55.0 → 0.57.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 +81 -3
- package/agents/claude-code-writer.md +15 -0
- package/agents/claude-code.md +15 -0
- package/agents/codex-exec-writer.md +15 -0
- package/agents/codex-exec.md +15 -0
- package/agents/cursor-agent-writer.md +14 -0
- package/agents/cursor-agent.md +14 -0
- package/docs/agents.md +111 -15
- package/docs/configuration.md +37 -0
- package/docs/extension-api.md +2 -2
- package/docs/models.md +6 -0
- package/docs/observability.md +8 -7
- package/docs/tool-reference.md +11 -1
- package/docs/workflows.md +20 -0
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +1 -1
- package/src/agents/agent-management.ts +13 -5
- package/src/agents/agent-refinements.ts +4 -4
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +242 -49
- package/src/agents/builtin-names.ts +6 -0
- package/src/agents/runtime-agent-registry.ts +8 -2
- package/src/api/preflight.ts +22 -3
- package/src/extension/config.ts +70 -0
- package/src/extension/doctor.ts +3 -3
- package/src/extension/index.ts +18 -4
- package/src/extension/public-execution.ts +29 -13
- package/src/extension/rpc.ts +20 -5
- package/src/extension/schemas.ts +9 -3
- package/src/extension/tool-description.ts +11 -9
- package/src/inspectors/herdr/actions.ts +2 -1
- package/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/src/integrations/herdr-status.ts +46 -3
- package/src/runs/background/active-async-capacity.ts +77 -10
- package/src/runs/background/async-execution.ts +148 -27
- package/src/runs/background/async-job-tracker.ts +5 -0
- package/src/runs/background/async-resume.ts +3 -0
- package/src/runs/background/async-retention.ts +20 -3
- package/src/runs/background/async-status.ts +5 -0
- package/src/runs/background/chain-root-attachment.ts +15 -1
- package/src/runs/background/fleet-view.ts +16 -10
- package/src/runs/background/inspect-rpc.ts +8 -8
- package/src/runs/background/result-files.ts +27 -14
- package/src/runs/background/result-watcher.ts +9 -5
- package/src/runs/background/run-status.ts +33 -6
- package/src/runs/background/scheduled-runs.ts +7 -1
- package/src/runs/background/subagent-runner.ts +254 -49
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/foreground/execution.ts +107 -16
- package/src/runs/foreground/foreground-control.ts +6 -0
- package/src/runs/foreground/foreground-history.ts +25 -3
- package/src/runs/foreground/subagent-executor.ts +324 -84
- package/src/runs/foreground/workflow-detach-reconcile.ts +31 -7
- package/src/runs/shared/acceptance.ts +10 -5
- package/src/runs/shared/agent-contract.ts +1 -1
- package/src/runs/shared/child-protocol.ts +21 -7
- package/src/runs/shared/claude-code-adapter.ts +129 -0
- package/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/src/runs/shared/completion-guard.ts +3 -2
- package/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/extension-bindings.ts +78 -0
- package/src/runs/shared/external-cli-contract.ts +167 -0
- package/src/runs/shared/external-cli-preflight.ts +122 -0
- package/src/runs/shared/external-cli-runner.ts +349 -54
- package/src/runs/shared/fast-mode-extension.ts +10 -0
- package/src/runs/shared/model-exclusions.ts +59 -8
- package/src/runs/shared/model-fallback.ts +25 -8
- package/src/runs/shared/mutation-evidence.ts +150 -0
- package/src/runs/shared/nested-events.ts +3 -1
- package/src/runs/shared/nested-render.ts +2 -2
- package/src/runs/shared/parallel-utils.ts +3 -0
- package/src/runs/shared/pi-args.ts +47 -0
- package/src/runs/shared/process-signal.ts +13 -0
- package/src/runs/shared/run-history.ts +21 -1
- package/src/runs/shared/structured-output.ts +18 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +9 -5
- package/src/shared/fork-context.ts +21 -0
- package/src/shared/formatters.ts +7 -1
- package/src/shared/launch-contract.ts +6 -0
- package/src/shared/pruned-fork.ts +450 -0
- package/src/shared/session-file-trust.ts +19 -0
- package/src/shared/session-tokens.ts +14 -3
- package/src/shared/settings.ts +16 -3
- package/src/shared/shortcuts.ts +17 -0
- package/src/shared/types.ts +166 -9
- package/src/shared/workflow-child-permit.ts +116 -0
- package/src/slash/delegation-adapters.ts +0 -1
- package/src/slash/slash-commands.ts +8 -6
- package/src/tui/fleet-status.ts +27 -10
- package/src/tui/fleet-transcript.ts +11 -5
- package/src/tui/fleet.ts +28 -13
- package/src/tui/render.ts +47 -21
- package/src/workflows/scripted-workflow.ts +298 -30
- package/src/workflows/workflow-child-summary.ts +117 -0
- package/src/workflows/workflow-receipt.ts +119 -4
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import type { ArtifactPaths, TimeoutRecoverySummary, TrackedMutationEvidence, TrackedMutationFingerprint, TrackedMutationSnapshot } from "../../shared/types.ts";
|
|
7
|
+
|
|
8
|
+
const MAX_TRACKED_PATHS = 500;
|
|
9
|
+
const MAX_HASH_BYTES = 1024 * 1024;
|
|
10
|
+
const MAX_TIMEOUT_FILES = 20;
|
|
11
|
+
|
|
12
|
+
function gitArguments(args: string[]): string[] {
|
|
13
|
+
// Mutation evidence must not block child startup on a stale fsmonitor daemon.
|
|
14
|
+
return ["-c", "core.fsmonitor=false", ...args];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function gitOutput(cwd: string, args: string[], maxBuffer = MAX_HASH_BYTES): string {
|
|
18
|
+
return execFileSync("git", gitArguments(args), { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], maxBuffer });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function splitNul(output: string): string[] {
|
|
22
|
+
return output.split("\0").filter((part) => part.length > 0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function hashLargeDiff(cwd: string, relativePath: string): string {
|
|
26
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-tracked-diff-"));
|
|
27
|
+
const diffPath = path.join(tempDir, "diff.patch");
|
|
28
|
+
try {
|
|
29
|
+
execFileSync("git", gitArguments(["diff", "--no-ext-diff", "--binary", `--output=${diffPath}`, "HEAD", "--", relativePath]), { cwd, stdio: "ignore" });
|
|
30
|
+
const hash = createHash("sha256");
|
|
31
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
32
|
+
const fd = fs.openSync(diffPath, "r");
|
|
33
|
+
try {
|
|
34
|
+
for (;;) {
|
|
35
|
+
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
|
|
36
|
+
if (bytesRead === 0) break;
|
|
37
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
38
|
+
}
|
|
39
|
+
} finally {
|
|
40
|
+
fs.closeSync(fd);
|
|
41
|
+
}
|
|
42
|
+
return hash.digest("hex");
|
|
43
|
+
} finally {
|
|
44
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function listChangedTrackedFiles(cwd: string): { paths: string[]; truncated: boolean } {
|
|
49
|
+
const paths = splitNul(gitOutput(cwd, ["diff", "--no-ext-diff", "--name-only", "-z", "HEAD", "--"], MAX_HASH_BYTES));
|
|
50
|
+
return { paths: paths.slice(0, MAX_TRACKED_PATHS), truncated: paths.length > MAX_TRACKED_PATHS };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fingerprintPath(cwd: string, relativePath: string): TrackedMutationFingerprint {
|
|
54
|
+
try {
|
|
55
|
+
const diff = gitOutput(cwd, ["diff", "--no-ext-diff", "--binary", "HEAD", "--", relativePath], MAX_HASH_BYTES);
|
|
56
|
+
return { kind: "diff", digest: createHash("sha256").update(diff).digest("hex") };
|
|
57
|
+
} catch {
|
|
58
|
+
return { kind: "diff", digest: hashLargeDiff(cwd, relativePath) };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function sameFingerprint(left: TrackedMutationFingerprint | undefined, right: TrackedMutationFingerprint): boolean {
|
|
63
|
+
if (!left || left.kind !== "diff" || right.kind !== "diff") return false;
|
|
64
|
+
return left.digest === right.digest;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function snapshotTrackedMutations(cwd: string): TrackedMutationSnapshot {
|
|
68
|
+
try {
|
|
69
|
+
const changed = listChangedTrackedFiles(cwd);
|
|
70
|
+
const fingerprints: Record<string, TrackedMutationFingerprint> = {};
|
|
71
|
+
for (const file of changed.paths) fingerprints[file] = fingerprintPath(cwd, file);
|
|
72
|
+
return { source: "tracked-files", trackedOnly: true, cwd, dirtyFiles: changed.paths, fingerprints, truncated: changed.truncated };
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return { source: "tracked-files", trackedOnly: true, cwd, dirtyFiles: [], fingerprints: {}, unavailable: error instanceof Error ? error.message : String(error) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function collectTrackedMutationEvidence(snapshot: TrackedMutationSnapshot, cwd = snapshot.cwd): TrackedMutationEvidence {
|
|
79
|
+
if (snapshot.unavailable) {
|
|
80
|
+
return { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: false, unavailable: snapshot.unavailable };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const current = listChangedTrackedFiles(cwd);
|
|
84
|
+
const startDirty = new Set(snapshot.dirtyFiles);
|
|
85
|
+
const candidates = new Set([...snapshot.dirtyFiles, ...current.paths]);
|
|
86
|
+
const changedFiles: string[] = [];
|
|
87
|
+
for (const file of candidates) {
|
|
88
|
+
if (!startDirty.has(file)) {
|
|
89
|
+
if (!snapshot.truncated) changedFiles.push(file);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!sameFingerprint(snapshot.fingerprints[file], fingerprintPath(cwd, file))) changedFiles.push(file);
|
|
93
|
+
}
|
|
94
|
+
changedFiles.sort();
|
|
95
|
+
return {
|
|
96
|
+
source: "tracked-files",
|
|
97
|
+
trackedOnly: true,
|
|
98
|
+
changedFiles,
|
|
99
|
+
attemptedMutation: changedFiles.length > 0,
|
|
100
|
+
truncated: snapshot.truncated || current.truncated || undefined,
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: false, unavailable: error instanceof Error ? error.message : String(error) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatPathList(paths: string[]): string {
|
|
108
|
+
if (paths.length === 0) return "none";
|
|
109
|
+
const shown = paths.slice(0, MAX_TIMEOUT_FILES).join(", ");
|
|
110
|
+
return paths.length > MAX_TIMEOUT_FILES ? `${shown}, ... (${paths.length - MAX_TIMEOUT_FILES} more)` : shown;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function buildTimeoutRecoverySummary(input: {
|
|
114
|
+
termination: "timed-out" | "stopped";
|
|
115
|
+
evidence: TrackedMutationEvidence;
|
|
116
|
+
currentTool?: string;
|
|
117
|
+
currentToolArgs?: string;
|
|
118
|
+
currentPath?: string;
|
|
119
|
+
sessionFile?: string;
|
|
120
|
+
transcriptPath?: string;
|
|
121
|
+
artifactPaths?: ArtifactPaths;
|
|
122
|
+
}): TimeoutRecoverySummary {
|
|
123
|
+
const warning = "Inspect partial changes before retrying or resuming the child.";
|
|
124
|
+
const changedFiles = input.evidence.changedFiles.slice(0, MAX_TIMEOUT_FILES);
|
|
125
|
+
const lines = [
|
|
126
|
+
"Recovery summary:",
|
|
127
|
+
`- termination: ${input.termination}`,
|
|
128
|
+
`- changed tracked files: ${input.evidence.unavailable ? `unavailable (${input.evidence.unavailable})` : formatPathList(input.evidence.changedFiles)}`,
|
|
129
|
+
];
|
|
130
|
+
if (input.currentTool) lines.push(`- active tool: ${input.currentTool}${input.currentToolArgs ? ` — ${input.currentToolArgs}` : ""}`);
|
|
131
|
+
if (input.currentPath) lines.push(`- active path: ${input.currentPath}`);
|
|
132
|
+
if (input.sessionFile) lines.push(`- session file: ${input.sessionFile}`);
|
|
133
|
+
if (input.transcriptPath) lines.push(`- transcript: ${input.transcriptPath}`);
|
|
134
|
+
if (input.artifactPaths?.outputPath) lines.push(`- output artifact: ${input.artifactPaths.outputPath}`);
|
|
135
|
+
if (input.artifactPaths?.metadataPath) lines.push(`- metadata artifact: ${input.artifactPaths.metadataPath}`);
|
|
136
|
+
lines.push(`Warning: ${warning}`);
|
|
137
|
+
return {
|
|
138
|
+
termination: input.termination,
|
|
139
|
+
changedFiles,
|
|
140
|
+
truncated: input.evidence.changedFiles.length > changedFiles.length || input.evidence.truncated || undefined,
|
|
141
|
+
currentTool: input.currentTool,
|
|
142
|
+
currentToolArgs: input.currentToolArgs,
|
|
143
|
+
currentPath: input.currentPath,
|
|
144
|
+
sessionFile: input.sessionFile,
|
|
145
|
+
transcriptPath: input.transcriptPath,
|
|
146
|
+
artifactPaths: input.artifactPaths,
|
|
147
|
+
warning,
|
|
148
|
+
message: lines.join("\n"),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -199,8 +199,10 @@ function sanitizeTokenUsage(value: unknown): NestedRunSummary["totalTokens"] | u
|
|
|
199
199
|
const input = clampNumber(raw.input);
|
|
200
200
|
const output = clampNumber(raw.output);
|
|
201
201
|
const total = clampNumber(raw.total);
|
|
202
|
+
const window = clampNumber(raw.window);
|
|
203
|
+
const windowPeak = clampNumber(raw.windowPeak);
|
|
202
204
|
return input !== undefined && output !== undefined && total !== undefined
|
|
203
|
-
? { input, output, total }
|
|
205
|
+
? { input, output, total, ...(window !== undefined ? { window } : {}), ...(windowPeak !== undefined ? { windowPeak } : {}) }
|
|
204
206
|
: undefined;
|
|
205
207
|
}
|
|
206
208
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatDuration, formatModelThinking,
|
|
1
|
+
import { formatDuration, formatModelThinking, formatTokenUsage, shortenPath } from "../../shared/formatters.ts";
|
|
2
2
|
import { formatActivityLabel } from "../../shared/status-format.ts";
|
|
3
3
|
import type { ActivityState, NestedRunSummary, NestedStepSummary } from "../../shared/types.ts";
|
|
4
4
|
|
|
@@ -68,7 +68,7 @@ function formatNestedActivity(input: {
|
|
|
68
68
|
if (input.currentPath) facts.push(shortenPath(input.currentPath));
|
|
69
69
|
if (input.turnCount !== undefined) facts.push(`${input.turnCount} turns`);
|
|
70
70
|
if (input.toolCount !== undefined) facts.push(`${input.toolCount} tools`);
|
|
71
|
-
if (input.totalTokens) facts.push(
|
|
71
|
+
if (input.totalTokens) facts.push(formatTokenUsage(input.totalTokens));
|
|
72
72
|
const activity = formatActivityLabel(input.lastActivityAt, input.activityState as ActivityState | undefined);
|
|
73
73
|
return activity || facts.length ? [activity, ...facts].filter(Boolean).join(" | ") : undefined;
|
|
74
74
|
}
|
|
@@ -29,6 +29,7 @@ export interface RunnerSubagentStep {
|
|
|
29
29
|
structured?: boolean;
|
|
30
30
|
cwd?: string;
|
|
31
31
|
model?: string;
|
|
32
|
+
fast?: boolean;
|
|
32
33
|
thinking?: string;
|
|
33
34
|
thinkingCeiling?: import("../../shared/model-info.ts").ThinkingLevel;
|
|
34
35
|
modelCandidates?: string[];
|
|
@@ -59,12 +60,14 @@ export interface RunnerSubagentStep {
|
|
|
59
60
|
schema: import("../../shared/types.ts").JsonSchemaObject;
|
|
60
61
|
schemaPath: string;
|
|
61
62
|
outputPath: string;
|
|
63
|
+
acceptanceReportPath?: string;
|
|
62
64
|
};
|
|
63
65
|
structuredOutputSchema?: import("../../shared/types.ts").JsonSchemaObject;
|
|
64
66
|
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
65
67
|
definitionDigest?: string;
|
|
66
68
|
launchBindingTask?: string;
|
|
67
69
|
launchContractDigest?: string;
|
|
70
|
+
extensionBindings?: import("./extension-bindings.ts").ExtensionBindings;
|
|
68
71
|
launchResolvedExtensions?: import("../../shared/types.ts").LaunchResolvedChildExtensionsV1;
|
|
69
72
|
runtimeAcknowledgedExtensions?: import("../../shared/types.ts").RuntimeAcknowledgedChildExtensionsV1;
|
|
70
73
|
effectiveAcceptance?: import("../../shared/types.ts").ResolvedAcceptanceConfig;
|
|
@@ -13,8 +13,10 @@ import {
|
|
|
13
13
|
type ResolvedMcpDirectToolSelection,
|
|
14
14
|
} from "./mcp-direct-tool-allowlist.ts";
|
|
15
15
|
import { resolvePiPackageRoot } from "./pi-spawn.ts";
|
|
16
|
+
import { encodeExtensionBindings, PI_SUBAGENT_EXTENSION_BINDINGS_ENV, type ExtensionBindings } from "./extension-bindings.ts";
|
|
16
17
|
import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
|
|
17
18
|
import {
|
|
19
|
+
STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV,
|
|
18
20
|
STRUCTURED_OUTPUT_CAPTURE_ENV,
|
|
19
21
|
STRUCTURED_OUTPUT_SCHEMA_ENV,
|
|
20
22
|
} from "./structured-output.ts";
|
|
@@ -104,6 +106,14 @@ const FANOUT_CHILD_EXTENSION_PATH = path.join(
|
|
|
104
106
|
"extension",
|
|
105
107
|
"fanout-child.ts",
|
|
106
108
|
);
|
|
109
|
+
const FAST_MODE_EXTENSION_PATH = path.join(
|
|
110
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
111
|
+
"fast-mode-extension.ts",
|
|
112
|
+
);
|
|
113
|
+
const FAST_MODE_ALLOWED_MODELS = new Set([
|
|
114
|
+
"openai-codex/gpt-5.6-luna",
|
|
115
|
+
"openai-codex/gpt-5.6-sol",
|
|
116
|
+
]);
|
|
107
117
|
export const SUBAGENT_CHILD_ENV = "PI_SUBAGENT_CHILD";
|
|
108
118
|
export const SUBAGENT_ORCHESTRATOR_TARGET_ENV =
|
|
109
119
|
"PI_SUBAGENT_ORCHESTRATOR_TARGET";
|
|
@@ -173,7 +183,10 @@ export interface BuildPiArgsInput {
|
|
|
173
183
|
schema: JsonSchemaObject;
|
|
174
184
|
schemaPath: string;
|
|
175
185
|
outputPath: string;
|
|
186
|
+
acceptanceReportPath?: string;
|
|
176
187
|
};
|
|
188
|
+
fast?: boolean;
|
|
189
|
+
modelCandidates?: readonly string[];
|
|
177
190
|
toolBudget?: ResolvedToolBudget;
|
|
178
191
|
allowZeroToolBudget?: boolean;
|
|
179
192
|
permissionRules?: PermissionRules;
|
|
@@ -188,6 +201,7 @@ export interface BuildPiArgsInput {
|
|
|
188
201
|
waitToolEnabled?: boolean;
|
|
189
202
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
190
203
|
thinkingCeiling?: import("../../shared/model-info.ts").ThinkingLevel;
|
|
204
|
+
extensionBindings?: ExtensionBindings;
|
|
191
205
|
}
|
|
192
206
|
|
|
193
207
|
export interface BuildPiArgsResult {
|
|
@@ -237,6 +251,28 @@ export function applyThinkingSuffix(
|
|
|
237
251
|
return `${model}:${thinking}`;
|
|
238
252
|
}
|
|
239
253
|
|
|
254
|
+
function stripThinkingSuffix(model: string): string {
|
|
255
|
+
const colonIdx = model.lastIndexOf(":");
|
|
256
|
+
if (colonIdx === -1) return model;
|
|
257
|
+
return THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))
|
|
258
|
+
? model.slice(0, colonIdx)
|
|
259
|
+
: model;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function resolveFastModeExtension(input: Pick<ResolvePiLaunchToolPlanInput, "fast" | "model" | "modelCandidates" | "agentName">): string[] {
|
|
263
|
+
if (!input.fast) return [];
|
|
264
|
+
const candidates = (input.modelCandidates?.length ? input.modelCandidates : input.model ? [input.model] : [])
|
|
265
|
+
.map(stripThinkingSuffix);
|
|
266
|
+
if (candidates.length === 0) {
|
|
267
|
+
throw new Error(`fast mode requires an explicit supported native OpenAI-Codex model${input.agentName ? ` for agent '${input.agentName}'` : ""}.`);
|
|
268
|
+
}
|
|
269
|
+
const unsupported = candidates.filter((model) => !FAST_MODE_ALLOWED_MODELS.has(model));
|
|
270
|
+
if (unsupported.length > 0) {
|
|
271
|
+
throw new Error(`fast mode supports only ${[...FAST_MODE_ALLOWED_MODELS].join(", ")}; unsupported model${unsupported.length === 1 ? "" : "s"}: ${unsupported.join(", ")}.`);
|
|
272
|
+
}
|
|
273
|
+
return [FAST_MODE_EXTENSION_PATH];
|
|
274
|
+
}
|
|
275
|
+
|
|
240
276
|
export interface ResolvePiLaunchToolPlanInput {
|
|
241
277
|
tools?: string[];
|
|
242
278
|
extensions?: string[];
|
|
@@ -251,6 +287,9 @@ export interface ResolvePiLaunchToolPlanInput {
|
|
|
251
287
|
schemaPath: string;
|
|
252
288
|
outputPath: string;
|
|
253
289
|
};
|
|
290
|
+
fast?: boolean;
|
|
291
|
+
model?: string;
|
|
292
|
+
modelCandidates?: readonly string[];
|
|
254
293
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
255
294
|
inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
256
295
|
agentName?: string;
|
|
@@ -464,8 +503,11 @@ export function resolvePiLaunchToolPlan(
|
|
|
464
503
|
: hasPermissionRules(input.permissionRules)
|
|
465
504
|
? resolvePermissionSystemExtension()
|
|
466
505
|
: undefined;
|
|
506
|
+
if (input.fast && capabilityCeiling?.denyExtensions) throw new Error("fast mode requires a child runtime extension, but this launch denies extensions.");
|
|
507
|
+
const fastModeExtensions = resolveFastModeExtension({ fast: input.fast, model: input.model, modelCandidates: input.modelCandidates, agentName: input.agentName });
|
|
467
508
|
const runtimeExtensions = [
|
|
468
509
|
PROMPT_RUNTIME_EXTENSION_PATH,
|
|
510
|
+
...fastModeExtensions,
|
|
469
511
|
...(fanoutAuthorized ? [FANOUT_CHILD_EXTENSION_PATH] : []),
|
|
470
512
|
...(permSystemExt ? [permSystemExt] : []),
|
|
471
513
|
];
|
|
@@ -605,6 +647,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
605
647
|
cwd: input.cwd,
|
|
606
648
|
requireReadTool: input.requireReadTool,
|
|
607
649
|
structuredOutput: input.structuredOutput,
|
|
650
|
+
fast: input.fast,
|
|
651
|
+
model: modelArg,
|
|
652
|
+
modelCandidates: input.modelCandidates,
|
|
608
653
|
capabilityCeiling: input.capabilityCeiling,
|
|
609
654
|
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(
|
|
610
655
|
process.env[SUBAGENT_CAPABILITY_CEILING_ENV],
|
|
@@ -668,6 +713,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
668
713
|
}
|
|
669
714
|
|
|
670
715
|
const env: Record<string, string | undefined> = {};
|
|
716
|
+
env[PI_SUBAGENT_EXTENSION_BINDINGS_ENV] = encodeExtensionBindings(input.extensionBindings);
|
|
671
717
|
const piPackageRoot =
|
|
672
718
|
process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
|
|
673
719
|
if (piPackageRoot) env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
|
|
@@ -838,6 +884,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
838
884
|
if (input.structuredOutput) {
|
|
839
885
|
env[STRUCTURED_OUTPUT_CAPTURE_ENV] = input.structuredOutput.outputPath;
|
|
840
886
|
env[STRUCTURED_OUTPUT_SCHEMA_ENV] = input.structuredOutput.schemaPath;
|
|
887
|
+
if (input.structuredOutput.acceptanceReportPath) env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV] = input.structuredOutput.acceptanceReportPath;
|
|
841
888
|
}
|
|
842
889
|
if (input.steerInboxDir) {
|
|
843
890
|
env[SUBAGENT_STEER_INBOX_ENV] = input.steerInboxDir;
|
|
@@ -2,6 +2,19 @@ export function formatProcessSignalError(signal: string): string {
|
|
|
2
2
|
return `Subagent process terminated by signal ${signal}.`;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
export function formatMidToolExitError(input: {
|
|
6
|
+
toolName: string;
|
|
7
|
+
exitCode: number | null;
|
|
8
|
+
processSignal?: string | null;
|
|
9
|
+
}): string {
|
|
10
|
+
const terminal = [`exit ${input.exitCode ?? "unknown"}`, ...(input.processSignal ? [`signal ${input.processSignal}`] : [])].join(", ");
|
|
11
|
+
return `Subagent process exited during '${input.toolName}' tool execution (${terminal}) before the tool completed. Earlier assistant output is not a terminal result.`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isOrdinaryToolForMidToolExit(toolName: string): boolean {
|
|
15
|
+
return toolName !== "intercom" && toolName !== "contact_supervisor";
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
export function isUnexplainedProcessSignal(input: {
|
|
6
19
|
processSignal?: string | null;
|
|
7
20
|
interrupted?: boolean;
|
|
@@ -2,6 +2,9 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { getAgentDir } from "../../shared/utils.ts";
|
|
5
|
+
import { isUnexplainedProcessSignal } from "./process-signal.ts";
|
|
6
|
+
|
|
7
|
+
export type RunOutcome = "completed" | "failed" | "timed_out" | "stopped" | "interrupted";
|
|
5
8
|
|
|
6
9
|
export interface RunEntry {
|
|
7
10
|
agent: string;
|
|
@@ -9,6 +12,7 @@ export interface RunEntry {
|
|
|
9
12
|
taskHash?: string;
|
|
10
13
|
ts: number;
|
|
11
14
|
status: "ok" | "error";
|
|
15
|
+
outcome?: RunOutcome;
|
|
12
16
|
duration: number;
|
|
13
17
|
exit?: number;
|
|
14
18
|
}
|
|
@@ -129,14 +133,30 @@ function appendPrivateHistoryLine(historyPath: string, line: string): void {
|
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
export function recordRun(
|
|
136
|
+
export function recordRun(
|
|
137
|
+
agent: string,
|
|
138
|
+
task: string,
|
|
139
|
+
exitCode: number,
|
|
140
|
+
durationMs: number,
|
|
141
|
+
terminal: { interrupted?: boolean; processSignal?: string | null; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean } = {},
|
|
142
|
+
): void {
|
|
133
143
|
try {
|
|
144
|
+
const outcome: RunOutcome = terminal.stopped
|
|
145
|
+
? "stopped"
|
|
146
|
+
: terminal.interrupted
|
|
147
|
+
? "interrupted"
|
|
148
|
+
: terminal.timedOut
|
|
149
|
+
? "timed_out"
|
|
150
|
+
: exitCode !== 0 && isUnexplainedProcessSignal(terminal)
|
|
151
|
+
? "stopped"
|
|
152
|
+
: exitCode === 0 ? "completed" : "failed";
|
|
134
153
|
const entry: RunEntry = {
|
|
135
154
|
agent,
|
|
136
155
|
task: REDACTED_TASK,
|
|
137
156
|
taskHash: hashTask(task),
|
|
138
157
|
ts: Math.floor(Date.now() / 1000),
|
|
139
158
|
status: exitCode === 0 ? "ok" : "error",
|
|
159
|
+
outcome,
|
|
140
160
|
duration: durationMs,
|
|
141
161
|
...(exitCode !== 0 ? { exit: exitCode } : {}),
|
|
142
162
|
};
|
|
@@ -8,12 +8,14 @@ import type { JsonSchemaObject } from "../../shared/types.ts";
|
|
|
8
8
|
|
|
9
9
|
export const STRUCTURED_OUTPUT_SCHEMA_ENV = "PI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA";
|
|
10
10
|
export const STRUCTURED_OUTPUT_CAPTURE_ENV = "PI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE";
|
|
11
|
+
export const STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV = "PI_SUBAGENT_STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE";
|
|
11
12
|
export const MISSING_STRUCTURED_OUTPUT_CALL_ERROR = "Missing structured_output call; this step has outputSchema and must finish by calling structured_output.";
|
|
12
13
|
|
|
13
14
|
export interface StructuredOutputRuntime {
|
|
14
15
|
schema: JsonSchemaObject;
|
|
15
16
|
schemaPath: string;
|
|
16
17
|
outputPath: string;
|
|
18
|
+
acceptanceReportPath?: string;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const SCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "$defs", "definitions", "dependentSchemas"] as const;
|
|
@@ -59,10 +61,13 @@ function rewriteLocalJsonPointerRefs(schema: unknown, pointerPrefix: string, inh
|
|
|
59
61
|
return rewritten;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
export function createStructuredOutputToolParameters(schema: JsonSchemaObject): JsonSchemaObject {
|
|
64
|
+
export function createStructuredOutputToolParameters(schema: JsonSchemaObject, options: { acceptanceReport?: boolean } = {}): JsonSchemaObject {
|
|
63
65
|
return {
|
|
64
66
|
type: "object",
|
|
65
|
-
properties: {
|
|
67
|
+
properties: {
|
|
68
|
+
value: rewriteLocalJsonPointerRefs(schema, "#/properties/value"),
|
|
69
|
+
...(options.acceptanceReport ? { acceptanceReport: { type: "object" } } : {}),
|
|
70
|
+
},
|
|
66
71
|
required: ["value"],
|
|
67
72
|
additionalProperties: false,
|
|
68
73
|
};
|
|
@@ -124,7 +129,7 @@ export function assertJsonSchemaObject(schema: unknown, label = "outputSchema"):
|
|
|
124
129
|
}
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string): StructuredOutputRuntime {
|
|
132
|
+
export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string, options: { captureAcceptanceReport?: boolean } = {}): StructuredOutputRuntime {
|
|
128
133
|
assertJsonSchemaObject(schema);
|
|
129
134
|
const rootDir = baseDir ?? os.tmpdir();
|
|
130
135
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
@@ -132,7 +137,7 @@ export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?
|
|
|
132
137
|
const schemaPath = path.join(dir, "schema.json");
|
|
133
138
|
const outputPath = path.join(dir, "output.json");
|
|
134
139
|
fs.writeFileSync(schemaPath, JSON.stringify(schema), { mode: 0o600 });
|
|
135
|
-
return { schema, schemaPath, outputPath };
|
|
140
|
+
return { schema, schemaPath, outputPath, ...(options.captureAcceptanceReport ? { acceptanceReportPath: path.join(dir, "acceptance-report.json") } : {}) };
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
export async function validateStructuredOutputValue(schema: JsonSchemaObject, value: unknown): Promise<{ status: "valid" } | { status: "invalid"; message: string }> {
|
|
@@ -172,6 +177,15 @@ export async function readStructuredOutput(runtime: StructuredOutputRuntime): Pr
|
|
|
172
177
|
return { value };
|
|
173
178
|
}
|
|
174
179
|
|
|
180
|
+
export function readStructuredOutputAcceptanceReport(runtime: StructuredOutputRuntime): { value?: unknown; error?: string } {
|
|
181
|
+
if (!runtime.acceptanceReportPath || !fs.existsSync(runtime.acceptanceReportPath)) return {};
|
|
182
|
+
try {
|
|
183
|
+
return { value: JSON.parse(fs.readFileSync(runtime.acceptanceReportPath, "utf-8")) };
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return { error: `Failed to read structured output acceptance report: ${error instanceof Error ? error.message : String(error)}` };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
175
189
|
export function cleanupStructuredOutputRuntime(runtime: StructuredOutputRuntime | undefined): void {
|
|
176
190
|
if (!runtime) return;
|
|
177
191
|
try {
|
|
@@ -8,7 +8,7 @@ import { decodePermissionRules, permissionDecision, PERMISSION_AUDIT_PATH_ENV, P
|
|
|
8
8
|
import { consumeSteerRequestsFromDir, MAX_STEER_QUEUE_SIZE, steerAckPathFromDir, writeSteerAckAt, writeSteerCapabilityAt, writeSteerRequestToDir, type SteerDeliveryStatus, type SteerRequest } from "../background/control-channel.ts";
|
|
9
9
|
import { SUBAGENT_CHILD_AGENT_ENV, SUBAGENT_CHILD_INDEX_ENV, SUBAGENT_FANOUT_CHILD_ENV, SUBAGENT_STEER_ACK_DIR_ENV, SUBAGENT_STEER_CAPABILITY_ENV, SUBAGENT_STEER_INBOX_ENV } from "./pi-args.ts";
|
|
10
10
|
import { RUNTIME_EXTENSION_ACK_EVENT, RUNTIME_EXTENSION_ACK_PATH_ENV, isRuntimeAcknowledgedExtensionId, writeRuntimeAcknowledgedExtensions } from "./runtime-acknowledged-extensions.ts";
|
|
11
|
-
import { createStructuredOutputToolParameters, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
|
|
11
|
+
import { createStructuredOutputToolParameters, STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
|
|
12
12
|
import {
|
|
13
13
|
CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
|
|
14
14
|
formatChildToolDiagnostic,
|
|
@@ -634,29 +634,33 @@ export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
|
|
|
634
634
|
await drainOutstandingWork({ state: waitState, events: pi.events });
|
|
635
635
|
});
|
|
636
636
|
const structuredOutputPath = process.env[STRUCTURED_OUTPUT_CAPTURE_ENV];
|
|
637
|
+
const structuredAcceptanceReportPath = process.env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV];
|
|
637
638
|
const structuredSchemaPath = process.env[STRUCTURED_OUTPUT_SCHEMA_ENV];
|
|
638
639
|
if (structuredOutputPath && structuredSchemaPath) {
|
|
639
640
|
const schema = JSON.parse(fs.readFileSync(structuredSchemaPath, "utf-8")) as JsonSchemaObject;
|
|
640
|
-
const parameters = createStructuredOutputToolParameters(schema);
|
|
641
|
+
const parameters = createStructuredOutputToolParameters(schema, { acceptanceReport: Boolean(structuredAcceptanceReportPath) });
|
|
641
642
|
const registerTool = pi.registerTool as unknown as (tool: {
|
|
642
643
|
name: string;
|
|
643
644
|
label: string;
|
|
644
645
|
description: string;
|
|
645
646
|
parameters: unknown;
|
|
646
|
-
execute: (_id: string, params: { value: unknown }) => Promise<unknown>;
|
|
647
|
+
execute: (_id: string, params: { value: unknown; acceptanceReport?: unknown }) => Promise<unknown>;
|
|
647
648
|
}) => void;
|
|
648
649
|
registerTool({
|
|
649
650
|
name: "structured_output",
|
|
650
651
|
label: "Structured Output",
|
|
651
652
|
description: "Submit the required final structured output for this subagent step. This terminates the step.",
|
|
652
|
-
parameters
|
|
653
|
-
async execute(_id: string, params: { value: unknown }) {
|
|
653
|
+
parameters,
|
|
654
|
+
async execute(_id: string, params: { value: unknown; acceptanceReport?: unknown }) {
|
|
654
655
|
const validation = await validateStructuredOutputValue(schema, params.value);
|
|
655
656
|
if (validation.status === "invalid") {
|
|
656
657
|
throw new Error(`Structured output validation failed: ${validation.message}`);
|
|
657
658
|
}
|
|
658
659
|
fs.mkdirSync(path.dirname(structuredOutputPath), { recursive: true });
|
|
659
660
|
fs.writeFileSync(structuredOutputPath, JSON.stringify(params.value), { mode: 0o600 });
|
|
661
|
+
if (structuredAcceptanceReportPath && params.acceptanceReport !== undefined) {
|
|
662
|
+
fs.writeFileSync(structuredAcceptanceReportPath, JSON.stringify(params.acceptanceReport), { mode: 0o600 });
|
|
663
|
+
}
|
|
660
664
|
return {
|
|
661
665
|
content: [{ type: "text", text: "Structured output captured." }],
|
|
662
666
|
details: { path: structuredOutputPath },
|
|
@@ -37,6 +37,8 @@ interface ForkableSessionManager {
|
|
|
37
37
|
|
|
38
38
|
interface ForkContextResolverOptions {
|
|
39
39
|
openSession?: (path: string, sessionDir?: string) => BranchSessionManager;
|
|
40
|
+
/** Rewrite a created fork before its path can be used to spawn a child. */
|
|
41
|
+
pruneSession?: (sessionFile: string) => Promise<void>;
|
|
40
42
|
/** Decide per child index whether a sanitized transcript must also disable the child's
|
|
41
43
|
* thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */
|
|
42
44
|
forceThinkingOffForIndex?: (index: number) => boolean;
|
|
@@ -48,6 +50,7 @@ interface ForkContextResolution {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
interface ForkContextResolver {
|
|
53
|
+
prepareSessionForIndex(index?: number): Promise<void>;
|
|
51
54
|
sessionFileForIndex(index?: number): string | undefined;
|
|
52
55
|
thinkingOverrideForIndex(index?: number): "off" | undefined;
|
|
53
56
|
}
|
|
@@ -190,6 +193,7 @@ export function createForkContextResolver(
|
|
|
190
193
|
): ForkContextResolver {
|
|
191
194
|
if (resolveSubagentContext(requestedContext) !== "fork") {
|
|
192
195
|
return {
|
|
196
|
+
prepareSessionForIndex: async () => {},
|
|
193
197
|
sessionFileForIndex: () => undefined,
|
|
194
198
|
thinkingOverrideForIndex: () => undefined,
|
|
195
199
|
};
|
|
@@ -226,6 +230,8 @@ export function createForkContextResolver(
|
|
|
226
230
|
"forks",
|
|
227
231
|
);
|
|
228
232
|
const cachedResolutions = new Map<number, ForkContextResolution>();
|
|
233
|
+
const preparedIndexes = new Set<number>();
|
|
234
|
+
const preparationPromises = new Map<number, Promise<void>>();
|
|
229
235
|
|
|
230
236
|
const resolveFork = (index = 0): ForkContextResolution => {
|
|
231
237
|
const cached = cachedResolutions.get(index);
|
|
@@ -274,7 +280,22 @@ export function createForkContextResolver(
|
|
|
274
280
|
};
|
|
275
281
|
|
|
276
282
|
return {
|
|
283
|
+
async prepareSessionForIndex(index = 0): Promise<void> {
|
|
284
|
+
const resolution = resolveFork(index);
|
|
285
|
+
if (!options.pruneSession || preparedIndexes.has(index)) return;
|
|
286
|
+
let preparation = preparationPromises.get(index);
|
|
287
|
+
if (!preparation) {
|
|
288
|
+
preparation = options.pruneSession(resolution.sessionFile).then(() => {
|
|
289
|
+
preparedIndexes.add(index);
|
|
290
|
+
});
|
|
291
|
+
preparationPromises.set(index, preparation);
|
|
292
|
+
}
|
|
293
|
+
await preparation;
|
|
294
|
+
},
|
|
277
295
|
sessionFileForIndex(index = 0): string | undefined {
|
|
296
|
+
if (options.pruneSession && !preparedIndexes.has(index)) {
|
|
297
|
+
throw new Error(`Pruned fork session ${index} was used before pruning completed.`);
|
|
298
|
+
}
|
|
278
299
|
return resolveFork(index).sessionFile;
|
|
279
300
|
},
|
|
280
301
|
thinkingOverrideForIndex(index = 0): "off" | undefined {
|
package/src/shared/formatters.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import * as path from "node:path";
|
|
7
|
-
import type { Usage, SingleResult } from "./types.ts";
|
|
7
|
+
import type { Usage, SingleResult, TokenUsage } from "./types.ts";
|
|
8
8
|
import type { ChainStep } from "./settings.ts";
|
|
9
9
|
import { isDynamicParallelStep, isParallelStep } from "./settings.ts";
|
|
10
10
|
import { previewDisplayText, sanitizeDisplayText } from "./display-text.ts";
|
|
@@ -17,6 +17,12 @@ export function formatTokens(n: number): string {
|
|
|
17
17
|
return n < 1000 ? String(n) : n < 10000 ? `${(n / 1000).toFixed(1)}k` : `${Math.round(n / 1000)}k`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function formatTokenUsage(usage: TokenUsage, legacyLabel = "tok"): string {
|
|
21
|
+
return usage.window !== undefined
|
|
22
|
+
? `${formatTokens(usage.window)} window · ${formatTokens(usage.total)} spent`
|
|
23
|
+
: `${formatTokens(usage.total)} ${legacyLabel}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
export function formatModelThinking(model?: string, thinking?: string): string {
|
|
21
27
|
const parsed = model ? splitKnownThinkingSuffix(model) : undefined;
|
|
22
28
|
let displayModel = parsed?.baseModel ?? model;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import type { AgentConfig } from "../agents/agents.ts";
|
|
4
|
+
import type { ExtensionBindings } from "../runs/shared/extension-bindings.ts";
|
|
4
5
|
|
|
5
6
|
export const AGENT_DEFINITION_PROJECTION_VERSION = 1 as const;
|
|
6
7
|
export const LAUNCH_BINDING_PROJECTION_VERSION = 1 as const;
|
|
@@ -46,6 +47,7 @@ export function projectAgentDefinition(agent: AgentConfig): Record<string, unkno
|
|
|
46
47
|
model: agent.model,
|
|
47
48
|
modelProvider: agent.modelProvider,
|
|
48
49
|
fallbackModels: agent.fallbackModels,
|
|
50
|
+
fast: agent.fast,
|
|
49
51
|
thinking: agent.thinking,
|
|
50
52
|
tools: agent.tools,
|
|
51
53
|
mcpDirectTools: agent.mcpDirectTools,
|
|
@@ -80,6 +82,7 @@ export interface LaunchBindingInput {
|
|
|
80
82
|
task?: string;
|
|
81
83
|
model?: string;
|
|
82
84
|
modelCandidates?: string[];
|
|
85
|
+
fast?: boolean;
|
|
83
86
|
thinking?: string;
|
|
84
87
|
systemPrompt?: string | null;
|
|
85
88
|
systemPromptMode?: AgentConfig["systemPromptMode"];
|
|
@@ -93,6 +96,7 @@ export interface LaunchBindingInput {
|
|
|
93
96
|
outputPath?: string;
|
|
94
97
|
outputMode?: string;
|
|
95
98
|
structuredOutputSchema?: unknown;
|
|
99
|
+
extensionBindings?: ExtensionBindings;
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
/** Canonical projection of the resolved inputs handed to the child. */
|
|
@@ -104,6 +108,7 @@ export function projectLaunchBinding(input: LaunchBindingInput): Record<string,
|
|
|
104
108
|
// The ordered candidate set already contains each attempted model; keeping only
|
|
105
109
|
// this set makes retries correlate to the same preflight binding.
|
|
106
110
|
modelCandidates: input.modelCandidates,
|
|
111
|
+
fast: input.fast,
|
|
107
112
|
thinking: input.thinking,
|
|
108
113
|
systemPromptDigest: input.systemPrompt === undefined || input.systemPrompt === null ? undefined : stableJsonDigest(input.systemPrompt),
|
|
109
114
|
systemPromptMode: input.systemPromptMode,
|
|
@@ -117,6 +122,7 @@ export function projectLaunchBinding(input: LaunchBindingInput): Record<string,
|
|
|
117
122
|
outputPath: input.outputPath,
|
|
118
123
|
outputMode: input.outputMode,
|
|
119
124
|
structuredOutputSchema: input.structuredOutputSchema,
|
|
125
|
+
extensionBindings: input.extensionBindings,
|
|
120
126
|
};
|
|
121
127
|
}
|
|
122
128
|
|