pi-subagents 0.80.0 → 0.82.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 +66 -0
- package/bundle/agents.js +18 -0
- package/bundle/background-work.js +16 -0
- package/bundle/capability-ceiling.js +22 -0
- package/bundle/child-tool-plan.js +11 -0
- package/bundle/chunk-43SCHIMD.js +57 -0
- package/bundle/chunk-4QFZZV3O.js +57 -0
- package/bundle/chunk-4SDU2BZX.js +1190 -0
- package/bundle/chunk-52UBALJL.js +753 -0
- package/bundle/chunk-5LFLVSTM.js +260 -0
- package/bundle/chunk-5TYMSZX3.js +235 -0
- package/bundle/chunk-62OCY2S4.js +14 -0
- package/bundle/chunk-7F6RKT6W.js +132 -0
- package/bundle/chunk-7IVSUMQQ.js +1337 -0
- package/bundle/chunk-7J4ZXMY7.js +18 -0
- package/bundle/chunk-7PCKZHR5.js +1234 -0
- package/bundle/chunk-7YMXOYEU.js +1373 -0
- package/bundle/chunk-B5EKXDHE.js +16870 -0
- package/bundle/chunk-CIZQXJ5Q.js +8619 -0
- package/bundle/chunk-CTPAE36Y.js +94 -0
- package/bundle/chunk-D4EHT5SJ.js +14 -0
- package/bundle/chunk-GC4DSPMH.js +946 -0
- package/bundle/chunk-GJECHSAR.js +179 -0
- package/bundle/chunk-M2CTFYT3.js +4164 -0
- package/bundle/chunk-MHZ4G23A.js +1296 -0
- package/bundle/chunk-N2BN7BF5.js +159 -0
- package/bundle/chunk-NF7IEKBH.js +490 -0
- package/bundle/chunk-NGGAFDL6.js +742 -0
- package/bundle/chunk-NIXWDZZV.js +9 -0
- package/bundle/chunk-NRQ75MGH.js +8327 -0
- package/bundle/chunk-OYF6AFDY.js +458 -0
- package/bundle/chunk-PLCCBLPY.js +526 -0
- package/bundle/chunk-RMQFMMAT.js +416 -0
- package/bundle/chunk-SIJ4S3EN.js +276 -0
- package/bundle/chunk-UKG4V45O.js +228 -0
- package/bundle/chunk-VH5OAWVG.js +4096 -0
- package/bundle/chunk-W2SZ22EM.js +5462 -0
- package/bundle/chunk-WVD4UEL4.js +9367 -0
- package/bundle/chunk-XSWOTIA3.js +175 -0
- package/bundle/chunk-YFCK7H2S.js +129 -0
- package/bundle/chunk-YKEWIB4M.js +380 -0
- package/bundle/chunk-YYREO77V.js +137 -0
- package/bundle/chunk-Z7KVHQHL.js +173 -0
- package/bundle/chunk-ZLINIYTP.js +8613 -0
- package/bundle/chunk-ZQGSDHL6.js +128 -0
- package/bundle/control-channel.js +11 -0
- package/bundle/delegation.js +14 -0
- package/bundle/external-job-provider.js +20 -0
- package/bundle/external-runs.js +21 -0
- package/bundle/index.js +30 -0
- package/bundle/inspectors.js +8 -0
- package/bundle/intercom-bridge.js +11 -0
- package/bundle/preflight.js +425 -0
- package/bundle/project-panes.js +28 -0
- package/bundle/required-child-extensions.js +6 -0
- package/bundle/shared-types.js +8 -0
- package/bundle/workflow-resources.js +7 -0
- package/docs/agents.md +31 -0
- package/docs/configuration.md +9 -1
- package/docs/extension-api.md +27 -0
- package/docs/observability.md +6 -2
- package/docs/tool-reference.md +3 -1
- package/docs/workflows.md +16 -0
- package/package.json +19 -19
- package/skills/pi-subagents/references/execution-controls.md +5 -4
- package/src/agents/agent-management.js +3 -3
- package/src/agents/agent-memory.js +3 -1
- package/src/agents/agents.js +6 -6
- package/src/agents/frontmatter.d.ts +4 -0
- package/src/agents/frontmatter.js +24 -3
- package/src/agents/skills.js +2 -1
- package/src/api/delegation.d.ts +20 -0
- package/src/extension/config.js +3 -0
- package/src/extension/herdr-pi-bridge.js +17 -10
- package/src/extension/index.js +109 -9
- package/src/extension/schemas.js +3 -3
- package/src/intercom/native-supervisor-channel.js +20 -0
- package/src/missions/lifecycle.js +7 -1
- package/src/runs/background/active-async-capacity.d.ts +1 -0
- package/src/runs/background/active-async-capacity.js +9 -3
- package/src/runs/background/async-execution.js +20 -10
- package/src/runs/background/async-job-tracker.d.ts +6 -1
- package/src/runs/background/async-job-tracker.js +9 -4
- package/src/runs/background/async-status.d.ts +2 -0
- package/src/runs/background/async-status.js +21 -2
- package/src/runs/background/await-async-run.js +3 -7
- package/src/runs/background/boot-id.d.ts +12 -0
- package/src/runs/background/boot-id.js +47 -0
- package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
- package/src/runs/background/interrupted-elsewhere.js +68 -0
- package/src/runs/background/notify.d.ts +2 -0
- package/src/runs/background/notify.js +13 -0
- package/src/runs/background/result-delivery-ownership.d.ts +6 -0
- package/src/runs/background/result-delivery-ownership.js +19 -1
- package/src/runs/background/result-watcher.d.ts +3 -1
- package/src/runs/background/result-watcher.js +24 -8
- package/src/runs/background/run-child-session.d.ts +1 -6
- package/src/runs/background/run-child-session.js +19 -1
- package/src/runs/background/run-status.js +2 -1
- package/src/runs/background/stale-run-reconciler.d.ts +25 -0
- package/src/runs/background/stale-run-reconciler.js +54 -13
- package/src/runs/background/subagent-runner-bootstrap.js +3 -0
- package/src/runs/background/subagent-runner.d.ts +4 -0
- package/src/runs/background/subagent-runner.js +13 -5
- package/src/runs/background/subagent-wait.d.ts +1 -1
- package/src/runs/background/subagent-wait.js +6 -1
- package/src/runs/background/terminal-run-index.d.ts +1 -0
- package/src/runs/background/terminal-run-index.js +4 -2
- package/src/runs/foreground/async-stop-action.js +3 -1
- package/src/runs/foreground/execution.js +45 -5
- package/src/runs/foreground/foreground-history.d.ts +16 -2
- package/src/runs/foreground/foreground-history.js +133 -31
- package/src/runs/foreground/subagent-executor.d.ts +11 -0
- package/src/runs/foreground/subagent-executor.js +277 -74
- package/src/runs/shared/child-launch.d.ts +2 -2
- package/src/runs/shared/child-session.d.ts +3 -2
- package/src/runs/shared/child-session.js +51 -8
- package/src/runs/shared/child-tool-plan.d.ts +7 -0
- package/src/runs/shared/child-tool-plan.js +21 -6
- package/src/runs/shared/herdr-connection.d.ts +17 -8
- package/src/runs/shared/herdr-connection.js +19 -7
- package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
- package/src/runs/shared/herdr-external-adapters.js +1 -1
- package/src/runs/shared/herdr-machine.d.ts +12 -2
- package/src/runs/shared/herdr-machine.js +55 -9
- package/src/runs/shared/herdr-pane-host.d.ts +88 -0
- package/src/runs/shared/herdr-pane-host.js +155 -0
- package/src/runs/shared/herdr-placed-run.d.ts +27 -150
- package/src/runs/shared/herdr-placed-run.js +48 -395
- package/src/runs/shared/local-machine-access.d.ts +5 -0
- package/src/runs/shared/local-machine-access.js +28 -0
- package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
- package/src/runs/shared/orca-progress-tabs.js +1 -24
- package/src/runs/shared/parallel-utils.d.ts +1 -1
- package/src/runs/shared/placed-run-owner.d.ts +49 -0
- package/src/runs/shared/placed-run-owner.js +93 -0
- package/src/runs/shared/placement-host.d.ts +99 -0
- package/src/runs/shared/placement-host.js +157 -0
- package/src/runs/shared/rex-machine.d.ts +25 -0
- package/src/runs/shared/rex-machine.js +83 -0
- package/src/runs/shared/rex-pane-host.d.ts +62 -0
- package/src/runs/shared/rex-pane-host.js +327 -0
- package/src/runs/shared/structured-output.d.ts +1 -0
- package/src/runs/shared/structured-output.js +1 -0
- package/src/shared/accessible-dir.js +4 -1
- package/src/shared/completion-owner.d.ts +5 -0
- package/src/shared/completion-owner.js +5 -0
- package/src/shared/provider-diagnostics.d.ts +9 -0
- package/src/shared/provider-diagnostics.js +75 -0
- package/src/shared/session-identity.d.ts +2 -0
- package/src/shared/session-identity.js +26 -0
- package/src/shared/types.d.ts +48 -6
- package/src/slash/delegation-adapters.d.ts +3 -1
- package/src/slash/delegation-adapters.js +7 -0
- package/src/slash/prompt-template-bridge.js +3 -1
- package/src/slash/subagents-admin.d.ts +1 -0
- package/src/slash/subagents-admin.js +1 -1
- package/src/tui/fleet-layout.d.ts +20 -0
- package/src/tui/fleet-layout.js +17 -0
- package/src/tui/fleet-viewport.d.ts +12 -0
- package/src/tui/fleet-viewport.js +35 -0
- package/src/tui/fleet.d.ts +6 -6
- package/src/tui/fleet.js +93 -66
- package/src/tui/render.d.ts +6 -0
- package/src/tui/render.js +3 -3
- package/src/tui/subagent-messages.js +1 -0
- package/src/workflows/scripted-workflow.d.ts +1 -1
- package/src/workflows/structured-workflow-scripts.d.ts +1 -0
- package/src/workflows/structured-workflow-scripts.js +5 -4
- package/src/workflows/workflow-auto-resume.d.ts +32 -0
- package/src/workflows/workflow-auto-resume.js +102 -0
- package/src/workflows/workflow-launch-record.d.ts +27 -0
- package/src/workflows/workflow-launch-record.js +48 -0
- package/src/workflows/workflow-resources.js +2 -2
- package/src/workflows/workflow-resume-claim.d.ts +82 -0
- package/src/workflows/workflow-resume-claim.js +210 -0
- package/src/workflows/workflow-resume.d.ts +56 -0
- package/src/workflows/workflow-resume.js +106 -0
- package/src/workflows/workflow-reuse.d.ts +15 -9
- package/src/workflows/workflow-reuse.js +77 -14
- package/src/workflows/workflow-revival.d.ts +2 -0
- package/src/workflows/workflow-revival.js +2 -1
- package/src/workflows/workflow-stop-cause.d.ts +6 -0
- package/src/workflows/workflow-stop-cause.js +8 -0
|
@@ -4,6 +4,7 @@ import { writeAtomicJson } from "../../shared/atomic-json.js";
|
|
|
4
4
|
import { resultFilePath, resultPayloadPathForSessionRun, writeAsyncResultFile } from "./result-files.js";
|
|
5
5
|
import { updateActiveRunIndex } from "./active-run-index.js";
|
|
6
6
|
import { readProcessTerminal } from "./process-terminal.js";
|
|
7
|
+
import { OWNING_SESSION_COMPLETION_OWNER } from "../../shared/completion-owner.js";
|
|
7
8
|
import { readStatus } from "../../shared/utils.js";
|
|
8
9
|
import { DIRS } from "../../shared/types.js";
|
|
9
10
|
import { resolveEffectiveThinking } from "../../shared/model-info.js";
|
|
@@ -11,6 +12,8 @@ import { normalizeParallelGroups } from "./parallel-groups.js";
|
|
|
11
12
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.js";
|
|
12
13
|
import { assertWorkflowGraphHostSteps } from "../shared/host-step-status.js";
|
|
13
14
|
import { currentPidNamespaceScope } from "./pid-namespace.js";
|
|
15
|
+
import { currentBootId } from "./boot-id.js";
|
|
16
|
+
import { WORKFLOW_STOP_CAUSE_OWNER_EXITED } from "../../workflows/workflow-stop-cause.js";
|
|
14
17
|
import { isTerminalAsyncState } from "./workflow-terminal-proof.js";
|
|
15
18
|
function getErrorMessage(error) {
|
|
16
19
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -215,9 +218,14 @@ function settledWorkflowChildStatus(asyncDir, workflowRunId, step, now) {
|
|
|
215
218
|
delete settled.stopped;
|
|
216
219
|
return settled;
|
|
217
220
|
}
|
|
218
|
-
function buildFailedRepair(status, asyncDir, now, reason) {
|
|
221
|
+
function buildFailedRepair(status, asyncDir, now, reason, ownerExited = false, adoptCompletionOwnerId) {
|
|
219
222
|
const runId = status.runId || path.basename(asyncDir);
|
|
220
223
|
const pid = typeof status.pid === "number" ? status.pid : "unknown";
|
|
224
|
+
// A dead workflow host is the Pi process itself; its journal keeps the finished children for a relaunch.
|
|
225
|
+
const exitedWorkflow = ownerExited && status.mode === "workflow" ? status.workflow : undefined;
|
|
226
|
+
// Its owner process is dead, so nothing else can deliver its notice: an owning session that repairs it
|
|
227
|
+
// adopts it, and a repair elsewhere leaves it to whichever session owns the run.
|
|
228
|
+
const completionOwnerId = exitedWorkflow ? adoptCompletionOwnerId || OWNING_SESSION_COMPLETION_OWNER : status.completionOwnerId;
|
|
221
229
|
const terminal = readProcessTerminal(asyncDir, { runId }) ?? status.processTerminal;
|
|
222
230
|
const runnerExit = terminal?.state === "observed" || terminal?.state === "unknown"
|
|
223
231
|
? terminal.instances?.find((instance) => instance.kind === "runner" && instance.processInstanceId === terminal.runnerProcessInstanceId)
|
|
@@ -225,7 +233,9 @@ function buildFailedRepair(status, asyncDir, now, reason) {
|
|
|
225
233
|
const exitText = runnerExit?.kind === "runner"
|
|
226
234
|
? `exited with code ${runnerExit.exitCode ?? "none"}${runnerExit.signal ? ` (signal ${runnerExit.signal})` : ""}`
|
|
227
235
|
: "exited or disappeared";
|
|
228
|
-
const baseMessage = reason ??
|
|
236
|
+
const baseMessage = reason ?? (exitedWorkflow
|
|
237
|
+
? `The Pi process (PID ${pid}) that ran workflow ${runId} exited before the workflow finished. Marked run failed by stale-run reconciliation. Its finished children are kept.`
|
|
238
|
+
: `Async runner process ${pid} ${exitText} before writing a result. Marked run failed by stale-run reconciliation.`);
|
|
229
239
|
const diagnostics = readRunnerStartupDiagnostics(asyncDir);
|
|
230
240
|
const message = diagnostics ? `${baseMessage}\n\nRunner stderr tail:\n${diagnostics}` : baseMessage;
|
|
231
241
|
const steps = status.steps?.length ? status.steps : [{ agent: "subagent", status: "running" }];
|
|
@@ -263,6 +273,9 @@ function buildFailedRepair(status, asyncDir, now, reason) {
|
|
|
263
273
|
lastUpdate: now,
|
|
264
274
|
endedAt: now,
|
|
265
275
|
steps: repairedSteps,
|
|
276
|
+
// Read from the pre-repair status: the repair time would make an old crash look new.
|
|
277
|
+
...(exitedWorkflow ? { workflow: { ...exitedWorkflow, stopCause: WORKFLOW_STOP_CAUSE_OWNER_EXITED, ownerExitedAt: status.lastUpdate ?? status.startedAt } } : {}),
|
|
278
|
+
...(completionOwnerId ? { completionOwnerId } : {}),
|
|
266
279
|
};
|
|
267
280
|
const resultAgent = repairedSteps[status.currentStep ?? 0]?.agent ?? repairedSteps[0]?.agent ?? "subagent";
|
|
268
281
|
return {
|
|
@@ -272,7 +285,11 @@ function buildFailedRepair(status, asyncDir, now, reason) {
|
|
|
272
285
|
id: runId,
|
|
273
286
|
agent: resultAgent,
|
|
274
287
|
mode: status.mode,
|
|
275
|
-
...(
|
|
288
|
+
...(exitedWorkflow ? { workflow: { stopCause: WORKFLOW_STOP_CAUSE_OWNER_EXITED } } : {}),
|
|
289
|
+
...(completionOwnerId ? { completionOwnerId } : {}),
|
|
290
|
+
...(status.completionOwnerProcess ? { completionOwnerProcess: status.completionOwnerProcess } : {}),
|
|
291
|
+
...(status.parentWorkflowRunId ? { parentWorkflowRunId: status.parentWorkflowRunId } : {}),
|
|
292
|
+
...(status.workflowKey ? { workflowKey: status.workflowKey } : {}),
|
|
276
293
|
success: false,
|
|
277
294
|
state: "failed",
|
|
278
295
|
summary: message,
|
|
@@ -296,8 +313,8 @@ function buildFailedRepair(status, asyncDir, now, reason) {
|
|
|
296
313
|
},
|
|
297
314
|
};
|
|
298
315
|
}
|
|
299
|
-
function writeFailedRepair(asyncDir, status, resultPath, now, reason) {
|
|
300
|
-
const repair = buildFailedRepair(status, asyncDir, now, reason);
|
|
316
|
+
function writeFailedRepair(asyncDir, status, resultPath, now, reason, ownerExited = false, adoptCompletionOwnerId) {
|
|
317
|
+
const repair = buildFailedRepair(status, asyncDir, now, reason, ownerExited, adoptCompletionOwnerId);
|
|
301
318
|
if (repair.result.sessionId)
|
|
302
319
|
writeAsyncResultFile(resultPath, repair.result);
|
|
303
320
|
writeAtomicJson(path.join(asyncDir, "status.json"), repair.status);
|
|
@@ -384,6 +401,32 @@ export function checkPidLiveness(pid, kill = process.kill, probeZombie = false)
|
|
|
384
401
|
return "unknown";
|
|
385
402
|
}
|
|
386
403
|
}
|
|
404
|
+
export function isRecordedPid(value) {
|
|
405
|
+
if (!value || typeof value !== "object")
|
|
406
|
+
return false;
|
|
407
|
+
const { pid, pidNamespaceScope, bootId } = value;
|
|
408
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0
|
|
409
|
+
&& (pidNamespaceScope === undefined || typeof pidNamespaceScope === "string")
|
|
410
|
+
&& (bootId === undefined || typeof bootId === "string");
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Liveness of a pid saved in a status file. A pid saved under another boot is dead without a
|
|
414
|
+
* probe, but only when both sides are in the same pid namespace or neither records one: a
|
|
415
|
+
* macOS host reading a Linux container's runs must not compare their boot ids.
|
|
416
|
+
*/
|
|
417
|
+
export function checkRecordedPidLiveness(recorded, observation = { observedScope: currentPidNamespaceScope(), observedBootId: currentBootId() }) {
|
|
418
|
+
const { observedScope, observedBootId } = observation;
|
|
419
|
+
if (recorded.pidNamespaceScope === observedScope && recorded.bootId && observedBootId && recorded.bootId !== observedBootId)
|
|
420
|
+
return "dead";
|
|
421
|
+
// An observer without a scope (macOS/Windows host sharing a container's temp root) cannot match a recorded one.
|
|
422
|
+
const scopeMismatch = recorded.pidNamespaceScope !== undefined && recorded.pidNamespaceScope !== observedScope;
|
|
423
|
+
// A local zombie is evidence about this pid only when its namespace is known to match.
|
|
424
|
+
const scopeVerified = recorded.pidNamespaceScope !== undefined && recorded.pidNamespaceScope === observedScope;
|
|
425
|
+
const liveness = observation.probe
|
|
426
|
+
? observation.probe(recorded.pid)
|
|
427
|
+
: checkPidLiveness(recorded.pid, observation.kill, scopeVerified);
|
|
428
|
+
return liveness === "dead" && scopeMismatch ? "unknown" : liveness;
|
|
429
|
+
}
|
|
387
430
|
export function reconcileAsyncRun(asyncDir, options = {}, observeStatus) {
|
|
388
431
|
const now = options.now?.() ?? Date.now();
|
|
389
432
|
const status = readStatus(asyncDir);
|
|
@@ -448,13 +491,11 @@ export function reconcileAsyncRun(asyncDir, options = {}, observeStatus) {
|
|
|
448
491
|
return { status: null, repaired: false, resultPath };
|
|
449
492
|
}
|
|
450
493
|
}
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const observedLiveness = checkPidLiveness(effectiveStatus.pid, options.kill, pidScopeVerified);
|
|
457
|
-
const liveness = observedLiveness === "dead" && pidScopeMismatch ? "unknown" : observedLiveness;
|
|
494
|
+
const liveness = checkRecordedPidLiveness({ pid: effectiveStatus.pid, pidNamespaceScope: effectiveStatus.pidNamespaceScope, bootId: effectiveStatus.bootId }, {
|
|
495
|
+
kill: options.kill,
|
|
496
|
+
observedScope: options.pidNamespaceScope ? options.pidNamespaceScope() : currentPidNamespaceScope(),
|
|
497
|
+
observedBootId: options.bootId ? options.bootId() : currentBootId(),
|
|
498
|
+
});
|
|
458
499
|
if (liveness !== "dead") {
|
|
459
500
|
const staleAfterMs = options.staleAlivePidMs ?? 24 * 60 * 60 * 1000;
|
|
460
501
|
const lastUpdate = effectiveStatus.lastUpdate ?? effectiveStatus.startedAt;
|
|
@@ -464,5 +505,5 @@ export function reconcileAsyncRun(asyncDir, options = {}, observeStatus) {
|
|
|
464
505
|
const message = `Async runner PID ${effectiveStatus.pid} ${probe}; status has not updated for ${now - lastUpdate}ms, so stale-run reconciliation marked the run failed because PID ownership is unverified.`;
|
|
465
506
|
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, message);
|
|
466
507
|
}
|
|
467
|
-
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now);
|
|
508
|
+
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, undefined, true, options.adoptCompletionOwnerId);
|
|
468
509
|
}
|
|
@@ -14,6 +14,7 @@ import { acquireSessionLease } from "../shared/session-lease.js";
|
|
|
14
14
|
import { readProcessTerminalCandidate, writeProcessTerminalCandidate, markProcessTerminalCandidateLeaseRelease } from "./process-terminal-candidate.js";
|
|
15
15
|
import { persistRunnerStartupFailure } from "./runner-startup-failure.js";
|
|
16
16
|
import { currentPidNamespaceScope } from "./pid-namespace.js";
|
|
17
|
+
import { primeBootId } from "./boot-id.js";
|
|
17
18
|
function isRecord(value) {
|
|
18
19
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19
20
|
}
|
|
@@ -103,6 +104,8 @@ async function waitForStartupControl(controlPath, token, action, timeoutMs = 30_
|
|
|
103
104
|
export async function runConfiguredSubagent(rawConfig, options = {}) {
|
|
104
105
|
validateSubagentRunConfig(rawConfig);
|
|
105
106
|
const config = rawConfig;
|
|
107
|
+
// The runner never reads the boot id from the OS (sysctl on macOS); Pi passes it to direct runners only.
|
|
108
|
+
primeBootId(typeof config.bootId === "string" && config.bootId ? config.bootId : undefined);
|
|
106
109
|
let lease;
|
|
107
110
|
let startupCommitted = config.revivalLease === undefined && config.launchBarrierToken === undefined;
|
|
108
111
|
let executionLoaded = false;
|
|
@@ -4,6 +4,7 @@ import { type RunnerSubagentStep as SubagentStep, type RunnerStep } from "../sha
|
|
|
4
4
|
import type { InheritedChildRuntime } from "../shared/child-launch.ts";
|
|
5
5
|
import type { ChildSessionFactory, DefaultChildSessionFactoryOptions } from "../shared/child-session.ts";
|
|
6
6
|
import { type ChildEvent, type SteerDelivery, type StepSteerHandler } from "./run-child-session.ts";
|
|
7
|
+
import type { RecordedPid } from "./stale-run-reconciler.ts";
|
|
7
8
|
import type { SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
8
9
|
import { createHerdrExternalAdapter, type HerdrExternalAdapterId, type HerdrExternalResult } from "../shared/herdr-external-adapters.ts";
|
|
9
10
|
import { type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
@@ -25,6 +26,7 @@ export interface SubagentRunConfig {
|
|
|
25
26
|
asyncDir: string;
|
|
26
27
|
sessionId?: string | null;
|
|
27
28
|
completionOwnerId?: string;
|
|
29
|
+
completionOwnerProcess?: RecordedPid;
|
|
28
30
|
piPackageRoot?: string;
|
|
29
31
|
/** Test seam: module the runner imports its `ChildSessionFactory` from. */
|
|
30
32
|
childSessionFactoryModule?: string;
|
|
@@ -76,6 +78,8 @@ export interface SubagentRunConfig {
|
|
|
76
78
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensions;
|
|
77
79
|
runnerProcessInstanceId?: string;
|
|
78
80
|
launchBarrierToken?: string;
|
|
81
|
+
/** Pi's boot id, set only for a direct (non-launcher) runner; the runner never reads its own. */
|
|
82
|
+
bootId?: string;
|
|
79
83
|
parentWorkflowRunId?: string;
|
|
80
84
|
workflowKey?: string;
|
|
81
85
|
lane?: WorkflowLaneMetadata;
|
|
@@ -49,7 +49,7 @@ import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDi
|
|
|
49
49
|
import { isContextOverflow } from "../shared/model-resolution.js";
|
|
50
50
|
import { processTerminalPath, writeProcessTerminalCandidate } from "./process-terminal.js";
|
|
51
51
|
import { persistRunnerStartupFailure } from "./runner-startup-failure.js";
|
|
52
|
-
import {
|
|
52
|
+
import { currentRecordedPid } from "./boot-id.js";
|
|
53
53
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, unconsumedSteerReason, updateSteeringTarget } from "./steering.js";
|
|
54
54
|
import { PROMPT_REDACTED, SUBAGENT_STOPPED_MESSAGE, SUBAGENT_TIMED_OUT_MESSAGE, detectSubagentError, extractTextFromContent, extractToolArgsPreview, formatEmptyTerminalAssistantResponseError, getAgentDir, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.js";
|
|
55
55
|
import { planAbortRecovery } from "../shared/abort-recovery.js";
|
|
@@ -77,6 +77,7 @@ import { resolveCodexExecLaunch } from "../shared/codex-exec-adapter.js";
|
|
|
77
77
|
import { resolveCursorAgentLaunch } from "../shared/cursor-agent-adapter.js";
|
|
78
78
|
import { resolveExternalCliRunnerStatus } from "../shared/external-cli-contract.js";
|
|
79
79
|
import { formatHerdrMachineHint, prepareHerdrMachineExternalCliRun } from "../shared/herdr-machine.js";
|
|
80
|
+
import { isRexLocalMachine } from "../shared/rex-machine.js";
|
|
80
81
|
import { HerdrExternalNeedsAttentionError, createHerdrExternalAdapter, prepareInternalHerdrExternalAdapter } from "../shared/herdr-external-adapters.js";
|
|
81
82
|
import { runExternalJob } from "../shared/external-job-runner.js";
|
|
82
83
|
import { createOrcaProgressTab } from "../shared/orca-progress-tabs.js";
|
|
@@ -522,6 +523,7 @@ export async function runSingleStepInner(step, ctx) {
|
|
|
522
523
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
523
524
|
inheritedCapabilityCeiling: ctx.inheritedChildRuntime?.capabilityCeiling,
|
|
524
525
|
requiredExtensions: step.requiredExtensions ?? ctx.inheritedChildRuntime?.requiredExtensions,
|
|
526
|
+
agentName: step.agent,
|
|
525
527
|
permissionRules: step.permissionRules,
|
|
526
528
|
}));
|
|
527
529
|
}
|
|
@@ -608,6 +610,8 @@ export async function runSingleStepInner(step, ctx) {
|
|
|
608
610
|
return omitUndefinedProperties({ agent: step.agent, context: step.context, output: message, error: message, exitCode: 1, stopped: stopped || undefined, timedOut: stopped ? undefined : true });
|
|
609
611
|
}
|
|
610
612
|
if (step.machine) {
|
|
613
|
+
if (step.machine.provider !== "herdr")
|
|
614
|
+
throw new Error("Rex supports native Pi children only.");
|
|
611
615
|
const runner = resolveExternalCliRunnerStatus({ ...step.runner, machine: step.machine });
|
|
612
616
|
const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
|
|
613
617
|
const placedRunId = `${ctx.id}-${ctx.flatIndex}`;
|
|
@@ -781,7 +785,8 @@ export async function runSingleStepInner(step, ctx) {
|
|
|
781
785
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
782
786
|
let toolBudgetBlocked = false;
|
|
783
787
|
let actualLaunchContractDigest = step.launchContractDigest;
|
|
784
|
-
|
|
788
|
+
// A machine 'local' child edits this disk, so its local Git evidence is authoritative, as for an unplaced run.
|
|
789
|
+
const mutationSnapshot = step.machine && !isRexLocalMachine(step.machine) ? { source: "tracked-files", trackedOnly: true, cwd: step.cwd ?? ctx.cwd, dirtyFiles: [], fingerprints: {}, unavailable: "Local Git evidence is not authoritative for a pane-native remote run." } : snapshotTrackedMutations(step.cwd ?? ctx.cwd);
|
|
785
790
|
let finalMutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
786
791
|
let contextOverflow = false;
|
|
787
792
|
let launchWarningsEmitted = false;
|
|
@@ -1004,7 +1009,7 @@ export async function runSingleStepInner(step, ctx) {
|
|
|
1004
1009
|
? formatEmptyTerminalAssistantResponseError(run.messages)
|
|
1005
1010
|
: undefined;
|
|
1006
1011
|
const remoteGitChanged = run.nativeMachine?.initialGit && run.nativeMachine.finalGit ? run.nativeMachine.initialGit.head !== run.nativeMachine.finalGit.head || run.nativeMachine.initialGit.dirty !== run.nativeMachine.finalGit.dirty : undefined;
|
|
1007
|
-
const mutationEvidence = run.nativeMachine ? { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1012
|
+
const mutationEvidence = run.nativeMachine && !isRexLocalMachine(step.machine) ? { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1008
1013
|
finalMutationEvidence = mutationEvidence;
|
|
1009
1014
|
const mutationAttemptObserved = run.observedMutationAttempt === true || mutationEvidence.attemptedMutation === true;
|
|
1010
1015
|
// Validated structured output is persistable file content even when the final text is only
|
|
@@ -1669,6 +1674,7 @@ export async function runSubagent(config, childSessions) {
|
|
|
1669
1674
|
...(config.toolCallId ? { toolCallId: config.toolCallId } : {}),
|
|
1670
1675
|
...(config.sessionId ? { sessionId: config.sessionId } : {}),
|
|
1671
1676
|
...(config.completionOwnerId ? { completionOwnerId: config.completionOwnerId } : {}),
|
|
1677
|
+
...(config.completionOwnerProcess ? { completionOwnerProcess: config.completionOwnerProcess } : {}),
|
|
1672
1678
|
mode: config.resultMode ?? (flatSteps.length > 1 ? "chain" : "single"),
|
|
1673
1679
|
...(config.nestedSelf ? { isNested: true } : {}),
|
|
1674
1680
|
state: "running",
|
|
@@ -1680,8 +1686,7 @@ export async function runSubagent(config, childSessions) {
|
|
|
1680
1686
|
...(config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {}),
|
|
1681
1687
|
...(config.toolBudget ? { toolBudget: initialToolBudgetState(config.toolBudget) } : {}),
|
|
1682
1688
|
...(config.usageBudget ? { usageBudget: usageBudgetState(config.usageBudget, undefined) } : {}),
|
|
1683
|
-
|
|
1684
|
-
pidNamespaceScope: currentPidNamespaceScope(),
|
|
1689
|
+
...currentRecordedPid(),
|
|
1685
1690
|
cwd,
|
|
1686
1691
|
currentStep: 0,
|
|
1687
1692
|
chainStepCount: steps.length,
|
|
@@ -1909,6 +1914,8 @@ export async function runSubagent(config, childSessions) {
|
|
|
1909
1914
|
cwd,
|
|
1910
1915
|
sessionId: config.sessionId,
|
|
1911
1916
|
completionOwnerId: config.completionOwnerId,
|
|
1917
|
+
completionOwnerProcess: config.completionOwnerProcess,
|
|
1918
|
+
parentWorkflowRunId: config.parentWorkflowRunId,
|
|
1912
1919
|
sessionFile: statusPayload.sessionFile ?? latestSessionFile,
|
|
1913
1920
|
}), (filePath, payload) => publishRunResult(filePath, () => writePendingAsyncResultFile(filePath, payload)));
|
|
1914
1921
|
};
|
|
@@ -4797,6 +4804,7 @@ export async function runSubagent(config, childSessions) {
|
|
|
4797
4804
|
runtimeAcknowledgedExtensions: singleRuntimeAcknowledgedExtensions,
|
|
4798
4805
|
sessionId: config.sessionId,
|
|
4799
4806
|
completionOwnerId: config.completionOwnerId,
|
|
4807
|
+
completionOwnerProcess: config.completionOwnerProcess,
|
|
4800
4808
|
sessionFile: effectiveSessionFile,
|
|
4801
4809
|
intercomTarget: config.controlIntercomTarget,
|
|
4802
4810
|
shareUrl,
|
|
@@ -54,7 +54,7 @@ export interface SubagentWaitParams {
|
|
|
54
54
|
all?: boolean;
|
|
55
55
|
/** Give up after this many milliseconds. Defaults to waitTool.defaultTimeoutMs, then 30 minutes. */
|
|
56
56
|
timeoutMs?: number;
|
|
57
|
-
/** False keeps a blocking wait open through
|
|
57
|
+
/** False keeps a blocking wait open through all attention except supervisor/contact requests, such as idle time, a tool held open, or repeated tool failures. */
|
|
58
58
|
stopOnAttention?: boolean;
|
|
59
59
|
}
|
|
60
60
|
/** Minimal event-bus surface wait subscribes to (matches pi.events). */
|
|
@@ -707,8 +707,13 @@ export async function waitForSubagents(params, signal, deps) {
|
|
|
707
707
|
const supervisorAttentionHint = relevantAttention.some(hasSupervisorTool)
|
|
708
708
|
? " Reply to any pending supervisor request. If subagent_supervisor({ action: \"pending\" }) is empty, check intercom({ action: \"pending\" }) because an external intercom tool may own the request."
|
|
709
709
|
: "";
|
|
710
|
+
// Only when watchdog attention alone ended the wait: stopOnAttention: false would have kept blocking.
|
|
711
|
+
const keepWaitingHint = stopOnAttention && supervisorAttentionHint === ""
|
|
712
|
+
&& (waitForAll || finishedAsyncCount + providerFinishedCount === 0)
|
|
713
|
+
? " These runs are still running and none is waiting on a supervisor reply. To keep waiting through this kind of attention, call bg_wait again with stopOnAttention: false; supervisor requests still end that wait."
|
|
714
|
+
: "";
|
|
710
715
|
const attentionNote = relevantAttention.length > 0
|
|
711
|
-
? ` ${relevantAttention.length} run(s) need attention: ${relevantAttention.map((run) => run.id).join(", ")} —${supervisorAttentionHint} inspect with subagent({ action: "status" }) then steer a top-level live async child, resume a paused/completed/failed child, or interrupt explicitly
|
|
716
|
+
? ` ${relevantAttention.length} run(s) need attention: ${relevantAttention.map((run) => run.id).join(", ")} —${supervisorAttentionHint} inspect with subagent({ action: "status" }) then steer a top-level live async child, resume a paused/completed/failed child, or interrupt explicitly.${keepWaitingHint}`
|
|
712
717
|
: "";
|
|
713
718
|
const stillRunning = active.filter((run) => initialAsyncIds.has(run.id)).length
|
|
714
719
|
+ providerActive.filter((item) => initialProviderIds.has(backgroundWorkIdentity(item))).length;
|
|
@@ -99,8 +99,10 @@ function sessionDirs(asyncDirRoot, sessionId) {
|
|
|
99
99
|
throw error;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
function recentMarkerFiles(dirs, limit) {
|
|
102
|
+
function recentMarkerFiles(dirs, limit, endedAfter) {
|
|
103
103
|
return dirs.flatMap(markerFiles)
|
|
104
|
+
// The name starts with the run's end time, so an old run is skipped without reading its marker.
|
|
105
|
+
.filter((marker) => endedAfter === undefined || !(Number(marker.name.slice(0, TIMESTAMP_WIDTH)) < endedAfter))
|
|
104
106
|
.sort((left, right) => right.name.localeCompare(left.name))
|
|
105
107
|
.slice(0, limit)
|
|
106
108
|
.map((marker) => path.join(marker.dir, marker.name));
|
|
@@ -135,7 +137,7 @@ export function readTerminalRunToolCallIndex(asyncDirRoot, toolCallId) {
|
|
|
135
137
|
}
|
|
136
138
|
export function readRecentTerminalRunIndex(asyncDirRoot, options = {}) {
|
|
137
139
|
const limit = options.limit === undefined ? Number.POSITIVE_INFINITY : Math.max(0, Math.floor(options.limit));
|
|
138
|
-
const candidates = recentMarkerFiles(sessionDirs(asyncDirRoot, options.sessionId), limit);
|
|
140
|
+
const candidates = recentMarkerFiles(sessionDirs(asyncDirRoot, options.sessionId), limit, options.endedAfter);
|
|
139
141
|
const runIds = [];
|
|
140
142
|
const seen = new Set();
|
|
141
143
|
for (const marker of candidates) {
|
|
@@ -44,6 +44,8 @@ function sealPausedRun(asyncDir, status, resultsDir) {
|
|
|
44
44
|
sessionId: status.sessionId,
|
|
45
45
|
asyncDir,
|
|
46
46
|
...(status.completionOwnerId ? { completionOwnerId: status.completionOwnerId } : {}),
|
|
47
|
+
...(status.completionOwnerProcess ? { completionOwnerProcess: status.completionOwnerProcess } : {}),
|
|
48
|
+
...(status.parentWorkflowRunId ? { parentWorkflowRunId: status.parentWorkflowRunId } : {}),
|
|
47
49
|
...(status.toolCallId ? { toolCallId: status.toolCallId } : {}),
|
|
48
50
|
results: (status.steps ?? []).map((step) => ({
|
|
49
51
|
agent: step.agent,
|
|
@@ -146,7 +148,7 @@ export function stopAsyncRun(state, runId, kill, location, childId) {
|
|
|
146
148
|
if (!target)
|
|
147
149
|
return null;
|
|
148
150
|
const status = reconcileAsyncRun(target.asyncDir, { kill }).status;
|
|
149
|
-
// A
|
|
151
|
+
// A relaunched workflow stops the exact child run it re-attached from its relaunch chain, whichever session launched it.
|
|
150
152
|
if (state.currentSessionId && !ownsRun(state.currentSessionId, status?.sessionId) && location?.reattachedRunId !== target.asyncId) {
|
|
151
153
|
return {
|
|
152
154
|
content: [{ type: "text", text: `Async run '${target.asyncId}' was not found in the active session.` }],
|
|
@@ -24,7 +24,7 @@ import { deriveChildSessionName } from "../../shared/child-session-name.js";
|
|
|
24
24
|
import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
|
|
25
25
|
import { qualifyModelWithProvider, resolveEffectiveThinking } from "../../shared/model-info.js";
|
|
26
26
|
import { assertThinkingWithinCeiling, intersectThinkingCeilings } from "../../shared/thinking-ceiling.js";
|
|
27
|
-
import { formatStructuredOutputRejectionError, MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR } from "../shared/structured-output.js";
|
|
27
|
+
import { formatStructuredOutputRejectionError, MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, STRUCTURED_OUTPUT_FOLLOW_UP } from "../shared/structured-output.js";
|
|
28
28
|
import { formatMidToolExitError, isOrdinaryToolForMidToolExit } from "../shared/process-signal.js";
|
|
29
29
|
import { formatChildToolDiagnostic } from "../shared/tool-availability.js";
|
|
30
30
|
import { formatChildModelResolutionDiagnostic, formatInheritedProviderDiagnostic, isChildModelResolutionFailure } from "../shared/model-resolution-diagnostic.js";
|
|
@@ -45,6 +45,8 @@ import { acceptChildWatchdogEvent, applyChildWatchdogMessage, childWatchdogIsAct
|
|
|
45
45
|
import { buildInProcessChildLaunch, createReportedChildSessionInput } from "../shared/child-launch.js";
|
|
46
46
|
import { childSessionFactory, childSessionHasQueuedMessages, projectChildSessionEventForJson } from "../shared/child-session.js";
|
|
47
47
|
import { reconcileAttemptUsage } from "../shared/usage-reconciliation.js";
|
|
48
|
+
import { clearProviderRetry, observeProviderDiagnostics, snapshotProviderDiagnostics } from "../../shared/provider-diagnostics.js";
|
|
49
|
+
import { isRexLocalMachine } from "../shared/rex-machine.js";
|
|
48
50
|
const artifactOutputByResult = new WeakMap();
|
|
49
51
|
const acceptanceOutputByResult = new WeakMap();
|
|
50
52
|
function emptyUsage() {
|
|
@@ -165,6 +167,7 @@ function snapshotProgress(progress) {
|
|
|
165
167
|
...progress,
|
|
166
168
|
task: PROMPT_REDACTED,
|
|
167
169
|
skills: progress.skills ? [...progress.skills] : undefined,
|
|
170
|
+
providerDiagnostics: snapshotProviderDiagnostics(progress.providerDiagnostics),
|
|
168
171
|
recentTools: boundStreamedRecentTools(progress.recentTools),
|
|
169
172
|
recentOutput: boundStreamedRecentOutput(progress.recentOutput),
|
|
170
173
|
};
|
|
@@ -175,6 +178,7 @@ function snapshotResult(result, progress) {
|
|
|
175
178
|
task: PROMPT_REDACTED,
|
|
176
179
|
messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
|
|
177
180
|
usage: { ...result.usage },
|
|
181
|
+
providerDiagnostics: snapshotProviderDiagnostics(result.providerDiagnostics),
|
|
178
182
|
skills: result.skills ? [...result.skills] : undefined,
|
|
179
183
|
controlEvents: result.controlEvents ? result.controlEvents.map((event) => ({ ...event })) : undefined,
|
|
180
184
|
progress,
|
|
@@ -198,6 +202,7 @@ function snapshotStreamResult(result, progress) {
|
|
|
198
202
|
}
|
|
199
203
|
function captureStructuredDelegationProgressState(progress, result) {
|
|
200
204
|
return {
|
|
205
|
+
providerDiagnostics: progress.providerDiagnostics,
|
|
201
206
|
currentTool: progress.currentTool,
|
|
202
207
|
currentToolArgs: progress.currentToolArgs,
|
|
203
208
|
recentOutput: [...progress.recentOutput],
|
|
@@ -210,7 +215,9 @@ function captureStructuredDelegationProgressState(progress, result) {
|
|
|
210
215
|
};
|
|
211
216
|
}
|
|
212
217
|
function structuredDelegationProgressChanged(previous, progress, result) {
|
|
213
|
-
|
|
218
|
+
// Observations replace internal diagnostics; no transcript walk or serialization on heartbeats.
|
|
219
|
+
if (previous.providerDiagnostics !== progress.providerDiagnostics
|
|
220
|
+
|| previous.currentTool !== progress.currentTool
|
|
214
221
|
|| previous.currentToolArgs !== progress.currentToolArgs
|
|
215
222
|
|| previous.activityState !== progress.activityState
|
|
216
223
|
|| previous.model !== (progress.model ?? result.model)
|
|
@@ -352,6 +359,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
352
359
|
exitCode: 0,
|
|
353
360
|
outputState: "absent",
|
|
354
361
|
messages: [],
|
|
362
|
+
providerDiagnostics: shared.providerDiagnostics,
|
|
355
363
|
usage: emptyUsage(),
|
|
356
364
|
model: modelArg,
|
|
357
365
|
...(resolvedThinking ? { thinking: resolvedThinking } : {}),
|
|
@@ -383,6 +391,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
383
391
|
let cacheReadUsageComplete = true;
|
|
384
392
|
let cacheWriteUsageComplete = true;
|
|
385
393
|
const progress = {
|
|
394
|
+
providerDiagnostics: result.providerDiagnostics,
|
|
386
395
|
index: options.index ?? 0,
|
|
387
396
|
agent: agent.name,
|
|
388
397
|
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
@@ -433,7 +442,9 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
433
442
|
};
|
|
434
443
|
return result;
|
|
435
444
|
}
|
|
436
|
-
|
|
445
|
+
// A machine 'local' child edits this disk, so its local Git evidence is authoritative, as for an unplaced run.
|
|
446
|
+
const mutationCwd = isRexLocalMachine(options.machine) ? options.machine.cwd : options.cwd ?? runtimeCwd;
|
|
447
|
+
const mutationSnapshot = options.machine && !isRexLocalMachine(options.machine) ? { source: "tracked-files", trackedOnly: true, cwd: mutationCwd, dirtyFiles: [], fingerprints: {}, unavailable: "Local Git evidence is not authoritative for a pane-native remote run." } : snapshotTrackedMutations(mutationCwd);
|
|
437
448
|
let structuredOutputToolInvoked = false;
|
|
438
449
|
let structuredOutputMessageStartIndex;
|
|
439
450
|
let toolAvailabilityError;
|
|
@@ -972,6 +983,14 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
972
983
|
return;
|
|
973
984
|
}
|
|
974
985
|
const now = Date.now();
|
|
986
|
+
const providerDiagnostics = observeProviderDiagnostics(result.providerDiagnostics, evt, now);
|
|
987
|
+
if (providerDiagnostics !== result.providerDiagnostics) {
|
|
988
|
+
result.providerDiagnostics = providerDiagnostics;
|
|
989
|
+
progress.providerDiagnostics = providerDiagnostics;
|
|
990
|
+
// message_end already emits synchronously below; retry-only transitions need their own update.
|
|
991
|
+
if (evt.type !== "message_end")
|
|
992
|
+
fireUpdate();
|
|
993
|
+
}
|
|
975
994
|
progress.durationMs = now - startTime;
|
|
976
995
|
progress.lastActivityAt = now;
|
|
977
996
|
updateActivityState(now);
|
|
@@ -1243,7 +1262,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
1243
1262
|
toolAvailabilityError = toolDiagnosticError;
|
|
1244
1263
|
result.runtimeAcknowledgedExtensions = capture.runtimeAcknowledgedExtensions();
|
|
1245
1264
|
if (session?.machineEvidence)
|
|
1246
|
-
result.nativeMachine = { provider:
|
|
1265
|
+
result.nativeMachine = { provider: session.machineEvidence.provider, machineId: session.machineEvidence.machineId, ...(session.machineEvidence.initial ? { initialGit: session.machineEvidence.initial } : {}), ...(session.machineEvidence.final ? { finalGit: session.machineEvidence.final } : {}) };
|
|
1247
1266
|
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1248
1267
|
const promptErrorMessage = promptError === undefined ? undefined : promptError instanceof Error ? promptError.message : String(promptError);
|
|
1249
1268
|
if (promptError !== undefined)
|
|
@@ -1396,6 +1415,24 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
1396
1415
|
// A runtime replacement can stop a child that detached while it was still being created.
|
|
1397
1416
|
if (!hostStopReason)
|
|
1398
1417
|
await created.prompt(`Task: ${task}`);
|
|
1418
|
+
// A structured child that finished normally without calling structured_output gets one more turn (#2997).
|
|
1419
|
+
if (options.structuredOutput
|
|
1420
|
+
&& !structuredOutputToolInvoked
|
|
1421
|
+
&& !sessionSettled
|
|
1422
|
+
&& !lifecycleFinished
|
|
1423
|
+
&& !hostStopReason
|
|
1424
|
+
&& !created.shutDown
|
|
1425
|
+
&& !abortedBySignal
|
|
1426
|
+
&& !interruptedByControl
|
|
1427
|
+
&& !result.timedOut
|
|
1428
|
+
&& !forcedTermination
|
|
1429
|
+
&& !result.error
|
|
1430
|
+
&& !assistantError
|
|
1431
|
+
&& !capture.toolDiagnostic()
|
|
1432
|
+
&& !(progress.currentTool && isOrdinaryToolForMidToolExit(progress.currentTool))) {
|
|
1433
|
+
applyChildLifecycle("cancel-drain");
|
|
1434
|
+
await created.prompt(STRUCTURED_OUTPUT_FOLLOW_UP);
|
|
1435
|
+
}
|
|
1399
1436
|
settle(undefined);
|
|
1400
1437
|
}
|
|
1401
1438
|
catch (error) {
|
|
@@ -1404,6 +1441,8 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
1404
1441
|
})();
|
|
1405
1442
|
});
|
|
1406
1443
|
result.exitCode = exitCode;
|
|
1444
|
+
result.providerDiagnostics = clearProviderRetry(result.providerDiagnostics);
|
|
1445
|
+
progress.providerDiagnostics = result.providerDiagnostics;
|
|
1407
1446
|
if (afterCompactionSettlement) {
|
|
1408
1447
|
result[AFTER_COMPACTION_SETTLEMENT] = true;
|
|
1409
1448
|
}
|
|
@@ -1497,7 +1536,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
1497
1536
|
durationMs: progress.durationMs,
|
|
1498
1537
|
};
|
|
1499
1538
|
const remoteGitChanged = result.nativeMachine?.initialGit && result.nativeMachine.finalGit ? result.nativeMachine.initialGit.head !== result.nativeMachine.finalGit.head || result.nativeMachine.initialGit.dirty !== result.nativeMachine.finalGit.dirty : undefined;
|
|
1500
|
-
const mutationEvidence = result.nativeMachine ? { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot,
|
|
1539
|
+
const mutationEvidence = result.nativeMachine && !isRexLocalMachine(options.machine) ? { source: "tracked-files", trackedOnly: true, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot, mutationCwd);
|
|
1501
1540
|
const acceptanceOutput = getFinalOutput(result.messages ?? []);
|
|
1502
1541
|
const structuredText = result.structuredOutput === undefined ? undefined : JSON.stringify(result.structuredOutput, null, 2);
|
|
1503
1542
|
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
@@ -1855,6 +1894,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
1855
1894
|
orcaProgressTab,
|
|
1856
1895
|
launchWarnings,
|
|
1857
1896
|
verifyModel,
|
|
1897
|
+
providerDiagnostics: lastResult?.providerDiagnostics,
|
|
1858
1898
|
});
|
|
1859
1899
|
lastResult = attemptResult;
|
|
1860
1900
|
sumUsage(aggregateUsage, attemptResult.usage);
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import type { SubagentState } from "../../shared/types.ts";
|
|
1
|
+
import type { ForegroundResumeChild, ForegroundResumeRun, SubagentState } from "../../shared/types.ts";
|
|
2
2
|
export declare const MAX_REMEMBERED_FOREGROUND_RUNS = 50;
|
|
3
|
-
export declare
|
|
3
|
+
export declare const FOREGROUND_OWNER_EXITED_STOP_ERROR = "Subagent stopped because the Pi process that ran it exited before the run finished.";
|
|
4
|
+
/**
|
|
5
|
+
* Saves one foreground run to its own history file. A run with a `running` child is a launch-time
|
|
6
|
+
* record; the run's final save replaces it.
|
|
7
|
+
*/
|
|
8
|
+
export declare function persistForegroundRun(run: ForegroundResumeRun, options?: {
|
|
4
9
|
resultsDir?: string;
|
|
5
10
|
limit?: number;
|
|
6
11
|
}): void;
|
|
12
|
+
/** Drops this process's `running` record for a launch that threw, so it never loads as interrupted. */
|
|
13
|
+
export declare function discardRunningForegroundRun(runId: string, options?: {
|
|
14
|
+
resultsDir?: string;
|
|
15
|
+
}): void;
|
|
16
|
+
/** For a record with a `running` child: its owning Pi process is gone. */
|
|
17
|
+
export declare function ownerConfirmedDead(run: ForegroundResumeRun): boolean;
|
|
18
|
+
/** State text for a remembered child; a child cut off by a Pi exit names the resume target. */
|
|
19
|
+
export declare function foregroundChildStateLabel(run: Pick<ForegroundResumeRun, "runId">, child: Pick<ForegroundResumeChild, "status" | "error" | "sessionFile">): string;
|
|
7
20
|
export declare function restoreForegroundRunHistory(state: SubagentState, options?: {
|
|
8
21
|
resultsDir?: string;
|
|
9
22
|
sessionId?: string | null;
|
|
10
23
|
limit?: number;
|
|
24
|
+
observeForeign?: (run: ForegroundResumeRun) => void;
|
|
11
25
|
}): number;
|