pi-subagents 0.47.1 → 0.49.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 +51 -0
- package/README.md +2 -0
- package/docs/agents.md +1 -0
- package/docs/configuration.md +74 -7
- package/docs/missions.md +4 -2
- package/docs/observability.md +27 -3
- package/docs/tool-reference.md +1 -1
- package/package.json +1 -1
- package/src/agents/agents.ts +36 -15
- package/src/api/preflight.ts +1 -1
- package/src/extension/config.ts +26 -0
- package/src/extension/doctor.ts +40 -0
- package/src/extension/index.ts +39 -8
- package/src/extension/public-execution.ts +35 -4
- package/src/extension/rpc.ts +3 -9
- package/src/extension/schemas.ts +10 -9
- package/src/extension/tool-description.ts +10 -8
- package/src/inspectors/herdr/actions.ts +11 -2
- package/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/src/intercom/intercom-bridge.ts +7 -2
- package/src/missions/lifecycle.ts +4 -7
- package/src/missions/store.ts +12 -7
- package/src/missions/workflow-state.ts +6 -2
- package/src/runs/background/active-async-capacity.ts +431 -0
- package/src/runs/background/active-run-index.ts +9 -5
- package/src/runs/background/async-execution.ts +141 -65
- package/src/runs/background/async-job-tracker.ts +4 -0
- package/src/runs/background/async-resume.ts +18 -2
- package/src/runs/background/async-status.ts +11 -5
- package/src/runs/background/chain-append.ts +33 -15
- package/src/runs/background/fleet-view.ts +18 -5
- package/src/runs/background/owned-process-tree.ts +104 -0
- package/src/runs/background/process-terminal.ts +17 -3
- package/src/runs/background/resume-guidance.ts +27 -7
- package/src/runs/background/retained-children.ts +14 -6
- package/src/runs/background/run-status.ts +101 -5
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +60 -32
- package/src/runs/foreground/chain-execution.ts +37 -2
- package/src/runs/foreground/execution.ts +90 -19
- package/src/runs/foreground/foreground-control.ts +12 -0
- package/src/runs/foreground/prompt-audit.ts +172 -0
- package/src/runs/foreground/subagent-executor.ts +829 -203
- package/src/runs/shared/acceptance.ts +13 -4
- package/src/runs/shared/completion-guard.ts +17 -1
- package/src/runs/shared/llm-intent-arbiter.ts +302 -0
- package/src/runs/shared/parallel-utils.ts +2 -0
- package/src/runs/shared/pi-args.ts +44 -1
- package/src/runs/shared/run-fanout-budget.ts +280 -0
- package/src/runs/shared/single-output.ts +4 -2
- package/src/runs/shared/task-intent.ts +19 -3
- package/src/runs/shared/worktree.ts +17 -5
- package/src/shared/agent-stream-options.ts +5 -0
- package/src/shared/artifacts.ts +2 -6
- package/src/shared/display-text.ts +50 -0
- package/src/shared/node-executable.ts +21 -0
- package/src/shared/types.ts +115 -4
- package/src/shared/utils.ts +3 -1
- package/src/tui/fleet-status.ts +7 -5
- package/src/tui/fleet-transcript.ts +1 -48
- package/src/tui/fleet.ts +228 -13
- package/src/tui/render.ts +86 -39
- package/src/watchdog/permission-arbiter.ts +2 -1
- package/src/watchdog/review.ts +4 -3
- package/src/workflows/chat-progress.ts +8 -2
- package/src/workflows/scripted-workflow.ts +66 -10
package/src/shared/types.ts
CHANGED
|
@@ -328,6 +328,7 @@ export interface FileMutationEffect {
|
|
|
328
328
|
expected: boolean;
|
|
329
329
|
attempted: boolean;
|
|
330
330
|
message?: string;
|
|
331
|
+
resolvedBy?: "llm-intent-arbiter";
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
export interface EffectsProjection {
|
|
@@ -343,6 +344,7 @@ export type ProcessTerminalReason =
|
|
|
343
344
|
| "runner-candidate-missing"
|
|
344
345
|
| "runner-instance-mismatch"
|
|
345
346
|
| "writer-close-unverified"
|
|
347
|
+
| "process-tree-unverified"
|
|
346
348
|
| "canonical-session-unavailable"
|
|
347
349
|
| "canonical-session-lease-active"
|
|
348
350
|
| "canonical-session-release-unverified"
|
|
@@ -357,6 +359,19 @@ export interface RunnerProcessInstanceExitV1 {
|
|
|
357
359
|
signal: string | null;
|
|
358
360
|
}
|
|
359
361
|
|
|
362
|
+
export type ProcessTreeTerminalV1 =
|
|
363
|
+
| {
|
|
364
|
+
state: "observed";
|
|
365
|
+
mechanism: "posix-process-group";
|
|
366
|
+
processGroupId: number;
|
|
367
|
+
verifiedAt: number;
|
|
368
|
+
}
|
|
369
|
+
| {
|
|
370
|
+
state: "unknown";
|
|
371
|
+
reason: "unsupported-platform" | "signal-failed" | "verification-failed";
|
|
372
|
+
diagnostic?: string;
|
|
373
|
+
};
|
|
374
|
+
|
|
360
375
|
export interface PiWriterProcessInstanceExitV1 {
|
|
361
376
|
processInstanceId: string;
|
|
362
377
|
kind: "pi-writer";
|
|
@@ -364,6 +379,7 @@ export interface PiWriterProcessInstanceExitV1 {
|
|
|
364
379
|
closeObservedAt: number;
|
|
365
380
|
exitCode: number | null;
|
|
366
381
|
signal: string | null;
|
|
382
|
+
processTree: ProcessTreeTerminalV1;
|
|
367
383
|
}
|
|
368
384
|
|
|
369
385
|
export type ProcessInstanceExitV1 = RunnerProcessInstanceExitV1 | PiWriterProcessInstanceExitV1;
|
|
@@ -462,9 +478,30 @@ export interface SteeringNotice {
|
|
|
462
478
|
currentSessionId?: string;
|
|
463
479
|
}
|
|
464
480
|
|
|
481
|
+
export interface RunFanoutBudgetDescriptor {
|
|
482
|
+
version: 1;
|
|
483
|
+
rootRunId: string;
|
|
484
|
+
directory: string;
|
|
485
|
+
limit: number;
|
|
486
|
+
parentPath?: string;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export interface RunFanoutBudgetSnapshot {
|
|
490
|
+
used: number;
|
|
491
|
+
limit: number;
|
|
492
|
+
remaining: number;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export interface RunFanoutRejection extends RunFanoutBudgetSnapshot {
|
|
496
|
+
code: "RUN_FANOUT_LIMIT";
|
|
497
|
+
path: string;
|
|
498
|
+
requested: number;
|
|
499
|
+
}
|
|
500
|
+
|
|
465
501
|
export interface SteeringRecoveryDescriptor {
|
|
466
502
|
version: 1;
|
|
467
503
|
launchContractDigest?: string;
|
|
504
|
+
runFanoutBudget: RunFanoutBudgetDescriptor;
|
|
468
505
|
sourceRunId: string;
|
|
469
506
|
agentContract?: AgentContract;
|
|
470
507
|
agent: string;
|
|
@@ -491,6 +528,8 @@ export interface SteeringRecoveryDescriptor {
|
|
|
491
528
|
structuredOutputSchema?: JsonSchemaObject;
|
|
492
529
|
acceptance?: AcceptanceInput;
|
|
493
530
|
controlConfig?: ResolvedControlConfig;
|
|
531
|
+
/** Raw per-run bridge override. Omitted descriptors continue to use global config. */
|
|
532
|
+
intercomBridge?: IntercomBridgeConfig;
|
|
494
533
|
absoluteDeadlineAt?: number;
|
|
495
534
|
initialTurnBudget?: ResolvedTurnBudget;
|
|
496
535
|
initialToolBudget?: ResolvedToolBudget;
|
|
@@ -1024,6 +1063,9 @@ export interface Details {
|
|
|
1024
1063
|
// Aggregated cost across all agents in the run
|
|
1025
1064
|
totalCost?: CostSummary;
|
|
1026
1065
|
spawnBudget?: SpawnBudgetSnapshot;
|
|
1066
|
+
runFanoutBudget?: RunFanoutBudgetSnapshot;
|
|
1067
|
+
runFanoutRejection?: RunFanoutRejection;
|
|
1068
|
+
activeAsyncCapacity?: ActiveAsyncCapacitySnapshot;
|
|
1027
1069
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
1028
1070
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
1029
1071
|
parallelHandoff?: ParallelHandoffReference;
|
|
@@ -1046,7 +1088,7 @@ export interface Details {
|
|
|
1046
1088
|
trace: Array<{
|
|
1047
1089
|
operation: "run" | "status";
|
|
1048
1090
|
key: string;
|
|
1049
|
-
state: "started" | "completed" | "failed" | "reused";
|
|
1091
|
+
state: "started" | "completed" | "failed" | "stopped" | "reused";
|
|
1050
1092
|
agent?: string;
|
|
1051
1093
|
runId?: string;
|
|
1052
1094
|
phase?: string;
|
|
@@ -1219,6 +1261,8 @@ export interface AsyncStartedEvent {
|
|
|
1219
1261
|
asyncDir?: string;
|
|
1220
1262
|
/** Parent-resolved launch directory, used as a trusted artifact root while this session is live. */
|
|
1221
1263
|
cwd?: string;
|
|
1264
|
+
/** Parent-resolved child session root, used only while this session owns the live run. */
|
|
1265
|
+
sessionRoot?: string;
|
|
1222
1266
|
pid?: number;
|
|
1223
1267
|
sessionId?: string;
|
|
1224
1268
|
mode?: SubagentRunMode;
|
|
@@ -1323,6 +1367,8 @@ export interface AsyncStatus {
|
|
|
1323
1367
|
workflowGraph?: WorkflowGraphSnapshot;
|
|
1324
1368
|
checkpoint?: ChainCheckpointState;
|
|
1325
1369
|
processTerminal?: ProcessTerminalV1;
|
|
1370
|
+
runFanoutBudget?: RunFanoutBudgetSnapshot;
|
|
1371
|
+
runFanoutBudgetDescriptor?: RunFanoutBudgetDescriptor;
|
|
1326
1372
|
launchContractDigest?: string;
|
|
1327
1373
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
1328
1374
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
@@ -1342,6 +1388,10 @@ export interface AsyncStatus {
|
|
|
1342
1388
|
phase?: string;
|
|
1343
1389
|
label?: string;
|
|
1344
1390
|
workflowKey?: string;
|
|
1391
|
+
/** Child run identity for workflow capacity reconciliation. */
|
|
1392
|
+
runId?: string;
|
|
1393
|
+
/** True only when this workflow child owns a detached async runner. */
|
|
1394
|
+
async?: boolean;
|
|
1345
1395
|
parentWorkflowRunId?: string;
|
|
1346
1396
|
outputName?: string;
|
|
1347
1397
|
structured?: boolean;
|
|
@@ -1416,6 +1466,8 @@ export interface AsyncJobState {
|
|
|
1416
1466
|
asyncDir: string;
|
|
1417
1467
|
/** Parent-resolved launch directory retained for trusted live artifact lookup. */
|
|
1418
1468
|
cwd?: string;
|
|
1469
|
+
/** Parent-resolved child session root retained for trusted live transcript lookup. */
|
|
1470
|
+
sessionRoot?: string;
|
|
1419
1471
|
status: "queued" | "running" | "complete" | "failed" | "paused" | "stopped" | "rejected";
|
|
1420
1472
|
/** Short caller-facing task/goal shown in fleet surfaces when available. */
|
|
1421
1473
|
description?: string;
|
|
@@ -1574,6 +1626,12 @@ export interface ForegroundRunControl {
|
|
|
1574
1626
|
toolCount?: number;
|
|
1575
1627
|
/** Independently tracked children for foreground parallel work and fleet inspection. */
|
|
1576
1628
|
activeChildren?: Map<number, ForegroundChildControl>;
|
|
1629
|
+
/** Live Prompt Audit redo callback. It is current-session memory only. */
|
|
1630
|
+
promptAuditRedo?: (index: number, guidance: string) => Promise<{ text: string; isError?: boolean }>;
|
|
1631
|
+
/** Memory-only source run for Prompt Audit redo. */
|
|
1632
|
+
sourceRunId?: string;
|
|
1633
|
+
/** Memory-only replacement run started by Prompt Audit redo. */
|
|
1634
|
+
supersededByRunId?: string;
|
|
1577
1635
|
/** Scheduling owners that may still launch another child. Removal is safe only at zero. */
|
|
1578
1636
|
schedulingOwners?: number;
|
|
1579
1637
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -1593,6 +1651,12 @@ export interface WaitSubscriptionRecord {
|
|
|
1593
1651
|
expiresAt: number;
|
|
1594
1652
|
}
|
|
1595
1653
|
|
|
1654
|
+
export interface ActiveAsyncCapacitySnapshot {
|
|
1655
|
+
used: number;
|
|
1656
|
+
/** Zero means the opt-in cap is disabled. */
|
|
1657
|
+
limit: number;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1596
1660
|
export interface SubagentState {
|
|
1597
1661
|
baseCwd: string;
|
|
1598
1662
|
currentSessionId: string | null;
|
|
@@ -1603,6 +1667,10 @@ export interface SubagentState {
|
|
|
1603
1667
|
/** Runtime mission-store snapshot used by optional inspector context. */
|
|
1604
1668
|
missionStoreConfig?: MissionStoreConfig;
|
|
1605
1669
|
parentSessionFile?: string | null;
|
|
1670
|
+
/** Extension-owned roots trusted for child session transcript reads. */
|
|
1671
|
+
trustedSessionRoots?: string[];
|
|
1672
|
+
/** Live async session roots created by this parent executor, keyed by run id. */
|
|
1673
|
+
liveAsyncSessionRoots?: Map<string, string>;
|
|
1606
1674
|
/** Last valid parent session model observed for this session; used when continuation contexts omit ctx.model. */
|
|
1607
1675
|
lastParentModel?: { provider: string; id: string };
|
|
1608
1676
|
subagentInProgress?: boolean;
|
|
@@ -1613,6 +1681,8 @@ export interface SubagentState {
|
|
|
1613
1681
|
granted?: number;
|
|
1614
1682
|
grantHistory?: SpawnBudgetGrant[];
|
|
1615
1683
|
};
|
|
1684
|
+
/** Current-session top-level async capacity projection. */
|
|
1685
|
+
activeAsyncCapacity?: ActiveAsyncCapacitySnapshot;
|
|
1616
1686
|
asyncJobs: Map<string, AsyncJobState>;
|
|
1617
1687
|
/** Current-session active and recent async runs for the native fleet inspector. */
|
|
1618
1688
|
fleetJobs?: Map<string, AsyncJobState>;
|
|
@@ -1730,9 +1800,12 @@ export interface RunSyncOptions {
|
|
|
1730
1800
|
/** Effective parent wait-tool setting propagated to the child runtime. */
|
|
1731
1801
|
waitToolEnabled?: boolean;
|
|
1732
1802
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
1803
|
+
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
1733
1804
|
nestedRoute?: NestedRouteInfo;
|
|
1734
1805
|
/** Override the agent's default model (format: "provider/id" or just "id") */
|
|
1735
1806
|
modelOverride?: string;
|
|
1807
|
+
/** LLM intent arbiter for the completion mutation guard (rescues read-only review runs). */
|
|
1808
|
+
llmIntentArbiter?: import("../runs/shared/llm-intent-arbiter.ts").TaskMutationArbiter;
|
|
1736
1809
|
/** Override the agent's default thinking level for this run */
|
|
1737
1810
|
thinkingOverride?: AgentConfig["thinking"];
|
|
1738
1811
|
/** Registry models available for heuristic bare-model resolution */
|
|
@@ -1756,6 +1829,8 @@ export interface RunSyncOptions {
|
|
|
1756
1829
|
dynamic?: boolean;
|
|
1757
1830
|
dynamicGroup?: boolean;
|
|
1758
1831
|
};
|
|
1832
|
+
/** Private live callback for the exact child prompt after runtime acceptance injection. */
|
|
1833
|
+
onEffectivePrompt?: (prompt: string) => void;
|
|
1759
1834
|
}
|
|
1760
1835
|
|
|
1761
1836
|
export type IntercomBridgeMode = "off" | "fork-only" | "always";
|
|
@@ -1817,6 +1892,13 @@ export const FLEET_KEYBINDING_ACTIONS = [
|
|
|
1817
1892
|
export type FleetKeybindingAction = typeof FLEET_KEYBINDING_ACTIONS[number];
|
|
1818
1893
|
export type FleetKeybindingsConfig = Partial<Record<FleetKeybindingAction, string[]>>;
|
|
1819
1894
|
|
|
1895
|
+
export interface MainWindowRendererConfig {
|
|
1896
|
+
/** Unit of horizontal space in main chat subagent call/result rows. Omit to preserve current spacing. Set 0 for no extra padding. */
|
|
1897
|
+
horizontalSpacing?: number;
|
|
1898
|
+
/** Maximum collapsed rich-result rows. Expanded output is not capped. */
|
|
1899
|
+
compactResultMaxLines?: number;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1820
1902
|
export interface ExtensionConfig {
|
|
1821
1903
|
asyncByDefault?: boolean;
|
|
1822
1904
|
/** Show the Claude Code-style navigable fleet. Defaults to true. */
|
|
@@ -1833,6 +1915,8 @@ export interface ExtensionConfig {
|
|
|
1833
1915
|
legacyChainControls?: boolean;
|
|
1834
1916
|
/** Inline chat rendering for the subagent tool. Defaults to rich. */
|
|
1835
1917
|
inlineToolDisplay?: InlineToolDisplay;
|
|
1918
|
+
/** Density controls for the main chat subagent call/result renderer. */
|
|
1919
|
+
mainWindowRenderer?: MainWindowRendererConfig;
|
|
1836
1920
|
forceTopLevelAsync?: boolean;
|
|
1837
1921
|
waitTool?: WaitToolConfig;
|
|
1838
1922
|
defaultSessionDir?: string;
|
|
@@ -1840,8 +1924,22 @@ export interface ExtensionConfig {
|
|
|
1840
1924
|
maxSubagentDepth?: number;
|
|
1841
1925
|
/** Optional cumulative session cap. Unset or 0 means unlimited. */
|
|
1842
1926
|
maxSubagentSpawnsPerSession?: number;
|
|
1927
|
+
/** Cumulative logical-child cap for one top-level run tree. Defaults to 64. */
|
|
1928
|
+
maxSubagentSpawnsPerRun?: number;
|
|
1929
|
+
/** Optional active top-level async run cap per parent session. Unset or 0 means unlimited. */
|
|
1930
|
+
maxActiveAsyncRunsPerSession?: number;
|
|
1843
1931
|
/** Global cap on simultaneously-running subagent tasks within a single run. Defaults to 20. */
|
|
1844
1932
|
globalConcurrencyLimit?: number;
|
|
1933
|
+
/**
|
|
1934
|
+
* Global default runtime deadline in milliseconds. It replaces the built-in
|
|
1935
|
+
* 30-minute backstop for single, parallel, and chain launches (foreground, plus
|
|
1936
|
+
* plain single-agent async runs) when neither the call (`timeoutMs`/`maxRuntimeMs`)
|
|
1937
|
+
* nor the selected agent provides a timeout. Explicit call values and agent
|
|
1938
|
+
* frontmatter defaults still win. Composite async runs (chain/parallel/workflow)
|
|
1939
|
+
* stay unbounded at the top level by design — their children are bounded individually.
|
|
1940
|
+
* Must be a positive integer; invalid values are ignored.
|
|
1941
|
+
*/
|
|
1942
|
+
timeoutMs?: number;
|
|
1845
1943
|
control?: ControlConfig;
|
|
1846
1944
|
completionBatch?: CompletionBatchConfig;
|
|
1847
1945
|
turnBudget?: TurnBudgetConfig;
|
|
@@ -1854,7 +1952,7 @@ export interface ExtensionConfig {
|
|
|
1854
1952
|
worktreeSetupHook?: string;
|
|
1855
1953
|
worktreeSetupHookTimeoutMs?: number;
|
|
1856
1954
|
worktreeBaseDir?: string;
|
|
1857
|
-
/** Where to store subagent artifact files. Defaults to "
|
|
1955
|
+
/** Where to store subagent artifact files. Defaults to "session" (the pi session directory, or OS temp when unavailable). Set to "project" for cwd/.pi/subagents. */
|
|
1858
1956
|
artifactDir?: ArtifactDirPreference;
|
|
1859
1957
|
/** Artifact cleanup retention. Set cleanupDays to 0 to disable cleanup. */
|
|
1860
1958
|
artifactConfig?: Pick<ArtifactConfig, "cleanupDays">;
|
|
@@ -1878,7 +1976,7 @@ export const DEFAULT_MAX_OUTPUT: Required<MaxOutputConfig> = {
|
|
|
1878
1976
|
|
|
1879
1977
|
export const DEFAULT_ARTIFACT_CONFIG: ArtifactConfig = {
|
|
1880
1978
|
enabled: true,
|
|
1881
|
-
dir: "
|
|
1979
|
+
dir: "session",
|
|
1882
1980
|
includeInput: true,
|
|
1883
1981
|
includeOutput: true,
|
|
1884
1982
|
includeJsonl: false,
|
|
@@ -1965,7 +2063,7 @@ export const SLASH_SUBAGENT_CANCEL_EVENT = "subagent:slash:cancel";
|
|
|
1965
2063
|
export const POLL_INTERVAL_MS = 250;
|
|
1966
2064
|
export const MAX_WIDGET_JOBS = 4;
|
|
1967
2065
|
export const DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
1968
|
-
export const SUBAGENT_ACTIONS = ["list", "get", "models", "children.list", "guide", "create", "update", "delete", "eject", "disable", "enable", "reset", "mission.create", "mission.list", "mission.show", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "worktree.discard", "refine", "refine.show", "refine.rollback", "inspector.open", "inspector.status", "inspector.close", "project.open", "project.status", "project.close", "status", "grant-spawn-budget", "interrupt", "resume", "steer", "stop", "dismiss", "append-step", "approve-checkpoint", "reject-checkpoint", "doctor", "watchdog.status", "watchdog.check", "watchdog.configure", "watchdog.recommend-model", "schedule.create", "schedule.list", "schedule.show", "schedule.history", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"] as const;
|
|
2066
|
+
export const SUBAGENT_ACTIONS = ["list", "get", "models", "children.list", "guide", "create", "update", "delete", "eject", "disable", "enable", "reset", "mission.create", "mission.list", "mission.show", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "worktree.discard", "refine", "refine.show", "refine.rollback", "inspector.open", "inspector.status", "inspector.close", "project.open", "project.status", "project.close", "status", "debug.run", "grant-spawn-budget", "interrupt", "resume", "steer", "stop", "dismiss", "append-step", "approve-checkpoint", "reject-checkpoint", "doctor", "watchdog.status", "watchdog.check", "watchdog.configure", "watchdog.recommend-model", "schedule.create", "schedule.list", "schedule.show", "schedule.history", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"] as const;
|
|
1969
2067
|
|
|
1970
2068
|
export const DEFAULT_FORK_PREAMBLE =
|
|
1971
2069
|
"You are a delegated subagent running from a fork of the parent session. " +
|
|
@@ -2057,6 +2155,19 @@ export function resolveMaxSubagentSpawnsPerSession(configMaxSpawns?: number): nu
|
|
|
2057
2155
|
return configuredMaxSpawns === 0 ? undefined : configuredMaxSpawns;
|
|
2058
2156
|
}
|
|
2059
2157
|
|
|
2158
|
+
export const DEFAULT_MAX_SUBAGENT_SPAWNS_PER_RUN = 64;
|
|
2159
|
+
|
|
2160
|
+
export function normalizeMaxSubagentSpawnsPerRun(value: unknown): number | undefined {
|
|
2161
|
+
const normalized = normalizeNonNegativeInteger(value);
|
|
2162
|
+
return normalized !== undefined && normalized > 0 ? normalized : undefined;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
export function resolveMaxSubagentSpawnsPerRun(configMaxSpawns?: number): number {
|
|
2166
|
+
return normalizeMaxSubagentSpawnsPerRun(process.env.PI_SUBAGENT_MAX_SPAWNS_PER_RUN)
|
|
2167
|
+
?? normalizeMaxSubagentSpawnsPerRun(configMaxSpawns)
|
|
2168
|
+
?? DEFAULT_MAX_SUBAGENT_SPAWNS_PER_RUN;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2060
2171
|
// ============================================================================
|
|
2061
2172
|
// Utility Functions
|
|
2062
2173
|
// ============================================================================
|
package/src/shared/utils.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, Neste
|
|
|
17
17
|
const DEFAULT_CONFIG_DIR_NAME = ".pi";
|
|
18
18
|
const PI_CODING_AGENT_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
19
19
|
export const PI_CODING_AGENT_PACKAGE_ROOT_ENV = "PI_SUBAGENTS_PI_CODING_AGENT_PACKAGE_ROOT";
|
|
20
|
+
export const PROMPT_REDACTED = "[prompt redacted]";
|
|
20
21
|
|
|
21
22
|
export function resolveWatchPath(
|
|
22
23
|
watchPath: string,
|
|
@@ -354,7 +355,7 @@ function compactCompletedProgress(progress: AgentProgress): AgentProgress {
|
|
|
354
355
|
agent: progress.agent,
|
|
355
356
|
status: progress.status,
|
|
356
357
|
activityState: progress.activityState,
|
|
357
|
-
task:
|
|
358
|
+
task: "[prompt redacted]",
|
|
358
359
|
skills: progress.skills,
|
|
359
360
|
toolCount: progress.toolCount,
|
|
360
361
|
tokens: progress.tokens,
|
|
@@ -428,6 +429,7 @@ export function compactForegroundResult(result: SingleResult): SingleResult {
|
|
|
428
429
|
const toolCalls = result.toolCalls?.length ? result.toolCalls : extractToolCallSummaries(result.messages);
|
|
429
430
|
return {
|
|
430
431
|
...result,
|
|
432
|
+
task: "[prompt redacted]",
|
|
431
433
|
messages: undefined,
|
|
432
434
|
progress: undefined,
|
|
433
435
|
toolCalls: toolCalls.length ? toolCalls : undefined,
|
package/src/tui/fleet-status.ts
CHANGED
|
@@ -318,7 +318,7 @@ export class SubagentFleetStatus {
|
|
|
318
318
|
this.clearWidget();
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
|
-
if (this.entries.length === 0) {
|
|
321
|
+
if (this.entries.length === 0 && !(this.state.activeAsyncCapacity?.used)) {
|
|
322
322
|
this.active = false;
|
|
323
323
|
this.selectedKey = "main";
|
|
324
324
|
this.lastRenderKey = "";
|
|
@@ -413,10 +413,13 @@ export class SubagentFleetStatus {
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
render(width: number, theme: Theme): string[] {
|
|
416
|
-
if (this.entries.length === 0) return [];
|
|
416
|
+
if (this.entries.length === 0 && !(this.state.activeAsyncCapacity?.used)) return [];
|
|
417
417
|
if (!this.active) {
|
|
418
418
|
const tokens = this.entries.reduce((total, entry) => total + entry.tokens, 0);
|
|
419
|
-
const
|
|
419
|
+
const capacity = this.state.activeAsyncCapacity;
|
|
420
|
+
const asyncRuns = capacity ? `Async runs ${capacity.used}/${capacity.limit || "∞"}` : "";
|
|
421
|
+
const agents = this.entries.length > 0 ? `${this.entries.length} active ${this.entries.length === 1 ? "agent" : "agents"}` : "";
|
|
422
|
+
const label = [agents, asyncRuns].filter(Boolean).join(" · ");
|
|
420
423
|
return [truncateToWidth(` ${theme.fg("muted", label)} · ${theme.fg("dim", `${formatFleetTokens(tokens)} · ↓/← to inspect`)}`, width)];
|
|
421
424
|
}
|
|
422
425
|
const roster = this.rosterKeys();
|
|
@@ -444,9 +447,8 @@ export class SubagentFleetStatus {
|
|
|
444
447
|
}
|
|
445
448
|
|
|
446
449
|
private renderEntry(rosterIndex: number, selectedIndex: number, entry: FleetStatusEntry, width: number, theme: Theme): string {
|
|
447
|
-
const description = entry.description?.replace(/\s+/g, " ").trim();
|
|
448
450
|
const agent = entry.modelThinking ? `${entry.agent} (${entry.modelThinking})` : entry.agent;
|
|
449
|
-
const left = ` ${this.bullet(rosterIndex, selectedIndex, theme)} ${theme.fg("muted", agent)} · ${entry.state}
|
|
451
|
+
const left = ` ${this.bullet(rosterIndex, selectedIndex, theme)} ${theme.fg("muted", agent)} · ${entry.state}`;
|
|
450
452
|
const elapsed = Date.now() - entry.startedAt;
|
|
451
453
|
const right = theme.fg("dim", `${formatFleetElapsed(elapsed)} · ${formatFleetTokens(entry.tokens)}`);
|
|
452
454
|
return rightAlign(left, right, width);
|
|
@@ -2,59 +2,12 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { getLanguageFromPath, highlightCode, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Markdown, truncateToWidth, visibleWidth, wrapTextWithAnsi, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
5
|
+
import { safeTerminalText as safeDisplayText } from "../shared/display-text.ts";
|
|
5
6
|
|
|
6
7
|
const DEFAULT_MAX_RECORDS = 240;
|
|
7
8
|
const DEFAULT_MAX_BYTES = 2 * 1024 * 1024;
|
|
8
9
|
const MAX_MESSAGE_CHARS = 64 * 1024;
|
|
9
10
|
const TOOL_PREVIEW_LINES = 7;
|
|
10
|
-
const BINARY_CONTENT_PLACEHOLDER = "[binary content omitted for safe display]";
|
|
11
|
-
|
|
12
|
-
function isUnsafeDisplayCodePoint(codePoint: number): boolean {
|
|
13
|
-
const terminalControl = (codePoint <= 0x1f && codePoint !== 0x09 && codePoint !== 0x0a)
|
|
14
|
-
|| (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
15
|
-
const bidiControl = codePoint === 0x061c
|
|
16
|
-
|| codePoint === 0x200e
|
|
17
|
-
|| codePoint === 0x200f
|
|
18
|
-
|| (codePoint >= 0x202a && codePoint <= 0x202e)
|
|
19
|
-
|| (codePoint >= 0x2066 && codePoint <= 0x2069);
|
|
20
|
-
const privateUse = (codePoint >= 0xe000 && codePoint <= 0xf8ff)
|
|
21
|
-
|| (codePoint >= 0xf0000 && codePoint <= 0xffffd)
|
|
22
|
-
|| (codePoint >= 0x100000 && codePoint <= 0x10fffd);
|
|
23
|
-
const invalidScalar = codePoint >= 0xd800 && codePoint <= 0xdfff;
|
|
24
|
-
const nonCharacter = (codePoint >= 0xfdd0 && codePoint <= 0xfdef)
|
|
25
|
-
|| (codePoint & 0xffff) === 0xfffe
|
|
26
|
-
|| (codePoint & 0xffff) === 0xffff;
|
|
27
|
-
return terminalControl || bidiControl || privateUse || invalidScalar || nonCharacter;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function looksLikeBinaryContent(text: string): boolean {
|
|
31
|
-
if (text.includes("\0")) return true;
|
|
32
|
-
let suspiciousControls = 0;
|
|
33
|
-
let replacementCharacters = 0;
|
|
34
|
-
let codePoints = 0;
|
|
35
|
-
for (const character of text) {
|
|
36
|
-
codePoints++;
|
|
37
|
-
const codePoint = character.codePointAt(0) ?? 0;
|
|
38
|
-
if ((codePoint <= 0x08) || (codePoint >= 0x0e && codePoint <= 0x1f)) suspiciousControls++;
|
|
39
|
-
if (codePoint === 0xfffd) replacementCharacters++;
|
|
40
|
-
}
|
|
41
|
-
if (codePoints === 0) return false;
|
|
42
|
-
return (suspiciousControls >= 4 && suspiciousControls / codePoints >= 0.1)
|
|
43
|
-
|| (replacementCharacters >= 3 && replacementCharacters / codePoints >= 0.1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function safeDisplayText(text: string): string {
|
|
47
|
-
const normalized = text.replace(/\r\n/g, "\n");
|
|
48
|
-
if (looksLikeBinaryContent(normalized)) return BINARY_CONTENT_PLACEHOLDER;
|
|
49
|
-
let safe = "";
|
|
50
|
-
for (const character of normalized) {
|
|
51
|
-
const codePoint = character.codePointAt(0) ?? 0;
|
|
52
|
-
safe += isUnsafeDisplayCodePoint(codePoint)
|
|
53
|
-
? `[U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}]`
|
|
54
|
-
: character;
|
|
55
|
-
}
|
|
56
|
-
return safe;
|
|
57
|
-
}
|
|
58
11
|
|
|
59
12
|
function sanitizeJsonDisplayValue(value: unknown): { value: unknown; changed: boolean } {
|
|
60
13
|
if (typeof value === "string") {
|