pi-subagents 0.56.0 → 0.58.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 +92 -0
- package/agents/claude-code-writer.md +15 -0
- package/agents/claude-code.md +15 -0
- package/agents/codex-exec-writer.md +15 -0
- package/agents/codex-exec.md +15 -0
- package/agents/cursor-agent-writer.md +14 -0
- package/agents/cursor-agent.md +14 -0
- package/docs/agents.md +124 -21
- package/docs/configuration.md +37 -0
- package/docs/extension-api.md +41 -2
- package/docs/models.md +3 -3
- package/docs/observability.md +8 -7
- package/docs/tool-reference.md +14 -3
- package/docs/workflows.md +44 -0
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -0
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/agent-management.ts +36 -5
- package/src/agents/agent-refinements.ts +4 -4
- package/src/agents/agent-serializer.ts +5 -0
- package/src/agents/agents.ts +257 -51
- package/src/agents/builtin-names.ts +6 -0
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +18 -4
- package/src/api/agents.ts +10 -5
- package/src/api/preflight.ts +28 -3
- package/src/extension/config.ts +70 -0
- package/src/extension/doctor.ts +3 -3
- package/src/extension/index.ts +33 -8
- package/src/extension/public-execution.ts +29 -13
- package/src/extension/rpc.ts +55 -19
- package/src/extension/schemas.ts +6 -5
- package/src/extension/tool-description.ts +18 -12
- package/src/inspectors/herdr/actions.ts +2 -1
- package/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/src/integrations/herdr-status.ts +51 -3
- package/src/runs/background/active-async-capacity.ts +77 -10
- package/src/runs/background/async-execution.ts +127 -19
- package/src/runs/background/async-job-tracker.ts +5 -0
- package/src/runs/background/async-resume.ts +6 -2
- package/src/runs/background/async-retention.ts +20 -3
- package/src/runs/background/async-status.ts +7 -0
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/chain-root-attachment.ts +15 -1
- package/src/runs/background/fleet-view.ts +16 -10
- package/src/runs/background/inspect-rpc.ts +8 -8
- package/src/runs/background/notify.ts +26 -3
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-files.ts +27 -14
- package/src/runs/background/result-watcher.ts +36 -15
- package/src/runs/background/run-status.ts +33 -6
- package/src/runs/background/scheduled-runs.ts +7 -1
- package/src/runs/background/subagent-runner.ts +239 -56
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/foreground/execution.ts +92 -11
- package/src/runs/foreground/foreground-control.ts +6 -0
- package/src/runs/foreground/foreground-history.ts +22 -1
- package/src/runs/foreground/subagent-executor.ts +322 -102
- package/src/runs/foreground/workflow-detach-reconcile.ts +144 -18
- package/src/runs/shared/child-protocol.ts +21 -7
- package/src/runs/shared/claude-code-adapter.ts +129 -0
- package/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/src/runs/shared/completion-guard.ts +4 -3
- package/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/external-cli-contract.ts +167 -0
- package/src/runs/shared/external-cli-preflight.ts +122 -0
- package/src/runs/shared/external-cli-runner.ts +348 -55
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +386 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +155 -42
- package/src/runs/shared/model-exclusions.ts +69 -7
- package/src/runs/shared/model-fallback.ts +39 -5
- package/src/runs/shared/mutation-evidence.ts +7 -2
- package/src/runs/shared/nested-events.ts +3 -1
- package/src/runs/shared/nested-render.ts +2 -2
- package/src/runs/shared/parallel-utils.ts +8 -1
- package/src/runs/shared/pi-args.ts +61 -6
- package/src/runs/shared/process-signal.ts +13 -0
- package/src/runs/shared/run-history.ts +21 -1
- package/src/runs/shared/single-output.ts +17 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +85 -9
- package/src/shared/fork-context.ts +21 -0
- package/src/shared/formatters.ts +13 -1
- package/src/shared/launch-contract.ts +4 -0
- package/src/shared/pruned-fork.ts +450 -0
- package/src/shared/session-file-trust.ts +19 -0
- package/src/shared/session-tokens.ts +14 -3
- package/src/shared/settings.ts +10 -2
- package/src/shared/shortcuts.ts +17 -0
- package/src/shared/types.ts +160 -10
- package/src/shared/utils.ts +6 -29
- package/src/shared/workflow-child-permit.ts +116 -0
- package/src/slash/delegation-adapters.ts +0 -1
- package/src/slash/slash-commands.ts +8 -6
- package/src/slash/subagents-admin.ts +3 -0
- package/src/tui/fleet-status.ts +27 -10
- package/src/tui/fleet-transcript.ts +11 -5
- package/src/tui/fleet.ts +28 -13
- package/src/tui/render.ts +55 -21
- package/src/workflows/scripted-workflow.ts +299 -31
- package/src/workflows/workflow-child-summary.ts +117 -0
- package/src/workflows/workflow-receipt.ts +155 -5
|
@@ -25,6 +25,7 @@ import { recordWaitCompletion } from "./wait-completions.ts";
|
|
|
25
25
|
import { MISSION_BINDING_FILE, syncMissionFromAsyncCompletion } from "../../missions/lifecycle.ts";
|
|
26
26
|
import { missionObserverResultCandidateFiles, promotePendingResultFile, removeMissionObserverIndex, removeResultIndex, resultCandidateFilesForSession, resultPayloadPathForIndexedRun, resultPayloadPathForMissionObserverRun, resultPayloadPathForSessionRun, writeAsyncResultFile, writeResultIndexForData } from "./result-files.ts";
|
|
27
27
|
import type { CompletionNotifier, CompletionNotification } from "./notify.ts";
|
|
28
|
+
import type { ResultDeliveryOwnership } from "./result-delivery-ownership.ts";
|
|
28
29
|
|
|
29
30
|
const WATCHER_RESTART_DELAY_MS = 3000;
|
|
30
31
|
const POLL_INTERVAL_MS = 3000;
|
|
@@ -60,6 +61,8 @@ type ResultWatcherDeps = {
|
|
|
60
61
|
/** Control how slow result-index scans are logged. Defaults to \"activity\". */
|
|
61
62
|
resultScanLogging?: "all" | "activity" | "off";
|
|
62
63
|
platform?: NodeJS.Platform;
|
|
64
|
+
/** Shared current/predecessor session ownership used by the notifier. */
|
|
65
|
+
ownership?: Pick<ResultDeliveryOwnership, "owns" | "claimedSessionIds">;
|
|
63
66
|
};
|
|
64
67
|
|
|
65
68
|
type ResultFileChild = {
|
|
@@ -148,6 +151,10 @@ function isNotFound(error: unknown): boolean {
|
|
|
148
151
|
return errorCode(error) === "ENOENT";
|
|
149
152
|
}
|
|
150
153
|
|
|
154
|
+
function isAbsentResultCandidate(error: unknown): boolean {
|
|
155
|
+
return isNotFound(error) || errorCode(error) === "ENAMETOOLONG";
|
|
156
|
+
}
|
|
157
|
+
|
|
151
158
|
function isAccessDenied(error: unknown): boolean {
|
|
152
159
|
const code = errorCode(error);
|
|
153
160
|
return code === "EPERM" || code === "EACCES";
|
|
@@ -196,6 +203,7 @@ export function createResultWatcher(
|
|
|
196
203
|
deps: ResultWatcherDeps = {},
|
|
197
204
|
): {
|
|
198
205
|
startResultWatcher: () => void;
|
|
206
|
+
transitionResultDelivery: () => void;
|
|
199
207
|
primeExistingResults: (options?: { triggerTurn?: boolean }) => void;
|
|
200
208
|
refreshResultDelivery: () => void;
|
|
201
209
|
stopResultWatcher: () => void;
|
|
@@ -214,14 +222,16 @@ export function createResultWatcher(
|
|
|
214
222
|
// The sole in-memory ownership lease. It is acquired for one active session
|
|
215
223
|
// and revoked before the watcher, queues, or callbacks are torn down.
|
|
216
224
|
let activeSessionId: string | null = null;
|
|
225
|
+
const ownsResult = deps.ownership?.owns
|
|
226
|
+
?? ((sessionId: string, completionOwnerId: unknown) => sessionId === state.currentSessionId
|
|
227
|
+
&& typeof completionOwnerId === "string"
|
|
228
|
+
&& completionOwnerId === state.completionOwnerId);
|
|
229
|
+
const claimedSessionIds = () => deps.ownership?.claimedSessionIds() ?? [];
|
|
217
230
|
|
|
218
231
|
const ownsCompletion = (sessionId: string, completionOwnerId: unknown, epoch: number) => {
|
|
219
232
|
if (!deliveryActive || epoch !== deliveryEpoch) return false;
|
|
220
233
|
if (!activeSessionId && state.currentSessionId) activeSessionId = state.currentSessionId;
|
|
221
|
-
return activeSessionId === sessionId
|
|
222
|
-
&& state.currentSessionId === sessionId
|
|
223
|
-
&& typeof completionOwnerId === "string"
|
|
224
|
-
&& completionOwnerId === state.completionOwnerId;
|
|
234
|
+
return activeSessionId === state.currentSessionId && ownsResult(sessionId, completionOwnerId);
|
|
225
235
|
};
|
|
226
236
|
|
|
227
237
|
const scheduleResult = (file: string, triggerTurn: boolean, delayMs = 0) => {
|
|
@@ -235,15 +245,18 @@ export function createResultWatcher(
|
|
|
235
245
|
try {
|
|
236
246
|
return fsApi.statSync(publicResultPath(file)).isFile();
|
|
237
247
|
} catch (error) {
|
|
238
|
-
if (!
|
|
248
|
+
if (!isAbsentResultCandidate(error)) console.error(`Failed to inspect subagent result file '${publicResultPath(file)}':`, error);
|
|
239
249
|
return false;
|
|
240
250
|
}
|
|
241
251
|
};
|
|
242
252
|
const resultPayloadPath = (file: string, observed?: ReadonlySet<string>): string | undefined => {
|
|
243
253
|
if (file !== path.basename(file) || !file.endsWith(".json")) return undefined;
|
|
244
254
|
const runId = file.replace(/\.json$/i, "");
|
|
245
|
-
const
|
|
246
|
-
|
|
255
|
+
for (const sessionId of [state.currentSessionId, ...claimedSessionIds()]) {
|
|
256
|
+
if (!sessionId) continue;
|
|
257
|
+
const sessionResult = resultPayloadPathForSessionRun(resultsDir, sessionId, runId);
|
|
258
|
+
if (sessionResult) return sessionResult;
|
|
259
|
+
}
|
|
247
260
|
const observerResult = resultPayloadPathForMissionObserverRun(resultsDir, runId);
|
|
248
261
|
if (observerResult) return observerResult;
|
|
249
262
|
if (observed?.has(runId)) {
|
|
@@ -265,7 +278,7 @@ export function createResultWatcher(
|
|
|
265
278
|
} catch (error) {
|
|
266
279
|
identityCache.delete(file);
|
|
267
280
|
if (isAccessDenied(error)) throw error;
|
|
268
|
-
if (!
|
|
281
|
+
if (!isAbsentResultCandidate(error)) console.error(`Failed to inspect subagent result file '${resultPath}':`, error);
|
|
269
282
|
return undefined;
|
|
270
283
|
}
|
|
271
284
|
};
|
|
@@ -283,7 +296,7 @@ export function createResultWatcher(
|
|
|
283
296
|
} catch (error) {
|
|
284
297
|
identityCache.delete(file);
|
|
285
298
|
if (isAccessDenied(error)) throw error;
|
|
286
|
-
if (!
|
|
299
|
+
if (!isAbsentResultCandidate(error)) console.error(`Failed to inspect subagent result file '${resultPath}':`, error);
|
|
287
300
|
return undefined;
|
|
288
301
|
}
|
|
289
302
|
};
|
|
@@ -304,7 +317,7 @@ export function createResultWatcher(
|
|
|
304
317
|
// Missing identity stays on the normal parser path so malformed or legacy
|
|
305
318
|
// files keep their existing diagnostics and compatibility behavior.
|
|
306
319
|
if (!identity.sessionId) return true;
|
|
307
|
-
if (identity.sessionId
|
|
320
|
+
if (ownsResult(identity.sessionId, identity.completionOwnerId)) return true;
|
|
308
321
|
if (identity.asyncDir && fsApi.existsSync(path.join(identity.asyncDir, MISSION_BINDING_FILE))) return true;
|
|
309
322
|
if (identity.runId && (observed ?? observedRunIds()).has(identity.runId)) return true;
|
|
310
323
|
return Boolean(deps.observeCompletion && !deps.observedCompletionRunIds);
|
|
@@ -317,7 +330,7 @@ export function createResultWatcher(
|
|
|
317
330
|
removeResultIndex(resultsDir, sessionId, runId, toolCallId);
|
|
318
331
|
return true;
|
|
319
332
|
} catch (error) {
|
|
320
|
-
if (!
|
|
333
|
+
if (!isAbsentResultCandidate(error)) {
|
|
321
334
|
console.error(`Failed to remove delivered subagent result '${publicResultPath(file)}'; will retry:`, error);
|
|
322
335
|
return false;
|
|
323
336
|
}
|
|
@@ -384,7 +397,7 @@ export function createResultWatcher(
|
|
|
384
397
|
if (!resultPayloadWasReplaced(data, readPublicResultIdentity())) return false;
|
|
385
398
|
} catch (error) {
|
|
386
399
|
if (isAccessDenied(error)) throw error;
|
|
387
|
-
if (
|
|
400
|
+
if (isAbsentResultCandidate(error)) return false;
|
|
388
401
|
console.error(`Failed to re-read subagent result file '${publicResultPath(file)}':`, error);
|
|
389
402
|
}
|
|
390
403
|
identityCache.delete(file);
|
|
@@ -617,8 +630,9 @@ export function createResultWatcher(
|
|
|
617
630
|
};
|
|
618
631
|
const indexedResultCandidates = (observed: ReadonlySet<string>): string[] => {
|
|
619
632
|
const files = new Set<string>();
|
|
620
|
-
|
|
621
|
-
|
|
633
|
+
for (const sessionId of [state.currentSessionId, ...claimedSessionIds()]) {
|
|
634
|
+
if (!sessionId) continue;
|
|
635
|
+
for (const file of resultCandidateFilesForSession(resultsDir, sessionId)) files.add(file);
|
|
622
636
|
}
|
|
623
637
|
for (const runId of state.asyncJobs.keys()) files.add(`${runId}.json`);
|
|
624
638
|
for (const file of missionObserverResultCandidateFiles(resultsDir)) files.add(file);
|
|
@@ -751,6 +765,13 @@ export function createResultWatcher(
|
|
|
751
765
|
}
|
|
752
766
|
};
|
|
753
767
|
|
|
768
|
+
const transitionResultDelivery = () => {
|
|
769
|
+
deliveryActive = true;
|
|
770
|
+
activeSessionId = state.currentSessionId;
|
|
771
|
+
deliveryEpoch += 1;
|
|
772
|
+
identityCache.clear();
|
|
773
|
+
};
|
|
774
|
+
|
|
754
775
|
const stopResultWatcher = () => {
|
|
755
776
|
deliveryActive = false;
|
|
756
777
|
activeSessionId = null;
|
|
@@ -765,5 +786,5 @@ export function createResultWatcher(
|
|
|
765
786
|
identityCache.clear();
|
|
766
787
|
};
|
|
767
788
|
|
|
768
|
-
return { startResultWatcher, primeExistingResults, stopResultWatcher, refreshResultDelivery: () => { primeExistingResults(); startDemandPolling(); } };
|
|
789
|
+
return { startResultWatcher, transitionResultDelivery, primeExistingResults, stopResultWatcher, refreshResultDelivery: () => { primeExistingResults(); startDemandPolling(); } };
|
|
769
790
|
}
|
|
@@ -11,6 +11,7 @@ import { DIRS, type AsyncStatus, type Details, type ForegroundResumeRun, type Ne
|
|
|
11
11
|
import { inspectActiveAsyncCapacityOwner, type ActiveAsyncCapacityInspection } from "./active-async-capacity.ts";
|
|
12
12
|
import { readStatus } from "../../shared/utils.ts";
|
|
13
13
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
14
|
+
import { normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
14
15
|
import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
|
|
15
16
|
import { readProcessTerminal, sanitizeProcessTerminal } from "./process-terminal.ts";
|
|
16
17
|
import { formatWaitSubscriptions } from "./wait-subscriptions.ts";
|
|
@@ -21,6 +22,7 @@ import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-
|
|
|
21
22
|
import { attachRootChildrenToSteps, findNestedRouteForRootId, projectNestedRegistryForRoot, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
22
23
|
import { readMissionBinding } from "../../missions/lifecycle.ts";
|
|
23
24
|
import { formatWorkflowJsonPreview } from "../../workflows/scripted-workflow.ts";
|
|
25
|
+
import { parseWorkflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
24
26
|
import { formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
25
27
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
26
28
|
|
|
@@ -103,6 +105,7 @@ interface RunStatusDeps {
|
|
|
103
105
|
nested?: NestedRunResolutionScope;
|
|
104
106
|
sessionRoots?: string[];
|
|
105
107
|
activeCapacityRoot?: string;
|
|
108
|
+
abandonedSlotReleaseAfterMs?: number | false;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
function hasExistingSessionFile(value: unknown): value is string {
|
|
@@ -405,7 +408,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
405
408
|
if (!status && diskStatus?.displayDismissedAt !== undefined) {
|
|
406
409
|
if (params.action === "debug.run") {
|
|
407
410
|
const { sidecar, overlay } = debugProcessTerminal(asyncDir, diskStatus);
|
|
408
|
-
const capacity = inspectActiveAsyncCapacityOwner({ runId: diskStatus.runId, sessionId: diskStatus.sessionId, asyncDir }, { rootDir: deps.activeCapacityRoot, liveWorkflowRunIds: new Set(deps.state?.workflowControllers?.keys() ?? []) });
|
|
411
|
+
const capacity = inspectActiveAsyncCapacityOwner({ runId: diskStatus.runId, sessionId: diskStatus.sessionId, asyncDir }, { rootDir: deps.activeCapacityRoot, liveWorkflowRunIds: new Set(deps.state?.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: deps.abandonedSlotReleaseAfterMs });
|
|
409
412
|
return {
|
|
410
413
|
content: [{ type: "text", text: formatRunLifecycleDebug({ status: diskStatus, asyncDir, sidecarProcessTerminal: sidecar, overlayProcessTerminal: overlay, capacity }) }],
|
|
411
414
|
details: { mode: "single", results: [], ...((sidecar ?? overlay) ? { lifecycleStatus: { processTerminal: sidecar ?? overlay } } : {}) },
|
|
@@ -432,7 +435,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
432
435
|
if (status) {
|
|
433
436
|
if (params.action === "debug.run") {
|
|
434
437
|
const { sidecar, overlay } = debugProcessTerminal(asyncDir, status);
|
|
435
|
-
const capacity = inspectActiveAsyncCapacityOwner({ runId: status.runId, sessionId: status.sessionId, asyncDir }, { rootDir: deps.activeCapacityRoot, liveWorkflowRunIds: new Set(deps.state?.workflowControllers?.keys() ?? []) });
|
|
438
|
+
const capacity = inspectActiveAsyncCapacityOwner({ runId: status.runId, sessionId: status.sessionId, asyncDir }, { rootDir: deps.activeCapacityRoot, liveWorkflowRunIds: new Set(deps.state?.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: deps.abandonedSlotReleaseAfterMs });
|
|
436
439
|
return {
|
|
437
440
|
content: [{ type: "text", text: formatRunLifecycleDebug({ status, asyncDir, sidecarProcessTerminal: sidecar, overlayProcessTerminal: overlay, capacity }) }],
|
|
438
441
|
details: { mode: "single", results: [], ...((sidecar ?? overlay) ? { lifecycleStatus: { processTerminal: sidecar ?? overlay } } : {}) },
|
|
@@ -536,9 +539,29 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
536
539
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
537
540
|
lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${steeringSuffix}${acceptanceText}${budgetText}${errorText}`);
|
|
538
541
|
if (step.runner?.type === "external-cli") {
|
|
539
|
-
|
|
542
|
+
const runner = normalizeExternalCliRunnerStatus(step.runner);
|
|
543
|
+
if (runner) {
|
|
544
|
+
lines.push(` Runner: external-cli (${runner.command}${runner.args.length ? ` ${runner.args.join(" ")}` : ""})`);
|
|
545
|
+
lines.push(` Adapter: ${runner.adapter.id} v${runner.adapter.version} (${runner.adapter.executionMode})`);
|
|
546
|
+
if (runner.safety) {
|
|
547
|
+
if ("approvalPolicy" in runner.safety) lines.push(` Safety: ${"access" in runner.safety ? `access=${runner.safety.access}, ` : ""}sandbox=${runner.safety.sandbox}, approval=${runner.safety.approvalPolicy}, ephemeral=${runner.safety.ephemeral}`);
|
|
548
|
+
else if ("mode" in runner.safety) lines.push(` Safety: access=${runner.safety.access}, auth=${runner.safety.authentication}, mode=${runner.safety.mode}, sandbox=${runner.safety.sandbox}, workspaceTrust=${runner.safety.workspaceTrust}, sessionReuse=${runner.safety.sessionReuse}`);
|
|
549
|
+
else if ("authentication" in runner.safety) lines.push(` Safety: access=${runner.safety.access}, auth=${runner.safety.authentication}, permission=${runner.safety.permissionMode}, tools=${runner.safety.tools}, mcp=${runner.safety.mcp}, settings=${runner.safety.settingSources}, settingsTrust=${runner.safety.userSettingsTrust}, persistence=${runner.safety.sessionPersistence}`);
|
|
550
|
+
else lines.push(` Safety: access=read-only, permission=${runner.safety.permissionMode}, tools=${runner.safety.tools}, mcp=${runner.safety.mcp}, settings=${runner.safety.settingSources}, persistence=${runner.safety.sessionPersistence}`);
|
|
551
|
+
}
|
|
552
|
+
lines.push(` Capabilities: stop=${runner.capabilities.stop}, steer=false, resume=false, structuredOutput=false, toolEvents=false, supervisor=unsupported, forkContext=false, extensionBindings=false`);
|
|
553
|
+
lines.push(` Unsupported steer: ${runner.unsupportedReasons.steer}`);
|
|
554
|
+
lines.push(` Unsupported resume: ${runner.nonResumableReason}`);
|
|
555
|
+
lines.push(` Unsupported supervisor: ${runner.unsupportedReasons.supervisor}`);
|
|
556
|
+
lines.push(` Context handoff: fresh only (${runner.unsupportedReasons.forkContext})`);
|
|
557
|
+
} else {
|
|
558
|
+
lines.push(" Runner: external-cli (invalid persisted runner metadata)");
|
|
559
|
+
}
|
|
540
560
|
if (step.externalProcess?.pid !== undefined) lines.push(` Process: ${step.externalProcess.pid}`);
|
|
541
|
-
if (step.externalProcess)
|
|
561
|
+
if (step.externalProcess) {
|
|
562
|
+
lines.push(` Stdout: ${step.externalProcess.stdoutPath}`, ` Stderr: ${step.externalProcess.stderrPath}`);
|
|
563
|
+
if (step.externalProcess.finalOutputPath) lines.push(` Final output: ${step.externalProcess.finalOutputPath}`);
|
|
564
|
+
}
|
|
542
565
|
} else if (step.runner?.type === "external-job") {
|
|
543
566
|
lines.push(` Runner: external-job (${step.runner.provider})`);
|
|
544
567
|
if (step.externalJob?.providerJobId) lines.push(` Provider job: ${step.externalJob.providerJobId}`);
|
|
@@ -583,7 +606,9 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
583
606
|
if (fs.existsSync(logPath)) lines.push(`Log: ${logPath}`);
|
|
584
607
|
if (fs.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
|
|
585
608
|
|
|
586
|
-
|
|
609
|
+
const workflowChildren = parseWorkflowChildSummary(status.workflowChildren);
|
|
610
|
+
if (workflowChildren && workflowChildren.workflowRunId !== status.runId) throw new Error("workflowChildren.workflowRunId does not match async status runId.");
|
|
611
|
+
return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "single", results: [], ...(workflowChildren ? { workflowChildren } : {}), ...(runFanoutBudget ? { runFanoutBudget } : {}), ...(processTerminal ? { lifecycleStatus: { processTerminal } } : {}) } };
|
|
587
612
|
}
|
|
588
613
|
}
|
|
589
614
|
|
|
@@ -627,7 +652,9 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
627
652
|
const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
|
|
628
653
|
lines.push(formatResumeGuidance(runId, children, data.sessionFile, { stopped: status === "stopped" }));
|
|
629
654
|
if (data.summary) lines.push("", data.summary);
|
|
630
|
-
|
|
655
|
+
const workflowChildren = parseWorkflowChildSummary((data as unknown as Record<string, unknown>).workflowChildren);
|
|
656
|
+
if (workflowChildren && workflowChildren.workflowRunId !== runId) throw new Error("workflowChildren.workflowRunId does not match the result run id.");
|
|
657
|
+
return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "single", results: [], ...(workflowChildren ? { workflowChildren } : {}) } };
|
|
631
658
|
} catch (error) {
|
|
632
659
|
const message = error instanceof Error ? error.message : String(error);
|
|
633
660
|
return {
|
|
@@ -294,6 +294,10 @@ function resolveMaxPending(config: ExtensionConfig): number {
|
|
|
294
294
|
return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : DEFAULT_MAX_PENDING;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
function hasPendingScheduleWork(schedule: ScheduleRecord): boolean {
|
|
298
|
+
return schedule.activeRunId !== undefined || schedule.trigger.nextRunAt !== undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
297
301
|
function nextAfter(trigger: ScheduleTrigger, plannedAt: number, now: number): string | undefined {
|
|
298
302
|
if (trigger.kind === "once") return undefined;
|
|
299
303
|
let next = plannedAt + trigger.everyMs;
|
|
@@ -479,7 +483,9 @@ export class ScheduledRunManager {
|
|
|
479
483
|
if (params.on !== undefined || params.timezone !== undefined || every === "day" || every === "week" || every === "month" || every === "year") return textResult("Calendar schedules are deferred from this first safe slice. Use a fixed interval such as every:'24h' or every:'7d'.", undefined, undefined, true);
|
|
480
484
|
const sessionId = ctx.sessionManager.getSessionId() ?? "unknown";
|
|
481
485
|
if (this.deps.resolveCapabilityCeiling?.(sessionId)) return textResult("Cannot persist a schedule while a capability ceiling is active.", undefined, undefined, true);
|
|
482
|
-
|
|
486
|
+
const pendingCount = store.list().filter(hasPendingScheduleWork).length;
|
|
487
|
+
const maxPending = resolveMaxPending(this.deps.config);
|
|
488
|
+
if (pendingCount >= maxPending) return textResult(`Schedule limit reached (${maxPending}).`, undefined, undefined, true);
|
|
483
489
|
const id = validateScheduleId((params.id?.trim() || this.randomId()));
|
|
484
490
|
if (store.ids().includes(id)) return textResult(`Schedule '${id}' already exists.`, undefined, undefined, true);
|
|
485
491
|
const now = this.now();
|