pi-subagents 0.49.0 → 0.51.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 +97 -0
- package/agents/gpt-pro.md +17 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +37 -2
- package/docs/configuration.md +76 -14
- package/docs/extension-api.md +78 -1
- package/docs/missions.md +1 -1
- package/docs/observability.md +20 -4
- package/docs/tool-reference.md +55 -39
- package/docs/workflows.md +171 -5
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +5 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +9 -6
- package/skills/pi-subagents/references/execution-controls.md +22 -18
- package/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/skills/pi-subagents/references/prompting-and-roles.md +33 -17
- package/src/agents/agent-management.ts +100 -345
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +135 -25
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +42 -11
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +36 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +210 -90
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +14 -36
- package/src/extension/tool-description.ts +37 -24
- package/src/inspectors/herdr/actions.ts +5 -9
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +4 -8
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +49 -51
- package/src/missions/goal-driver.ts +3 -1
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +94 -1
- package/src/runs/background/async-execution.ts +98 -38
- package/src/runs/background/async-job-tracker.ts +21 -4
- package/src/runs/background/async-resume.ts +30 -17
- package/src/runs/background/async-retention.ts +888 -0
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +47 -56
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/notify.ts +3 -1
- package/src/runs/background/result-files.ts +505 -0
- package/src/runs/background/result-watcher.ts +250 -51
- package/src/runs/background/retained-children.ts +79 -20
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +37 -29
- package/src/runs/background/run-status.ts +34 -20
- package/src/runs/background/scheduled-runs.ts +71 -27
- package/src/runs/background/stale-run-reconciler.ts +33 -16
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +535 -161
- package/src/runs/background/subagent-wait.ts +9 -7
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +22 -2
- package/src/runs/background/wait-subscriptions.ts +80 -1
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +221 -15
- package/src/runs/foreground/subagent-executor.ts +529 -1519
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/external-job-bridge.ts +450 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +34 -3
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +437 -0
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +6 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/subagent-control.ts +41 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -15
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +12 -1
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +88 -27
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-commands.ts +45 -28
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +186 -28
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +7 -5
- package/src/workflows/scripted-workflow.ts +424 -125
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1565
package/src/extension/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Toggle: async parameter (default: true; set asyncByDefault:false in config.json to opt out)
|
|
10
10
|
*
|
|
11
11
|
* Config file: ~/.pi/agent/extensions/subagent/config.json
|
|
12
|
-
* { "asyncByDefault": true, "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
|
|
12
|
+
* { "asyncByDefault": true, "defaultSubagentContext": "fork", "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
@@ -23,6 +23,7 @@ import { discoverAgents } from "../agents/agents.ts";
|
|
|
23
23
|
import { ensureAccessibleDir } from "../shared/accessible-dir.ts";
|
|
24
24
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
25
25
|
import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
26
|
+
import { currentCompletionOwnerId } from "../shared/completion-owner.ts";
|
|
26
27
|
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
27
28
|
import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.ts";
|
|
28
29
|
import { openSubagentFleet } from "../tui/fleet.ts";
|
|
@@ -31,6 +32,8 @@ import { createSubagentParamsSchema } from "./schemas.ts";
|
|
|
31
32
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
32
33
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
33
34
|
import { getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.ts";
|
|
35
|
+
import { cleanupResultIndexes, missionObserverResultCandidateFiles } from "../runs/background/result-files.ts";
|
|
36
|
+
import { ASYNC_RETENTION_DELAY_MS, cleanupAsyncRetention } from "../runs/background/async-retention.ts";
|
|
34
37
|
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
35
38
|
import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
|
|
36
39
|
import { registerSlashCommands } from "../slash/slash-commands.ts";
|
|
@@ -52,7 +55,7 @@ import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/
|
|
|
52
55
|
import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
53
56
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
54
57
|
import { loadConfig, resolveAsyncByDefault, resolveScheduledStoreRoot } from "./config.ts";
|
|
55
|
-
import { buildSubagentToolDescription } from "./tool-description.ts";
|
|
58
|
+
import { buildSubagentToolDescription, buildSubagentToolPromptMetadata } from "./tool-description.ts";
|
|
56
59
|
import { collectGoalContinuationNotices } from "../missions/goal-driver.ts";
|
|
57
60
|
import { restoreForegroundRunHistory } from "../runs/foreground/foreground-history.ts";
|
|
58
61
|
import { resolveMissionStoreLocation } from "../missions/store.ts";
|
|
@@ -82,6 +85,44 @@ import {
|
|
|
82
85
|
|
|
83
86
|
export { loadConfig, resolveAsyncByDefault } from "./config.ts";
|
|
84
87
|
|
|
88
|
+
const SLOW_RELOAD_PHASE_MS = 250;
|
|
89
|
+
const RUNTIME_REGISTRY_STORE_KEY = "__piSubagentRuntimeRegistry";
|
|
90
|
+
|
|
91
|
+
interface SubagentRuntimeEntry {
|
|
92
|
+
cleanup(): void;
|
|
93
|
+
sessionManager: object | null;
|
|
94
|
+
visibleControlNotices: Set<string>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface SubagentRuntimeRegistry {
|
|
98
|
+
bySessionManager: WeakMap<object, SubagentRuntimeEntry>;
|
|
99
|
+
visibleControlNoticesBySessionManager: WeakMap<object, Set<string>>;
|
|
100
|
+
activeEntries: Set<SubagentRuntimeEntry>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getRuntimeRegistry(): SubagentRuntimeRegistry {
|
|
104
|
+
const globalStore = globalThis as Record<string, unknown>;
|
|
105
|
+
const existing = globalStore[RUNTIME_REGISTRY_STORE_KEY] as Partial<SubagentRuntimeRegistry> | undefined;
|
|
106
|
+
if (existing?.bySessionManager instanceof WeakMap && existing.activeEntries instanceof Set) {
|
|
107
|
+
if (!(existing.visibleControlNoticesBySessionManager instanceof WeakMap)) {
|
|
108
|
+
existing.visibleControlNoticesBySessionManager = new WeakMap();
|
|
109
|
+
}
|
|
110
|
+
return existing as SubagentRuntimeRegistry;
|
|
111
|
+
}
|
|
112
|
+
const registry: SubagentRuntimeRegistry = {
|
|
113
|
+
bySessionManager: new WeakMap(),
|
|
114
|
+
visibleControlNoticesBySessionManager: new WeakMap(),
|
|
115
|
+
activeEntries: new Set(),
|
|
116
|
+
};
|
|
117
|
+
globalStore[RUNTIME_REGISTRY_STORE_KEY] = registry;
|
|
118
|
+
return registry;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function logSlowPhase(label: string, startedAt: number): void {
|
|
122
|
+
const elapsed = Date.now() - startedAt;
|
|
123
|
+
if (elapsed >= SLOW_RELOAD_PHASE_MS) console.error(`Subagent reload phase '${label}' took ${elapsed}ms.`);
|
|
124
|
+
}
|
|
125
|
+
|
|
85
126
|
function workflowLaneKeys(script: string): string[] {
|
|
86
127
|
const keys: string[] = [];
|
|
87
128
|
const seen = new Set<string>();
|
|
@@ -255,12 +296,13 @@ function rebuildSlashResultContainer(
|
|
|
255
296
|
options: { expanded: boolean },
|
|
256
297
|
theme: ExtensionContext["ui"]["theme"],
|
|
257
298
|
rendererConfig?: MainWindowRendererConfig,
|
|
299
|
+
foregroundDetachShortcut?: string,
|
|
258
300
|
): void {
|
|
259
301
|
container.clear();
|
|
260
302
|
container.addChild(new Spacer(1));
|
|
261
303
|
const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
|
|
262
304
|
const box = new Box(1, 1, (text: string) => theme.bg(boxTheme, text));
|
|
263
|
-
box.addChild(renderSubagentResult(result, options, theme, undefined, rendererConfig));
|
|
305
|
+
box.addChild(renderSubagentResult(result, options, theme, undefined, rendererConfig, foregroundDetachShortcut));
|
|
264
306
|
container.addChild(box);
|
|
265
307
|
}
|
|
266
308
|
|
|
@@ -269,6 +311,7 @@ function createSlashResultComponent(
|
|
|
269
311
|
options: { expanded: boolean },
|
|
270
312
|
theme: ExtensionContext["ui"]["theme"],
|
|
271
313
|
rendererConfig?: MainWindowRendererConfig,
|
|
314
|
+
foregroundDetachShortcut?: string,
|
|
272
315
|
): Container {
|
|
273
316
|
const container = new Container();
|
|
274
317
|
let lastVersion = -1;
|
|
@@ -276,7 +319,7 @@ function createSlashResultComponent(
|
|
|
276
319
|
const snapshot = getSlashRenderableSnapshot(details);
|
|
277
320
|
if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
|
|
278
321
|
lastVersion = snapshot.version;
|
|
279
|
-
rebuildSlashResultContainer(container, snapshot.result, options, theme, rendererConfig);
|
|
322
|
+
rebuildSlashResultContainer(container, snapshot.result, options, theme, rendererConfig, foregroundDetachShortcut);
|
|
280
323
|
}
|
|
281
324
|
return Container.prototype.render.call(container, width);
|
|
282
325
|
};
|
|
@@ -348,16 +391,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
348
391
|
if (process.env[SUBAGENT_CHILD_ENV] === "1") {
|
|
349
392
|
return;
|
|
350
393
|
}
|
|
351
|
-
const
|
|
352
|
-
const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
|
|
353
|
-
const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
|
|
354
|
-
if (typeof previousRuntimeCleanup === "function") {
|
|
355
|
-
try {
|
|
356
|
-
previousRuntimeCleanup();
|
|
357
|
-
} catch {
|
|
358
|
-
// Best effort cleanup for stale timers from an older reload.
|
|
359
|
-
}
|
|
360
|
-
}
|
|
394
|
+
const runtimeRegistry = getRuntimeRegistry();
|
|
361
395
|
|
|
362
396
|
DIRS.results = ensureAccessibleDir(DIRS.results);
|
|
363
397
|
DIRS.async = ensureAccessibleDir(DIRS.async);
|
|
@@ -373,10 +407,19 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
373
407
|
const tempArtifactsDir = getArtifactsDir(null);
|
|
374
408
|
const artifactCleanupDays = config.artifactConfig?.cleanupDays ?? DEFAULT_ARTIFACT_CONFIG.cleanupDays;
|
|
375
409
|
cleanupAllArtifactDirs(artifactCleanupDays);
|
|
410
|
+
const resultIndexCleanupTimer = setTimeout(() => {
|
|
411
|
+
try {
|
|
412
|
+
cleanupResultIndexes(DIRS.results);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error("Failed to clean stale subagent result indexes:", error);
|
|
415
|
+
}
|
|
416
|
+
}, 30_000);
|
|
417
|
+
resultIndexCleanupTimer.unref?.();
|
|
376
418
|
|
|
377
419
|
const state: SubagentState = {
|
|
378
420
|
baseCwd: "",
|
|
379
421
|
currentSessionId: null,
|
|
422
|
+
completionOwnerId: currentCompletionOwnerId(),
|
|
380
423
|
artifactDirPreference: config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
381
424
|
...(config.authorityPolicy ? { authorityPolicy: config.authorityPolicy } : {}),
|
|
382
425
|
...(config.missions ? { missionStoreConfig: config.missions } : {}),
|
|
@@ -422,6 +465,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
422
465
|
: undefined;
|
|
423
466
|
let executorScheduled: ((id: string, params: SubagentParamsLike, signal: AbortSignal, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>) | undefined;
|
|
424
467
|
let goalTurnId = 0;
|
|
468
|
+
let parentSessionEnvValue: string | null = null;
|
|
425
469
|
const scheduledStoreRoot = config.scheduledRuns?.storeRoot === undefined ? undefined : resolveScheduledStoreRoot(config.scheduledRuns.storeRoot);
|
|
426
470
|
const scheduledRunManager = createScheduledRunManager({
|
|
427
471
|
config,
|
|
@@ -438,10 +482,18 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
438
482
|
},
|
|
439
483
|
resolveCapabilityCeiling: (sessionId) => resolveCurrentSubagentCapabilityCeiling(sessionId),
|
|
440
484
|
});
|
|
485
|
+
let refreshResultDelivery = () => {};
|
|
486
|
+
const hasResultDeliveryDemand = () => {
|
|
487
|
+
if ([...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running")) return true;
|
|
488
|
+
if (state.foregroundControls.size > 0) return true;
|
|
489
|
+
if (scheduledRunManager.observedCompletionRunIds().size > 0) return true;
|
|
490
|
+
return missionObserverResultCandidateFiles(DIRS.results).length > 0;
|
|
491
|
+
};
|
|
441
492
|
const { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs, dispose: disposeAsyncJobTracker } = createAsyncJobTracker(pi, state, DIRS.async, {
|
|
442
493
|
widgetEnabled: asyncWidgetEnabled,
|
|
494
|
+
onJobTerminal: () => refreshResultDelivery(),
|
|
443
495
|
});
|
|
444
|
-
const
|
|
496
|
+
const resultWatcher = createResultWatcher(
|
|
445
497
|
pi,
|
|
446
498
|
state,
|
|
447
499
|
DIRS.results,
|
|
@@ -450,22 +502,30 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
450
502
|
notifier: completionNotifier,
|
|
451
503
|
observeCompletion: (result) => scheduledRunManager.handleAsyncCompletion(result),
|
|
452
504
|
observedCompletionRunIds: () => scheduledRunManager.observedCompletionRunIds(),
|
|
505
|
+
hasDeliveryDemand: hasResultDeliveryDemand,
|
|
453
506
|
deliverIntercomResults: config.intercomBridge?.resultDelivery === true,
|
|
454
507
|
},
|
|
455
508
|
);
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
509
|
+
const { startResultWatcher, primeExistingResults, stopResultWatcher } = resultWatcher;
|
|
510
|
+
refreshResultDelivery = resultWatcher.refreshResultDelivery;
|
|
511
|
+
const asyncRetentionAbort = new AbortController();
|
|
512
|
+
const asyncRetentionTimer = setTimeout(async () => {
|
|
513
|
+
try {
|
|
514
|
+
await cleanupAsyncRetention({
|
|
515
|
+
asyncDirRoot: DIRS.async,
|
|
516
|
+
resultsDir: DIRS.results,
|
|
517
|
+
signal: asyncRetentionAbort.signal,
|
|
518
|
+
protectedRunIds: new Set([
|
|
519
|
+
...state.asyncJobs.keys(),
|
|
520
|
+
...(state.workflowControllers?.keys() ?? []),
|
|
521
|
+
...scheduledRunManager.referencedAsyncRunIds(),
|
|
522
|
+
]),
|
|
523
|
+
});
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error("Failed to clean retained async subagent state:", error);
|
|
526
|
+
}
|
|
527
|
+
}, ASYNC_RETENTION_DELAY_MS);
|
|
528
|
+
asyncRetentionTimer.unref?.();
|
|
469
529
|
|
|
470
530
|
const executor = createSubagentExecutor({
|
|
471
531
|
pi,
|
|
@@ -479,13 +539,15 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
479
539
|
getSubagentSessionRoot,
|
|
480
540
|
expandTilde,
|
|
481
541
|
discoverAgents,
|
|
542
|
+
activateSupervisorTransport: () => supervisorChannel.activateTransport(),
|
|
543
|
+
refreshResultDelivery: () => refreshResultDelivery(),
|
|
482
544
|
});
|
|
483
545
|
executorScheduled = executor.executeScheduled;
|
|
484
546
|
|
|
485
547
|
pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
|
|
486
548
|
const details = resolveSlashMessageDetails(message.details);
|
|
487
549
|
if (!details) return undefined;
|
|
488
|
-
return createSlashResultComponent(details, options, theme, config.mainWindowRenderer);
|
|
550
|
+
return createSlashResultComponent(details, options, theme, config.mainWindowRenderer, config.foregroundDetachShortcut);
|
|
489
551
|
});
|
|
490
552
|
|
|
491
553
|
pi.registerMessageRenderer<undefined>(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
|
|
@@ -573,11 +635,12 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
573
635
|
});
|
|
574
636
|
|
|
575
637
|
|
|
576
|
-
const parameters = createSubagentParamsSchema(
|
|
638
|
+
const parameters = createSubagentParamsSchema();
|
|
577
639
|
const tool: ToolDefinition<typeof parameters, Details> = {
|
|
578
640
|
name: "subagent",
|
|
579
641
|
label: "Subagent",
|
|
580
642
|
description: buildSubagentToolDescription(config),
|
|
643
|
+
...buildSubagentToolPromptMetadata(config),
|
|
581
644
|
parameters,
|
|
582
645
|
|
|
583
646
|
execute(id, params, signal, onUpdate, ctx) {
|
|
@@ -588,7 +651,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
588
651
|
const gap = " ".repeat(config.mainWindowRenderer?.horizontalSpacing ?? 1);
|
|
589
652
|
const title = theme.fg("toolTitle", theme.bold("subagent"));
|
|
590
653
|
if (args.action) {
|
|
591
|
-
const target = args.agent ||
|
|
654
|
+
const target = args.agent || "";
|
|
592
655
|
return new Text(
|
|
593
656
|
`${title}${gap}${args.action}${target ? `${gap}${theme.fg("accent", target)}` : ""}`,
|
|
594
657
|
0, 0,
|
|
@@ -613,7 +676,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
613
676
|
const renderedResult = { ...result, isError: context.isError };
|
|
614
677
|
return summaryInlineToolDisplay
|
|
615
678
|
? renderSubagentSummary(renderedResult, options, theme)
|
|
616
|
-
: renderSubagentResult(renderedResult, options, theme, undefined, config.mainWindowRenderer);
|
|
679
|
+
: renderSubagentResult(renderedResult, options, theme, undefined, config.mainWindowRenderer, config.foregroundDetachShortcut);
|
|
617
680
|
},
|
|
618
681
|
|
|
619
682
|
};
|
|
@@ -643,24 +706,12 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
643
706
|
}
|
|
644
707
|
});
|
|
645
708
|
|
|
646
|
-
registerSlashCommands(pi, state, {
|
|
709
|
+
registerSlashCommands(pi, state, {
|
|
710
|
+
fleetKeybindings: config.fleetKeybindings,
|
|
711
|
+
foregroundDetachShortcut: config.foregroundDetachShortcut,
|
|
712
|
+
});
|
|
647
713
|
|
|
648
|
-
|
|
649
|
-
const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
|
|
650
|
-
const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
|
|
651
|
-
if (Array.isArray(previousEventUnsubscribes)) {
|
|
652
|
-
for (const unsubscribe of previousEventUnsubscribes) {
|
|
653
|
-
if (typeof unsubscribe !== "function") continue;
|
|
654
|
-
try {
|
|
655
|
-
unsubscribe();
|
|
656
|
-
} catch {
|
|
657
|
-
// Best effort cleanup for stale handlers from an older reload.
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
|
|
662
|
-
const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices as Set<string> : new Set<string>();
|
|
663
|
-
globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
|
|
714
|
+
let visibleControlNotices = new Set<string>();
|
|
664
715
|
const activeHerdrRuns = () => projectActiveHerdrRuns(state);
|
|
665
716
|
const herdrStatusBridge = registerHerdrStatusBridge({
|
|
666
717
|
events: pi.events,
|
|
@@ -682,10 +733,13 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
682
733
|
};
|
|
683
734
|
const asyncStartedHandler = (payload: unknown) => {
|
|
684
735
|
handleStarted(payload);
|
|
736
|
+
supervisorChannel.activateTransport();
|
|
737
|
+
refreshResultDelivery();
|
|
685
738
|
fleetStatus?.refresh();
|
|
686
739
|
};
|
|
687
740
|
const asyncCompleteHandler = (payload: unknown) => {
|
|
688
741
|
handleComplete(payload);
|
|
742
|
+
refreshResultDelivery();
|
|
689
743
|
refreshActiveAsyncCapacity();
|
|
690
744
|
scheduledRunManager.handleAsyncCompletion(payload);
|
|
691
745
|
fleetStatus?.refresh();
|
|
@@ -702,7 +756,6 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
702
756
|
herdrStatusBridge.dispose,
|
|
703
757
|
rpcBridge.dispose,
|
|
704
758
|
];
|
|
705
|
-
globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
|
|
706
759
|
|
|
707
760
|
pi.on("tool_result", (event, ctx) => {
|
|
708
761
|
if (event.toolName !== "subagent") return;
|
|
@@ -780,24 +833,128 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
780
833
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
781
834
|
if (sessionId) {
|
|
782
835
|
process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId;
|
|
836
|
+
parentSessionEnvValue = sessionId;
|
|
783
837
|
}
|
|
784
838
|
}
|
|
785
839
|
state.lastUiContext = ctx;
|
|
840
|
+
let phaseStartedAt = Date.now();
|
|
786
841
|
refreshActiveAsyncCapacity();
|
|
842
|
+
logSlowPhase("active-capacity", phaseStartedAt);
|
|
843
|
+
phaseStartedAt = Date.now();
|
|
787
844
|
cleanupSessionArtifacts(ctx);
|
|
845
|
+
logSlowPhase("session-artifact-cleanup", phaseStartedAt);
|
|
788
846
|
state.foregroundControls.clear();
|
|
789
847
|
state.lastForegroundControlId = null;
|
|
848
|
+
phaseStartedAt = Date.now();
|
|
790
849
|
resetJobs(ctx);
|
|
850
|
+
logSlowPhase("reset-jobs", phaseStartedAt);
|
|
851
|
+
phaseStartedAt = Date.now();
|
|
791
852
|
restoreForegroundRunHistory(state, { resultsDir: DIRS.results });
|
|
853
|
+
logSlowPhase("foreground-history", phaseStartedAt);
|
|
854
|
+
phaseStartedAt = Date.now();
|
|
792
855
|
restoreActiveJobs(ctx);
|
|
856
|
+
logSlowPhase("active-job-restore", phaseStartedAt);
|
|
857
|
+
phaseStartedAt = Date.now();
|
|
793
858
|
scheduledRunManager.bindSession(ctx);
|
|
859
|
+
logSlowPhase("scheduled-runs", phaseStartedAt);
|
|
860
|
+
phaseStartedAt = Date.now();
|
|
794
861
|
restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
|
|
862
|
+
logSlowPhase("slash-snapshots", phaseStartedAt);
|
|
863
|
+
phaseStartedAt = Date.now();
|
|
795
864
|
waitSubscriptionManager.restore();
|
|
865
|
+
logSlowPhase("wait-subscriptions", phaseStartedAt);
|
|
866
|
+
phaseStartedAt = Date.now();
|
|
796
867
|
startResultWatcher();
|
|
868
|
+
logSlowPhase("result-watcher-start", phaseStartedAt);
|
|
869
|
+
phaseStartedAt = Date.now();
|
|
797
870
|
primeExistingResults({ triggerTurn: !recovering });
|
|
871
|
+
logSlowPhase("result-prime", phaseStartedAt);
|
|
798
872
|
fleetStatus?.setContext(ctx);
|
|
799
873
|
};
|
|
800
874
|
|
|
875
|
+
let runtimeCleaned = false;
|
|
876
|
+
const runtimeEntry: SubagentRuntimeEntry = {
|
|
877
|
+
sessionManager: null,
|
|
878
|
+
visibleControlNotices,
|
|
879
|
+
cleanup() {
|
|
880
|
+
if (runtimeCleaned) return;
|
|
881
|
+
runtimeCleaned = true;
|
|
882
|
+
const shuttingDownParentSession = parentSessionEnvValue;
|
|
883
|
+
clearTimeout(resultIndexCleanupTimer);
|
|
884
|
+
clearTimeout(asyncRetentionTimer);
|
|
885
|
+
asyncRetentionAbort.abort();
|
|
886
|
+
stopResultWatcher();
|
|
887
|
+
completionNotifier.dispose();
|
|
888
|
+
mainWatchdog.dispose();
|
|
889
|
+
scheduledRunManager.stop();
|
|
890
|
+
supervisorChannel.dispose();
|
|
891
|
+
waitSubscriptionManager.dispose();
|
|
892
|
+
fleetStatus?.dispose();
|
|
893
|
+
disposeAsyncJobTracker();
|
|
894
|
+
for (const timer of state.cleanupTimers.values()) clearTimeout(timer);
|
|
895
|
+
state.cleanupTimers.clear();
|
|
896
|
+
state.asyncJobs.clear();
|
|
897
|
+
for (const unsubscribe of eventUnsubscribes) {
|
|
898
|
+
try {
|
|
899
|
+
unsubscribe();
|
|
900
|
+
} catch {
|
|
901
|
+
// Best effort cleanup during shutdown or reload.
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
slashBridge.cancelAll();
|
|
905
|
+
slashBridge.dispose();
|
|
906
|
+
promptTemplateBridge.cancelAll();
|
|
907
|
+
promptTemplateBridge.dispose();
|
|
908
|
+
state.widgetsSuspended = false;
|
|
909
|
+
state.currentSessionId = null;
|
|
910
|
+
state.parentSessionFile = null;
|
|
911
|
+
parentSessionEnvValue = null;
|
|
912
|
+
if (shuttingDownParentSession && process.env[SUBAGENT_PARENT_SESSION_ENV] === shuttingDownParentSession) {
|
|
913
|
+
delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
914
|
+
}
|
|
915
|
+
if (runtimeEntry.sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
916
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
917
|
+
}
|
|
918
|
+
runtimeRegistry.activeEntries.delete(runtimeEntry);
|
|
919
|
+
if (runtimeRegistry.activeEntries.size === 0) clearSlashSnapshots();
|
|
920
|
+
try {
|
|
921
|
+
if (state.lastUiContext?.hasUI) state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
922
|
+
} catch (error) {
|
|
923
|
+
if (!isStaleExtensionContextError(error)) throw error;
|
|
924
|
+
}
|
|
925
|
+
state.lastUiContext = null;
|
|
926
|
+
},
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const installRuntime = (ctx: ExtensionContext) => {
|
|
930
|
+
if (runtimeCleaned) {
|
|
931
|
+
throw new Error("Cannot restart a cleaned pi-subagents extension runtime; register a new extension instance.");
|
|
932
|
+
}
|
|
933
|
+
const sessionManager = ctx.sessionManager as object;
|
|
934
|
+
const previousRuntime = runtimeRegistry.bySessionManager.get(sessionManager);
|
|
935
|
+
const existingVisibleControlNotices = runtimeRegistry.visibleControlNoticesBySessionManager.get(sessionManager);
|
|
936
|
+
if (existingVisibleControlNotices) {
|
|
937
|
+
visibleControlNotices = existingVisibleControlNotices;
|
|
938
|
+
} else {
|
|
939
|
+
runtimeRegistry.visibleControlNoticesBySessionManager.set(sessionManager, visibleControlNotices);
|
|
940
|
+
}
|
|
941
|
+
runtimeEntry.visibleControlNotices = visibleControlNotices;
|
|
942
|
+
if (runtimeEntry.sessionManager && runtimeEntry.sessionManager !== sessionManager
|
|
943
|
+
&& runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
944
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
945
|
+
}
|
|
946
|
+
runtimeEntry.sessionManager = sessionManager;
|
|
947
|
+
runtimeRegistry.bySessionManager.set(sessionManager, runtimeEntry);
|
|
948
|
+
runtimeRegistry.activeEntries.add(runtimeEntry);
|
|
949
|
+
if (previousRuntime && previousRuntime !== runtimeEntry) {
|
|
950
|
+
try {
|
|
951
|
+
previousRuntime.cleanup();
|
|
952
|
+
} catch {
|
|
953
|
+
// Best effort cleanup for stale resources from the replaced runtime.
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
|
|
801
958
|
pi.on("agent_start", () => {
|
|
802
959
|
resumeWidgetsAfterCompaction();
|
|
803
960
|
herdrStatusBridge.agentStarted();
|
|
@@ -825,6 +982,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
825
982
|
});
|
|
826
983
|
|
|
827
984
|
pi.on("session_start", (event, ctx) => {
|
|
985
|
+
installRuntime(ctx);
|
|
828
986
|
const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
|
|
829
987
|
resetSessionState(ctx, recovering);
|
|
830
988
|
herdrStatusBridge.sessionStarted({
|
|
@@ -833,49 +991,11 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
833
991
|
});
|
|
834
992
|
rpcBridge.emitReady(ctx);
|
|
835
993
|
supervisorChannel.start();
|
|
994
|
+
supervisorChannel.activateTransport();
|
|
836
995
|
});
|
|
837
996
|
|
|
838
997
|
pi.on("session_shutdown", async () => {
|
|
839
|
-
|
|
840
|
-
stopResultWatcher();
|
|
841
|
-
state.currentSessionId = null;
|
|
842
|
-
state.parentSessionFile = null;
|
|
843
|
-
completionNotifier.dispose();
|
|
844
|
-
delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
845
|
-
for (const unsubscribe of eventUnsubscribes) {
|
|
846
|
-
try {
|
|
847
|
-
unsubscribe();
|
|
848
|
-
} catch {
|
|
849
|
-
// Best effort cleanup during shutdown.
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
|
|
853
|
-
delete globalStore[eventUnsubscribeStoreKey];
|
|
854
|
-
}
|
|
855
|
-
scheduledRunManager.stop();
|
|
856
|
-
disposeAsyncJobTracker();
|
|
857
|
-
for (const timer of state.cleanupTimers.values()) {
|
|
858
|
-
clearTimeout(timer);
|
|
859
|
-
}
|
|
860
|
-
state.cleanupTimers.clear();
|
|
861
|
-
state.asyncJobs.clear();
|
|
862
|
-
clearSlashSnapshots();
|
|
863
|
-
slashBridge.cancelAll();
|
|
864
|
-
slashBridge.dispose();
|
|
865
|
-
promptTemplateBridge.cancelAll();
|
|
866
|
-
promptTemplateBridge.dispose();
|
|
867
|
-
supervisorChannel.dispose();
|
|
868
|
-
fleetStatus?.dispose();
|
|
869
|
-
if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
|
|
870
|
-
delete globalStore[runtimeCleanupStoreKey];
|
|
871
|
-
}
|
|
872
|
-
try {
|
|
873
|
-
if (state.lastUiContext?.hasUI) {
|
|
874
|
-
state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
875
|
-
}
|
|
876
|
-
} catch (error) {
|
|
877
|
-
if (!isStaleExtensionContextError(error)) throw error;
|
|
878
|
-
}
|
|
998
|
+
runtimeEntry.cleanup();
|
|
879
999
|
await herdrStatusBridge.flush();
|
|
880
1000
|
});
|
|
881
1001
|
}
|
|
@@ -8,7 +8,12 @@ export interface PublicSubagentExecutionParams {
|
|
|
8
8
|
parallel?: unknown;
|
|
9
9
|
concurrency?: unknown;
|
|
10
10
|
chainDir?: unknown;
|
|
11
|
+
chainName?: unknown;
|
|
12
|
+
config?: unknown;
|
|
11
13
|
workflowScript?: unknown;
|
|
14
|
+
isolation?: unknown;
|
|
15
|
+
worktree?: unknown;
|
|
16
|
+
output?: unknown;
|
|
12
17
|
resume?: unknown;
|
|
13
18
|
clarify?: unknown;
|
|
14
19
|
runFanoutBudget?: unknown;
|
|
@@ -26,6 +31,17 @@ export type PublicSubagentExecutionNormalization<T> =
|
|
|
26
31
|
* Internal runs.run children and structured owned delegation bypass this boundary.
|
|
27
32
|
*/
|
|
28
33
|
export function normalizePublicSubagentExecution<T extends PublicSubagentExecutionParams>(params: T): PublicSubagentExecutionNormalization<T> {
|
|
34
|
+
if (params.isolation !== undefined) {
|
|
35
|
+
if (params.isolation !== "none" && params.isolation !== "worktree") {
|
|
36
|
+
return { ok: false, error: "isolation must be 'none' or 'worktree'.", mode: params.workflowScript !== undefined ? "workflow" : "management" };
|
|
37
|
+
}
|
|
38
|
+
const isolationWorktree = params.isolation === "worktree";
|
|
39
|
+
if (params.worktree !== undefined && params.worktree !== isolationWorktree) {
|
|
40
|
+
return { ok: false, error: `isolation '${params.isolation}' conflicts with worktree: ${String(params.worktree)}.`, mode: params.workflowScript !== undefined ? "workflow" : "management" };
|
|
41
|
+
}
|
|
42
|
+
const { isolation: _isolation, ...normalizedParams } = params;
|
|
43
|
+
params = { ...normalizedParams, worktree: isolationWorktree } as T;
|
|
44
|
+
}
|
|
29
45
|
if (params.runFanoutBudget !== undefined || params.runFanoutAdmitted !== undefined) {
|
|
30
46
|
return { ok: false, error: "Public execution does not accept internal run fan-out fields.", mode: params.workflowScript !== undefined ? "workflow" : "management" };
|
|
31
47
|
}
|
|
@@ -37,6 +53,12 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
|
|
|
37
53
|
if (params.clarify !== undefined) {
|
|
38
54
|
return { ok: false, error: "Public workflowScript execution does not support clarify UI.", mode: "workflow" };
|
|
39
55
|
}
|
|
56
|
+
if (params.chainName !== undefined) {
|
|
57
|
+
return { ok: false, error: "Durable chain management was removed; use workflowScript or /prompt-workflow for repeatable workflows.", mode: "management" };
|
|
58
|
+
}
|
|
59
|
+
if (params.config && typeof params.config === "object" && !Array.isArray(params.config) && Object.prototype.hasOwnProperty.call(params.config, "steps")) {
|
|
60
|
+
return { ok: false, error: "Durable chain definitions were removed; use workflowScript or /prompt-workflow for repeatable workflows.", mode: "management" };
|
|
61
|
+
}
|
|
40
62
|
if (params.resume !== undefined) {
|
|
41
63
|
return { ok: false, error: "Top-level resume execution is not available. Put resume on a workflowScript runs.run/runs.all item.", mode: "workflow" };
|
|
42
64
|
}
|
|
@@ -46,6 +68,12 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
|
|
|
46
68
|
}
|
|
47
69
|
if (normalizedAction !== undefined) {
|
|
48
70
|
const legacyAction = normalizedAction.toLowerCase();
|
|
71
|
+
if (legacyAction === "append-step") {
|
|
72
|
+
return { ok: false, error: "Legacy append-step control was removed from the public subagent tool; use current workflowScript orchestration.", mode: "management" };
|
|
73
|
+
}
|
|
74
|
+
if (legacyAction === "approve-checkpoint" || legacyAction === "reject-checkpoint") {
|
|
75
|
+
return { ok: false, error: "Legacy checkpoint approval controls were removed from the public subagent tool; use current workflowScript orchestration.", mode: "management" };
|
|
76
|
+
}
|
|
49
77
|
if (legacyAction === "single") {
|
|
50
78
|
return { ok: false, error: "action='single' is not supported. Omit action and pass { agent, task } for one child.", mode: "workflow" };
|
|
51
79
|
}
|
|
@@ -70,7 +98,7 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
|
|
|
70
98
|
return { ok: true, params: { ...params, action: normalizedAction } };
|
|
71
99
|
}
|
|
72
100
|
if (params.step !== undefined) {
|
|
73
|
-
return { ok: false, error: "step is
|
|
101
|
+
return { ok: false, error: "step is not a public execution field; use workflowScript for orchestration.", mode: "workflow" };
|
|
74
102
|
}
|
|
75
103
|
if (params.workflowScript !== undefined && (params.agent !== undefined || params.task !== undefined)) {
|
|
76
104
|
return { ok: false, error: "Structured single-child execution cannot be combined with workflowScript.", mode: "workflow" };
|
|
@@ -82,10 +110,11 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
|
|
|
82
110
|
if (params.task !== undefined && typeof params.task !== "string") {
|
|
83
111
|
return { ok: false, error: "Structured single-child task must be a string when provided.", mode: "workflow" };
|
|
84
112
|
}
|
|
85
|
-
const { agent: _agent, task: _task, ...workflowDefaults } = params;
|
|
113
|
+
const { agent: _agent, task: _task, output, ...workflowDefaults } = params;
|
|
86
114
|
const child = {
|
|
87
115
|
agent: params.agent.trim(),
|
|
88
116
|
...(params.task !== undefined ? { task: params.task } : {}),
|
|
117
|
+
output: output === undefined ? true : output,
|
|
89
118
|
};
|
|
90
119
|
return {
|
|
91
120
|
ok: true,
|
package/src/extension/rpc.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { sanitizeDisplayText, truncateDisplayText } from "../shared/display-text
|
|
|
20
20
|
import { readStatus } from "../shared/utils.ts";
|
|
21
21
|
import { SubagentParams } from "./schemas.ts";
|
|
22
22
|
import { normalizePublicSubagentExecution } from "./public-execution.ts";
|
|
23
|
+
import { ASYNC_STATUS_SNAPSHOT_KIND, ASYNC_STATUS_SNAPSHOT_VERSION, buildAsyncStatusSnapshotForState } from "../runs/background/async-status-snapshot.ts";
|
|
23
24
|
|
|
24
25
|
export const SUBAGENT_RPC_PROTOCOL_VERSION = 1;
|
|
25
26
|
export const SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request";
|
|
@@ -375,6 +376,7 @@ function pingData(ctx: ExtensionContext | null) {
|
|
|
375
376
|
capabilities: {
|
|
376
377
|
status: true,
|
|
377
378
|
fleetStatus: { version: 1 },
|
|
379
|
+
asyncStatusSnapshot: { kind: ASYNC_STATUS_SNAPSHOT_KIND, version: ASYNC_STATUS_SNAPSHOT_VERSION },
|
|
378
380
|
asyncSpawn: true,
|
|
379
381
|
steer: true,
|
|
380
382
|
nonRecoveringSteer: true,
|
|
@@ -544,13 +546,15 @@ async function handleRequest(
|
|
|
544
546
|
request.method,
|
|
545
547
|
{ action: "status", ...normalizeTargetParams(request.params, "status") },
|
|
546
548
|
);
|
|
549
|
+
const sessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
547
550
|
return {
|
|
548
551
|
...status,
|
|
549
552
|
fleet: buildFleetStatus(
|
|
550
553
|
options.state,
|
|
551
554
|
fleetKeys,
|
|
552
|
-
|
|
555
|
+
sessionId,
|
|
553
556
|
),
|
|
557
|
+
asyncSnapshot: buildAsyncStatusSnapshotForState(options.state, sessionId),
|
|
554
558
|
};
|
|
555
559
|
}
|
|
556
560
|
if (request.method === "steer") {
|