pi-subagents 0.62.0 → 0.64.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 +52 -1
- package/docs/agents.md +5 -4
- package/docs/configuration.md +28 -2
- package/docs/models.md +5 -5
- package/docs/observability.md +4 -1
- package/docs/tool-reference.md +2 -2
- package/docs/watchdog.md +92 -114
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +4 -3
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
- package/src/agents/agent-management.ts +22 -4
- package/src/agents/agents.ts +107 -125
- package/src/api/shared-types.ts +3 -0
- package/src/extension/config.ts +20 -0
- package/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/src/runs/background/active-async-capacity.ts +0 -1
- package/src/runs/background/async-execution.ts +56 -7
- package/src/runs/background/async-resume.ts +3 -0
- package/src/runs/background/async-status.ts +18 -2
- package/src/runs/background/notify.ts +54 -3
- package/src/runs/background/run-status.ts +22 -2
- package/src/runs/background/subagent-runner.ts +53 -3
- package/src/runs/background/wait-completions.ts +13 -0
- package/src/runs/foreground/execution.ts +7 -1
- package/src/runs/foreground/subagent-executor.ts +56 -4
- package/src/runs/shared/acceptance.ts +25 -9
- package/src/runs/shared/async-status-projection.ts +11 -43
- package/src/runs/shared/lane-metadata.ts +24 -3
- package/src/runs/shared/parallel-handoff.ts +4 -0
- package/src/runs/shared/pi-args.ts +9 -6
- package/src/runs/shared/subagent-control.ts +4 -2
- package/src/runs/shared/task-intent.ts +5 -2
- package/src/runs/shared/worktree.ts +467 -63
- package/src/shared/types.ts +38 -2
- package/src/shared/utils.ts +18 -7
- package/src/slash/subagents-admin.ts +24 -12
- package/src/tui/fleet-status.ts +61 -2
- package/src/tui/fleet.ts +12 -7
- package/src/tui/render.ts +227 -14
- package/src/watchdog/child-status.ts +54 -33
- package/src/watchdog/diff-tool.ts +77 -0
- package/src/watchdog/emission-guard.ts +5 -3
- package/src/watchdog/guidance.ts +20 -0
- package/src/watchdog/register-child.ts +16 -13
- package/src/watchdog/register-main.ts +10 -9
- package/src/watchdog/render.ts +4 -5
- package/src/watchdog/review.ts +15 -4
- package/src/watchdog/rules.ts +70 -0
- package/src/watchdog/runtime.ts +75 -92
- package/src/watchdog/scope.ts +0 -11
- package/src/watchdog/settings.ts +48 -104
- package/src/watchdog/types.ts +18 -32
- package/src/watchdog/warning-format.ts +0 -1
- package/src/workflows/chat-progress.ts +3 -2
- package/src/workflows/workflow-checklist.ts +439 -0
- package/src/workflows/workflow-preflight.ts +28 -1
|
@@ -16,7 +16,8 @@ import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
|
16
16
|
import { planChildLaunch, resolveStepBehavior, suppressProgressForReadOnlyTask, type ResolvedStepBehavior } from "../shared/child-launch-plan.ts";
|
|
17
17
|
import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
|
|
18
18
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
19
|
-
import {
|
|
19
|
+
import { applyWatchdogLaunchRules, sendRuleViolationWarning } from "../../watchdog/rules.ts";
|
|
20
|
+
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveExistingReadInstructionPaths, resolveExistingReadPaths, writeInitialProgressFile, type ChainStep, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
20
21
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
21
22
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
22
23
|
import { resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
@@ -31,7 +32,7 @@ import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout
|
|
|
31
32
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
32
33
|
import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
33
34
|
import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
|
|
34
|
-
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
35
|
+
import { resolveExpectedWorktreeAgentCwd, resolveWorktreeProvider, shouldDeferWorktreeCwd, WORKTREE_AGENT_CWD_PLACEHOLDER } from "../shared/worktree.ts";
|
|
35
36
|
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
36
37
|
import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
37
38
|
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
@@ -178,6 +179,8 @@ interface AsyncChainParams {
|
|
|
178
179
|
worktreeSetupHook?: string;
|
|
179
180
|
worktreeSetupHookTimeoutMs?: number;
|
|
180
181
|
worktreeBaseDir?: string;
|
|
182
|
+
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
183
|
+
worktreeBranchPrefix?: string;
|
|
181
184
|
controlConfig?: ResolvedControlConfig;
|
|
182
185
|
controlIntercomTarget?: string;
|
|
183
186
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
@@ -245,6 +248,8 @@ interface AsyncSingleParams {
|
|
|
245
248
|
worktreeSetupHook?: string;
|
|
246
249
|
worktreeSetupHookTimeoutMs?: number;
|
|
247
250
|
worktreeBaseDir?: string;
|
|
251
|
+
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
252
|
+
worktreeBranchPrefix?: string;
|
|
248
253
|
worktree?: boolean;
|
|
249
254
|
controlConfig?: ResolvedControlConfig;
|
|
250
255
|
intercomBridge?: IntercomBridgeConfig;
|
|
@@ -310,6 +315,8 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
310
315
|
waitToolEnabled?: boolean;
|
|
311
316
|
waitToolDefaultTimeoutMs?: number;
|
|
312
317
|
worktreeBaseDir?: string;
|
|
318
|
+
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
319
|
+
worktreeBranchPrefix?: string;
|
|
313
320
|
asyncDir: string;
|
|
314
321
|
outputBaseDir?: string;
|
|
315
322
|
validateOutputBindings?: boolean;
|
|
@@ -740,6 +747,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
740
747
|
thinkingOverridesByFlatIndex,
|
|
741
748
|
maxSubagentDepth,
|
|
742
749
|
worktreeBaseDir,
|
|
750
|
+
worktreeProvider,
|
|
751
|
+
worktreeBranchPrefix,
|
|
743
752
|
asyncDir,
|
|
744
753
|
} = params;
|
|
745
754
|
const outputBaseDir = params.outputBaseDir;
|
|
@@ -747,6 +756,15 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
747
756
|
const chainSkills = params.chainSkills ?? [];
|
|
748
757
|
const availableModels = params.availableModels;
|
|
749
758
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
759
|
+
let managedWorktreeProvider: "native" | "worktrunk" | undefined;
|
|
760
|
+
try {
|
|
761
|
+
if (chain.some((step) => "worktree" in step && step.worktree === true)) {
|
|
762
|
+
const resolved = resolveWorktreeProvider(worktreeProvider, worktreeBaseDir);
|
|
763
|
+
managedWorktreeProvider = shouldDeferWorktreeCwd(worktreeProvider, worktreeBaseDir) ? "worktrunk" : resolved;
|
|
764
|
+
}
|
|
765
|
+
} catch (error) {
|
|
766
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
767
|
+
}
|
|
750
768
|
const progressDir = params.progressDir ?? runnerCwd;
|
|
751
769
|
const graphChain: ChainStep[] = params.attachRoot
|
|
752
770
|
? [{
|
|
@@ -873,7 +891,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
873
891
|
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
874
892
|
let taskTemplate = s.task ?? "{previous}";
|
|
875
893
|
taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
|
|
876
|
-
taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
|
|
894
|
+
taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, behaviorCwd ?? runnerCwd);
|
|
877
895
|
const taskText = `${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`;
|
|
878
896
|
const task = namespaceOutputPath ? taskText : injectSingleOutputInstruction(taskText, outputPath, a);
|
|
879
897
|
|
|
@@ -922,6 +940,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
922
940
|
throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
|
|
923
941
|
}
|
|
924
942
|
}
|
|
943
|
+
const launchRuleError = applyWatchdogLaunchRules({ cwd: stepCwd, agent: a.name, model: modelCandidates[0] ?? model, warn: (violation) => sendRuleViolationWarning(ctx.pi, violation) });
|
|
944
|
+
if (launchRuleError) throw new AsyncStartValidationError(launchRuleError);
|
|
925
945
|
const fast = s.fast ?? params.fast ?? a.fast;
|
|
926
946
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
927
947
|
tools: a.tools,
|
|
@@ -1058,7 +1078,9 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1058
1078
|
return {
|
|
1059
1079
|
parallel: s.parallel.map((t, taskIndex) => {
|
|
1060
1080
|
let behaviorCwd: string | undefined;
|
|
1061
|
-
if (s.worktree) {
|
|
1081
|
+
if (s.worktree && managedWorktreeProvider === "worktrunk") {
|
|
1082
|
+
behaviorCwd = WORKTREE_AGENT_CWD_PLACEHOLDER;
|
|
1083
|
+
} else if (s.worktree && managedWorktreeProvider === "native") {
|
|
1062
1084
|
try {
|
|
1063
1085
|
behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, taskIndex, worktreeBaseDir);
|
|
1064
1086
|
} catch {
|
|
@@ -1113,7 +1135,9 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1113
1135
|
}
|
|
1114
1136
|
const sequential = s as SequentialStep;
|
|
1115
1137
|
let behaviorCwd: string | undefined;
|
|
1116
|
-
if (sequential.worktree) {
|
|
1138
|
+
if (sequential.worktree && managedWorktreeProvider === "worktrunk") {
|
|
1139
|
+
behaviorCwd = WORKTREE_AGENT_CWD_PLACEHOLDER;
|
|
1140
|
+
} else if (sequential.worktree && managedWorktreeProvider === "native") {
|
|
1117
1141
|
try {
|
|
1118
1142
|
behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, 0, worktreeBaseDir);
|
|
1119
1143
|
} catch {
|
|
@@ -1183,6 +1207,8 @@ export function executeAsyncChain(
|
|
|
1183
1207
|
worktreeSetupHook,
|
|
1184
1208
|
worktreeSetupHookTimeoutMs,
|
|
1185
1209
|
worktreeBaseDir,
|
|
1210
|
+
worktreeProvider,
|
|
1211
|
+
worktreeBranchPrefix,
|
|
1186
1212
|
controlConfig,
|
|
1187
1213
|
controlIntercomTarget,
|
|
1188
1214
|
childIntercomTarget,
|
|
@@ -1239,6 +1265,8 @@ export function executeAsyncChain(
|
|
|
1239
1265
|
waitToolEnabled: params.waitToolEnabled,
|
|
1240
1266
|
waitToolDefaultTimeoutMs: params.waitToolDefaultTimeoutMs,
|
|
1241
1267
|
worktreeBaseDir,
|
|
1268
|
+
worktreeProvider,
|
|
1269
|
+
worktreeBranchPrefix,
|
|
1242
1270
|
asyncDir,
|
|
1243
1271
|
fast: params.fast,
|
|
1244
1272
|
toolBudget: params.toolBudget,
|
|
@@ -1314,6 +1342,8 @@ export function executeAsyncChain(
|
|
|
1314
1342
|
worktreeSetupHook,
|
|
1315
1343
|
worktreeSetupHookTimeoutMs,
|
|
1316
1344
|
worktreeBaseDir,
|
|
1345
|
+
worktreeProvider,
|
|
1346
|
+
worktreeBranchPrefix,
|
|
1317
1347
|
controlConfig,
|
|
1318
1348
|
toolBudget: params.toolBudget,
|
|
1319
1349
|
usageBudget: params.usageBudget,
|
|
@@ -1511,6 +1541,8 @@ export function executeAsyncSingle(
|
|
|
1511
1541
|
worktreeSetupHook,
|
|
1512
1542
|
worktreeSetupHookTimeoutMs,
|
|
1513
1543
|
worktreeBaseDir,
|
|
1544
|
+
worktreeProvider,
|
|
1545
|
+
worktreeBranchPrefix,
|
|
1514
1546
|
controlConfig,
|
|
1515
1547
|
controlIntercomTarget,
|
|
1516
1548
|
childIntercomTarget,
|
|
@@ -1556,7 +1588,18 @@ export function executeAsyncSingle(
|
|
|
1556
1588
|
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1557
1589
|
}
|
|
1558
1590
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
1559
|
-
|
|
1591
|
+
let managedWorktreeProvider: "native" | "worktrunk" | undefined;
|
|
1592
|
+
if (params.worktree === true) {
|
|
1593
|
+
try {
|
|
1594
|
+
const resolved = resolveWorktreeProvider(params.worktreeProvider, worktreeBaseDir);
|
|
1595
|
+
managedWorktreeProvider = shouldDeferWorktreeCwd(params.worktreeProvider, worktreeBaseDir) ? "worktrunk" : resolved;
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
const instructionCwd = params.worktree === true && managedWorktreeProvider === "worktrunk"
|
|
1601
|
+
? WORKTREE_AGENT_CWD_PLACEHOLDER
|
|
1602
|
+
: params.worktree === true && managedWorktreeProvider === "native"
|
|
1560
1603
|
? resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s0`, 0, worktreeBaseDir)
|
|
1561
1604
|
: runnerCwd;
|
|
1562
1605
|
const readExistenceCwd = params.worktree === true ? runnerCwd : instructionCwd;
|
|
@@ -1610,7 +1653,11 @@ export function executeAsyncSingle(
|
|
|
1610
1653
|
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
1611
1654
|
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
1612
1655
|
const reads = params.reads !== undefined ? params.reads : agentConfig.defaultReads ?? false;
|
|
1613
|
-
const readPaths = Array.isArray(reads)
|
|
1656
|
+
const readPaths = Array.isArray(reads)
|
|
1657
|
+
? managedWorktreeProvider === "worktrunk"
|
|
1658
|
+
? resolveExistingReadInstructionPaths(reads, instructionCwd, readExistenceCwd)
|
|
1659
|
+
: resolveExistingReadPaths(reads, readExistenceCwd)
|
|
1660
|
+
: [];
|
|
1614
1661
|
const readsInstruction = readPaths.length > 0
|
|
1615
1662
|
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
1616
1663
|
: "";
|
|
@@ -1901,6 +1948,8 @@ export function executeAsyncSingle(
|
|
|
1901
1948
|
worktreeSetupHook,
|
|
1902
1949
|
worktreeSetupHookTimeoutMs,
|
|
1903
1950
|
worktreeBaseDir,
|
|
1951
|
+
worktreeProvider,
|
|
1952
|
+
worktreeBranchPrefix,
|
|
1904
1953
|
controlConfig,
|
|
1905
1954
|
timeoutMs,
|
|
1906
1955
|
deadlineAt,
|
|
@@ -45,6 +45,8 @@ export type AsyncResumeTarget = {
|
|
|
45
45
|
sessionName?: string;
|
|
46
46
|
index: number;
|
|
47
47
|
cwd?: string;
|
|
48
|
+
/** True when cwd is the retained managed worktree recorded by the handoff. */
|
|
49
|
+
managedWorktree?: boolean;
|
|
48
50
|
sessionFile?: string;
|
|
49
51
|
model?: string;
|
|
50
52
|
thinking?: string;
|
|
@@ -569,6 +571,7 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
569
571
|
...(statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName ? { sessionName: statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName } : {}),
|
|
570
572
|
index,
|
|
571
573
|
...(resumeCwd ? { cwd: resumeCwd } : {}),
|
|
574
|
+
...(managedWorktreeCwd ? { managedWorktree: true } : {}),
|
|
572
575
|
...(resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {}),
|
|
573
576
|
...(stepModel ? { model: stepModel } : {}),
|
|
574
577
|
...(stepThinking ? { thinking: stepThinking } : {}),
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
|
|
4
4
|
import { previewDisplayText } from "../../shared/display-text.ts";
|
|
5
5
|
import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
|
|
6
|
-
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type HostStepNodeV1, type HostStepState, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TimeoutRecoveryProjection, type TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorkflowPreflightV1, type WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
6
|
+
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type HostStepNodeV1, type HostStepState, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TimeoutRecoveryProjection, type TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorktreeNaming, type WorkflowPreflightV1, type WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
7
7
|
import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../shared/capability-ceiling.ts";
|
|
8
8
|
import { readStatus } from "../../shared/utils.ts";
|
|
9
9
|
import { attachRootChildrenToSteps, buildNestedRouteIndex, findNestedRouteForRootId, type NestedRoute, projectNestedEvents } from "../shared/nested-events.ts";
|
|
@@ -24,6 +24,7 @@ import { validateAsyncStatusLaneMetadata } from "../shared/lane-metadata.ts";
|
|
|
24
24
|
import { formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary } from "../../workflows/workflow-preflight.ts";
|
|
25
25
|
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
26
26
|
import { formatTimeoutRecoveryLines, projectTimeoutRecovery } from "../shared/mutation-evidence.ts";
|
|
27
|
+
import { formatWorkflowChecklistText, projectWorkflowChecklist } from "../../workflows/workflow-checklist.ts";
|
|
27
28
|
|
|
28
29
|
interface AsyncRunStepSummary {
|
|
29
30
|
index: number;
|
|
@@ -39,6 +40,8 @@ interface AsyncRunStepSummary {
|
|
|
39
40
|
lane?: AsyncJobStep["lane"];
|
|
40
41
|
worktreePath?: string;
|
|
41
42
|
branch?: string;
|
|
43
|
+
provider?: "native" | "worktrunk";
|
|
44
|
+
naming?: WorktreeNaming;
|
|
42
45
|
runId?: string;
|
|
43
46
|
outputName?: string;
|
|
44
47
|
structured?: boolean;
|
|
@@ -337,6 +340,8 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
337
340
|
...(step.lane ? { lane: step.lane } : {}),
|
|
338
341
|
...(step.worktreePath ? { worktreePath: step.worktreePath } : {}),
|
|
339
342
|
...(step.branch ? { branch: step.branch } : {}),
|
|
343
|
+
...(step.provider ? { provider: step.provider } : {}),
|
|
344
|
+
...(step.naming ? { naming: step.naming } : {}),
|
|
340
345
|
...(step.runId ? { runId: step.runId } : {}),
|
|
341
346
|
...(step.outputName ? { outputName: step.outputName } : {}),
|
|
342
347
|
...(step.structured ? { structured: step.structured } : {}),
|
|
@@ -614,7 +619,7 @@ function formatStepLine(step: AsyncRunStepSummary): string {
|
|
|
614
619
|
if (step.durationMs !== undefined) parts.push(formatDuration(step.durationMs));
|
|
615
620
|
if (step.tokens) parts.push(`${formatTokens(step.tokens.total)} tok`);
|
|
616
621
|
if (step.lane) parts.push(`lane ${step.lane.key}`);
|
|
617
|
-
if (step.worktreePath) parts.push(`worktree ${shortenPath(step.worktreePath)} · branch ${step.branch ?? "unknown"}`);
|
|
622
|
+
if (step.worktreePath) parts.push(`worktree ${shortenPath(step.worktreePath)} · branch ${step.branch ?? "unknown"}${step.provider ? ` · provider ${step.provider}` : ""}`);
|
|
618
623
|
return parts.join(" | ");
|
|
619
624
|
}
|
|
620
625
|
|
|
@@ -707,6 +712,17 @@ export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active as
|
|
|
707
712
|
if (run.preflight) lines.push(formatWorkflowPreflightPlanSummary(run.preflight, { indent: " " }));
|
|
708
713
|
const preflightWarning = formatWorkflowPreflightWarningSummary(run.workflow?.preflightWarnings, { indent: " " });
|
|
709
714
|
if (preflightWarning) lines.push(preflightWarning);
|
|
715
|
+
if (run.mode === "workflow") {
|
|
716
|
+
const checklist = projectWorkflowChecklist({
|
|
717
|
+
graph: run.workflowGraph,
|
|
718
|
+
steps: run.steps,
|
|
719
|
+
hostSteps: run.hostSteps,
|
|
720
|
+
preflight: run.preflight,
|
|
721
|
+
trace: run.workflow?.trace,
|
|
722
|
+
now: run.lastUpdate ?? run.endedAt ?? Date.now(),
|
|
723
|
+
});
|
|
724
|
+
lines.push(...formatWorkflowChecklistText(checklist, " ", { includeItems: false }));
|
|
725
|
+
}
|
|
710
726
|
for (const step of run.steps) {
|
|
711
727
|
lines.push(` ${formatStepLine(step)}`);
|
|
712
728
|
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
createCompletionBatcher,
|
|
15
15
|
resolveCompletionBatchConfig,
|
|
16
16
|
} from "./completion-batcher.ts";
|
|
17
|
-
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
|
|
17
|
+
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ChildWatchdogProgress, type ChildWatchdogWarningSummary, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
|
|
18
18
|
import { safeTerminalText } from "../../shared/display-text.ts";
|
|
19
19
|
import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
|
|
20
20
|
import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
@@ -31,6 +31,8 @@ export interface SubagentNotifyChildOutput {
|
|
|
31
31
|
previewUnavailableReason?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export type SubagentNotifyWatchdogBlocker = Pick<ChildWatchdogWarningSummary, "summary" | "addressed" | "stalemate"> & { agent: string };
|
|
35
|
+
|
|
34
36
|
export interface SubagentNotifyDetails {
|
|
35
37
|
agent: string;
|
|
36
38
|
status: "completed" | "failed" | "paused" | "stopped";
|
|
@@ -47,6 +49,7 @@ export interface SubagentNotifyDetails {
|
|
|
47
49
|
handoffPath?: string;
|
|
48
50
|
/** Present when a durable schedule launched the run. */
|
|
49
51
|
scheduleOrigin?: ScheduleOrigin;
|
|
52
|
+
watchdogBlockers?: SubagentNotifyWatchdogBlocker[];
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export interface CompletionNotification {
|
|
@@ -85,7 +88,9 @@ export interface CompletionNotification {
|
|
|
85
88
|
timedOut?: boolean;
|
|
86
89
|
stopped?: boolean;
|
|
87
90
|
turnBudgetExceeded?: boolean;
|
|
91
|
+
watchdog?: ChildWatchdogProgress;
|
|
88
92
|
}>;
|
|
93
|
+
watchdog?: ChildWatchdogProgress;
|
|
89
94
|
timestamp?: number;
|
|
90
95
|
durationMs?: number;
|
|
91
96
|
cwd?: string;
|
|
@@ -249,9 +254,28 @@ function formatCorrelationLines(details: SubagentNotifyDetails): string[] {
|
|
|
249
254
|
].filter((line): line is string => line !== undefined);
|
|
250
255
|
}
|
|
251
256
|
|
|
257
|
+
const WATCHDOG_BLOCKERS_HEADING = "Watchdog blockers:";
|
|
258
|
+
|
|
259
|
+
function formatWatchdogBlockerLines(details: SubagentNotifyDetails): string[] {
|
|
260
|
+
if (!details.watchdogBlockers?.length) return [];
|
|
261
|
+
return [WATCHDOG_BLOCKERS_HEADING, ...details.watchdogBlockers.map((blocker) => `- ${blocker.agent}: ${blocker.summary} (${blocker.stalemate ? "stalemate" : blocker.addressed ? "addressed" : "unaddressed"})`)];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// A stalemate blocker parses back as unaddressed; acceptance treats both as unresolved.
|
|
265
|
+
function parseWatchdogBlockerLines(lines: string[]): SubagentNotifyWatchdogBlocker[] {
|
|
266
|
+
const blockers: SubagentNotifyWatchdogBlocker[] = [];
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
const match = line.match(/^- (.+?): (.+) \((addressed|unaddressed|stalemate)\)$/);
|
|
269
|
+
if (!match) break;
|
|
270
|
+
blockers.push({ agent: match[1]!, summary: match[2]!, addressed: match[3] === "addressed", stalemate: match[3] === "stalemate" });
|
|
271
|
+
}
|
|
272
|
+
return blockers;
|
|
273
|
+
}
|
|
274
|
+
|
|
252
275
|
export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
253
276
|
const sessionLine = formatSessionLine(details);
|
|
254
277
|
const correlationLines = formatCorrelationLines(details);
|
|
278
|
+
const watchdogLines = formatWatchdogBlockerLines(details);
|
|
255
279
|
const taskKind = details.source === "foreground" ? "Detached foreground task" : "Background task";
|
|
256
280
|
const scheduleLine = details.scheduleOrigin
|
|
257
281
|
? `Scheduled run from **${details.scheduleOrigin.name ?? details.scheduleOrigin.id}** (schedule ${details.scheduleOrigin.id}).`
|
|
@@ -262,6 +286,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
|
262
286
|
scheduleLine,
|
|
263
287
|
scheduleLine ? "" : undefined,
|
|
264
288
|
formatResultPreview(details),
|
|
289
|
+
...(watchdogLines.length ? ["", ...watchdogLines] : []),
|
|
265
290
|
details.handoffPath ? "" : undefined,
|
|
266
291
|
details.handoffPath ? `Parallel handoff: ${details.handoffPath}` : undefined,
|
|
267
292
|
correlationLines.length && !details.handoffPath ? "" : undefined,
|
|
@@ -300,7 +325,9 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
300
325
|
const workflowRunIndex = body.findIndex((line) => line.startsWith("Workflow run: "));
|
|
301
326
|
const childRunsIndex = body.findIndex((line) => line.startsWith("Child runs: "));
|
|
302
327
|
const reconciledIndex = body.findIndex((line) => line.startsWith("Reconciled detached child: "));
|
|
303
|
-
const
|
|
328
|
+
const watchdogIndex = body.findIndex((line) => line === WATCHDOG_BLOCKERS_HEADING);
|
|
329
|
+
const watchdogBlockers = watchdogIndex >= 0 ? parseWatchdogBlockerLines(body.slice(watchdogIndex + 1)) : [];
|
|
330
|
+
const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex, watchdogIndex].filter((index) => index >= 0);
|
|
304
331
|
const firstMetadataIndex = metadataIndexes.length ? Math.min(...metadataIndexes) : body.length;
|
|
305
332
|
const resultEnd = firstMetadataIndex > 0 && body[firstMetadataIndex - 1]?.trim() === "" ? firstMetadataIndex - 1 : firstMetadataIndex;
|
|
306
333
|
const resultPreview = body.slice(0, resultEnd).join("\n").trim() || "(no output)";
|
|
@@ -336,6 +363,7 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
336
363
|
...(workflowRunId ? { workflowRunId } : {}),
|
|
337
364
|
...(childRuns?.length ? { childRuns } : {}),
|
|
338
365
|
...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
|
|
366
|
+
...(watchdogBlockers.length ? { watchdogBlockers } : {}),
|
|
339
367
|
...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
|
|
340
368
|
};
|
|
341
369
|
}
|
|
@@ -349,6 +377,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
|
|
|
349
377
|
const sessionLine = formatSessionLine(detail);
|
|
350
378
|
blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
|
|
351
379
|
blocks.push(formatResultPreview(detail));
|
|
380
|
+
blocks.push(...formatWatchdogBlockerLines(detail));
|
|
352
381
|
if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
|
|
353
382
|
blocks.push(...formatCorrelationLines(detail));
|
|
354
383
|
if (sessionLine) blocks.push(sessionLine);
|
|
@@ -420,6 +449,18 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
420
449
|
const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
|
|
421
450
|
const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
|
|
422
451
|
const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
|
|
452
|
+
const directChild = !workflowRunId && result.results?.length === 1 ? result.results[0]! : undefined;
|
|
453
|
+
const directStructuredPreview = directChild
|
|
454
|
+
? childInlinePreview(directChild).preview
|
|
455
|
+
: undefined;
|
|
456
|
+
const directSummary = summary.trim();
|
|
457
|
+
const directAgent = typeof directChild?.agent === "string" ? directChild.agent : agent;
|
|
458
|
+
const directNoOutputSummary = directChild && (!directSummary
|
|
459
|
+
|| directSummary === "(no output)"
|
|
460
|
+
|| (directAgent && directSummary === `${directAgent}:\n(no output)`));
|
|
461
|
+
const resultPreview = directStructuredPreview && directNoOutputSummary
|
|
462
|
+
? `Structured output:\n${directStructuredPreview}`
|
|
463
|
+
: summary;
|
|
423
464
|
const childRuns = result.results?.flatMap((child) => {
|
|
424
465
|
const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
|
|
425
466
|
const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
|
|
@@ -450,6 +491,15 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
450
491
|
})
|
|
451
492
|
: undefined;
|
|
452
493
|
const reconciledFromDetachedChild = typeof result.reconciledFromDetachedChild === "string" ? result.reconciledFromDetachedChild : undefined;
|
|
494
|
+
const watchdogBlockers: SubagentNotifyWatchdogBlocker[] = [];
|
|
495
|
+
const collectWatchdogBlockers = (owner: string, progress: ChildWatchdogProgress | undefined) => {
|
|
496
|
+
for (const warning of progress?.warnings ?? []) {
|
|
497
|
+
if (warning.severity !== "blocker") continue;
|
|
498
|
+
watchdogBlockers.push({ agent: owner, summary: warning.summary, addressed: warning.addressed, stalemate: warning.stalemate });
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
collectWatchdogBlockers(agent, result.watchdog);
|
|
502
|
+
for (const child of result.results ?? []) collectWatchdogBlockers(typeof child.agent === "string" ? child.agent : agent, child.watchdog);
|
|
453
503
|
const session =
|
|
454
504
|
result.shareUrl
|
|
455
505
|
? { label: "Session", value: result.shareUrl }
|
|
@@ -468,13 +518,14 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
468
518
|
...(scheduleOrigin ? { scheduleOrigin } : {}),
|
|
469
519
|
...(result.source ? { source: result.source } : {}),
|
|
470
520
|
...(taskInfo ? { taskInfo } : {}),
|
|
471
|
-
resultPreview
|
|
521
|
+
resultPreview,
|
|
472
522
|
...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
|
|
473
523
|
...(handoffPath ? { handoffPath } : {}),
|
|
474
524
|
...(workflowRunId ? { workflowRunId } : {}),
|
|
475
525
|
...(childRuns.length ? { childRuns } : {}),
|
|
476
526
|
...(childOutputs?.length ? { childOutputs } : {}),
|
|
477
527
|
...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
|
|
528
|
+
...(watchdogBlockers.length ? { watchdogBlockers } : {}),
|
|
478
529
|
...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
|
|
479
530
|
};
|
|
480
531
|
}
|
|
@@ -28,6 +28,8 @@ import { formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetD
|
|
|
28
28
|
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
29
29
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
30
30
|
import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.ts";
|
|
31
|
+
import { formatWorkflowChecklistText, projectWorkflowChecklist } from "../../workflows/workflow-checklist.ts";
|
|
32
|
+
import { validHostStepNodes } from "../shared/host-step-status.ts";
|
|
31
33
|
|
|
32
34
|
interface RunStatusParams {
|
|
33
35
|
action?: string;
|
|
@@ -74,7 +76,7 @@ function formatWorkflowDebug(status: AsyncStatus): string[] {
|
|
|
74
76
|
status.lane ? `Lane: ${status.lane.key}${status.lane.mode ? ` (${status.lane.mode})` : ""}` : undefined,
|
|
75
77
|
].filter((line): line is string => line !== undefined);
|
|
76
78
|
for (const [index, step] of (status.steps ?? []).entries()) {
|
|
77
|
-
lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}` : ""}`);
|
|
79
|
+
lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}${step.provider ? ` · provider ${step.provider}` : ""}` : ""}`);
|
|
78
80
|
}
|
|
79
81
|
return lines;
|
|
80
82
|
}
|
|
@@ -522,6 +524,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
522
524
|
status.mode === "workflow" && workflowReturnPreview !== undefined ? `Return: ${workflowReturnPreview}` : undefined,
|
|
523
525
|
status.mode === "workflow" && workflowEmitPreview !== undefined ? `Latest emit: ${workflowEmitPreview}` : undefined,
|
|
524
526
|
`Progress: ${progressLabel}`,
|
|
527
|
+
...(status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
|
|
528
|
+
graph: status.workflowGraph,
|
|
529
|
+
steps: status.steps,
|
|
530
|
+
hostSteps: validHostStepNodes(status.workflowGraph),
|
|
531
|
+
preflight: status.preflight,
|
|
532
|
+
trace: status.workflow?.trace,
|
|
533
|
+
now: status.lastUpdate ?? status.endedAt ?? Date.now(),
|
|
534
|
+
}), "", { includeItems: false }) : []),
|
|
525
535
|
status.pendingAppends ? `Pending appends: ${status.pendingAppends}` : undefined,
|
|
526
536
|
`Started: ${started}`,
|
|
527
537
|
`Updated: ${updated}`,
|
|
@@ -551,6 +561,9 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
551
561
|
const display = runStatusStepDisplayName(step);
|
|
552
562
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
553
563
|
lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${steeringSuffix}${acceptanceText}${budgetText}${errorText}`);
|
|
564
|
+
const structuredOutputPreview = step.structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(step.structuredOutput, 4_000);
|
|
565
|
+
if (structuredOutputPreview !== undefined) lines.push(` Structured output: ${structuredOutputPreview}`);
|
|
566
|
+
if (step.structuredOutputPath) lines.push(` Structured output path: ${step.structuredOutputPath}`);
|
|
554
567
|
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
555
568
|
if (step.runner?.type === "external-cli") {
|
|
556
569
|
const runner = normalizeExternalCliRunnerStatus(step.runner);
|
|
@@ -670,7 +683,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
670
683
|
if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);
|
|
671
684
|
const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
|
|
672
685
|
lines.push(...formatTimeoutRecoveryLines(data.timeoutRecovery, " "));
|
|
673
|
-
for (const child of children
|
|
686
|
+
for (const [index, child] of children.entries()) {
|
|
687
|
+
const structuredOutput = (child as { structuredOutput?: unknown }).structuredOutput;
|
|
688
|
+
const structuredOutputPreview = structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(structuredOutput, 4_000);
|
|
689
|
+
if (structuredOutputPreview !== undefined) lines.push(` Structured output${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPreview}`);
|
|
690
|
+
const structuredOutputPath = (child as { structuredOutputPath?: unknown }).structuredOutputPath;
|
|
691
|
+
if (typeof structuredOutputPath === "string" && structuredOutputPath.trim()) lines.push(` Structured output path${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPath}`);
|
|
692
|
+
lines.push(...formatTimeoutRecoveryLines(child.timeoutRecovery, " "));
|
|
693
|
+
}
|
|
674
694
|
lines.push(formatResumeGuidance(runId, children, data.sessionFile, { stopped: status === "stopped" }));
|
|
675
695
|
if (data.summary) lines.push("", data.summary);
|
|
676
696
|
const workflowChildren = parseWorkflowChildSummary((data as unknown as Record<string, unknown>).workflowChildren);
|