pi-subagents 0.24.4 → 0.27.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 +29 -0
- package/README.md +145 -27
- package/package.json +1 -1
- package/prompts/parallel-context-build.md +3 -1
- package/prompts/parallel-handoff-plan.md +3 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +71 -20
- package/src/agents/agent-management.ts +57 -15
- package/src/agents/agent-serializer.ts +3 -2
- package/src/agents/agents.ts +47 -16
- package/src/agents/chain-serializer.ts +120 -0
- package/src/extension/fanout-child.ts +171 -0
- package/src/extension/index.ts +7 -2
- package/src/extension/schemas.ts +138 -5
- package/src/intercom/result-intercom.ts +108 -0
- package/src/runs/background/async-execution.ts +185 -10
- package/src/runs/background/async-job-tracker.ts +41 -6
- package/src/runs/background/async-resume.ts +28 -15
- package/src/runs/background/async-status.ts +71 -31
- package/src/runs/background/result-watcher.ts +111 -54
- package/src/runs/background/run-id-resolver.ts +83 -0
- package/src/runs/background/run-status.ts +89 -4
- package/src/runs/background/stale-run-reconciler.ts +46 -1
- package/src/runs/background/subagent-runner.ts +648 -42
- package/src/runs/foreground/chain-execution.ts +331 -118
- package/src/runs/foreground/execution.ts +226 -10
- package/src/runs/foreground/subagent-executor.ts +377 -14
- package/src/runs/shared/acceptance-contract.ts +291 -0
- package/src/runs/shared/acceptance-evaluation.ts +221 -0
- package/src/runs/shared/acceptance-finalization.ts +161 -0
- package/src/runs/shared/acceptance-reports.ts +127 -0
- package/src/runs/shared/acceptance.ts +22 -0
- package/src/runs/shared/chain-outputs.ts +101 -0
- package/src/runs/shared/completion-guard.ts +26 -3
- package/src/runs/shared/dynamic-fanout.ts +293 -0
- package/src/runs/shared/nested-events.ts +819 -0
- package/src/runs/shared/nested-path.ts +52 -0
- package/src/runs/shared/nested-render.ts +115 -0
- package/src/runs/shared/parallel-utils.ts +31 -1
- package/src/runs/shared/pi-args.ts +73 -5
- package/src/runs/shared/structured-output.ts +77 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +77 -7
- package/src/runs/shared/workflow-graph.ts +206 -0
- package/src/shared/formatters.ts +2 -2
- package/src/shared/settings.ts +53 -4
- package/src/shared/types.ts +345 -0
- package/src/slash/slash-commands.ts +41 -3
- package/src/tui/render.ts +268 -43
package/src/shared/types.ts
CHANGED
|
@@ -19,6 +19,51 @@ export interface MaxOutputConfig {
|
|
|
19
19
|
|
|
20
20
|
export type OutputMode = "inline" | "file-only";
|
|
21
21
|
|
|
22
|
+
export type JsonSchemaObject = Record<string, unknown>;
|
|
23
|
+
|
|
24
|
+
export interface ChainOutputMapEntry {
|
|
25
|
+
text: string;
|
|
26
|
+
structured?: unknown;
|
|
27
|
+
agent: string;
|
|
28
|
+
stepIndex: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ChainOutputMap = Record<string, ChainOutputMapEntry>;
|
|
32
|
+
|
|
33
|
+
export type WorkflowNodeStatus = "pending" | "running" | "completed" | "failed" | "paused" | "detached";
|
|
34
|
+
|
|
35
|
+
export interface WorkflowGraphNode {
|
|
36
|
+
id: string;
|
|
37
|
+
kind: "step" | "parallel-group" | "dynamic-parallel-group" | "agent";
|
|
38
|
+
agent?: string;
|
|
39
|
+
phase?: string;
|
|
40
|
+
label: string;
|
|
41
|
+
status: WorkflowNodeStatus;
|
|
42
|
+
flatIndex?: number;
|
|
43
|
+
stepIndex?: number;
|
|
44
|
+
children?: WorkflowGraphNode[];
|
|
45
|
+
dynamic?: {
|
|
46
|
+
sourceOutput: string;
|
|
47
|
+
sourcePath: string;
|
|
48
|
+
itemName: string;
|
|
49
|
+
maxItems?: number;
|
|
50
|
+
collectAs?: string;
|
|
51
|
+
};
|
|
52
|
+
itemKey?: string;
|
|
53
|
+
outputName?: string;
|
|
54
|
+
structured?: boolean;
|
|
55
|
+
acceptanceStatus?: AcceptanceLedgerStatus;
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WorkflowGraphSnapshot {
|
|
60
|
+
runId: string;
|
|
61
|
+
mode: "chain" | "parallel" | "single";
|
|
62
|
+
phases: Array<{ title: string; nodeIds: string[] }>;
|
|
63
|
+
nodes: WorkflowGraphNode[];
|
|
64
|
+
currentNodeId?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
22
67
|
export interface SavedOutputReference {
|
|
23
68
|
path: string;
|
|
24
69
|
bytes: number;
|
|
@@ -83,6 +128,8 @@ export interface ControlEvent {
|
|
|
83
128
|
agent: string;
|
|
84
129
|
index?: number;
|
|
85
130
|
runId: string;
|
|
131
|
+
nestedRunId?: string;
|
|
132
|
+
nestingPath?: NestedRunAddress["path"];
|
|
86
133
|
message: string;
|
|
87
134
|
reason?: "idle" | "completion_guard" | "active_long_running" | "tool_failures" | "time_threshold" | "turn_threshold" | "token_threshold";
|
|
88
135
|
turns?: number;
|
|
@@ -98,6 +145,21 @@ export interface ControlEvent {
|
|
|
98
145
|
export type SubagentResultStatus = "completed" | "failed" | "paused" | "detached";
|
|
99
146
|
export type SubagentRunMode = "single" | "parallel" | "chain";
|
|
100
147
|
|
|
148
|
+
export type PublicNestedStepSummary = Pick<
|
|
149
|
+
NestedStepSummary,
|
|
150
|
+
"agent" | "status" | "sessionFile" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "startedAt" | "endedAt" | "error"
|
|
151
|
+
> & {
|
|
152
|
+
children?: PublicNestedRunSummary[];
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export type PublicNestedRunSummary = Pick<
|
|
156
|
+
NestedRunSummary,
|
|
157
|
+
"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" | "totalTokens" | "startedAt" | "endedAt" | "lastUpdate" | "error"
|
|
158
|
+
> & {
|
|
159
|
+
steps?: PublicNestedStepSummary[];
|
|
160
|
+
children?: PublicNestedRunSummary[];
|
|
161
|
+
};
|
|
162
|
+
|
|
101
163
|
export interface SubagentResultIntercomChild {
|
|
102
164
|
agent: string;
|
|
103
165
|
status: SubagentResultStatus;
|
|
@@ -106,6 +168,7 @@ export interface SubagentResultIntercomChild {
|
|
|
106
168
|
artifactPath?: string;
|
|
107
169
|
sessionPath?: string;
|
|
108
170
|
intercomTarget?: string;
|
|
171
|
+
children?: PublicNestedRunSummary[];
|
|
109
172
|
}
|
|
110
173
|
|
|
111
174
|
export interface SubagentResultIntercomPayload {
|
|
@@ -176,6 +239,175 @@ export interface ModelAttempt {
|
|
|
176
239
|
usage?: Usage;
|
|
177
240
|
}
|
|
178
241
|
|
|
242
|
+
export type AcceptanceProvenanceLevel = "none" | "attested" | "checked" | "verified" | "reviewed";
|
|
243
|
+
|
|
244
|
+
export type AcceptanceEvidenceKind =
|
|
245
|
+
| "changed-files"
|
|
246
|
+
| "tests-added"
|
|
247
|
+
| "commands-run"
|
|
248
|
+
| "validation-output"
|
|
249
|
+
| "residual-risks"
|
|
250
|
+
| "no-staged-files"
|
|
251
|
+
| "diff-summary"
|
|
252
|
+
| "review-findings"
|
|
253
|
+
| "manual-notes";
|
|
254
|
+
|
|
255
|
+
export interface AcceptanceGate {
|
|
256
|
+
id: string;
|
|
257
|
+
must: string;
|
|
258
|
+
evidence?: AcceptanceEvidenceKind[];
|
|
259
|
+
severity?: "required" | "recommended";
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export interface AcceptanceVerifyCommand {
|
|
263
|
+
id: string;
|
|
264
|
+
command: string;
|
|
265
|
+
timeoutMs?: number;
|
|
266
|
+
cwd?: string;
|
|
267
|
+
env?: Record<string, string>;
|
|
268
|
+
allowFailure?: boolean;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface AcceptanceReviewGate {
|
|
272
|
+
agent?: string;
|
|
273
|
+
focus?: string;
|
|
274
|
+
required?: boolean;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface AcceptanceConfig {
|
|
278
|
+
criteria?: Array<string | AcceptanceGate>;
|
|
279
|
+
evidence?: AcceptanceEvidenceKind[];
|
|
280
|
+
verify?: AcceptanceVerifyCommand[];
|
|
281
|
+
review?: AcceptanceReviewGate;
|
|
282
|
+
stopRules?: string[];
|
|
283
|
+
maxFinalizationTurns?: number;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export type AcceptanceInput = AcceptanceConfig;
|
|
287
|
+
|
|
288
|
+
export interface ResolvedAcceptanceGate extends AcceptanceGate {
|
|
289
|
+
id: string;
|
|
290
|
+
must: string;
|
|
291
|
+
evidence: AcceptanceEvidenceKind[];
|
|
292
|
+
severity: "required" | "recommended";
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface ResolvedAcceptanceConfig {
|
|
296
|
+
level: AcceptanceProvenanceLevel;
|
|
297
|
+
explicit: boolean;
|
|
298
|
+
inferredReason: string[];
|
|
299
|
+
criteria: ResolvedAcceptanceGate[];
|
|
300
|
+
evidence: AcceptanceEvidenceKind[];
|
|
301
|
+
verify: AcceptanceVerifyCommand[];
|
|
302
|
+
review?: AcceptanceReviewGate;
|
|
303
|
+
stopRules: string[];
|
|
304
|
+
finalization: {
|
|
305
|
+
mode: "none" | "self-review-loop";
|
|
306
|
+
maxTurns: number;
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface AcceptanceReport {
|
|
311
|
+
criteriaSatisfied?: Array<{
|
|
312
|
+
id?: string;
|
|
313
|
+
status: "satisfied" | "not-satisfied" | "not-applicable";
|
|
314
|
+
evidence: string;
|
|
315
|
+
}>;
|
|
316
|
+
changedFiles?: string[];
|
|
317
|
+
testsAddedOrUpdated?: string[];
|
|
318
|
+
commandsRun?: Array<{
|
|
319
|
+
command: string;
|
|
320
|
+
result: "passed" | "failed" | "not-run";
|
|
321
|
+
summary: string;
|
|
322
|
+
}>;
|
|
323
|
+
validationOutput?: string[];
|
|
324
|
+
residualRisks?: string[];
|
|
325
|
+
noStagedFiles?: boolean;
|
|
326
|
+
diffSummary?: string;
|
|
327
|
+
reviewFindings?: string[];
|
|
328
|
+
manualNotes?: string;
|
|
329
|
+
notes?: string;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export type AcceptanceRuntimeCheckStatus = "passed" | "failed" | "not-applicable";
|
|
333
|
+
|
|
334
|
+
export interface AcceptanceRuntimeCheck {
|
|
335
|
+
id: string;
|
|
336
|
+
status: AcceptanceRuntimeCheckStatus;
|
|
337
|
+
message: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export interface AcceptanceVerifyResult {
|
|
341
|
+
id: string;
|
|
342
|
+
command: string;
|
|
343
|
+
cwd?: string;
|
|
344
|
+
exitCode: number | null;
|
|
345
|
+
status: "passed" | "failed" | "timed-out" | "allowed-failure";
|
|
346
|
+
stdout?: string;
|
|
347
|
+
stderr?: string;
|
|
348
|
+
durationMs: number;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface AcceptanceReviewResult {
|
|
352
|
+
status: "no-blockers" | "blockers" | "needs-parent-decision";
|
|
353
|
+
findings: Array<{
|
|
354
|
+
severity: "blocker" | "non-blocking";
|
|
355
|
+
file?: string;
|
|
356
|
+
issue: string;
|
|
357
|
+
rationale: string;
|
|
358
|
+
}>;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export type AcceptanceLedgerStatus =
|
|
362
|
+
| "not-required"
|
|
363
|
+
| "claimed"
|
|
364
|
+
| "attested"
|
|
365
|
+
| "checked"
|
|
366
|
+
| "verified"
|
|
367
|
+
| "reviewed"
|
|
368
|
+
| "accepted"
|
|
369
|
+
| "rejected";
|
|
370
|
+
|
|
371
|
+
export interface AcceptanceFinalizationTurn {
|
|
372
|
+
turn: number;
|
|
373
|
+
prompt: string;
|
|
374
|
+
status: AcceptanceLedgerStatus;
|
|
375
|
+
rawOutput?: string;
|
|
376
|
+
report?: AcceptanceReport;
|
|
377
|
+
parseError?: string;
|
|
378
|
+
runtimeChecks: AcceptanceRuntimeCheck[];
|
|
379
|
+
verifyRuns: AcceptanceVerifyResult[];
|
|
380
|
+
failureMessage?: string;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export interface AcceptanceFinalizationLedger {
|
|
384
|
+
mode: "self-review-loop";
|
|
385
|
+
status: "not-run" | "completed" | "failed";
|
|
386
|
+
maxTurns: number;
|
|
387
|
+
turns: AcceptanceFinalizationTurn[];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export interface AcceptanceLedger {
|
|
391
|
+
status: AcceptanceLedgerStatus;
|
|
392
|
+
explicit: boolean;
|
|
393
|
+
effectiveAcceptance: ResolvedAcceptanceConfig;
|
|
394
|
+
inferredReason: string[];
|
|
395
|
+
criteria: ResolvedAcceptanceGate[];
|
|
396
|
+
childReport?: AcceptanceReport;
|
|
397
|
+
childReportParseError?: string;
|
|
398
|
+
initialChildReport?: AcceptanceReport;
|
|
399
|
+
initialChildReportParseError?: string;
|
|
400
|
+
runtimeChecks: AcceptanceRuntimeCheck[];
|
|
401
|
+
verifyRuns: AcceptanceVerifyResult[];
|
|
402
|
+
reviewResult?: AcceptanceReviewResult;
|
|
403
|
+
finalization?: AcceptanceFinalizationLedger;
|
|
404
|
+
parentDecision?: {
|
|
405
|
+
status: "accepted" | "rejected";
|
|
406
|
+
at: string;
|
|
407
|
+
reason?: string;
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
179
411
|
export interface SingleResult {
|
|
180
412
|
agent: string;
|
|
181
413
|
task: string;
|
|
@@ -203,6 +435,10 @@ export interface SingleResult {
|
|
|
203
435
|
savedOutputPath?: string;
|
|
204
436
|
outputReference?: SavedOutputReference;
|
|
205
437
|
outputSaveError?: string;
|
|
438
|
+
structuredOutput?: unknown;
|
|
439
|
+
structuredOutputPath?: string;
|
|
440
|
+
structuredOutputSchemaPath?: string;
|
|
441
|
+
acceptance?: AcceptanceLedger;
|
|
206
442
|
}
|
|
207
443
|
|
|
208
444
|
export interface Details {
|
|
@@ -229,6 +465,8 @@ export interface Details {
|
|
|
229
465
|
chainAgents?: string[]; // Agent names in order, e.g., ["scout", "planner"]
|
|
230
466
|
totalSteps?: number; // Total steps in chain
|
|
231
467
|
currentStepIndex?: number; // 0-indexed current step (for running chains)
|
|
468
|
+
workflowGraph?: WorkflowGraphSnapshot;
|
|
469
|
+
outputs?: ChainOutputMap;
|
|
232
470
|
}
|
|
233
471
|
|
|
234
472
|
// ============================================================================
|
|
@@ -261,6 +499,76 @@ export interface AsyncParallelGroupStatus {
|
|
|
261
499
|
stepIndex: number;
|
|
262
500
|
}
|
|
263
501
|
|
|
502
|
+
export type NestedRunState = "queued" | "running" | "complete" | "failed" | "paused";
|
|
503
|
+
export type NestedOwnerState = "live" | "gone" | "unknown";
|
|
504
|
+
|
|
505
|
+
export interface NestedRunAddress {
|
|
506
|
+
id: string;
|
|
507
|
+
parentRunId: string;
|
|
508
|
+
parentStepIndex?: number;
|
|
509
|
+
parentAgent?: string;
|
|
510
|
+
depth: number;
|
|
511
|
+
path: Array<{ runId: string; stepIndex?: number; agent?: string }>;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export interface NestedStepSummary {
|
|
515
|
+
agent: string;
|
|
516
|
+
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
|
|
517
|
+
sessionFile?: string;
|
|
518
|
+
activityState?: ActivityState;
|
|
519
|
+
lastActivityAt?: number;
|
|
520
|
+
currentTool?: string;
|
|
521
|
+
currentToolStartedAt?: number;
|
|
522
|
+
currentPath?: string;
|
|
523
|
+
turnCount?: number;
|
|
524
|
+
toolCount?: number;
|
|
525
|
+
startedAt?: number;
|
|
526
|
+
endedAt?: number;
|
|
527
|
+
error?: string;
|
|
528
|
+
children?: NestedRunSummary[];
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export interface NestedRunSummary extends NestedRunAddress {
|
|
532
|
+
asyncDir?: string;
|
|
533
|
+
pid?: number;
|
|
534
|
+
sessionId?: string;
|
|
535
|
+
sessionFile?: string;
|
|
536
|
+
intercomTarget?: string;
|
|
537
|
+
ownerIntercomTarget?: string;
|
|
538
|
+
leafIntercomTarget?: string;
|
|
539
|
+
ownerState?: NestedOwnerState;
|
|
540
|
+
controlInbox?: string;
|
|
541
|
+
capabilityToken?: string;
|
|
542
|
+
mode?: SubagentRunMode;
|
|
543
|
+
state: NestedRunState;
|
|
544
|
+
agent?: string;
|
|
545
|
+
agents?: string[];
|
|
546
|
+
currentStep?: number;
|
|
547
|
+
chainStepCount?: number;
|
|
548
|
+
parallelGroups?: AsyncParallelGroupStatus[];
|
|
549
|
+
steps?: NestedStepSummary[];
|
|
550
|
+
children?: NestedRunSummary[];
|
|
551
|
+
activityState?: ActivityState;
|
|
552
|
+
lastActivityAt?: number;
|
|
553
|
+
currentTool?: string;
|
|
554
|
+
currentToolStartedAt?: number;
|
|
555
|
+
currentPath?: string;
|
|
556
|
+
turnCount?: number;
|
|
557
|
+
toolCount?: number;
|
|
558
|
+
totalTokens?: TokenUsage;
|
|
559
|
+
startedAt?: number;
|
|
560
|
+
endedAt?: number;
|
|
561
|
+
lastUpdate?: number;
|
|
562
|
+
error?: string;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export interface NestedRouteInfo {
|
|
566
|
+
rootRunId: string;
|
|
567
|
+
eventSink: string;
|
|
568
|
+
controlInbox: string;
|
|
569
|
+
capabilityToken: string;
|
|
570
|
+
}
|
|
571
|
+
|
|
264
572
|
export interface AsyncStartedEvent {
|
|
265
573
|
id?: string;
|
|
266
574
|
asyncDir?: string;
|
|
@@ -272,6 +580,8 @@ export interface AsyncStartedEvent {
|
|
|
272
580
|
chain?: string[];
|
|
273
581
|
chainStepCount?: number;
|
|
274
582
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
583
|
+
workflowGraph?: WorkflowGraphSnapshot;
|
|
584
|
+
nestedRoute?: NestedRouteInfo;
|
|
275
585
|
}
|
|
276
586
|
|
|
277
587
|
export interface AsyncStatus {
|
|
@@ -294,9 +604,15 @@ export interface AsyncStatus {
|
|
|
294
604
|
currentStep?: number;
|
|
295
605
|
chainStepCount?: number;
|
|
296
606
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
607
|
+
workflowGraph?: WorkflowGraphSnapshot;
|
|
297
608
|
steps?: Array<{
|
|
298
609
|
agent: string;
|
|
610
|
+
phase?: string;
|
|
611
|
+
label?: string;
|
|
612
|
+
outputName?: string;
|
|
613
|
+
structured?: boolean;
|
|
299
614
|
status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
|
|
615
|
+
children?: NestedRunSummary[];
|
|
300
616
|
sessionFile?: string;
|
|
301
617
|
activityState?: ActivityState;
|
|
302
618
|
lastActivityAt?: number;
|
|
@@ -319,11 +635,16 @@ export interface AsyncStatus {
|
|
|
319
635
|
attemptedModels?: string[];
|
|
320
636
|
modelAttempts?: ModelAttempt[];
|
|
321
637
|
error?: string;
|
|
638
|
+
structuredOutput?: unknown;
|
|
639
|
+
structuredOutputPath?: string;
|
|
640
|
+
structuredOutputSchemaPath?: string;
|
|
641
|
+
acceptance?: AcceptanceLedger;
|
|
322
642
|
}>;
|
|
323
643
|
sessionDir?: string;
|
|
324
644
|
outputFile?: string;
|
|
325
645
|
totalTokens?: TokenUsage;
|
|
326
646
|
sessionFile?: string;
|
|
647
|
+
outputs?: ChainOutputMap;
|
|
327
648
|
}
|
|
328
649
|
|
|
329
650
|
export type AsyncJobStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
@@ -361,6 +682,8 @@ export interface AsyncJobState {
|
|
|
361
682
|
totalTokens?: TokenUsage;
|
|
362
683
|
sessionFile?: string;
|
|
363
684
|
controlEventCursor?: number;
|
|
685
|
+
nestedRoute?: NestedRouteInfo;
|
|
686
|
+
nestedChildren?: NestedRunSummary[];
|
|
364
687
|
}
|
|
365
688
|
|
|
366
689
|
export interface ForegroundResumeChild {
|
|
@@ -398,6 +721,8 @@ export interface SubagentState {
|
|
|
398
721
|
turnCount?: number;
|
|
399
722
|
tokens?: number;
|
|
400
723
|
toolCount?: number;
|
|
724
|
+
nestedRoute?: NestedRouteInfo;
|
|
725
|
+
nestedChildren?: NestedRunSummary[];
|
|
401
726
|
interrupt?: () => boolean;
|
|
402
727
|
}>;
|
|
403
728
|
lastForegroundControlId: string | null;
|
|
@@ -473,6 +798,7 @@ export interface RunSyncOptions {
|
|
|
473
798
|
outputPath?: string;
|
|
474
799
|
outputMode?: OutputMode;
|
|
475
800
|
maxSubagentDepth?: number;
|
|
801
|
+
nestedRoute?: NestedRouteInfo;
|
|
476
802
|
/** Override the agent's default model (format: "provider/id" or just "id") */
|
|
477
803
|
modelOverride?: string;
|
|
478
804
|
/** Registry models available for heuristic bare-model resolution */
|
|
@@ -481,6 +807,18 @@ export interface RunSyncOptions {
|
|
|
481
807
|
preferredModelProvider?: string;
|
|
482
808
|
/** Skills to inject (overrides agent default if provided) */
|
|
483
809
|
skills?: string[];
|
|
810
|
+
structuredOutput?: {
|
|
811
|
+
schema: JsonSchemaObject;
|
|
812
|
+
schemaPath: string;
|
|
813
|
+
outputPath: string;
|
|
814
|
+
};
|
|
815
|
+
acceptance?: AcceptanceInput;
|
|
816
|
+
acceptanceContext?: {
|
|
817
|
+
mode?: SubagentRunMode;
|
|
818
|
+
async?: boolean;
|
|
819
|
+
dynamic?: boolean;
|
|
820
|
+
dynamicGroup?: boolean;
|
|
821
|
+
};
|
|
484
822
|
}
|
|
485
823
|
|
|
486
824
|
export type IntercomBridgeMode = "off" | "fork-only" | "always";
|
|
@@ -495,6 +833,12 @@ interface TopLevelParallelConfig {
|
|
|
495
833
|
concurrency?: number;
|
|
496
834
|
}
|
|
497
835
|
|
|
836
|
+
interface ExtensionChainConfig {
|
|
837
|
+
dynamicFanout?: {
|
|
838
|
+
maxItems?: number;
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
498
842
|
export interface ExtensionConfig {
|
|
499
843
|
asyncByDefault?: boolean;
|
|
500
844
|
forceTopLevelAsync?: boolean;
|
|
@@ -502,6 +846,7 @@ export interface ExtensionConfig {
|
|
|
502
846
|
maxSubagentDepth?: number;
|
|
503
847
|
control?: ControlConfig;
|
|
504
848
|
parallel?: TopLevelParallelConfig;
|
|
849
|
+
chain?: ExtensionChainConfig;
|
|
505
850
|
worktreeSetupHook?: string;
|
|
506
851
|
worktreeSetupHookTimeoutMs?: number;
|
|
507
852
|
intercomBridge?: IntercomBridgeConfig;
|
|
@@ -5,7 +5,8 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
5
5
|
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
6
6
|
import { discoverAgents, discoverAgentsAll, type ChainConfig } from "../agents/agents.ts";
|
|
7
7
|
import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
8
|
-
import { isParallelStep, type ChainStep } from "../shared/settings.ts";
|
|
8
|
+
import { isDynamicParallelStep, isParallelStep, type ChainStep } from "../shared/settings.ts";
|
|
9
|
+
import { assertJsonSchemaObject } from "../runs/shared/structured-output.ts";
|
|
9
10
|
import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
|
|
10
11
|
import {
|
|
11
12
|
applySlashUpdate,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
SLASH_SUBAGENT_RESPONSE_EVENT,
|
|
21
22
|
SLASH_SUBAGENT_STARTED_EVENT,
|
|
22
23
|
SLASH_SUBAGENT_UPDATE_EVENT,
|
|
24
|
+
type JsonSchemaObject,
|
|
23
25
|
type SingleResult,
|
|
24
26
|
type SubagentState,
|
|
25
27
|
} from "../shared/types.ts";
|
|
@@ -123,12 +125,48 @@ const makeChainCompletions = (state: SubagentState) => (prefix: string) => {
|
|
|
123
125
|
.map((chain) => ({ value: chain.name, label: chain.name }));
|
|
124
126
|
};
|
|
125
127
|
|
|
128
|
+
function loadSavedOutputSchema(chain: ChainConfig, stepAgent: string, outputSchema: unknown): JsonSchemaObject | undefined {
|
|
129
|
+
if (outputSchema === undefined) return undefined;
|
|
130
|
+
if (typeof outputSchema === "string") {
|
|
131
|
+
const schemaPath = path.isAbsolute(outputSchema)
|
|
132
|
+
? outputSchema
|
|
133
|
+
: path.join(path.dirname(chain.filePath), outputSchema);
|
|
134
|
+
const parsed = JSON.parse(fs.readFileSync(schemaPath, "utf-8")) as unknown;
|
|
135
|
+
assertJsonSchemaObject(parsed, `outputSchema for chain '${chain.name}' step '${stepAgent}' (${schemaPath})`);
|
|
136
|
+
return parsed;
|
|
137
|
+
}
|
|
138
|
+
assertJsonSchemaObject(outputSchema, `outputSchema for chain '${chain.name}' step '${stepAgent}'`);
|
|
139
|
+
return outputSchema;
|
|
140
|
+
}
|
|
141
|
+
|
|
126
142
|
const mapSavedChainSteps = (chain: ChainConfig, worktree = false): ChainStep[] => {
|
|
127
|
-
return (chain.steps as Array<ChainStep & { skills?: string[] | false }>).map((step) => {
|
|
128
|
-
if (isParallelStep(step))
|
|
143
|
+
return (chain.steps as unknown as Array<ChainStep & { skills?: string[] | false }>).map((step) => {
|
|
144
|
+
if (isParallelStep(step)) {
|
|
145
|
+
const parallel = step.parallel.map((task) => {
|
|
146
|
+
const { outputSchema: rawOutputSchema, ...rest } = task as typeof task & { outputSchema?: unknown };
|
|
147
|
+
const outputSchema = loadSavedOutputSchema(chain, task.agent, rawOutputSchema);
|
|
148
|
+
return { ...rest, ...(outputSchema ? { outputSchema } : {}) };
|
|
149
|
+
});
|
|
150
|
+
return { ...step, parallel, ...(worktree ? { worktree: true } : {}) };
|
|
151
|
+
}
|
|
152
|
+
if (isDynamicParallelStep(step)) {
|
|
153
|
+
const { outputSchema: rawOutputSchema, ...parallelRest } = step.parallel as typeof step.parallel & { outputSchema?: unknown };
|
|
154
|
+
const outputSchema = loadSavedOutputSchema(chain, step.parallel.agent, rawOutputSchema);
|
|
155
|
+
const collectSchema = loadSavedOutputSchema(chain, `${step.collect.as} collection`, step.collect.outputSchema);
|
|
156
|
+
return {
|
|
157
|
+
...step,
|
|
158
|
+
parallel: { ...parallelRest, ...(outputSchema ? { outputSchema } : {}) },
|
|
159
|
+
collect: { ...step.collect, ...(collectSchema ? { outputSchema: collectSchema } : {}) },
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const outputSchema = loadSavedOutputSchema(chain, step.agent, (step as { outputSchema?: unknown }).outputSchema);
|
|
129
163
|
return {
|
|
130
164
|
agent: step.agent,
|
|
131
165
|
task: step.task || undefined,
|
|
166
|
+
...(step.phase ? { phase: step.phase } : {}),
|
|
167
|
+
...(step.label ? { label: step.label } : {}),
|
|
168
|
+
...(step.as ? { as: step.as } : {}),
|
|
169
|
+
...(outputSchema ? { outputSchema } : {}),
|
|
132
170
|
output: step.output,
|
|
133
171
|
outputMode: step.outputMode,
|
|
134
172
|
reads: step.reads,
|