pi-subagents 0.35.1 → 0.37.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +224 -31
  3. package/agents/advisor.md +73 -0
  4. package/agents/delegate.md +2 -0
  5. package/agents/worker.md +2 -0
  6. package/package.json +11 -13
  7. package/skills/pi-subagents/SKILL.md +42 -13
  8. package/src/agents/agent-management.ts +7 -1
  9. package/src/agents/agents.ts +121 -17
  10. package/src/api/capability-ceiling.ts +17 -0
  11. package/src/api/delegation.ts +127 -0
  12. package/src/api/preflight.ts +399 -0
  13. package/src/extension/config.ts +7 -1
  14. package/src/extension/index.ts +52 -38
  15. package/src/extension/rpc.ts +31 -2
  16. package/src/extension/schemas.ts +29 -3
  17. package/src/extension/tool-description.ts +4 -2
  18. package/src/intercom/intercom-bridge.ts +1 -1
  19. package/src/intercom/native-supervisor-channel.ts +45 -6
  20. package/src/intercom/result-intercom.ts +7 -0
  21. package/src/runs/background/async-execution.ts +158 -17
  22. package/src/runs/background/async-job-tracker.ts +4 -0
  23. package/src/runs/background/async-resume.ts +47 -8
  24. package/src/runs/background/async-status.ts +94 -3
  25. package/src/runs/background/chain-append.ts +2 -0
  26. package/src/runs/background/completion-batcher.ts +6 -4
  27. package/src/runs/background/completion-dedupe.ts +2 -11
  28. package/src/runs/background/fleet-view.ts +9 -4
  29. package/src/runs/background/notify.ts +132 -120
  30. package/src/runs/background/process-terminal.ts +280 -0
  31. package/src/runs/background/result-watcher.ts +138 -78
  32. package/src/runs/background/run-status.ts +10 -2
  33. package/src/runs/background/scheduled-runs.ts +6 -1
  34. package/src/runs/background/stale-run-reconciler.ts +6 -0
  35. package/src/runs/background/subagent-runner.ts +406 -54
  36. package/src/runs/background/subagent-wait.ts +130 -4
  37. package/src/runs/background/wait-tool.ts +2 -2
  38. package/src/runs/foreground/chain-execution.ts +181 -111
  39. package/src/runs/foreground/execution.ts +147 -47
  40. package/src/runs/foreground/foreground-control.ts +90 -0
  41. package/src/runs/foreground/subagent-executor.ts +426 -165
  42. package/src/runs/shared/acceptance.ts +94 -41
  43. package/src/runs/shared/agent-contract.ts +38 -0
  44. package/src/runs/shared/capability-ceiling.ts +177 -0
  45. package/src/runs/shared/child-protocol.ts +1 -1
  46. package/src/runs/shared/completion-guard.ts +36 -5
  47. package/src/runs/shared/context-mode.ts +44 -0
  48. package/src/runs/shared/dynamic-fanout.ts +5 -5
  49. package/src/runs/shared/long-running-guard.ts +4 -0
  50. package/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  51. package/src/runs/shared/nested-events.ts +35 -3
  52. package/src/runs/shared/parallel-handoff.ts +154 -0
  53. package/src/runs/shared/parallel-utils.ts +11 -0
  54. package/src/runs/shared/pi-args.ts +148 -56
  55. package/src/runs/shared/run-history.ts +90 -5
  56. package/src/runs/shared/session-lease.ts +25 -5
  57. package/src/runs/shared/structured-output.ts +112 -7
  58. package/src/runs/shared/subagent-control.ts +4 -0
  59. package/src/runs/shared/subagent-prompt-runtime.ts +31 -19
  60. package/src/runs/shared/task-intent.ts +10 -5
  61. package/src/runs/shared/tool-availability.ts +21 -3
  62. package/src/runs/shared/tool-budget.ts +11 -5
  63. package/src/runs/shared/turn-budget.ts +2 -1
  64. package/src/runs/shared/worktree.ts +63 -14
  65. package/src/shared/accessible-dir.ts +25 -0
  66. package/src/shared/artifacts.ts +37 -7
  67. package/src/shared/atomic-json.ts +14 -42
  68. package/src/shared/child-transcript.ts +52 -0
  69. package/src/shared/file-system-retry.ts +47 -0
  70. package/src/shared/launch-contract.ts +123 -0
  71. package/src/shared/settings.ts +9 -1
  72. package/src/shared/types.ts +314 -28
  73. package/src/shared/utils.ts +17 -42
  74. package/src/slash/delegation-adapters.ts +153 -6
  75. package/src/slash/delegation-json.ts +108 -0
  76. package/src/slash/delegation-request.ts +182 -36
  77. package/src/slash/prompt-template-bridge.ts +222 -37
  78. package/src/slash/selector.ts +147 -0
  79. package/src/slash/slash-commands.ts +15 -6
  80. package/src/slash/slash-live-state.ts +2 -2
  81. package/src/slash/subagents-admin.ts +42 -42
  82. package/src/tui/fleet-status.ts +362 -0
  83. package/src/tui/fleet-transcript.ts +472 -0
  84. package/src/tui/fleet.ts +318 -59
  85. package/src/tui/render.ts +17 -15
  86. package/src/watchdog/change-signature.ts +105 -12
  87. package/src/watchdog/review.ts +7 -2
  88. package/src/watchdog/runtime.ts +5 -3
  89. package/src/slash/subagents-editor.ts +0 -86
