pi-subagents 0.33.1 → 0.35.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 +84 -4
- package/README.md +220 -31
- package/index.ts +1 -0
- package/install.mjs +1 -1
- package/package.json +23 -8
- package/prompts/review-loop.md +3 -1
- package/skills/pi-subagents/SKILL.md +87 -25
- package/src/agents/agent-management.ts +82 -15
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +94 -52
- package/src/agents/frontmatter.ts +67 -13
- package/src/agents/skills.ts +25 -12
- package/src/api/background-work.ts +197 -0
- package/src/api/delegation.ts +158 -0
- package/src/extension/chain-validation.ts +165 -0
- package/src/extension/doctor.ts +15 -0
- package/src/extension/fanout-child.ts +3 -1
- package/src/extension/index.ts +66 -124
- package/src/extension/rpc.ts +10 -2
- package/src/extension/schemas.ts +18 -14
- package/src/extension/steering-notices.ts +35 -0
- package/src/extension/tool-description.ts +20 -9
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/native-supervisor-channel.ts +184 -27
- package/src/intercom/result-intercom.ts +4 -0
- package/src/runs/background/async-execution.ts +293 -44
- package/src/runs/background/async-job-tracker.ts +56 -9
- package/src/runs/background/async-resume.ts +159 -52
- package/src/runs/background/async-status.ts +25 -18
- package/src/runs/background/auto-drain.ts +67 -0
- package/src/runs/background/chain-root-attachment.ts +16 -8
- package/src/runs/background/control-channel.ts +260 -13
- package/src/runs/background/fleet-view.ts +23 -2
- package/src/runs/background/notify.ts +79 -10
- package/src/runs/background/result-watcher.ts +12 -9
- package/src/runs/background/run-id-resolver.ts +14 -2
- package/src/runs/background/run-status.ts +23 -15
- package/src/runs/background/scheduled-runs.ts +3 -0
- package/src/runs/background/stale-run-reconciler.ts +32 -10
- package/src/runs/background/steering.ts +237 -0
- package/src/runs/background/subagent-runner.ts +898 -236
- package/src/runs/background/subagent-wait.ts +484 -0
- package/src/runs/background/top-level-async.ts +2 -1
- package/src/runs/background/wait-config.ts +36 -0
- package/src/runs/background/wait-tool.ts +26 -0
- package/src/runs/foreground/async-steering-action.ts +230 -0
- package/src/runs/foreground/chain-clarify.ts +22 -6
- package/src/runs/foreground/chain-execution.ts +50 -32
- package/src/runs/foreground/execution.ts +308 -94
- package/src/runs/foreground/subagent-executor.ts +592 -268
- package/src/runs/shared/acceptance.ts +355 -97
- package/src/runs/shared/child-protocol.ts +121 -0
- package/src/runs/shared/completion-guard.ts +8 -91
- package/src/runs/shared/dynamic-fanout.ts +6 -4
- package/src/runs/shared/model-fallback.ts +36 -0
- package/src/runs/shared/nested-events.ts +9 -4
- package/src/runs/shared/nested-render.ts +4 -1
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +36 -9
- package/src/runs/shared/pi-spawn.ts +18 -12
- package/src/runs/shared/session-lease.ts +279 -0
- package/src/runs/shared/single-output.ts +61 -6
- package/src/runs/shared/spawn-budget.ts +128 -0
- package/src/runs/shared/subagent-control.ts +10 -6
- package/src/runs/shared/subagent-prompt-runtime.ts +127 -26
- package/src/runs/shared/task-intent.ts +176 -0
- package/src/runs/shared/tool-availability.ts +65 -0
- package/src/runs/shared/turn-budget.ts +49 -4
- package/src/shared/atomic-json.ts +4 -1
- package/src/shared/fork-context.ts +28 -3
- package/src/shared/model-info.ts +8 -5
- package/src/shared/status-format.ts +7 -1
- package/src/shared/types.ts +210 -25
- package/src/shared/utils.ts +35 -7
- package/src/slash/delegation-adapters.ts +457 -0
- package/src/slash/delegation-request.ts +103 -0
- package/src/slash/prompt-template-bridge.ts +167 -344
- package/src/slash/slash-commands.ts +239 -6
- package/src/slash/subagents-admin.ts +428 -0
- package/src/slash/subagents-editor.ts +86 -0
- package/src/tui/fleet.ts +405 -0
- package/src/tui/render.ts +90 -16
- package/src/watchdog/change-signature.ts +127 -0
- package/src/watchdog/child-status.ts +205 -0
- package/src/watchdog/emission-guard.ts +123 -0
- package/src/watchdog/lsp-diagnostics.ts +532 -0
- package/src/watchdog/model-selection.ts +167 -0
- package/src/watchdog/register-child.ts +117 -0
- package/src/watchdog/register-main.ts +433 -0
- package/src/watchdog/render.ts +54 -0
- package/src/watchdog/review.ts +293 -0
- package/src/watchdog/runtime.ts +712 -0
- package/src/watchdog/settings.ts +528 -0
- package/src/watchdog/tool-actions.ts +155 -0
- package/src/watchdog/turn-delta.ts +161 -0
- package/src/watchdog/types.ts +188 -0
- package/src/watchdog/warning-format.ts +73 -0
- package/src/runs/background/wait.ts +0 -353
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { findModelInfo, type ModelInfo } from "./model-info.ts";
|
|
5
6
|
|
|
6
7
|
type SubagentExecutionContext = "fresh" | "fork";
|
|
7
8
|
|
|
@@ -35,6 +36,9 @@ interface ForkableSessionManager {
|
|
|
35
36
|
|
|
36
37
|
interface ForkContextResolverOptions {
|
|
37
38
|
openSession?: (path: string, sessionDir?: string) => BranchSessionManager;
|
|
39
|
+
/** Decide per child index whether a sanitized transcript must also disable the child's
|
|
40
|
+
* thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */
|
|
41
|
+
forceThinkingOffForIndex?: (index: number) => boolean;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
interface ForkContextResolution {
|
|
@@ -51,6 +55,20 @@ export function resolveSubagentContext(value: unknown): SubagentExecutionContext
|
|
|
51
55
|
return value === "fork" ? "fork" : "fresh";
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
/** Decide whether a resolved child model uses Anthropic's provider or message API, which
|
|
59
|
+
* requires the sanitized fork to disable thinking. Unknown models stay conservative. */
|
|
60
|
+
export function forkedChildRequiresThinkingOff(
|
|
61
|
+
model: string | undefined,
|
|
62
|
+
availableModels?: ModelInfo[],
|
|
63
|
+
preferredProvider?: string,
|
|
64
|
+
): boolean {
|
|
65
|
+
if (!model) return true;
|
|
66
|
+
const info = findModelInfo(model, availableModels, preferredProvider);
|
|
67
|
+
if (!info) return true;
|
|
68
|
+
return info.provider.toLowerCase() === "anthropic"
|
|
69
|
+
|| info.api?.toLowerCase() === "anthropic-messages";
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
function isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry["message"], block: unknown): boolean {
|
|
55
73
|
if (!message || !block || typeof block !== "object" || !("type" in block)) return false;
|
|
56
74
|
const provider = typeof message.provider === "string" ? message.provider.toLowerCase() : "";
|
|
@@ -94,7 +112,6 @@ function sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {
|
|
|
94
112
|
entry.message.content = filtered;
|
|
95
113
|
sanitized = true;
|
|
96
114
|
}
|
|
97
|
-
if (sanitized) appendThinkingOffEntry(entries);
|
|
98
115
|
return sanitized;
|
|
99
116
|
}
|
|
100
117
|
|
|
@@ -150,6 +167,8 @@ export function createForkContextResolver(
|
|
|
150
167
|
if (!sessionFile) {
|
|
151
168
|
throw new Error("Session manager did not return a forked session file.");
|
|
152
169
|
}
|
|
170
|
+
const forceThinkingOff = (sanitized: boolean): boolean =>
|
|
171
|
+
sanitized && (options.forceThinkingOffForIndex?.(index) ?? true);
|
|
153
172
|
let thinkingOverride: "off" | undefined;
|
|
154
173
|
if (!fs.existsSync(sessionFile)) {
|
|
155
174
|
const header = sourceManager.getHeader?.();
|
|
@@ -157,13 +176,19 @@ export function createForkContextResolver(
|
|
|
157
176
|
if (!header || !entries) {
|
|
158
177
|
throw new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);
|
|
159
178
|
}
|
|
160
|
-
if (sanitizeUnsafeThinkingBlocks(entries))
|
|
179
|
+
if (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {
|
|
180
|
+
appendThinkingOffEntry(entries);
|
|
181
|
+
thinkingOverride = "off";
|
|
182
|
+
}
|
|
161
183
|
fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
|
|
162
184
|
fs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
163
185
|
} else {
|
|
164
186
|
const entries = readSessionEntries(sessionFile);
|
|
165
187
|
if (sanitizeUnsafeThinkingBlocks(entries)) {
|
|
166
|
-
|
|
188
|
+
if (forceThinkingOff(true)) {
|
|
189
|
+
appendThinkingOffEntry(entries);
|
|
190
|
+
thinkingOverride = "off";
|
|
191
|
+
}
|
|
167
192
|
fs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
168
193
|
}
|
|
169
194
|
}
|
package/src/shared/model-info.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
1
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
2
2
|
export type ThinkingLevel = typeof THINKING_LEVELS[number];
|
|
3
3
|
export type ThinkingLevelMap = Partial<Record<ThinkingLevel, string | null>>;
|
|
4
4
|
|
|
@@ -6,6 +6,7 @@ export interface ModelInfo {
|
|
|
6
6
|
provider: string;
|
|
7
7
|
id: string;
|
|
8
8
|
fullId: string;
|
|
9
|
+
api?: string;
|
|
9
10
|
reasoning?: boolean;
|
|
10
11
|
thinkingLevelMap?: ThinkingLevelMap;
|
|
11
12
|
}
|
|
@@ -13,6 +14,7 @@ export interface ModelInfo {
|
|
|
13
14
|
interface RegistryModelLike {
|
|
14
15
|
provider: string;
|
|
15
16
|
id: string;
|
|
17
|
+
api?: string;
|
|
16
18
|
reasoning?: boolean;
|
|
17
19
|
thinkingLevelMap?: ThinkingLevelMap;
|
|
18
20
|
}
|
|
@@ -22,6 +24,7 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
|
|
|
22
24
|
provider: model.provider,
|
|
23
25
|
id: model.id,
|
|
24
26
|
fullId: `${model.provider}/${model.id}`,
|
|
27
|
+
api: model.api,
|
|
25
28
|
reasoning: model.reasoning,
|
|
26
29
|
thinkingLevelMap: model.thinkingLevelMap,
|
|
27
30
|
};
|
|
@@ -30,7 +33,7 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
|
|
|
30
33
|
/** Resolve the effective thinking level from a model string (which may contain a known suffix like `:high`)
|
|
31
34
|
* and an explicit thinking config value. Returns `undefined` when no thinking is applicable
|
|
32
35
|
* (e.g. no model was specified, or the model has no suffix and no config was provided). */
|
|
33
|
-
export function resolveEffectiveThinking(model: string | undefined, configThinking: string | undefined): string | undefined {
|
|
36
|
+
export function resolveEffectiveThinking(model: string | undefined, configThinking: string | false | undefined): string | undefined {
|
|
34
37
|
if (!model) return undefined;
|
|
35
38
|
const { thinkingSuffix } = splitKnownThinkingSuffix(model);
|
|
36
39
|
if (thinkingSuffix) return thinkingSuffix.slice(1);
|
|
@@ -63,15 +66,15 @@ export function findModelInfo(model: string | undefined, availableModels: ModelI
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export function getSupportedThinkingLevels(model: ModelInfo | undefined): ThinkingLevel[] {
|
|
66
|
-
if (!model) return
|
|
69
|
+
if (!model) return THINKING_LEVELS.filter((level) => level !== "max");
|
|
67
70
|
if (model.reasoning === false) return ["off"];
|
|
68
71
|
|
|
69
|
-
if (!model.thinkingLevelMap) return
|
|
72
|
+
if (!model.thinkingLevelMap) return THINKING_LEVELS.filter((level) => level !== "max");
|
|
70
73
|
|
|
71
74
|
const levels = THINKING_LEVELS.filter((level) => {
|
|
72
75
|
const mapped = model.thinkingLevelMap?.[level];
|
|
73
76
|
if (mapped === null) return false;
|
|
74
|
-
if (level === "xhigh") return mapped !== undefined;
|
|
77
|
+
if (level === "xhigh" || level === "max") return mapped !== undefined;
|
|
75
78
|
return true;
|
|
76
79
|
});
|
|
77
80
|
return levels;
|
|
@@ -16,7 +16,10 @@ export function formatActivityLabel(lastActivityAt: number | undefined, activity
|
|
|
16
16
|
}
|
|
17
17
|
const age = formatActivityAge(Math.max(0, now - lastActivityAt));
|
|
18
18
|
if (activityState === "needs_attention") return `no activity for ${age}`;
|
|
19
|
-
if (activityState === "active_long_running")
|
|
19
|
+
if (activityState === "active_long_running") {
|
|
20
|
+
const activityAge = age === "now" ? "now" : `${age} ago`;
|
|
21
|
+
return `active but long-running · last activity ${activityAge}`;
|
|
22
|
+
}
|
|
20
23
|
return age === "now" ? "active now" : `active ${age} ago`;
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -27,6 +30,7 @@ function isCompletedStepStatus(status: AsyncJobStep["status"]): boolean {
|
|
|
27
30
|
export function aggregateStepStatus(steps: StepStatusLike[]): AsyncJobStep["status"] {
|
|
28
31
|
if (steps.some((step) => step.status === "running")) return "running";
|
|
29
32
|
if (steps.some((step) => step.status === "failed")) return "failed";
|
|
33
|
+
if (steps.some((step) => step.status === "stopped")) return "stopped";
|
|
30
34
|
if (steps.some((step) => step.status === "paused")) return "paused";
|
|
31
35
|
if (steps.length > 0 && steps.every((step) => isCompletedStepStatus(step.status))) return "complete";
|
|
32
36
|
return "pending";
|
|
@@ -40,10 +44,12 @@ export function formatParallelOutcome(steps: StepStatusLike[], total: number, op
|
|
|
40
44
|
const running = steps.filter((step) => step.status === "running").length;
|
|
41
45
|
const done = steps.filter((step) => isCompletedStepStatus(step.status)).length;
|
|
42
46
|
const failed = steps.filter((step) => step.status === "failed").length;
|
|
47
|
+
const stopped = steps.filter((step) => step.status === "stopped").length;
|
|
43
48
|
const paused = steps.filter((step) => step.status === "paused").length;
|
|
44
49
|
const parts = [`${done}/${total} done`];
|
|
45
50
|
if (options.showRunning !== false && running > 0) parts.unshift(formatAgentRunningLabel(running));
|
|
46
51
|
if (failed > 0) parts.push(`${failed} failed`);
|
|
52
|
+
if (stopped > 0) parts.push(`${stopped} stopped`);
|
|
47
53
|
if (paused > 0) parts.push(`${paused} paused`);
|
|
48
54
|
return parts.join(" · ");
|
|
49
55
|
}
|
package/src/shared/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface MaxOutputConfig {
|
|
|
20
20
|
|
|
21
21
|
export type OutputMode = "inline" | "file-only";
|
|
22
22
|
|
|
23
|
+
export type AcceptanceRole = "read-only" | "writer";
|
|
24
|
+
|
|
23
25
|
export type JsonSchemaObject = Record<string, unknown>;
|
|
24
26
|
|
|
25
27
|
export interface ChainOutputMapEntry {
|
|
@@ -31,7 +33,7 @@ export interface ChainOutputMapEntry {
|
|
|
31
33
|
|
|
32
34
|
export type ChainOutputMap = Record<string, ChainOutputMapEntry>;
|
|
33
35
|
|
|
34
|
-
export type WorkflowNodeStatus = "pending" | "running" | "completed" | "failed" | "paused" | "detached";
|
|
36
|
+
export type WorkflowNodeStatus = "pending" | "running" | "completed" | "failed" | "paused" | "stopped" | "detached";
|
|
35
37
|
|
|
36
38
|
export interface WorkflowGraphNode {
|
|
37
39
|
id: string;
|
|
@@ -121,12 +123,13 @@ export interface ToolBudgetState extends ResolvedToolBudget {
|
|
|
121
123
|
blockedTool?: string;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
export type TurnBudgetOutcome = "within-budget" | "wrap-up-requested" | "exceeded";
|
|
126
|
+
export type TurnBudgetOutcome = "within-budget" | "wrap-up-requested" | "termination-deferred" | "exceeded";
|
|
125
127
|
|
|
126
128
|
export interface TurnBudgetState extends ResolvedTurnBudget {
|
|
127
129
|
outcome: TurnBudgetOutcome;
|
|
128
130
|
turnCount: number;
|
|
129
131
|
wrapUpRequestedAtTurn?: number;
|
|
132
|
+
terminationDeferredAtTurn?: number;
|
|
130
133
|
exceededAtTurn?: number;
|
|
131
134
|
}
|
|
132
135
|
|
|
@@ -181,6 +184,12 @@ export interface CompletionBatchConfig {
|
|
|
181
184
|
stragglerWindowMs?: number;
|
|
182
185
|
}
|
|
183
186
|
|
|
187
|
+
export interface WaitToolConfigObject {
|
|
188
|
+
enabled?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type WaitToolConfig = boolean | WaitToolConfigObject;
|
|
192
|
+
|
|
184
193
|
export interface ControlEvent {
|
|
185
194
|
type: ControlEventType;
|
|
186
195
|
from?: ActivityState;
|
|
@@ -203,14 +212,113 @@ export interface ControlEvent {
|
|
|
203
212
|
recentFailureSummary?: string;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
|
-
export type SubagentResultStatus = "completed" | "failed" | "paused" | "detached";
|
|
215
|
+
export type SubagentResultStatus = "completed" | "failed" | "paused" | "stopped" | "detached";
|
|
207
216
|
export type SubagentRunMode = "single" | "parallel" | "chain";
|
|
208
|
-
export const SUBAGENT_LIFECYCLE_ARTIFACT_VERSION =
|
|
217
|
+
export const SUBAGENT_LIFECYCLE_ARTIFACT_VERSION = 2;
|
|
209
218
|
export type SubagentLifecycleArtifactVersion = typeof SUBAGENT_LIFECYCLE_ARTIFACT_VERSION;
|
|
210
219
|
|
|
220
|
+
export type SteeringActionState = "delivered" | "scheduled" | "pending" | "partial" | "recovered" | "failed";
|
|
221
|
+
export type SteeringTargetState = "scheduled" | "routed" | "delivered" | "late" | "failed" | "recovered";
|
|
222
|
+
|
|
223
|
+
export interface SteeringTargetStatus {
|
|
224
|
+
index: number;
|
|
225
|
+
state: SteeringTargetState;
|
|
226
|
+
routedAt?: number;
|
|
227
|
+
deliveredAt?: number;
|
|
228
|
+
lateDeliveredAt?: number;
|
|
229
|
+
failedAt?: number;
|
|
230
|
+
recoveredAt?: number;
|
|
231
|
+
reason?: string;
|
|
232
|
+
replacementRunId?: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface SteeringRequestStatus {
|
|
236
|
+
id: string;
|
|
237
|
+
requestedAt: number;
|
|
238
|
+
source?: string;
|
|
239
|
+
messagePreview: string;
|
|
240
|
+
targets: SteeringTargetStatus[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface SteeringStatus {
|
|
244
|
+
requested: number;
|
|
245
|
+
scheduled: number;
|
|
246
|
+
pending: number;
|
|
247
|
+
delivered: number;
|
|
248
|
+
failed: number;
|
|
249
|
+
recovered: number;
|
|
250
|
+
lastRequestedAt?: number;
|
|
251
|
+
lastDeliveredAt?: number;
|
|
252
|
+
recent: SteeringRequestStatus[];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface SteerActionTarget {
|
|
256
|
+
index: number;
|
|
257
|
+
state: SteeringTargetState;
|
|
258
|
+
deliveredAt?: number;
|
|
259
|
+
lateDeliveredAt?: number;
|
|
260
|
+
reason?: string;
|
|
261
|
+
replacementRunId?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface SteerActionResult {
|
|
265
|
+
requestId: string;
|
|
266
|
+
state: SteeringActionState;
|
|
267
|
+
sourceRunId: string;
|
|
268
|
+
replacementRunId?: string;
|
|
269
|
+
targets: SteerActionTarget[];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface SteeringNotice {
|
|
273
|
+
type: "subagent.steering.notice";
|
|
274
|
+
ts: number;
|
|
275
|
+
runId: string;
|
|
276
|
+
requestId: string;
|
|
277
|
+
state: "failed" | "partial" | "recovered";
|
|
278
|
+
message: string;
|
|
279
|
+
currentSessionId?: string;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface SteeringRecoveryDescriptor {
|
|
283
|
+
version: 1;
|
|
284
|
+
sourceRunId: string;
|
|
285
|
+
agent: string;
|
|
286
|
+
sessionFile?: string;
|
|
287
|
+
cwd: string;
|
|
288
|
+
model?: string;
|
|
289
|
+
fallbackModels?: string[];
|
|
290
|
+
thinking?: string;
|
|
291
|
+
tools?: string[];
|
|
292
|
+
extensions?: string[];
|
|
293
|
+
subagentOnlyExtensions?: string[];
|
|
294
|
+
mcpDirectTools?: string[];
|
|
295
|
+
systemPrompt?: string;
|
|
296
|
+
systemPromptMode: "append" | "replace";
|
|
297
|
+
inheritProjectContext: boolean;
|
|
298
|
+
inheritSkills: boolean;
|
|
299
|
+
skills?: string[];
|
|
300
|
+
skillPath?: string[];
|
|
301
|
+
agentFilePath?: string;
|
|
302
|
+
completionGuard?: boolean;
|
|
303
|
+
memory?: { scope: "project" | "user"; path: string };
|
|
304
|
+
outputPath?: string;
|
|
305
|
+
outputMode: "inline" | "file-only";
|
|
306
|
+
acceptance?: AcceptanceInput;
|
|
307
|
+
controlConfig?: ResolvedControlConfig;
|
|
308
|
+
absoluteDeadlineAt?: number;
|
|
309
|
+
initialTurnBudget?: ResolvedTurnBudget;
|
|
310
|
+
initialToolBudget?: ResolvedToolBudget;
|
|
311
|
+
maxSubagentDepth: number;
|
|
312
|
+
maxOutput?: MaxOutputConfig;
|
|
313
|
+
share: boolean;
|
|
314
|
+
sessionDir?: string;
|
|
315
|
+
artifactsDir?: string;
|
|
316
|
+
artifactConfig?: ArtifactConfig;
|
|
317
|
+
}
|
|
318
|
+
|
|
211
319
|
export type PublicNestedStepSummary = Pick<
|
|
212
320
|
NestedStepSummary,
|
|
213
|
-
"agent" | "status" | "sessionFile" | "transcriptPath" | "transcriptError" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "toolBudget" | "toolBudgetBlocked" | "startedAt" | "endedAt" | "error" | "timedOut"
|
|
321
|
+
"agent" | "status" | "sessionFile" | "transcriptPath" | "transcriptError" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "toolBudget" | "toolBudgetBlocked" | "startedAt" | "endedAt" | "error" | "timedOut" | "stopped"
|
|
214
322
|
> & {
|
|
215
323
|
children?: PublicNestedRunSummary[];
|
|
216
324
|
};
|
|
@@ -223,7 +331,7 @@ export type CostSummary = {
|
|
|
223
331
|
|
|
224
332
|
export type PublicNestedRunSummary = Pick<
|
|
225
333
|
NestedRunSummary,
|
|
226
|
-
"id" | "parentRunId" | "parentStepIndex" | "parentAgent" | "depth" | "path" | "asyncDir" | "sessionId" | "sessionFile" | "intercomTarget" | "ownerIntercomTarget" | "leafIntercomTarget" | "ownerState" | "mode" | "state" | "agent" | "agents" | "currentStep" | "chainStepCount" | "parallelGroups" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "toolBudget" | "toolBudgetBlocked" | "totalTokens" | "totalCost" | "startedAt" | "endedAt" | "lastUpdate" | "error" | "timeoutMs" | "deadlineAt" | "timedOut" | "turnBudget" | "turnBudgetExceeded" | "wrapUpRequested"
|
|
334
|
+
"id" | "parentRunId" | "parentStepIndex" | "parentAgent" | "depth" | "path" | "asyncDir" | "sessionId" | "sessionFile" | "intercomTarget" | "ownerIntercomTarget" | "leafIntercomTarget" | "ownerState" | "mode" | "state" | "agent" | "agents" | "currentStep" | "chainStepCount" | "parallelGroups" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "toolBudget" | "toolBudgetBlocked" | "totalTokens" | "totalCost" | "startedAt" | "endedAt" | "lastUpdate" | "error" | "timeoutMs" | "deadlineAt" | "timedOut" | "stopped" | "turnBudget" | "turnBudgetExceeded" | "wrapUpRequested"
|
|
227
335
|
> & {
|
|
228
336
|
steps?: PublicNestedStepSummary[];
|
|
229
337
|
children?: PublicNestedRunSummary[];
|
|
@@ -263,6 +371,15 @@ export interface SubagentResultIntercomPayload {
|
|
|
263
371
|
// Progress Tracking
|
|
264
372
|
// ============================================================================
|
|
265
373
|
|
|
374
|
+
export interface ChildWatchdogProgress {
|
|
375
|
+
phase: "idle" | "reviewing" | "autofollow" | "settling" | "stale" | "failed";
|
|
376
|
+
seq: number;
|
|
377
|
+
lastUpdate: number;
|
|
378
|
+
followUpPending: boolean;
|
|
379
|
+
reason?: string;
|
|
380
|
+
timedOut?: boolean;
|
|
381
|
+
}
|
|
382
|
+
|
|
266
383
|
export interface AgentProgress {
|
|
267
384
|
index: number;
|
|
268
385
|
agent: string;
|
|
@@ -283,6 +400,7 @@ export interface AgentProgress {
|
|
|
283
400
|
durationMs: number;
|
|
284
401
|
error?: string;
|
|
285
402
|
failedTool?: string;
|
|
403
|
+
watchdog?: ChildWatchdogProgress;
|
|
286
404
|
}
|
|
287
405
|
|
|
288
406
|
export interface ToolCallSummary {
|
|
@@ -353,7 +471,8 @@ export interface AcceptanceConfig {
|
|
|
353
471
|
reason?: string;
|
|
354
472
|
}
|
|
355
473
|
|
|
356
|
-
|
|
474
|
+
/** Bare "none" is not accepted: use { level: "none", reason: "..." }; false remains a deprecated shorthand. */
|
|
475
|
+
export type AcceptanceInput = Exclude<AcceptanceLevel, "none"> | false | AcceptanceConfig;
|
|
357
476
|
|
|
358
477
|
export interface ResolvedAcceptanceGate extends AcceptanceGate {
|
|
359
478
|
id: string;
|
|
@@ -426,6 +545,7 @@ export interface AcceptanceReviewResult {
|
|
|
426
545
|
}
|
|
427
546
|
|
|
428
547
|
export type AcceptanceLedgerStatus =
|
|
548
|
+
| "pending"
|
|
429
549
|
| "not-required"
|
|
430
550
|
| "claimed"
|
|
431
551
|
| "attested"
|
|
@@ -453,6 +573,15 @@ export interface AcceptanceLedger {
|
|
|
453
573
|
};
|
|
454
574
|
}
|
|
455
575
|
|
|
576
|
+
export interface ProtocolOutputLimit {
|
|
577
|
+
code: "protocol_output_limit";
|
|
578
|
+
stream: "stdout" | "stderr";
|
|
579
|
+
limitBytes: number;
|
|
580
|
+
observedBytes: number;
|
|
581
|
+
diagnosticPrefix: string;
|
|
582
|
+
diagnosticTail: string;
|
|
583
|
+
}
|
|
584
|
+
|
|
456
585
|
export interface SingleResult {
|
|
457
586
|
agent: string;
|
|
458
587
|
task: string;
|
|
@@ -461,6 +590,7 @@ export interface SingleResult {
|
|
|
461
590
|
detachedReason?: string;
|
|
462
591
|
interrupted?: boolean;
|
|
463
592
|
timedOut?: boolean;
|
|
593
|
+
stopped?: boolean;
|
|
464
594
|
turnBudget?: TurnBudgetState;
|
|
465
595
|
turnBudgetExceeded?: boolean;
|
|
466
596
|
wrapUpRequested?: boolean;
|
|
@@ -473,6 +603,7 @@ export interface SingleResult {
|
|
|
473
603
|
modelAttempts?: ModelAttempt[];
|
|
474
604
|
controlEvents?: ControlEvent[];
|
|
475
605
|
error?: string;
|
|
606
|
+
protocolError?: ProtocolOutputLimit;
|
|
476
607
|
sessionFile?: string;
|
|
477
608
|
skills?: string[];
|
|
478
609
|
skillsWarning?: string;
|
|
@@ -493,6 +624,25 @@ export interface SingleResult {
|
|
|
493
624
|
transcriptPath?: string;
|
|
494
625
|
transcriptError?: string;
|
|
495
626
|
children?: NestedRunSummary[];
|
|
627
|
+
watchdog?: ChildWatchdogProgress;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export interface SpawnBudgetGrant {
|
|
631
|
+
sessionId: string;
|
|
632
|
+
amount: number;
|
|
633
|
+
grantedAt: number;
|
|
634
|
+
previousLimit: number;
|
|
635
|
+
limit: number;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export interface SpawnBudgetSnapshot {
|
|
639
|
+
used: number;
|
|
640
|
+
configuredLimit: number | null;
|
|
641
|
+
granted: number;
|
|
642
|
+
limit: number | null;
|
|
643
|
+
remaining: number | null;
|
|
644
|
+
grantRemaining: number | null;
|
|
645
|
+
grantHistory: SpawnBudgetGrant[];
|
|
496
646
|
}
|
|
497
647
|
|
|
498
648
|
export interface Details {
|
|
@@ -501,11 +651,13 @@ export interface Details {
|
|
|
501
651
|
context?: "fresh" | "fork";
|
|
502
652
|
results: SingleResult[];
|
|
503
653
|
controlEvents?: ControlEvent[];
|
|
654
|
+
steering?: SteerActionResult;
|
|
504
655
|
asyncId?: string;
|
|
505
656
|
asyncDir?: string;
|
|
506
657
|
timeoutMs?: number;
|
|
507
658
|
deadlineAt?: number;
|
|
508
659
|
timedOut?: boolean;
|
|
660
|
+
stopped?: boolean;
|
|
509
661
|
turnBudget?: ResolvedTurnBudget;
|
|
510
662
|
toolBudget?: ResolvedToolBudget;
|
|
511
663
|
progress?: AgentProgress[];
|
|
@@ -530,6 +682,7 @@ export interface Details {
|
|
|
530
682
|
totalChildUsage?: Usage;
|
|
531
683
|
// Aggregated cost across all agents in the run
|
|
532
684
|
totalCost?: CostSummary;
|
|
685
|
+
spawnBudget?: SpawnBudgetSnapshot;
|
|
533
686
|
}
|
|
534
687
|
|
|
535
688
|
// ============================================================================
|
|
@@ -564,7 +717,7 @@ export interface AsyncParallelGroupStatus {
|
|
|
564
717
|
stepIndex: number;
|
|
565
718
|
}
|
|
566
719
|
|
|
567
|
-
export type NestedRunState = "queued" | "running" | "complete" | "failed" | "paused";
|
|
720
|
+
export type NestedRunState = "queued" | "running" | "complete" | "failed" | "paused" | "stopped";
|
|
568
721
|
export type NestedOwnerState = "live" | "gone" | "unknown";
|
|
569
722
|
|
|
570
723
|
export interface NestedRunAddress {
|
|
@@ -578,7 +731,7 @@ export interface NestedRunAddress {
|
|
|
578
731
|
|
|
579
732
|
export interface NestedStepSummary {
|
|
580
733
|
agent: string;
|
|
581
|
-
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
|
|
734
|
+
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused" | "stopped";
|
|
582
735
|
sessionFile?: string;
|
|
583
736
|
transcriptPath?: string;
|
|
584
737
|
transcriptError?: string;
|
|
@@ -592,7 +745,9 @@ export interface NestedStepSummary {
|
|
|
592
745
|
startedAt?: number;
|
|
593
746
|
endedAt?: number;
|
|
594
747
|
error?: string;
|
|
748
|
+
watchdog?: ChildWatchdogProgress;
|
|
595
749
|
timedOut?: boolean;
|
|
750
|
+
stopped?: boolean;
|
|
596
751
|
turnBudget?: TurnBudgetState;
|
|
597
752
|
turnBudgetExceeded?: boolean;
|
|
598
753
|
wrapUpRequested?: boolean;
|
|
@@ -636,6 +791,7 @@ export interface NestedRunSummary extends NestedRunAddress {
|
|
|
636
791
|
timeoutMs?: number;
|
|
637
792
|
deadlineAt?: number;
|
|
638
793
|
timedOut?: boolean;
|
|
794
|
+
stopped?: boolean;
|
|
639
795
|
turnBudget?: TurnBudgetState;
|
|
640
796
|
turnBudgetExceeded?: boolean;
|
|
641
797
|
wrapUpRequested?: boolean;
|
|
@@ -660,6 +816,10 @@ export interface AsyncStartedEvent {
|
|
|
660
816
|
mode?: SubagentRunMode;
|
|
661
817
|
agent?: string;
|
|
662
818
|
agents?: string[];
|
|
819
|
+
/** Truncated first child task retained for backwards compatibility. */
|
|
820
|
+
task?: string;
|
|
821
|
+
/** Workflow-level caller task, falling back to the first child task. */
|
|
822
|
+
goal?: string;
|
|
663
823
|
chain?: string[];
|
|
664
824
|
chainStepCount?: number;
|
|
665
825
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
@@ -675,7 +835,8 @@ export interface AsyncStatus {
|
|
|
675
835
|
runId: string;
|
|
676
836
|
sessionId?: string;
|
|
677
837
|
mode: SubagentRunMode;
|
|
678
|
-
|
|
838
|
+
isNested?: boolean;
|
|
839
|
+
state: "queued" | "running" | "complete" | "failed" | "paused" | "stopped";
|
|
679
840
|
error?: string;
|
|
680
841
|
activityState?: ActivityState;
|
|
681
842
|
lastActivityAt?: number;
|
|
@@ -684,14 +845,14 @@ export interface AsyncStatus {
|
|
|
684
845
|
currentPath?: string;
|
|
685
846
|
turnCount?: number;
|
|
686
847
|
toolCount?: number;
|
|
687
|
-
|
|
688
|
-
lastSteerAt?: number;
|
|
848
|
+
steering?: SteeringStatus;
|
|
689
849
|
startedAt: number;
|
|
690
850
|
endedAt?: number;
|
|
691
851
|
lastUpdate?: number;
|
|
692
852
|
timeoutMs?: number;
|
|
693
853
|
deadlineAt?: number;
|
|
694
854
|
timedOut?: boolean;
|
|
855
|
+
stopped?: boolean;
|
|
695
856
|
turnBudget?: TurnBudgetState;
|
|
696
857
|
turnBudgetExceeded?: boolean;
|
|
697
858
|
wrapUpRequested?: boolean;
|
|
@@ -710,7 +871,7 @@ export interface AsyncStatus {
|
|
|
710
871
|
label?: string;
|
|
711
872
|
outputName?: string;
|
|
712
873
|
structured?: boolean;
|
|
713
|
-
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
|
|
874
|
+
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused" | "stopped";
|
|
714
875
|
children?: NestedRunSummary[];
|
|
715
876
|
sessionFile?: string;
|
|
716
877
|
transcriptPath?: string;
|
|
@@ -730,6 +891,7 @@ export interface AsyncStatus {
|
|
|
730
891
|
durationMs?: number;
|
|
731
892
|
exitCode?: number | null;
|
|
732
893
|
timedOut?: boolean;
|
|
894
|
+
stopped?: boolean;
|
|
733
895
|
turnBudget?: TurnBudgetState;
|
|
734
896
|
turnBudgetExceeded?: boolean;
|
|
735
897
|
wrapUpRequested?: boolean;
|
|
@@ -742,13 +904,13 @@ export interface AsyncStatus {
|
|
|
742
904
|
attemptedModels?: string[];
|
|
743
905
|
modelAttempts?: ModelAttempt[];
|
|
744
906
|
totalCost?: CostSummary;
|
|
745
|
-
|
|
746
|
-
lastSteerAt?: number;
|
|
907
|
+
steering?: SteeringStatus;
|
|
747
908
|
error?: string;
|
|
748
909
|
structuredOutput?: unknown;
|
|
749
910
|
structuredOutputPath?: string;
|
|
750
911
|
structuredOutputSchemaPath?: string;
|
|
751
912
|
acceptance?: AcceptanceLedger;
|
|
913
|
+
watchdog?: ChildWatchdogProgress;
|
|
752
914
|
}>;
|
|
753
915
|
sessionDir?: string;
|
|
754
916
|
outputFile?: string;
|
|
@@ -765,7 +927,7 @@ export type AsyncJobStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
|
765
927
|
export interface AsyncJobState {
|
|
766
928
|
asyncId: string;
|
|
767
929
|
asyncDir: string;
|
|
768
|
-
status: "queued" | "running" | "complete" | "failed" | "paused";
|
|
930
|
+
status: "queued" | "running" | "complete" | "failed" | "paused" | "stopped";
|
|
769
931
|
pid?: number;
|
|
770
932
|
sessionId?: string;
|
|
771
933
|
activityState?: ActivityState;
|
|
@@ -775,8 +937,7 @@ export interface AsyncJobState {
|
|
|
775
937
|
currentPath?: string;
|
|
776
938
|
turnCount?: number;
|
|
777
939
|
toolCount?: number;
|
|
778
|
-
|
|
779
|
-
lastSteerAt?: number;
|
|
940
|
+
steering?: SteeringStatus;
|
|
780
941
|
mode?: SubagentRunMode;
|
|
781
942
|
agents?: string[];
|
|
782
943
|
currentStep?: number;
|
|
@@ -793,6 +954,7 @@ export interface AsyncJobState {
|
|
|
793
954
|
timeoutMs?: number;
|
|
794
955
|
deadlineAt?: number;
|
|
795
956
|
timedOut?: boolean;
|
|
957
|
+
stopped?: boolean;
|
|
796
958
|
turnBudget?: TurnBudgetState;
|
|
797
959
|
turnBudgetExceeded?: boolean;
|
|
798
960
|
wrapUpRequested?: boolean;
|
|
@@ -813,11 +975,16 @@ export interface ForegroundResumeChild {
|
|
|
813
975
|
sessionFile?: string;
|
|
814
976
|
status: SubagentResultStatus;
|
|
815
977
|
exitCode?: number;
|
|
978
|
+
error?: string;
|
|
816
979
|
finalOutput?: string;
|
|
980
|
+
outputMode?: OutputMode;
|
|
981
|
+
savedOutputPath?: string;
|
|
982
|
+
outputSaveError?: string;
|
|
817
983
|
artifactPaths?: ArtifactPaths;
|
|
818
984
|
transcriptPath?: string;
|
|
819
985
|
transcriptError?: string;
|
|
820
986
|
detachedReason?: string;
|
|
987
|
+
acceptance?: AcceptanceLedger;
|
|
821
988
|
updatedAt?: number;
|
|
822
989
|
}
|
|
823
990
|
|
|
@@ -825,6 +992,8 @@ export interface ForegroundResumeRun {
|
|
|
825
992
|
runId: string;
|
|
826
993
|
mode: SubagentRunMode;
|
|
827
994
|
cwd: string;
|
|
995
|
+
/** Originating parent session. Detached exits can outlive the active session. */
|
|
996
|
+
sessionId?: string;
|
|
828
997
|
updatedAt: number;
|
|
829
998
|
children: ForegroundResumeChild[];
|
|
830
999
|
}
|
|
@@ -833,8 +1002,16 @@ export interface SubagentState {
|
|
|
833
1002
|
baseCwd: string;
|
|
834
1003
|
currentSessionId: string | null;
|
|
835
1004
|
subagentInProgress?: boolean;
|
|
836
|
-
subagentSpawns?: {
|
|
1005
|
+
subagentSpawns?: {
|
|
1006
|
+
sessionId: string | null;
|
|
1007
|
+
count: number;
|
|
1008
|
+
configuredLimit?: number | null;
|
|
1009
|
+
granted?: number;
|
|
1010
|
+
grantHistory?: SpawnBudgetGrant[];
|
|
1011
|
+
};
|
|
837
1012
|
asyncJobs: Map<string, AsyncJobState>;
|
|
1013
|
+
/** Current-session active and recent async runs for the native fleet inspector. */
|
|
1014
|
+
fleetJobs?: Map<string, AsyncJobState>;
|
|
838
1015
|
foregroundRuns?: Map<string, ForegroundResumeRun>;
|
|
839
1016
|
foregroundControls: Map<string, {
|
|
840
1017
|
runId: string;
|
|
@@ -897,8 +1074,10 @@ export const INTERCOM_DETACH_REQUEST_EVENT = "pi-intercom:detach-request";
|
|
|
897
1074
|
export const INTERCOM_DETACH_RESPONSE_EVENT = "pi-intercom:detach-response";
|
|
898
1075
|
export const SUBAGENT_ASYNC_STARTED_EVENT = "subagent:async-started";
|
|
899
1076
|
export const SUBAGENT_ASYNC_COMPLETE_EVENT = "subagent:async-complete";
|
|
1077
|
+
export const SUBAGENT_FOREGROUND_COMPLETE_EVENT = "subagent:foreground-complete";
|
|
900
1078
|
export const SUBAGENT_CONTROL_EVENT = "subagent:control-event";
|
|
901
1079
|
export const SUBAGENT_CONTROL_INTERCOM_EVENT = "subagent:control-intercom";
|
|
1080
|
+
export const SUBAGENT_STEERING_NOTICE_EVENT = "subagent:steering-notice";
|
|
902
1081
|
export const SUBAGENT_RESULT_INTERCOM_EVENT = "subagent:result-intercom";
|
|
903
1082
|
export const SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT = "subagent:result-intercom-delivery";
|
|
904
1083
|
|
|
@@ -935,6 +1114,8 @@ export interface RunSyncOptions {
|
|
|
935
1114
|
outputPath?: string;
|
|
936
1115
|
outputMode?: OutputMode;
|
|
937
1116
|
maxSubagentDepth?: number;
|
|
1117
|
+
/** Effective parent wait-tool setting propagated to the child runtime. */
|
|
1118
|
+
waitToolEnabled?: boolean;
|
|
938
1119
|
nestedRoute?: NestedRouteInfo;
|
|
939
1120
|
/** Override the agent's default model (format: "provider/id" or just "id") */
|
|
940
1121
|
modelOverride?: string;
|
|
@@ -997,12 +1178,16 @@ export interface ScheduledRunsConfig {
|
|
|
997
1178
|
|
|
998
1179
|
export interface ExtensionConfig {
|
|
999
1180
|
asyncByDefault?: boolean;
|
|
1181
|
+
/** Show the above-editor async runs widget. Defaults to true. */
|
|
1182
|
+
asyncWidget?: boolean;
|
|
1000
1183
|
/** Tool description variant registered for the parent-facing subagent tool. Defaults to full. */
|
|
1001
1184
|
toolDescriptionMode?: ToolDescriptionMode;
|
|
1002
1185
|
forceTopLevelAsync?: boolean;
|
|
1186
|
+
waitTool?: WaitToolConfig;
|
|
1003
1187
|
defaultSessionDir?: string;
|
|
1004
1188
|
singleRunOutputBaseDir?: string;
|
|
1005
1189
|
maxSubagentDepth?: number;
|
|
1190
|
+
/** Optional cumulative session cap. Unset or 0 means unlimited. */
|
|
1006
1191
|
maxSubagentSpawnsPerSession?: number;
|
|
1007
1192
|
/** Global cap on simultaneously-running subagent tasks within a single run. Defaults to 20. */
|
|
1008
1193
|
globalConcurrencyLimit?: number;
|
|
@@ -1110,8 +1295,7 @@ export const SLASH_SUBAGENT_CANCEL_EVENT = "subagent:slash:cancel";
|
|
|
1110
1295
|
export const POLL_INTERVAL_MS = 250;
|
|
1111
1296
|
export const MAX_WIDGET_JOBS = 4;
|
|
1112
1297
|
export const DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
1113
|
-
export const
|
|
1114
|
-
export const SUBAGENT_ACTIONS = ["list", "get", "models", "create", "update", "delete", "eject", "disable", "enable", "reset", "status", "interrupt", "resume", "steer", "append-step", "doctor", "schedule", "schedule-list", "schedule-status", "schedule-cancel"] as const;
|
|
1298
|
+
export const SUBAGENT_ACTIONS = ["list", "get", "models", "create", "update", "delete", "eject", "disable", "enable", "reset", "status", "grant-spawn-budget", "interrupt", "resume", "steer", "stop", "append-step", "doctor", "watchdog.status", "watchdog.check", "watchdog.configure", "watchdog.recommend-model", "schedule", "schedule-list", "schedule-status", "schedule-cancel"] as const;
|
|
1115
1299
|
|
|
1116
1300
|
export const DEFAULT_FORK_PREAMBLE =
|
|
1117
1301
|
"You are a delegated subagent running from a fork of the parent session. " +
|
|
@@ -1196,10 +1380,11 @@ export function normalizeMaxSubagentSpawnsPerSession(value: unknown): number | u
|
|
|
1196
1380
|
return normalizeNonNegativeInteger(value);
|
|
1197
1381
|
}
|
|
1198
1382
|
|
|
1199
|
-
export function resolveMaxSubagentSpawnsPerSession(configMaxSpawns?: number): number {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1383
|
+
export function resolveMaxSubagentSpawnsPerSession(configMaxSpawns?: number): number | undefined {
|
|
1384
|
+
const envMaxSpawns = normalizeMaxSubagentSpawnsPerSession(process.env.PI_SUBAGENT_MAX_SPAWNS_PER_SESSION);
|
|
1385
|
+
if (envMaxSpawns !== undefined) return envMaxSpawns === 0 ? undefined : envMaxSpawns;
|
|
1386
|
+
const configuredMaxSpawns = normalizeMaxSubagentSpawnsPerSession(configMaxSpawns);
|
|
1387
|
+
return configuredMaxSpawns === 0 ? undefined : configuredMaxSpawns;
|
|
1203
1388
|
}
|
|
1204
1389
|
|
|
1205
1390
|
// ============================================================================
|