pi-subagents 0.47.0 → 0.48.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 +37 -0
- package/README.md +2 -0
- package/agents/reviewer.md +3 -4
- package/docs/configuration.md +46 -2
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +1 -1
- package/package.json +1 -1
- package/src/agents/agents.ts +9 -3
- package/src/extension/config.ts +16 -0
- package/src/extension/doctor.ts +40 -0
- package/src/extension/index.ts +30 -11
- package/src/extension/public-execution.ts +5 -0
- package/src/extension/rpc.ts +7 -9
- package/src/extension/schemas.ts +4 -4
- package/src/intercom/intercom-bridge.ts +4 -1
- package/src/intercom/native-supervisor-channel.ts +102 -4
- package/src/missions/lifecycle.ts +4 -7
- package/src/missions/store.ts +4 -4
- package/src/missions/workflow-state.ts +6 -2
- package/src/runs/background/active-async-capacity.ts +374 -0
- package/src/runs/background/active-run-index.ts +46 -0
- package/src/runs/background/async-execution.ts +117 -29
- package/src/runs/background/async-job-tracker.ts +279 -134
- package/src/runs/background/async-resume.ts +7 -1
- package/src/runs/background/async-status.ts +33 -4
- package/src/runs/background/chain-append.ts +33 -15
- package/src/runs/background/control-channel.ts +55 -17
- package/src/runs/background/owned-process-tree.ts +104 -0
- package/src/runs/background/process-terminal.ts +17 -3
- package/src/runs/background/result-watcher.ts +87 -6
- package/src/runs/background/run-status.ts +23 -1
- package/src/runs/background/scheduled-runs.ts +23 -0
- package/src/runs/background/stale-run-reconciler.ts +10 -3
- package/src/runs/background/subagent-runner.ts +75 -33
- package/src/runs/foreground/async-dismiss-action.ts +85 -0
- package/src/runs/foreground/async-steering-action.ts +6 -7
- 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 +171 -0
- package/src/runs/foreground/subagent-executor.ts +648 -195
- package/src/runs/shared/acceptance.ts +13 -4
- package/src/runs/shared/llm-intent-arbiter.ts +286 -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/subagent-prompt-runtime.ts +24 -5
- package/src/runs/shared/task-intent.ts +19 -3
- package/src/runs/shared/worktree.ts +17 -5
- package/src/shared/artifacts.ts +1 -1
- package/src/shared/file-coalescer.ts +9 -0
- package/src/shared/types.ts +97 -1
- package/src/shared/utils.ts +3 -1
- package/src/tui/fleet-status.ts +7 -5
- package/src/tui/fleet.ts +225 -12
- package/src/workflows/scripted-workflow.ts +26 -6
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;
|
|
@@ -1024,6 +1061,9 @@ export interface Details {
|
|
|
1024
1061
|
// Aggregated cost across all agents in the run
|
|
1025
1062
|
totalCost?: CostSummary;
|
|
1026
1063
|
spawnBudget?: SpawnBudgetSnapshot;
|
|
1064
|
+
runFanoutBudget?: RunFanoutBudgetSnapshot;
|
|
1065
|
+
runFanoutRejection?: RunFanoutRejection;
|
|
1066
|
+
activeAsyncCapacity?: ActiveAsyncCapacitySnapshot;
|
|
1027
1067
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
1028
1068
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
1029
1069
|
parallelHandoff?: ParallelHandoffReference;
|
|
@@ -1290,6 +1330,8 @@ export interface AsyncStatus {
|
|
|
1290
1330
|
context?: "fresh" | "fork" | "mixed";
|
|
1291
1331
|
isNested?: boolean;
|
|
1292
1332
|
state: "queued" | "running" | "complete" | "failed" | "paused" | "stopped" | "rejected";
|
|
1333
|
+
/** Display-only dismissal marker for a reload-orphaned workflow. */
|
|
1334
|
+
displayDismissedAt?: number;
|
|
1293
1335
|
error?: string;
|
|
1294
1336
|
activityState?: ActivityState;
|
|
1295
1337
|
lastActivityAt?: number;
|
|
@@ -1321,6 +1363,8 @@ export interface AsyncStatus {
|
|
|
1321
1363
|
workflowGraph?: WorkflowGraphSnapshot;
|
|
1322
1364
|
checkpoint?: ChainCheckpointState;
|
|
1323
1365
|
processTerminal?: ProcessTerminalV1;
|
|
1366
|
+
runFanoutBudget?: RunFanoutBudgetSnapshot;
|
|
1367
|
+
runFanoutBudgetDescriptor?: RunFanoutBudgetDescriptor;
|
|
1324
1368
|
launchContractDigest?: string;
|
|
1325
1369
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
1326
1370
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
@@ -1340,6 +1384,10 @@ export interface AsyncStatus {
|
|
|
1340
1384
|
phase?: string;
|
|
1341
1385
|
label?: string;
|
|
1342
1386
|
workflowKey?: string;
|
|
1387
|
+
/** Child run identity for workflow capacity reconciliation. */
|
|
1388
|
+
runId?: string;
|
|
1389
|
+
/** True only when this workflow child owns a detached async runner. */
|
|
1390
|
+
async?: boolean;
|
|
1343
1391
|
parentWorkflowRunId?: string;
|
|
1344
1392
|
outputName?: string;
|
|
1345
1393
|
structured?: boolean;
|
|
@@ -1572,6 +1620,12 @@ export interface ForegroundRunControl {
|
|
|
1572
1620
|
toolCount?: number;
|
|
1573
1621
|
/** Independently tracked children for foreground parallel work and fleet inspection. */
|
|
1574
1622
|
activeChildren?: Map<number, ForegroundChildControl>;
|
|
1623
|
+
/** Live Prompt Audit redo callback. It is current-session memory only. */
|
|
1624
|
+
promptAuditRedo?: (index: number, guidance: string) => Promise<{ text: string; isError?: boolean }>;
|
|
1625
|
+
/** Memory-only source run for Prompt Audit redo. */
|
|
1626
|
+
sourceRunId?: string;
|
|
1627
|
+
/** Memory-only replacement run started by Prompt Audit redo. */
|
|
1628
|
+
supersededByRunId?: string;
|
|
1575
1629
|
/** Scheduling owners that may still launch another child. Removal is safe only at zero. */
|
|
1576
1630
|
schedulingOwners?: number;
|
|
1577
1631
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -1591,6 +1645,12 @@ export interface WaitSubscriptionRecord {
|
|
|
1591
1645
|
expiresAt: number;
|
|
1592
1646
|
}
|
|
1593
1647
|
|
|
1648
|
+
export interface ActiveAsyncCapacitySnapshot {
|
|
1649
|
+
used: number;
|
|
1650
|
+
/** Zero means the opt-in cap is disabled. */
|
|
1651
|
+
limit: number;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1594
1654
|
export interface SubagentState {
|
|
1595
1655
|
baseCwd: string;
|
|
1596
1656
|
currentSessionId: string | null;
|
|
@@ -1611,6 +1671,8 @@ export interface SubagentState {
|
|
|
1611
1671
|
granted?: number;
|
|
1612
1672
|
grantHistory?: SpawnBudgetGrant[];
|
|
1613
1673
|
};
|
|
1674
|
+
/** Current-session top-level async capacity projection. */
|
|
1675
|
+
activeAsyncCapacity?: ActiveAsyncCapacitySnapshot;
|
|
1614
1676
|
asyncJobs: Map<string, AsyncJobState>;
|
|
1615
1677
|
/** Current-session active and recent async runs for the native fleet inspector. */
|
|
1616
1678
|
fleetJobs?: Map<string, AsyncJobState>;
|
|
@@ -1728,9 +1790,12 @@ export interface RunSyncOptions {
|
|
|
1728
1790
|
/** Effective parent wait-tool setting propagated to the child runtime. */
|
|
1729
1791
|
waitToolEnabled?: boolean;
|
|
1730
1792
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
1793
|
+
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
1731
1794
|
nestedRoute?: NestedRouteInfo;
|
|
1732
1795
|
/** Override the agent's default model (format: "provider/id" or just "id") */
|
|
1733
1796
|
modelOverride?: string;
|
|
1797
|
+
/** LLM intent arbiter for the completion mutation guard (rescues read-only review runs). */
|
|
1798
|
+
llmIntentArbiter?: import("../runs/shared/llm-intent-arbiter.ts").TaskMutationArbiter;
|
|
1734
1799
|
/** Override the agent's default thinking level for this run */
|
|
1735
1800
|
thinkingOverride?: AgentConfig["thinking"];
|
|
1736
1801
|
/** Registry models available for heuristic bare-model resolution */
|
|
@@ -1754,6 +1819,8 @@ export interface RunSyncOptions {
|
|
|
1754
1819
|
dynamic?: boolean;
|
|
1755
1820
|
dynamicGroup?: boolean;
|
|
1756
1821
|
};
|
|
1822
|
+
/** Private live callback for the exact child prompt after runtime acceptance injection. */
|
|
1823
|
+
onEffectivePrompt?: (prompt: string) => void;
|
|
1757
1824
|
}
|
|
1758
1825
|
|
|
1759
1826
|
export type IntercomBridgeMode = "off" | "fork-only" | "always";
|
|
@@ -1838,8 +1905,22 @@ export interface ExtensionConfig {
|
|
|
1838
1905
|
maxSubagentDepth?: number;
|
|
1839
1906
|
/** Optional cumulative session cap. Unset or 0 means unlimited. */
|
|
1840
1907
|
maxSubagentSpawnsPerSession?: number;
|
|
1908
|
+
/** Cumulative logical-child cap for one top-level run tree. Defaults to 64. */
|
|
1909
|
+
maxSubagentSpawnsPerRun?: number;
|
|
1910
|
+
/** Optional active top-level async run cap per parent session. Unset or 0 means unlimited. */
|
|
1911
|
+
maxActiveAsyncRunsPerSession?: number;
|
|
1841
1912
|
/** Global cap on simultaneously-running subagent tasks within a single run. Defaults to 20. */
|
|
1842
1913
|
globalConcurrencyLimit?: number;
|
|
1914
|
+
/**
|
|
1915
|
+
* Global default runtime deadline in milliseconds. It replaces the built-in
|
|
1916
|
+
* 30-minute backstop for single, parallel, and chain launches (foreground, plus
|
|
1917
|
+
* plain single-agent async runs) when neither the call (`timeoutMs`/`maxRuntimeMs`)
|
|
1918
|
+
* nor the selected agent provides a timeout. Explicit call values and agent
|
|
1919
|
+
* frontmatter defaults still win. Composite async runs (chain/parallel/workflow)
|
|
1920
|
+
* stay unbounded at the top level by design — their children are bounded individually.
|
|
1921
|
+
* Must be a positive integer; invalid values are ignored.
|
|
1922
|
+
*/
|
|
1923
|
+
timeoutMs?: number;
|
|
1843
1924
|
control?: ControlConfig;
|
|
1844
1925
|
completionBatch?: CompletionBatchConfig;
|
|
1845
1926
|
turnBudget?: TurnBudgetConfig;
|
|
@@ -1854,6 +1935,8 @@ export interface ExtensionConfig {
|
|
|
1854
1935
|
worktreeBaseDir?: string;
|
|
1855
1936
|
/** Where to store subagent artifact files. Defaults to "project" (cwd/.pi/subagents). Set to "session" for pi session dir, or "temp" for OS temp. */
|
|
1856
1937
|
artifactDir?: ArtifactDirPreference;
|
|
1938
|
+
/** Artifact cleanup retention. Set cleanupDays to 0 to disable cleanup. */
|
|
1939
|
+
artifactConfig?: Pick<ArtifactConfig, "cleanupDays">;
|
|
1857
1940
|
intercomBridge?: IntercomBridgeConfig;
|
|
1858
1941
|
proactiveSkillSubagents?: ProactiveSkillSubagentsConfig | false;
|
|
1859
1942
|
scheduledRuns?: ScheduledRunsConfig;
|
|
@@ -1961,7 +2044,7 @@ export const SLASH_SUBAGENT_CANCEL_EVENT = "subagent:slash:cancel";
|
|
|
1961
2044
|
export const POLL_INTERVAL_MS = 250;
|
|
1962
2045
|
export const MAX_WIDGET_JOBS = 4;
|
|
1963
2046
|
export const DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
1964
|
-
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", "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;
|
|
2047
|
+
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;
|
|
1965
2048
|
|
|
1966
2049
|
export const DEFAULT_FORK_PREAMBLE =
|
|
1967
2050
|
"You are a delegated subagent running from a fork of the parent session. " +
|
|
@@ -2053,6 +2136,19 @@ export function resolveMaxSubagentSpawnsPerSession(configMaxSpawns?: number): nu
|
|
|
2053
2136
|
return configuredMaxSpawns === 0 ? undefined : configuredMaxSpawns;
|
|
2054
2137
|
}
|
|
2055
2138
|
|
|
2139
|
+
export const DEFAULT_MAX_SUBAGENT_SPAWNS_PER_RUN = 64;
|
|
2140
|
+
|
|
2141
|
+
export function normalizeMaxSubagentSpawnsPerRun(value: unknown): number | undefined {
|
|
2142
|
+
const normalized = normalizeNonNegativeInteger(value);
|
|
2143
|
+
return normalized !== undefined && normalized > 0 ? normalized : undefined;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
export function resolveMaxSubagentSpawnsPerRun(configMaxSpawns?: number): number {
|
|
2147
|
+
return normalizeMaxSubagentSpawnsPerRun(process.env.PI_SUBAGENT_MAX_SPAWNS_PER_RUN)
|
|
2148
|
+
?? normalizeMaxSubagentSpawnsPerRun(configMaxSpawns)
|
|
2149
|
+
?? DEFAULT_MAX_SUBAGENT_SPAWNS_PER_RUN;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2056
2152
|
// ============================================================================
|
|
2057
2153
|
// Utility Functions
|
|
2058
2154
|
// ============================================================================
|
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);
|