pi-subagents 0.50.0 → 0.52.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 +103 -0
- package/agents/oracle.md +3 -1
- package/agents/reviewer.md +1 -0
- package/agents/scout.md +2 -2
- package/agents/worker.md +2 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +40 -2
- package/docs/configuration.md +30 -12
- package/docs/extension-api.md +42 -1
- package/docs/models.md +2 -0
- package/docs/observability.md +41 -5
- package/docs/tool-reference.md +38 -39
- package/docs/workflows.md +171 -3
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +6 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +12 -6
- package/skills/pi-subagents/references/execution-controls.md +29 -15
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +4 -2
- package/src/agents/agent-management.ts +124 -351
- package/src/agents/agents.ts +157 -31
- package/src/agents/skills.ts +1 -1
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/preflight.ts +36 -8
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +3 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +166 -88
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/schemas.ts +12 -35
- package/src/extension/tool-description.ts +35 -24
- package/src/inspectors/herdr/actions.ts +2 -2
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +2 -2
- package/src/intercom/native-supervisor-channel.ts +32 -10
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/missions/workflow-state.ts +2 -2
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +31 -8
- package/src/runs/background/async-execution.ts +68 -40
- package/src/runs/background/async-job-tracker.ts +20 -4
- package/src/runs/background/async-resume.ts +47 -15
- package/src/runs/background/async-retention.ts +886 -0
- package/src/runs/background/async-status.ts +39 -53
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/completion-dedupe.ts +5 -1
- package/src/runs/background/completion-replay.ts +22 -12
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +68 -19
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/inspect-rpc.ts +443 -0
- package/src/runs/background/notify.ts +31 -5
- package/src/runs/background/result-files.ts +158 -90
- package/src/runs/background/result-watcher.ts +116 -20
- package/src/runs/background/resume-guidance.ts +8 -5
- package/src/runs/background/retained-children.ts +13 -3
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +11 -9
- package/src/runs/background/run-status.ts +27 -18
- package/src/runs/background/scheduled-runs.ts +24 -4
- package/src/runs/background/stale-run-reconciler.ts +6 -3
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +253 -140
- package/src/runs/background/subagent-wait.ts +8 -8
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +21 -4
- package/src/runs/background/wait-subscriptions.ts +81 -2
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +666 -1579
- package/src/runs/foreground/workflow-detach-reconcile.ts +194 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/acceptance.ts +4 -4
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/external-job-bridge.ts +444 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +98 -38
- package/src/runs/shared/orca-progress-tabs.ts +84 -22
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +4 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/session-lease.ts +0 -6
- package/src/runs/shared/subagent-control.ts +26 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +14 -2
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +71 -26
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-bridge.ts +2 -1
- package/src/slash/slash-commands.ts +29 -3
- package/src/tui/fleet.ts +63 -15
- package/src/watchdog/change-signature.ts +1 -1
- package/src/watchdog/lsp-diagnostics.ts +1 -0
- package/src/workflows/chat-progress.ts +2 -2
- package/src/workflows/scripted-workflow.ts +220 -67
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1581
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AsyncStatus } from "../../shared/types.ts";
|
|
4
|
+
import { readStatus } from "../../shared/utils.ts";
|
|
5
|
+
import { encodeIndexSegment } from "./index-segment.ts";
|
|
6
|
+
import { updateTerminalRunIndex } from "./terminal-run-index.ts";
|
|
7
|
+
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
4
8
|
|
|
5
9
|
export const ACTIVE_RUN_INDEX_DIR = ".active-runs";
|
|
6
10
|
export const DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS = 24 * 60 * 60 * 1000;
|
|
@@ -12,7 +16,7 @@ function indexDir(asyncDirRoot: string): string {
|
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
function toolCallIndexDir(asyncDirRoot: string, toolCallId: string): string {
|
|
15
|
-
return path.join(indexDir(asyncDirRoot), TOOL_CALL_INDEX_DIR,
|
|
19
|
+
return path.join(indexDir(asyncDirRoot), TOOL_CALL_INDEX_DIR, encodeIndexSegment(toolCallId));
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
function toolCallIndexPath(asyncDir: string, toolCallId: string): string {
|
|
@@ -45,8 +49,11 @@ function releaseToolCallAliases(asyncDir: string): void {
|
|
|
45
49
|
try {
|
|
46
50
|
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
47
51
|
} catch (error) {
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
53
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
54
|
+
console.error(`Failed to inspect async active-run tool-call index root '${root}':`, error);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
50
57
|
}
|
|
51
58
|
for (const entry of entries) {
|
|
52
59
|
if (!entry.isDirectory()) continue;
|
|
@@ -69,19 +76,34 @@ export function releaseActiveRunIndex(asyncDir: string): void {
|
|
|
69
76
|
releaseToolCallAliases(asyncDir);
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
export function updateActiveRunIndex(asyncDir: string, state: AsyncStatus["state"], toolCallId?: string): void {
|
|
79
|
+
export function updateActiveRunIndex(asyncDir: string, state: AsyncStatus["state"], toolCallId?: string, options: { retryCapacityErrors?: boolean } = {}): void {
|
|
73
80
|
const marker = markerPath(asyncDir);
|
|
74
81
|
if (isActiveAsyncState(state)) {
|
|
75
82
|
fs.mkdirSync(path.dirname(marker), { recursive: true });
|
|
76
83
|
fs.writeFileSync(marker, "", { flag: "a" });
|
|
77
84
|
if (toolCallId) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
try {
|
|
86
|
+
const toolCallMarker = toolCallIndexPath(asyncDir, toolCallId);
|
|
87
|
+
fs.mkdirSync(path.dirname(toolCallMarker), { recursive: true });
|
|
88
|
+
fs.writeFileSync(toolCallMarker, "", { flag: "a" });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Tool-call aliases are optional. The authoritative active-run marker
|
|
91
|
+
// must keep the launch discoverable even when alias indexing fails.
|
|
92
|
+
console.error(`Failed to write async active-run tool-call index for '${asyncDir}':`, error);
|
|
93
|
+
}
|
|
81
94
|
}
|
|
82
95
|
return;
|
|
83
96
|
}
|
|
84
97
|
releaseActiveRunIndex(asyncDir);
|
|
98
|
+
const status = readStatus(asyncDir);
|
|
99
|
+
if (status && status.state === state) {
|
|
100
|
+
try {
|
|
101
|
+
updateTerminalRunIndex(asyncDir, status);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (options.retryCapacityErrors && isStorageCapacityError(error)) throw error;
|
|
104
|
+
console.error(`Failed to write async terminal-run index for '${asyncDir}':`, error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
85
107
|
}
|
|
86
108
|
|
|
87
109
|
export function activeRunMarkerAgeMs(asyncDir: string, now = Date.now()): number | undefined {
|
|
@@ -110,7 +132,8 @@ export function readActiveRunToolCallIndex(asyncDirRoot: string, toolCallId: str
|
|
|
110
132
|
.filter((entry) => entry.isFile())
|
|
111
133
|
.map((entry) => entry.name);
|
|
112
134
|
} catch (error) {
|
|
113
|
-
|
|
135
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
136
|
+
if (code === "ENOENT" || code === "ENOTDIR") return [];
|
|
114
137
|
throw error;
|
|
115
138
|
}
|
|
116
139
|
}
|
|
@@ -13,9 +13,10 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
13
13
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
14
14
|
import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.ts";
|
|
15
15
|
import { writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
16
|
+
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
16
17
|
import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
|
|
17
18
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
18
|
-
import { buildChainInstructions,
|
|
19
|
+
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveChainPath, resolveExistingReadPaths, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
19
20
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
20
21
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
21
22
|
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
@@ -23,7 +24,7 @@ import { resolveNodeExecutable } from "../../shared/node-executable.ts";
|
|
|
23
24
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
24
25
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
25
26
|
import { PI_CODING_AGENT_PACKAGE_ROOT_ENV, PROMPT_REDACTED, resolveChildCwd } from "../../shared/utils.ts";
|
|
26
|
-
import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
27
|
+
import { buildModelCandidates, inheritsParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
27
28
|
import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
28
29
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
29
30
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
@@ -125,6 +126,7 @@ interface AsyncExecutionContext {
|
|
|
125
126
|
pi: ExtensionAPI;
|
|
126
127
|
cwd: string;
|
|
127
128
|
currentSessionId: string;
|
|
129
|
+
completionOwnerId?: string;
|
|
128
130
|
/** Parent session id used by permission-system ask forwarding. */
|
|
129
131
|
parentSessionId?: string;
|
|
130
132
|
permissions?: PermissionConfig;
|
|
@@ -144,7 +146,7 @@ interface AsyncChainParams {
|
|
|
144
146
|
/** Raw caller-facing goal used only by the started event. */
|
|
145
147
|
goal?: string;
|
|
146
148
|
attachRoot?: ImportedAsyncRoot & { agent: string; outputName?: string; label?: string };
|
|
147
|
-
resultMode?:
|
|
149
|
+
resultMode?: SubagentRunMode;
|
|
148
150
|
agents: AgentConfig[];
|
|
149
151
|
ctx: AsyncExecutionContext;
|
|
150
152
|
availableModels?: AvailableModelInfo[];
|
|
@@ -218,6 +220,7 @@ interface AsyncSingleParams {
|
|
|
218
220
|
agentContract?: AgentContract;
|
|
219
221
|
structuredOutputSchema?: JsonSchemaObject;
|
|
220
222
|
modelOverride?: string;
|
|
223
|
+
modelOverrideFromParent?: boolean;
|
|
221
224
|
thinkingOverride?: AgentConfig["thinking"];
|
|
222
225
|
availableModels?: AvailableModelInfo[];
|
|
223
226
|
maxSubagentDepth: number;
|
|
@@ -225,6 +228,7 @@ interface AsyncSingleParams {
|
|
|
225
228
|
worktreeSetupHook?: string;
|
|
226
229
|
worktreeSetupHookTimeoutMs?: number;
|
|
227
230
|
worktreeBaseDir?: string;
|
|
231
|
+
worktree?: boolean;
|
|
228
232
|
controlConfig?: ResolvedControlConfig;
|
|
229
233
|
intercomBridge?: IntercomBridgeConfig;
|
|
230
234
|
controlIntercomTarget?: string;
|
|
@@ -428,7 +432,7 @@ function terminateRunnerBeforeProceed(pid: number): boolean {
|
|
|
428
432
|
return !runnerIsAlive(pid);
|
|
429
433
|
}
|
|
430
434
|
|
|
431
|
-
function persistPreProceedStartupFailure(asyncDir: string, runId: string, runnerProcessInstanceId: string, sessionId: string | undefined, message: string): void {
|
|
435
|
+
function persistPreProceedStartupFailure(asyncDir: string, runId: string, runnerProcessInstanceId: string, sessionId: string | undefined, completionOwnerId: string | undefined, message: string): void {
|
|
432
436
|
const now = Date.now();
|
|
433
437
|
try {
|
|
434
438
|
const statusPath = path.join(asyncDir, "status.json");
|
|
@@ -440,6 +444,7 @@ function persistPreProceedStartupFailure(asyncDir: string, runId: string, runner
|
|
|
440
444
|
...status,
|
|
441
445
|
runId,
|
|
442
446
|
...(sessionId ? { sessionId } : {}),
|
|
447
|
+
...(completionOwnerId ? { completionOwnerId } : {}),
|
|
443
448
|
state: "failed",
|
|
444
449
|
lastUpdate: now,
|
|
445
450
|
error: message,
|
|
@@ -491,10 +496,11 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, onProcessTerminal
|
|
|
491
496
|
fs.writeFileSync(cfgPath, JSON.stringify(launchConfig));
|
|
492
497
|
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
493
498
|
const nodeCommand = resolveNodeExecutable();
|
|
494
|
-
const launchForStartup = launchConfig as typeof launchConfig & { asyncDir?: unknown; id?: unknown; sessionId?: unknown; revivalLease?: unknown };
|
|
499
|
+
const launchForStartup = launchConfig as typeof launchConfig & { asyncDir?: unknown; id?: unknown; sessionId?: unknown; completionOwnerId?: unknown; revivalLease?: unknown };
|
|
495
500
|
const launchAsyncDir = typeof launchForStartup.asyncDir === "string" ? launchForStartup.asyncDir : undefined;
|
|
496
501
|
const launchRunId = typeof launchForStartup.id === "string" ? launchForStartup.id : suffix;
|
|
497
502
|
const launchSessionId = typeof launchForStartup.sessionId === "string" ? launchForStartup.sessionId : undefined;
|
|
503
|
+
const launchCompletionOwnerId = typeof launchForStartup.completionOwnerId === "string" ? launchForStartup.completionOwnerId : undefined;
|
|
498
504
|
const startupPath = typeof launchForStartup.revivalLease === "object" && launchAsyncDir
|
|
499
505
|
? path.join(launchAsyncDir, "runner-startup.json")
|
|
500
506
|
: undefined;
|
|
@@ -584,7 +590,7 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, onProcessTerminal
|
|
|
584
590
|
proc.unref();
|
|
585
591
|
if (startupPath && startupAckPath && startupProceedPath) {
|
|
586
592
|
const persistStartupFailure = (message: string) => {
|
|
587
|
-
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, message);
|
|
593
|
+
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
|
|
588
594
|
};
|
|
589
595
|
const ready = waitForRunnerStartup(startupPath, "ready", RUNNER_STARTUP_TIMEOUT_MS);
|
|
590
596
|
if (ready.ok === false) {
|
|
@@ -669,13 +675,11 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
669
675
|
: chain;
|
|
670
676
|
const firstStep = chain[0];
|
|
671
677
|
const originalTask = params.task ?? (firstStep
|
|
672
|
-
? (
|
|
673
|
-
?
|
|
674
|
-
:
|
|
675
|
-
? firstStep.parallel
|
|
676
|
-
:
|
|
677
|
-
? firstStep.parallel.task
|
|
678
|
-
: (firstStep as SequentialStep).task)
|
|
678
|
+
? (isParallelStep(firstStep)
|
|
679
|
+
? firstStep.parallel[0]?.task
|
|
680
|
+
: isDynamicParallelStep(firstStep)
|
|
681
|
+
? firstStep.parallel.task
|
|
682
|
+
: (firstStep as SequentialStep).task)
|
|
679
683
|
: undefined);
|
|
680
684
|
try {
|
|
681
685
|
if (params.validateOutputBindings !== false) {
|
|
@@ -688,13 +692,11 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
688
692
|
const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: graphChain });
|
|
689
693
|
|
|
690
694
|
for (const s of chain) {
|
|
691
|
-
const stepAgents =
|
|
692
|
-
?
|
|
693
|
-
:
|
|
694
|
-
? s.parallel.
|
|
695
|
-
:
|
|
696
|
-
? [s.parallel.agent]
|
|
697
|
-
: [(s as SequentialStep).agent];
|
|
695
|
+
const stepAgents = isParallelStep(s)
|
|
696
|
+
? s.parallel.map((t) => t.agent)
|
|
697
|
+
: isDynamicParallelStep(s)
|
|
698
|
+
? [s.parallel.agent]
|
|
699
|
+
: [(s as SequentialStep).agent];
|
|
698
700
|
for (const agentName of stepAgents) {
|
|
699
701
|
if (!agents.find((x) => x.name === agentName)) {
|
|
700
702
|
return { error: `Unknown agent: ${agentName}` };
|
|
@@ -716,7 +718,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
716
718
|
};
|
|
717
719
|
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number, parallelOutputNamespace?: { stepIndex: number; taskIndex?: number }, runFanoutPath?: string) => {
|
|
718
720
|
const a = agents.find((x) => x.name === s.agent)!;
|
|
719
|
-
const externalRunner = a.runner?.type === "external-cli";
|
|
721
|
+
const externalRunner = a.runner?.type === "external-cli" || a.runner?.type === "external-job";
|
|
722
|
+
const externalRunnerType = a.runner?.type;
|
|
720
723
|
if (externalRunner) {
|
|
721
724
|
const unsupported: string[] = [];
|
|
722
725
|
if (s.model !== undefined) unsupported.push("model override");
|
|
@@ -724,7 +727,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
724
727
|
if (s.acceptance !== undefined || params.agentContract !== undefined || s.agentContract !== undefined) unsupported.push("acceptance/agent contract");
|
|
725
728
|
if (s.toolBudget !== undefined || params.toolBudget !== undefined || a.toolBudget !== undefined || params.configToolBudget !== undefined) unsupported.push("tool budget");
|
|
726
729
|
if (params.contextForAgent?.(s.agent) === "fork") unsupported.push("fork context");
|
|
727
|
-
if (unsupported.length > 0) throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='
|
|
730
|
+
if (unsupported.length > 0) throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
|
|
728
731
|
}
|
|
729
732
|
try {
|
|
730
733
|
assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])));
|
|
@@ -815,7 +818,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
815
818
|
const launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);
|
|
816
819
|
const permissionRules = resolvePermissionRules(ctx.permissions, a.permissions);
|
|
817
820
|
if (externalRunner && permissionRules) {
|
|
818
|
-
throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='
|
|
821
|
+
throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}', which cannot enforce native Pi child permission rules.`);
|
|
819
822
|
}
|
|
820
823
|
return {
|
|
821
824
|
parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
|
|
@@ -835,7 +838,10 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
835
838
|
model,
|
|
836
839
|
thinking: resolveEffectiveThinking(model, effectiveThinking),
|
|
837
840
|
launchResolvedExtensions,
|
|
838
|
-
modelCandidates: externalRunner ? undefined : buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider, {
|
|
841
|
+
modelCandidates: externalRunner ? undefined : buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider, {
|
|
842
|
+
scope: ctx.modelScope,
|
|
843
|
+
primaryModelFromParent: inheritsParentModel(s.model, a.model, ctx.currentModel),
|
|
844
|
+
}).map((candidate) =>
|
|
839
845
|
applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined),
|
|
840
846
|
),
|
|
841
847
|
tools: a.tools,
|
|
@@ -872,6 +878,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
872
878
|
...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
|
|
873
879
|
...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
|
|
874
880
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
881
|
+
...(s.worktree ? { worktree: true } : {}),
|
|
875
882
|
};
|
|
876
883
|
};
|
|
877
884
|
|
|
@@ -890,9 +897,6 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
890
897
|
|
|
891
898
|
try {
|
|
892
899
|
const builtSteps = chain.map((s, stepIndex) => {
|
|
893
|
-
if (isCheckpointStep(s)) {
|
|
894
|
-
return { checkpoint: s.checkpoint, ...(s.message ? { message: s.message } : {}), phase: s.phase, label: s.label };
|
|
895
|
-
}
|
|
896
900
|
if (isParallelStep(s)) {
|
|
897
901
|
const parallelBehaviors = s.parallel.map((task) => {
|
|
898
902
|
const agent = agents.find((candidate) => candidate.name === task.agent)!;
|
|
@@ -958,8 +962,17 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
958
962
|
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
959
963
|
};
|
|
960
964
|
}
|
|
965
|
+
const sequential = s as SequentialStep;
|
|
966
|
+
let behaviorCwd: string | undefined;
|
|
967
|
+
if (sequential.worktree) {
|
|
968
|
+
try {
|
|
969
|
+
behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, 0, worktreeBaseDir);
|
|
970
|
+
} catch {
|
|
971
|
+
behaviorCwd = undefined;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
961
974
|
const staticStep = nextFlatStep();
|
|
962
|
-
return buildSeqStep(
|
|
975
|
+
return buildSeqStep(sequential, staticStep.sessionFile, behaviorCwd, false, undefined, staticStep.index, undefined, `chain[${stepIndex}]`);
|
|
963
976
|
});
|
|
964
977
|
const steps = params.attachRoot
|
|
965
978
|
? [{
|
|
@@ -1028,7 +1041,6 @@ export function executeAsyncChain(
|
|
|
1028
1041
|
const resultMode = params.resultMode ?? "chain";
|
|
1029
1042
|
const acceptanceErrors = validateExecutionAcceptance({
|
|
1030
1043
|
chain: chain.map((step) => {
|
|
1031
|
-
if (isCheckpointStep(step)) return {};
|
|
1032
1044
|
if (isParallelStep(step)) return { parallel: step.parallel };
|
|
1033
1045
|
if (isDynamicParallelStep(step)) return { acceptance: step.acceptance, parallel: step.parallel };
|
|
1034
1046
|
return { acceptance: step.acceptance };
|
|
@@ -1127,6 +1139,7 @@ export function executeAsyncChain(
|
|
|
1127
1139
|
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
1128
1140
|
asyncDir,
|
|
1129
1141
|
sessionId: ctx.currentSessionId,
|
|
1142
|
+
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1130
1143
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1131
1144
|
piPackageRoot,
|
|
1132
1145
|
piArgv1: process.argv[1],
|
|
@@ -1253,6 +1266,7 @@ export function executeAsyncChain(
|
|
|
1253
1266
|
id,
|
|
1254
1267
|
pid: spawnResult.pid,
|
|
1255
1268
|
sessionId: ctx.currentSessionId,
|
|
1269
|
+
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1256
1270
|
mode: resultMode,
|
|
1257
1271
|
agent: firstAgents[0],
|
|
1258
1272
|
agents: flatAgents,
|
|
@@ -1323,7 +1337,8 @@ export function executeAsyncSingle(
|
|
|
1323
1337
|
const task = params.task ?? "";
|
|
1324
1338
|
const acceptanceErrors = validateAcceptanceInput(params.acceptance);
|
|
1325
1339
|
if (acceptanceErrors.length > 0) return formatAsyncStartError("single", acceptanceErrors.join(" "));
|
|
1326
|
-
const externalRunner = agentConfig.runner?.type === "external-cli";
|
|
1340
|
+
const externalRunner = agentConfig.runner?.type === "external-cli" || agentConfig.runner?.type === "external-job";
|
|
1341
|
+
const externalRunnerType = agentConfig.runner?.type;
|
|
1327
1342
|
const permissionRules = resolvePermissionRules(ctx.permissions, agentConfig.permissions);
|
|
1328
1343
|
if (externalRunner) {
|
|
1329
1344
|
const unsupported: string[] = [];
|
|
@@ -1335,7 +1350,7 @@ export function executeAsyncSingle(
|
|
|
1335
1350
|
if (params.context === "fork") unsupported.push("fork context");
|
|
1336
1351
|
if ((params.skills?.length ?? 0) > 0) unsupported.push("skills");
|
|
1337
1352
|
if (permissionRules) unsupported.push("native Pi child permissions");
|
|
1338
|
-
if (unsupported.length > 0) return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='
|
|
1353
|
+
if (unsupported.length > 0) return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
|
|
1339
1354
|
}
|
|
1340
1355
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
|
|
1341
1356
|
try {
|
|
@@ -1344,6 +1359,10 @@ export function executeAsyncSingle(
|
|
|
1344
1359
|
return formatAsyncStartError("single", error instanceof Error ? error.message : String(error));
|
|
1345
1360
|
}
|
|
1346
1361
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
1362
|
+
const instructionCwd = params.worktree === true
|
|
1363
|
+
? resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s0`, 0, worktreeBaseDir)
|
|
1364
|
+
: runnerCwd;
|
|
1365
|
+
const readExistenceCwd = params.worktree === true ? runnerCwd : instructionCwd;
|
|
1347
1366
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
1348
1367
|
const availableModels = params.availableModels;
|
|
1349
1368
|
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
@@ -1385,7 +1404,7 @@ export function executeAsyncSingle(
|
|
|
1385
1404
|
}
|
|
1386
1405
|
|
|
1387
1406
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
1388
|
-
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd,
|
|
1407
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
|
|
1389
1408
|
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, agentConfig);
|
|
1390
1409
|
const outputMode = params.outputMode ?? "inline";
|
|
1391
1410
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
@@ -1394,17 +1413,19 @@ export function executeAsyncSingle(
|
|
|
1394
1413
|
// Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
|
|
1395
1414
|
// absolute paths pass through; relative paths resolve against the child cwd.
|
|
1396
1415
|
const reads = params.reads !== undefined ? params.reads : agentConfig.defaultReads ?? false;
|
|
1397
|
-
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads,
|
|
1416
|
+
const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, readExistenceCwd) : [];
|
|
1398
1417
|
const readsInstruction = readPaths.length > 0
|
|
1399
1418
|
? `[Read from: ${readPaths.join(", ")}]\n\n`
|
|
1400
1419
|
: "";
|
|
1401
1420
|
const taskText = readsInstruction + taskWithOutputInstruction;
|
|
1402
|
-
const primaryModel = externalRunner ? undefined :
|
|
1403
|
-
params.modelOverride
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1421
|
+
const primaryModel = externalRunner ? undefined : params.modelOverrideFromParent
|
|
1422
|
+
? params.modelOverride
|
|
1423
|
+
: resolveSubagentModelOverride(
|
|
1424
|
+
params.modelOverride ?? agentConfig.model,
|
|
1425
|
+
ctx.currentModel,
|
|
1426
|
+
availableModels,
|
|
1427
|
+
ctx.currentModelProvider,
|
|
1428
|
+
);
|
|
1408
1429
|
const effectiveThinking = externalRunner ? undefined : params.thinkingOverride ?? agentConfig.thinking;
|
|
1409
1430
|
const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel, effectiveThinking, params.thinkingOverride !== undefined);
|
|
1410
1431
|
const toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;
|
|
@@ -1431,7 +1452,10 @@ export function executeAsyncSingle(
|
|
|
1431
1452
|
: undefined;
|
|
1432
1453
|
const modelCandidates = externalRunner
|
|
1433
1454
|
? []
|
|
1434
|
-
: buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, {
|
|
1455
|
+
: buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, {
|
|
1456
|
+
scope: ctx.modelScope,
|
|
1457
|
+
primaryModelFromParent: params.modelOverrideFromParent,
|
|
1458
|
+
})
|
|
1435
1459
|
.flatMap((candidate) => {
|
|
1436
1460
|
const resolved = applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined);
|
|
1437
1461
|
return resolved ? [resolved] : [];
|
|
@@ -1489,6 +1513,7 @@ export function executeAsyncSingle(
|
|
|
1489
1513
|
...(sessionFile ? { sessionFile } : {}),
|
|
1490
1514
|
cwd: runnerCwd,
|
|
1491
1515
|
...(model ? { model } : {}),
|
|
1516
|
+
...(params.modelOverrideFromParent ? { modelOverrideFromParent: true } : {}),
|
|
1492
1517
|
...(recoveryAgentConfig.fallbackModels ? { fallbackModels: [...recoveryAgentConfig.fallbackModels] } : {}),
|
|
1493
1518
|
...(effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1494
1519
|
...(recoveryAgentConfig.tools ? { tools: [...recoveryAgentConfig.tools] } : {}),
|
|
@@ -1570,6 +1595,7 @@ export function executeAsyncSingle(
|
|
|
1570
1595
|
...(structuredOutput ? { structuredOutput } : {}),
|
|
1571
1596
|
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1572
1597
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
1598
|
+
...(params.worktree === true ? { worktree: true } : {}),
|
|
1573
1599
|
},
|
|
1574
1600
|
],
|
|
1575
1601
|
resultPath: params.parentWorkflowRunId !== undefined && params.revivalLease !== undefined
|
|
@@ -1584,6 +1610,7 @@ export function executeAsyncSingle(
|
|
|
1584
1610
|
sessionDir: resolvedSessionDir,
|
|
1585
1611
|
asyncDir,
|
|
1586
1612
|
sessionId: ctx.currentSessionId,
|
|
1613
|
+
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1587
1614
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1588
1615
|
piPackageRoot,
|
|
1589
1616
|
piArgv1: process.argv[1],
|
|
@@ -1677,6 +1704,7 @@ export function executeAsyncSingle(
|
|
|
1677
1704
|
id,
|
|
1678
1705
|
pid: spawnResult.pid,
|
|
1679
1706
|
sessionId: ctx.currentSessionId,
|
|
1707
|
+
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1680
1708
|
mode: "single",
|
|
1681
1709
|
agent,
|
|
1682
1710
|
task: task?.trim() ? PROMPT_REDACTED : undefined,
|
|
@@ -20,6 +20,8 @@ import { normalizeParallelGroups } from "./parallel-groups.ts";
|
|
|
20
20
|
import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
|
|
21
21
|
import { findNestedRouteForRootId, hasLiveNestedDescendants, updateAsyncJobNestedProjection } from "../shared/nested-events.ts";
|
|
22
22
|
import { listAsyncRuns, type AsyncRunSummary } from "./async-status.ts";
|
|
23
|
+
import { EXTERNAL_JOB_BRIDGE_REQUEST_DIR, serviceExternalJobBridgeRequests } from "../shared/external-job-bridge.ts";
|
|
24
|
+
import { shouldUseNativeFsWatch } from "../../shared/watch-strategy.ts";
|
|
23
25
|
|
|
24
26
|
interface AsyncJobTrackerOptions {
|
|
25
27
|
completionRetentionMs?: number;
|
|
@@ -27,6 +29,9 @@ interface AsyncJobTrackerOptions {
|
|
|
27
29
|
pollIntervalMs?: number;
|
|
28
30
|
resultsDir?: string;
|
|
29
31
|
widgetEnabled?: boolean;
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
onJobTerminal?: () => void;
|
|
34
|
+
watch?: typeof fs.watch;
|
|
30
35
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
31
36
|
now?: () => number;
|
|
32
37
|
}
|
|
@@ -66,6 +71,9 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
66
71
|
const runningJobIds = new Set<string>();
|
|
67
72
|
let rootWatcher: fs.FSWatcher | undefined;
|
|
68
73
|
let nextLivenessAt = Date.now() + livenessIntervalMs;
|
|
74
|
+
const watch = options.watch ?? fs.watch;
|
|
75
|
+
const useNativeWatcher = () => shouldUseNativeFsWatch("async-job-tracker", options.platform);
|
|
76
|
+
const terminalStatus = (status: string) => status === "complete" || status === "failed" || status === "paused" || status === "stopped";
|
|
69
77
|
const rerenderWidget = (ctx: ExtensionContext, jobs = Array.from(state.asyncJobs.values())) => {
|
|
70
78
|
if (state.widgetsSuspended) return;
|
|
71
79
|
renderWidget(ctx, options.widgetEnabled === false ? [] : jobs);
|
|
@@ -139,6 +147,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
139
147
|
mode: run.mode,
|
|
140
148
|
context: run.context,
|
|
141
149
|
cwd: run.cwd,
|
|
150
|
+
sessionRoot: run.sessionRoot,
|
|
142
151
|
agents: visibleSteps.map((step) => step.agent),
|
|
143
152
|
currentStep: run.currentStep,
|
|
144
153
|
chainStepCount: run.chainStepCount,
|
|
@@ -327,6 +336,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
327
336
|
};
|
|
328
337
|
try {
|
|
329
338
|
emitNewControlEvents(job);
|
|
339
|
+
serviceExternalJobBridgeRequests(job.asyncDir);
|
|
330
340
|
try {
|
|
331
341
|
if (job.nestedRoute) reconcileNestedAsyncDescendants(job.nestedRoute, { resultsDir, kill: options.kill, now: options.now });
|
|
332
342
|
} catch (error) {
|
|
@@ -342,6 +352,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
342
352
|
runId: job.asyncId,
|
|
343
353
|
pid: job.pid,
|
|
344
354
|
sessionId: job.sessionId,
|
|
355
|
+
completionOwnerId: job.completionOwnerId,
|
|
345
356
|
mode: job.mode,
|
|
346
357
|
agents: job.agents,
|
|
347
358
|
chainStepCount: job.chainStepCount,
|
|
@@ -404,7 +415,8 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
404
415
|
job.turnBudgetExceeded = status.turnBudgetExceeded ?? job.turnBudgetExceeded;
|
|
405
416
|
job.wrapUpRequested = status.wrapUpRequested ?? job.wrapUpRequested;
|
|
406
417
|
job.sessionFile = status.sessionFile ?? job.sessionFile;
|
|
407
|
-
if (job.status
|
|
418
|
+
if (terminalStatus(job.status)) {
|
|
419
|
+
if (!terminalStatus(previousStatus)) options.onJobTerminal?.();
|
|
408
420
|
rememberFleetJob(state, job);
|
|
409
421
|
if (!nestedRefreshFailed && !hasLiveNestedDescendants(job.nestedChildren) && (previousStatus !== job.status || !state.cleanupTimers.has(job.asyncId))) {
|
|
410
422
|
scheduleCleanup(job.asyncId);
|
|
@@ -442,6 +454,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
442
454
|
};
|
|
443
455
|
|
|
444
456
|
const watchJob = (job: AsyncJobState) => {
|
|
457
|
+
if (!useNativeWatcher()) return;
|
|
445
458
|
const watched = jobWatchers.get(job.asyncId) ?? { watchers: new Map<string, fs.FSWatcher>() };
|
|
446
459
|
const active = job.status === "queued" || job.status === "running";
|
|
447
460
|
const watchPath = (watchPath: string): boolean => {
|
|
@@ -449,11 +462,11 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
449
462
|
const nestedEventSink = job.nestedRoute?.eventSink === watchPath;
|
|
450
463
|
let watcher: fs.FSWatcher;
|
|
451
464
|
try {
|
|
452
|
-
watcher =
|
|
465
|
+
watcher = watch(resolveWatchPath(watchPath), (_event, file) => {
|
|
453
466
|
const rawFileName = file?.toString();
|
|
454
467
|
const fileName = rawFileName ?? path.basename(watchPath);
|
|
455
468
|
const nestedEventFile = nestedEventSink && (!rawFileName || rawFileName.endsWith(".json") || rawFileName.endsWith(".jsonl"));
|
|
456
|
-
if (fileName === "status.json" || fileName === "events.jsonl" || nestedEventFile) {
|
|
469
|
+
if (fileName === "status.json" || fileName === "events.jsonl" || fileName === EXTERNAL_JOB_BRIDGE_REQUEST_DIR || fileName.endsWith(".json") || nestedEventFile) {
|
|
457
470
|
scheduleJobRefresh(job.asyncId);
|
|
458
471
|
}
|
|
459
472
|
if (watchPath !== job.asyncDir && !nestedEventSink) {
|
|
@@ -480,6 +493,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
480
493
|
const statusWatched = watchPath(statusPath);
|
|
481
494
|
if (statusWatched && !hadStatusWatcher) scheduleJobRefresh(job.asyncId);
|
|
482
495
|
watchPath(path.join(job.asyncDir, "events.jsonl"));
|
|
496
|
+
watchPath(path.join(job.asyncDir, EXTERNAL_JOB_BRIDGE_REQUEST_DIR));
|
|
483
497
|
if (job.nestedRoute) watchPath(job.nestedRoute.eventSink);
|
|
484
498
|
if (!statusWatched && active && !watched.retryTimer) {
|
|
485
499
|
watched.retryTimer = setTimeout(() => {
|
|
@@ -497,9 +511,10 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
497
511
|
};
|
|
498
512
|
|
|
499
513
|
const watchAsyncRoot = () => {
|
|
514
|
+
if (!useNativeWatcher()) return;
|
|
500
515
|
if (rootWatcher) return;
|
|
501
516
|
try {
|
|
502
|
-
rootWatcher =
|
|
517
|
+
rootWatcher = watch(resolveWatchPath(asyncDirRoot), (_event, file) => {
|
|
503
518
|
const runDirName = file?.toString();
|
|
504
519
|
for (const job of state.asyncJobs.values()) {
|
|
505
520
|
if (jobWatchers.has(job.asyncId) || (runDirName && path.basename(job.asyncDir) !== runDirName)) continue;
|
|
@@ -568,6 +583,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
568
583
|
status: "queued",
|
|
569
584
|
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
570
585
|
...(typeof info.sessionId === "string" ? { sessionId: info.sessionId } : {}),
|
|
586
|
+
...(typeof info.completionOwnerId === "string" ? { completionOwnerId: info.completionOwnerId } : {}),
|
|
571
587
|
mode: info.mode ?? (info.chain ? "chain" : "single"),
|
|
572
588
|
description: info.goal ?? info.task,
|
|
573
589
|
agents,
|