@@ -5,9 +5,11 @@
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import type { Message } from "@earendil-works/pi-ai";
8
+ import type { AgentConfig } from "../agents/agents.ts";
8
9
  import type { FSWatcher } from "node:fs";
9
10
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
10
11
  import type { ModelScopeConfig } from "../runs/shared/model-scope.ts";
12
+ import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../runs/shared/capability-ceiling.ts";
11
13
 
12
14
  // ============================================================================
13
15
  // Basic Types
@@ -201,7 +203,7 @@ export interface ControlEvent {
201
203
  nestedRunId?: string;
202
204
  nestingPath?: NestedRunAddress["path"];
203
205
  message: string;
204
- reason?: "idle" | "completion_guard" | "active_long_running" | "tool_failures" | "time_threshold" | "turn_threshold" | "token_threshold";
206
+ reason?: "idle" | "completion_guard" | "active_long_running" | "tool_failures" | "supervisor_request" | "time_threshold" | "turn_threshold" | "token_threshold";
205
207
  turns?: number;
206
208
  tokens?: number;
207
209
  toolCount?: number;
@@ -214,9 +216,171 @@ export interface ControlEvent {
214
216
 
215
217
  export type SubagentResultStatus = "completed" | "failed" | "paused" | "stopped" | "detached";
216
218
  export type SubagentRunMode = "single" | "parallel" | "chain";
217
- export const SUBAGENT_LIFECYCLE_ARTIFACT_VERSION = 2;
219
+
220
+ export interface ParallelHandoffPatch {
221
+ path: string;
222
+ branch: string;
223
+ changed: boolean;
224
+ diffStat: string;
225
+ filesChanged: number;
226
+ insertions: number;
227
+ deletions: number;
228
+ error?: string;
229
+ }
230
+
231
+ export interface ParallelHandoffChild {
232
+ index: number;
233
+ taskIndex: number;
234
+ agent: string;
235
+ status: SubagentResultStatus;
236
+ summary: string;
237
+ outputPath?: string;
238
+ structuredOutput?: unknown;
239
+ structuredOutputPath?: string;
240
+ sessionPath?: string;
241
+ patch: ParallelHandoffPatch;
242
+ }
243
+
244
+ export interface ParallelHandoffCleanupTask {
245
+ index: number;
246
+ path: string;
247
+ branch: string;
248
+ worktreeRemoved: boolean;
249
+ branchRemoved: boolean;
250
+ errors?: string[];
251
+ }
252
+
253
+ export interface ParallelHandoffGroup {
254
+ stepIndex: number;
255
+ baseCommit: string;
256
+ repoRoot: string;
257
+ children: ParallelHandoffChild[];
258
+ cleanup: {
259
+ state: "complete" | "partial";
260
+ tasks: ParallelHandoffCleanupTask[];
261
+ pruned: boolean;
262
+ errors?: string[];
263
+ };
264
+ }
265
+
266
+ export interface ParallelHandoffManifest {
267
+ version: 1;
268
+ runId: string;
269
+ mode: "parallel" | "chain";
270
+ source: "foreground" | "async";
271
+ cwd: string;
272
+ createdAt: number;
273
+ updatedAt: number;
274
+ groups: ParallelHandoffGroup[];
275
+ }
276
+
277
+ export interface ParallelHandoffReference {
278
+ version: 1;
279
+ path: string;
280
+ groupCount: number;
281
+ childCount: number;
282
+ changedPatches: number;
283
+ cleanupState: "complete" | "partial";
284
+ }
285
+
286
+ export interface AgentContract {
287
+ version: 1;
288
+ }
289
+
290
+ export type ChainGateLayer = "execution" | "acceptance";
291
+
292
+ export type ExecutionProjectionStatus = "completed" | "failed" | "paused" | "stopped" | "detached";
293
+
294
+ export interface ExecutionProjection {
295
+ status: ExecutionProjectionStatus;
296
+ success: boolean;
297
+ exitCode: number;
298
+ error?: string;
299
+ interrupted?: boolean;
300
+ timedOut?: boolean;
301
+ stopped?: boolean;
302
+ detached?: boolean;
303
+ }
304
+
305
+ export interface ReviewProjection {
306
+ status: "not-requested" | "review-required" | "reviewed" | "blockers";
307
+ findings?: AcceptanceReviewResult["findings"];
308
+ }
309
+
310
+ export interface FileMutationEffect {
311
+ status: "not-requested" | "not-applicable" | "observed" | "missing";
312
+ expected: boolean;
313
+ attempted: boolean;
314
+ message?: string;
315
+ }
316
+
317
+ export interface EffectsProjection {
318
+ fileMutation?: FileMutationEffect;
319
+ }
320
+
321
+ export const SUBAGENT_LIFECYCLE_ARTIFACT_VERSION = 3;
218
322
  export type SubagentLifecycleArtifactVersion = typeof SUBAGENT_LIFECYCLE_ARTIFACT_VERSION;
219
323
 
324
+ export type ProcessTerminalState = "pending" | "observed" | "unknown" | "not-started";
325
+ export type ProcessTerminalReason =
326
+ | "observer-unavailable"
327
+ | "runner-candidate-missing"
328
+ | "runner-instance-mismatch"
329
+ | "writer-close-unverified"
330
+ | "canonical-session-unavailable"
331
+ | "canonical-session-lease-active"
332
+ | "canonical-session-release-unverified"
333
+ | "proof-write-failed"
334
+ | "stale-repair";
335
+
336
+ export interface RunnerProcessInstanceExitV1 {
337
+ processInstanceId: string;
338
+ kind: "runner";
339
+ closeObservedAt: number;
340
+ exitCode: number | null;
341
+ signal: string | null;
342
+ }
343
+
344
+ export interface PiWriterProcessInstanceExitV1 {
345
+ processInstanceId: string;
346
+ kind: "pi-writer";
347
+ attempt: number;
348
+ closeObservedAt: number;
349
+ exitCode: number | null;
350
+ signal: string | null;
351
+ }
352
+
353
+ export type ProcessInstanceExitV1 = RunnerProcessInstanceExitV1 | PiWriterProcessInstanceExitV1;
354
+
355
+ export interface CanonicalSessionTerminalV1 {
356
+ canonicalSessionId: string;
357
+ leaseDisposition: "released" | "not-held";
358
+ freeAtObservation: true;
359
+ canonicalSessionLeaseReleased?: true;
360
+ }
361
+
362
+ interface ProcessTerminalBaseV1 {
363
+ version: 1;
364
+ runId: string;
365
+ childIndex?: number;
366
+ runnerProcessInstanceId: string;
367
+ resumeDisposition?: "resumable" | "non-resumable" | "unavailable";
368
+ }
369
+
370
+ export type ProcessTerminalV1 =
371
+ | (ProcessTerminalBaseV1 & { state: "pending" | "not-started" })
372
+ | (ProcessTerminalBaseV1 & {
373
+ state: "observed";
374
+ observedAt: number;
375
+ instances: ProcessInstanceExitV1[];
376
+ canonicalSession?: CanonicalSessionTerminalV1;
377
+ })
378
+ | (ProcessTerminalBaseV1 & {
379
+ state: "unknown";
380
+ reason: ProcessTerminalReason;
381
+ diagnostic?: string;
382
+ });
383
+
220
384
  export type SteeringActionState = "delivered" | "scheduled" | "pending" | "partial" | "recovered" | "failed";
221
385
  export type SteeringTargetState = "scheduled" | "routed" | "delivered" | "late" | "failed" | "recovered";
222
386
 
@@ -281,7 +445,9 @@ export interface SteeringNotice {
281
445
 
282
446
  export interface SteeringRecoveryDescriptor {
283
447
  version: 1;
448
+ launchContractDigest?: string;
284
449
  sourceRunId: string;
450
+ agentContract?: AgentContract;
285
451
  agent: string;
286
452
  sessionFile?: string;
287
453
  cwd: string;
@@ -303,6 +469,7 @@ export interface SteeringRecoveryDescriptor {
303
469
  memory?: { scope: "project" | "user"; path: string };
304
470
  outputPath?: string;
305
471
  outputMode: "inline" | "file-only";
472
+ structuredOutputSchema?: JsonSchemaObject;
306
473
  acceptance?: AcceptanceInput;
307
474
  controlConfig?: ResolvedControlConfig;
308
475
  absoluteDeadlineAt?: number;
@@ -310,6 +477,7 @@ export interface SteeringRecoveryDescriptor {
310
477
  initialToolBudget?: ResolvedToolBudget;
311
478
  maxSubagentDepth: number;
312
479
  maxOutput?: MaxOutputConfig;
480
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
313
481
  share: boolean;
314
482
  sessionDir?: string;
315
483
  artifactsDir?: string;
@@ -365,6 +533,7 @@ export interface SubagentResultIntercomPayload {
365
533
  index?: number;
366
534
  artifactPath?: string;
367
535
  sessionPath?: string;
536
+ parallelHandoff?: ParallelHandoffReference;
368
537
  }
369
538
 
370
539
  // ============================================================================
@@ -426,7 +595,7 @@ export interface ModelAttempt {
426
595
  usage?: Usage;
427
596
  }
428
597
 
429
- export type AcceptanceLevel = "auto" | "none" | "attested" | "checked" | "verified" | "reviewed";
598
+ export type AcceptanceLevel = "auto" | "none" | "attested" | "checked" | "verified";
430
599
 
431
600
  export type AcceptanceEvidenceKind =
432
601
  | "changed-files"
@@ -535,7 +704,7 @@ export interface AcceptanceVerifyResult {
535
704
  }
536
705
 
537
706
  export interface AcceptanceReviewResult {
538
- status: "no-blockers" | "blockers" | "needs-parent-decision";
707
+ status: "review-required" | "reviewed" | "blockers";
539
708
  findings: Array<{
540
709
  severity: "blocker" | "non-blocking";
541
710
  file?: string;
@@ -544,19 +713,24 @@ export interface AcceptanceReviewResult {
544
713
  }>;
545
714
  }
546
715
 
547
- export type AcceptanceLedgerStatus =
716
+ export type AcceptanceEvidenceStatus =
548
717
  | "pending"
549
718
  | "not-required"
550
719
  | "claimed"
551
720
  | "attested"
552
721
  | "checked"
553
722
  | "verified"
554
- | "reviewed"
555
- | "accepted"
556
723
  | "rejected";
557
724
 
725
+ export type AcceptanceLedgerStatus =
726
+ | AcceptanceEvidenceStatus
727
+ | "review-required"
728
+ | "reviewed"
729
+ | "accepted";
730
+
558
731
  export interface AcceptanceLedger {
559
732
  status: AcceptanceLedgerStatus;
733
+ evidenceStatus: AcceptanceEvidenceStatus;
560
734
  explicit: boolean;
561
735
  effectiveAcceptance: ResolvedAcceptanceConfig;
562
736
  inferredReason: string[];
@@ -585,6 +759,8 @@ export interface ProtocolOutputLimit {
585
759
  export interface SingleResult {
586
760
  agent: string;
587
761
  task: string;
762
+ /** Resolved launch context for this child. */
763
+ context?: "fresh" | "fork";
588
764
  exitCode: number;
589
765
  detached?: boolean;
590
766
  detachedReason?: string;
@@ -599,6 +775,8 @@ export interface SingleResult {
599
775
  messages?: Message[];
600
776
  usage: Usage;
601
777
  model?: string;
778
+ /** Effective thinking level used by this foreground child, when known. */
779
+ thinking?: string;
602
780
  attemptedModels?: string[];
603
781
  modelAttempts?: ModelAttempt[];
604
782
  controlEvents?: ControlEvent[];
@@ -618,13 +796,21 @@ export interface SingleResult {
618
796
  outputReference?: SavedOutputReference;
619
797
  outputSaveError?: string;
620
798
  structuredOutput?: unknown;
799
+ structuredOutputFailed?: boolean;
621
800
  structuredOutputPath?: string;
622
801
  structuredOutputSchemaPath?: string;
623
802
  acceptance?: AcceptanceLedger;
803
+ agentContract?: AgentContract;
804
+ launchContractDigest?: string;
805
+ execution?: ExecutionProjection;
806
+ review?: ReviewProjection;
807
+ effects?: EffectsProjection;
624
808
  transcriptPath?: string;
625
809
  transcriptError?: string;
626
810
  children?: NestedRunSummary[];
627
811
  watchdog?: ChildWatchdogProgress;
812
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
813
+ capabilityAudit?: SubagentCapabilityAudit;
628
814
  }
629
815
 
630
816
  export interface SpawnBudgetGrant {
@@ -648,7 +834,8 @@ export interface SpawnBudgetSnapshot {
648
834
  export interface Details {
649
835
  mode: SubagentRunMode | "management";
650
836
  runId?: string;
651
- context?: "fresh" | "fork";
837
+ /** Run-level context summary. "mixed" when children resolved to different modes. */
838
+ context?: "fresh" | "fork" | "mixed";
652
839
  results: SingleResult[];
653
840
  controlEvents?: ControlEvent[];
654
841
  steering?: SteerActionResult;
@@ -683,6 +870,13 @@ export interface Details {
683
870
  // Aggregated cost across all agents in the run
684
871
  totalCost?: CostSummary;
685
872
  spawnBudget?: SpawnBudgetSnapshot;
873
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
874
+ capabilityAudit?: SubagentCapabilityAudit;
875
+ parallelHandoff?: ParallelHandoffReference;
876
+ lifecycleStatus?: {
877
+ processTerminal?: ProcessTerminalV1;
878
+ };
879
+ launchContractDigest?: string;
686
880
  }
687
881
 
688
882
  // ============================================================================
@@ -697,8 +891,11 @@ export interface ArtifactPaths {
697
891
  metadataPath: string;
698
892
  }
699
893
 
894
+ export type ArtifactDirPreference = "project" | "session" | "temp";
895
+
700
896
  export interface ArtifactConfig {
701
897
  enabled: boolean;
898
+ dir?: ArtifactDirPreference;
702
899
  includeInput: boolean;
703
900
  includeOutput: boolean;
704
901
  includeJsonl: boolean;
@@ -753,6 +950,9 @@ export interface NestedStepSummary {
753
950
  wrapUpRequested?: boolean;
754
951
  toolBudget?: ToolBudgetState;
755
952
  toolBudgetBlocked?: boolean;
953
+ processTerminal?: ProcessTerminalV1;
954
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
955
+ capabilityAudit?: SubagentCapabilityAudit;
756
956
  children?: NestedRunSummary[];
757
957
  }
758
958
 
@@ -768,6 +968,9 @@ export interface NestedRunSummary extends NestedRunAddress {
768
968
  controlInbox?: string;
769
969
  capabilityToken?: string;
770
970
  mode?: SubagentRunMode;
971
+ processTerminal?: ProcessTerminalV1;
972
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
973
+ capabilityAudit?: SubagentCapabilityAudit;
771
974
  state: NestedRunState;
772
975
  agent?: string;
773
976
  agents?: string[];
@@ -811,6 +1014,8 @@ export interface AsyncStartedEvent {
811
1014
  lifecycleArtifactVersion?: SubagentLifecycleArtifactVersion;
812
1015
  id?: string;
813
1016
  asyncDir?: string;
1017
+ /** Parent-resolved launch directory, used as a trusted artifact root while this session is live. */
1018
+ cwd?: string;
814
1019
  pid?: number;
815
1020
  sessionId?: string;
816
1021
  mode?: SubagentRunMode;
@@ -824,10 +1029,12 @@ export interface AsyncStartedEvent {
824
1029
  chainStepCount?: number;
825
1030
  parallelGroups?: AsyncParallelGroupStatus[];
826
1031
  workflowGraph?: WorkflowGraphSnapshot;
1032
+ launchContractDigest?: string;
827
1033
  timeoutMs?: number;
828
1034
  deadlineAt?: number;
829
1035
  turnBudget?: TurnBudgetState;
830
1036
  nestedRoute?: NestedRouteInfo;
1037
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
831
1038
  }
832
1039
 
833
1040
  export interface AsyncStatus {
@@ -865,8 +1072,14 @@ export interface AsyncStatus {
865
1072
  pendingAppends?: number;
866
1073
  parallelGroups?: AsyncParallelGroupStatus[];
867
1074
  workflowGraph?: WorkflowGraphSnapshot;
1075
+ processTerminal?: ProcessTerminalV1;
1076
+ launchContractDigest?: string;
1077
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
1078
+ capabilityAudit?: SubagentCapabilityAudit;
868
1079
  steps?: Array<{
869
1080
  agent: string;
1081
+ /** Resolved launch context for this child step. */
1082
+ context?: "fresh" | "fork";
870
1083
  phase?: string;
871
1084
  label?: string;
872
1085
  outputName?: string;
@@ -910,7 +1123,15 @@ export interface AsyncStatus {
910
1123
  structuredOutputPath?: string;
911
1124
  structuredOutputSchemaPath?: string;
912
1125
  acceptance?: AcceptanceLedger;
1126
+ agentContract?: AgentContract;
1127
+ launchContractDigest?: string;
1128
+ execution?: ExecutionProjection;
1129
+ review?: ReviewProjection;
1130
+ effects?: EffectsProjection;
913
1131
  watchdog?: ChildWatchdogProgress;
1132
+ processTerminal?: ProcessTerminalV1;
1133
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
1134
+ capabilityAudit?: SubagentCapabilityAudit;
914
1135
  }>;
915
1136
  sessionDir?: string;
916
1137
  outputFile?: string;
@@ -918,6 +1139,7 @@ export interface AsyncStatus {
918
1139
  totalCost?: CostSummary;
919
1140
  sessionFile?: string;
920
1141
  outputs?: ChainOutputMap;
1142
+ parallelHandoff?: ParallelHandoffReference;
921
1143
  }
922
1144
 
923
1145
  export type AsyncJobStep = NonNullable<AsyncStatus["steps"]>[number] & {
@@ -927,7 +1149,11 @@ export type AsyncJobStep = NonNullable<AsyncStatus["steps"]>[number] & {
927
1149
  export interface AsyncJobState {
928
1150
  asyncId: string;
929
1151
  asyncDir: string;
1152
+ /** Parent-resolved launch directory retained for trusted live artifact lookup. */
1153
+ cwd?: string;
930
1154
  status: "queued" | "running" | "complete" | "failed" | "paused" | "stopped";
1155
+ /** Short caller-facing task/goal shown in fleet surfaces when available. */
1156
+ description?: string;
931
1157
  pid?: number;
932
1158
  sessionId?: string;
933
1159
  activityState?: ActivityState;
@@ -939,6 +1165,8 @@ export interface AsyncJobState {
939
1165
  toolCount?: number;
940
1166
  steering?: SteeringStatus;
941
1167
  mode?: SubagentRunMode;
1168
+ /** Run-level context summary derived from step contexts. */
1169
+ context?: "fresh" | "fork" | "mixed";
942
1170
  agents?: string[];
943
1171
  currentStep?: number;
944
1172
  chainStepCount?: number;
@@ -972,8 +1200,17 @@ export interface AsyncJobState {
972
1200
  export interface ForegroundResumeChild {
973
1201
  agent: string;
974
1202
  index: number;
1203
+ context?: "fresh" | "fork";
975
1204
  sessionFile?: string;
976
1205
  status: SubagentResultStatus;
1206
+ activityState?: ActivityState;
1207
+ lastActivityAt?: number;
1208
+ currentTool?: string;
1209
+ currentToolStartedAt?: number;
1210
+ currentPath?: string;
1211
+ turnCount?: number;
1212
+ tokens?: number;
1213
+ toolCount?: number;
977
1214
  exitCode?: number;
978
1215
  error?: string;
979
1216
  finalOutput?: string;
@@ -985,6 +1222,12 @@ export interface ForegroundResumeChild {
985
1222
  transcriptError?: string;
986
1223
  detachedReason?: string;
987
1224
  acceptance?: AcceptanceLedger;
1225
+ agentContract?: AgentContract;
1226
+ execution?: ExecutionProjection;
1227
+ review?: ReviewProjection;
1228
+ effects?: EffectsProjection;
1229
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
1230
+ capabilityAudit?: SubagentCapabilityAudit;
988
1231
  updatedAt?: number;
989
1232
  }
990
1233
 
@@ -998,9 +1241,55 @@ export interface ForegroundResumeRun {
998
1241
  children: ForegroundResumeChild[];
999
1242
  }
1000
1243
 
1244
+ export interface ForegroundChildControl {
1245
+ index: number;
1246
+ agent: string;
1247
+ description?: string;
1248
+ startedAt: number;
1249
+ updatedAt: number;
1250
+ currentActivityState?: ActivityState;
1251
+ lastActivityAt?: number;
1252
+ currentTool?: string;
1253
+ currentToolStartedAt?: number;
1254
+ currentPath?: string;
1255
+ turnCount?: number;
1256
+ tokens?: number;
1257
+ toolCount?: number;
1258
+ interrupt?: () => boolean;
1259
+ }
1260
+
1261
+ export interface ForegroundRunControl {
1262
+ runId: string;
1263
+ mode: SubagentRunMode;
1264
+ startedAt: number;
1265
+ updatedAt: number;
1266
+ /** Effective working directory used to resolve live transcript artifacts. */
1267
+ cwd?: string;
1268
+ currentAgent?: string;
1269
+ currentIndex?: number;
1270
+ /** Short caller-facing task/goal shown in fleet surfaces when available. */
1271
+ description?: string;
1272
+ currentActivityState?: ActivityState;
1273
+ lastActivityAt?: number;
1274
+ currentTool?: string;
1275
+ currentToolStartedAt?: number;
1276
+ currentPath?: string;
1277
+ turnCount?: number;
1278
+ tokens?: number;
1279
+ toolCount?: number;
1280
+ /** Independently tracked children for foreground parallel work and fleet inspection. */
1281
+ activeChildren?: Map<number, ForegroundChildControl>;
1282
+ nestedRoute?: NestedRouteInfo;
1283
+ nestedChildren?: NestedRunSummary[];
1284
+ interrupt?: () => boolean;
1285
+ }
1286
+
1001
1287
  export interface SubagentState {
1002
1288
  baseCwd: string;
1003
1289
  currentSessionId: string | null;
1290
+ /** Runtime-owned artifact resolution inputs used by Fleet transcript targeting. */
1291
+ artifactDirPreference?: ArtifactDirPreference;
1292
+ parentSessionFile?: string | null;
1004
1293
  subagentInProgress?: boolean;
1005
1294
  subagentSpawns?: {
1006
1295
  sessionId: string | null;
@@ -1012,26 +1301,10 @@ export interface SubagentState {
1012
1301
  asyncJobs: Map<string, AsyncJobState>;
1013
1302
  /** Current-session active and recent async runs for the native fleet inspector. */
1014
1303
  fleetJobs?: Map<string, AsyncJobState>;
1304
+ /** Suppress dynamic status widgets while the fleet overlay owns the viewport. */
1305
+ fleetInspectorOpen?: boolean;
1015
1306
  foregroundRuns?: Map<string, ForegroundResumeRun>;
1016
- foregroundControls: Map<string, {
1017
- runId: string;
1018
- mode: SubagentRunMode;
1019
- startedAt: number;
1020
- updatedAt: number;
1021
- currentAgent?: string;
1022
- currentIndex?: number;
1023
- currentActivityState?: ActivityState;
1024
- lastActivityAt?: number;
1025
- currentTool?: string;
1026
- currentToolStartedAt?: number;
1027
- currentPath?: string;
1028
- turnCount?: number;
1029
- tokens?: number;
1030
- toolCount?: number;
1031
- nestedRoute?: NestedRouteInfo;
1032
- nestedChildren?: NestedRunSummary[];
1033
- interrupt?: () => boolean;
1034
- }>;
1307
+ foregroundControls: Map<string, ForegroundRunControl>;
1035
1308
  lastForegroundControlId: string | null;
1036
1309
  pendingForegroundControlNotices?: Map<string, ReturnType<typeof setTimeout>>;
1037
1310
  cleanupTimers: Map<string, ReturnType<typeof setTimeout>>;
@@ -1074,6 +1347,7 @@ export const INTERCOM_DETACH_REQUEST_EVENT = "pi-intercom:detach-request";
1074
1347
  export const INTERCOM_DETACH_RESPONSE_EVENT = "pi-intercom:detach-response";
1075
1348
  export const SUBAGENT_ASYNC_STARTED_EVENT = "subagent:async-started";
1076
1349
  export const SUBAGENT_ASYNC_COMPLETE_EVENT = "subagent:async-complete";
1350
+ export const SUBAGENT_PROCESS_TERMINAL_EVENT = "subagent:process-terminal";
1077
1351
  export const SUBAGENT_FOREGROUND_COMPLETE_EVENT = "subagent:foreground-complete";
1078
1352
  export const SUBAGENT_CONTROL_EVENT = "subagent:control-event";
1079
1353
  export const SUBAGENT_CONTROL_INTERCOM_EVENT = "subagent:control-intercom";
@@ -1088,13 +1362,18 @@ export const SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT = "subagent:result-intercom
1088
1362
  export interface RunSyncOptions {
1089
1363
  /** Session id of the direct parent session for permission-system ask forwarding. */
1090
1364
  parentSessionId?: string;
1365
+ /** Resolved launch context for this child. */
1366
+ context?: "fresh" | "fork";
1091
1367
  cwd?: string;
1092
1368
  signal?: AbortSignal;
1093
1369
  interruptSignal?: AbortSignal;
1094
1370
  timeoutMs?: number;
1095
1371
  deadlineAt?: number;
1096
1372
  turnBudget?: ResolvedTurnBudget;
1373
+ /** Enforce maxTurns + graceTurns as a hard model-turn boundary. */
1374
+ enforceHardTurnLimit?: boolean;
1097
1375
  toolBudget?: ResolvedToolBudget;
1376
+ allowZeroToolBudget?: boolean;
1098
1377
  allowIntercomDetach?: boolean;
1099
1378
  intercomEvents?: IntercomEventBus;
1100
1379
  onUpdate?: (r: import("@earendil-works/pi-agent-core").AgentToolResult<Details>) => void;
@@ -1116,6 +1395,7 @@ export interface RunSyncOptions {
1116
1395
  maxSubagentDepth?: number;
1117
1396
  /** Effective parent wait-tool setting propagated to the child runtime. */
1118
1397
  waitToolEnabled?: boolean;
1398
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
1119
1399
  nestedRoute?: NestedRouteInfo;
1120
1400
  /** Override the agent's default model (format: "provider/id" or just "id") */
1121
1401
  modelOverride?: string;
@@ -1134,6 +1414,7 @@ export interface RunSyncOptions {
1134
1414
  schemaPath: string;
1135
1415
  outputPath: string;
1136
1416
  };
1417
+ agentContract?: AgentContract;
1137
1418
  acceptance?: AcceptanceInput;
1138
1419
  acceptanceContext?: {
1139
1420
  mode?: SubagentRunMode;
@@ -1178,7 +1459,9 @@ export interface ScheduledRunsConfig {
1178
1459
 
1179
1460
  export interface ExtensionConfig {
1180
1461
  asyncByDefault?: boolean;
1181
- /** Show the above-editor async runs widget. Defaults to true. */
1462
+ /** Show the Claude Code-style navigable fleet below the editor. Defaults to true. */
1463
+ fleetView?: boolean;
1464
+ /** Show the legacy above-editor async runs widget. Defaults to true only when fleetView is disabled. */
1182
1465
  asyncWidget?: boolean;
1183
1466
  /** Tool description variant registered for the parent-facing subagent tool. Defaults to full. */
1184
1467
  toolDescriptionMode?: ToolDescriptionMode;
@@ -1200,6 +1483,8 @@ export interface ExtensionConfig {
1200
1483
  worktreeSetupHook?: string;
1201
1484
  worktreeSetupHookTimeoutMs?: number;
1202
1485
  worktreeBaseDir?: string;
1486
+ /** Where to store subagent artifact files. Defaults to "project" (cwd/.pi-subagents). Set to "session" for pi session dir, or "temp" for OS temp. */
1487
+ artifactDir?: ArtifactDirPreference;
1203
1488
  intercomBridge?: IntercomBridgeConfig;
1204
1489
  proactiveSkillSubagents?: ProactiveSkillSubagentsConfig | false;
1205
1490
  scheduledRuns?: ScheduledRunsConfig;
@@ -1216,6 +1501,7 @@ export const DEFAULT_MAX_OUTPUT: Required<MaxOutputConfig> = {
1216
1501
 
1217
1502
  export const DEFAULT_ARTIFACT_CONFIG: ArtifactConfig = {
1218
1503
  enabled: true,
1504
+ dir: "project",
1219
1505
  includeInput: true,
1220
1506
  includeOutput: true,
1221
1507
  includeJsonl: false,
@@ -412,6 +412,14 @@ export function compactForegroundDetails(details: Details): Details {
412
412
  };
413
413
  }
414
414
 
415
+ export function hasEmptyTerminalAssistantResponse(messages: Message[]): boolean {
416
+ const lastAssistant = messages.findLast((message) => message.role === "assistant");
417
+ return lastAssistant?.role === "assistant"
418
+ && Array.isArray(lastAssistant.content)
419
+ && lastAssistant.content.length === 0
420
+ && lastAssistant.usage.output === 0;
421
+ }
422
+
415
423
  /**
416
424
  * Detect errors in subagent execution from messages (only errors with no subsequent success)
417
425
  */
@@ -438,50 +446,17 @@ export function detectSubagentError(messages: Message[]): ErrorInfo {
438
446
  const toolName = "toolName" in msg && typeof msg.toolName === "string" ? msg.toolName : undefined;
439
447
  const isError = "isError" in msg && msg.isError === true;
440
448
 
441
- if (isError) {
442
- const text = msg.content.find((c) => c.type === "text");
443
- const details = text && "text" in text ? text.text : undefined;
444
- const exitMatch = details?.match(/exit(?:ed)?\s*(?:with\s*)?(?:code|status)?\s*[:\s]?\s*(\d+)/i);
445
- return {
446
- hasError: true,
447
- exitCode: exitMatch ? parseInt(exitMatch[1], 10) : 1,
448
- errorType: toolName || "tool",
449
- details: details?.slice(0, 200),
450
- };
451
- }
452
-
453
- if (toolName !== "bash") continue;
449
+ if (!isError) continue;
454
450
 
455
451
  const text = msg.content.find((c) => c.type === "text");
456
- if (!text || !("text" in text)) continue;
457
- const output = text.text;
458
-
459
- const exitMatch = output.match(/exit(?:ed)?\s*(?:with\s*)?(?:code|status)?\s*[:\s]?\s*(\d+)/i);
460
- if (exitMatch) {
461
- const code = parseInt(exitMatch[1], 10);
462
- if (code !== 0) {
463
- return { hasError: true, exitCode: code, errorType: "bash", details: output.slice(0, 200) };
464
- }
465
- }
466
-
467
- // NOTE: These patterns can match legitimate output (grep results, logs,
468
- // testing). With the assistant-message check above, most false positives
469
- // are mitigated since the agent will have responded after routine errors.
470
- const fatalPatterns = [
471
- /command not found/i,
472
- /permission denied/i,
473
- /no such file or directory/i,
474
- /segmentation fault/i,
475
- /killed|terminated/i,
476
- /out of memory/i,
477
- /connection refused/i,
478
- /timeout/i,
479
- ];
480
- for (const pattern of fatalPatterns) {
481
- if (pattern.test(output)) {
482
- return { hasError: true, exitCode: 1, errorType: "bash", details: output.slice(0, 200) };
483
- }
484
- }
452
+ const details = text && "text" in text ? text.text : undefined;
453
+ const exitMatch = details?.match(/exit(?:ed)?\s*(?:with\s*)?(?:code|status)?\s*[:\s]?\s*(\d+)/i);
454
+ return {
455
+ hasError: true,
456
+ exitCode: exitMatch ? parseInt(exitMatch[1], 10) : 1,
457
+ errorType: toolName || "tool",
458
+ details: details?.slice(0, 200),
459
+ };
485
460
  }
486
461
 
487
462
  return { hasError: false };