pi-subagents 0.66.0 → 0.67.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 +68 -0
- package/README.md +4 -3
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +6 -3
- package/docs/configuration.md +7 -5
- package/docs/extension-api.md +33 -18
- package/docs/missions.md +8 -0
- package/docs/models.md +1 -1
- package/docs/observability.md +4 -4
- package/docs/standalone-background.md +49 -0
- package/docs/tool-reference.md +18 -8
- package/docs/watchdog.md +35 -4
- package/docs/workflows.md +26 -12
- package/inspector-runner.mjs +2 -2
- package/package.json +1 -1
- package/prompts/parallel-review.md +1 -1
- package/{runner-server-preload.mjs → runner-peer-preload.mjs} +8 -3
- package/skills/pi-subagents/SKILL.md +14 -0
- package/skills/pi-subagents/references/execution-controls.md +7 -5
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agents.ts +6 -0
- package/src/agents/builtin-names.ts +1 -0
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +76 -45
- package/src/api/shared-types.ts +2 -0
- package/src/extension/fanout-child.ts +63 -4
- package/src/extension/index.ts +20 -8
- package/src/extension/public-execution.ts +4 -2
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +67 -78
- package/src/extension/tool-description.ts +29 -82
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +22 -13
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/async-execution.ts +45 -56
- package/src/runs/background/async-resume.ts +5 -9
- package/src/runs/background/auto-drain.ts +6 -3
- package/src/runs/background/binary-bootstrap.ts +33 -0
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +31 -1
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/run-child-session.ts +61 -4
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +11 -3
- package/src/runs/background/runner-child-launch.ts +2 -0
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/scheduled-runs.ts +40 -13
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +47 -31
- package/src/runs/background/subagent-wait.ts +51 -8
- package/src/runs/background/wait-tool.ts +1 -1
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/execution.ts +44 -31
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +110 -100
- package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
- package/src/runs/shared/acceptance.ts +5 -2
- package/src/runs/shared/async-status-projection.ts +4 -0
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch.ts +12 -2
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +3 -1
- package/src/runs/shared/child-session.ts +33 -2
- package/src/runs/shared/child-tool-plan.ts +122 -3
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-fallback.ts +2 -0
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +9 -3
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree.ts +42 -12
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +65 -2
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/types.ts +4 -1
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -1
- package/src/slash/subagents-admin.ts +11 -4
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +149 -28
- package/src/watchdog/child-status.ts +8 -0
- package/src/watchdog/model-selection.ts +20 -0
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +1 -0
- package/src/watchdog/register-main.ts +31 -27
- package/src/watchdog/review.ts +132 -67
- package/src/watchdog/runtime.ts +82 -20
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +9 -3
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +4 -0
- package/src/workflows/scripted-workflow.ts +237 -7
- package/src/workflows/workflow-checklist.ts +2 -2
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
|
@@ -29,6 +29,7 @@ import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/
|
|
|
29
29
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
30
30
|
import { applyWatchdogLaunchRules } from "../../watchdog/rules.ts";
|
|
31
31
|
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type ModelOrigin, type ParentModel } from "../shared/model-fallback.ts";
|
|
32
|
+
import { getHostBuiltinToolNames } from "../shared/child-tool-plan.ts";
|
|
32
33
|
import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
|
|
33
34
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
34
35
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -53,7 +54,7 @@ import { encodeIndexSegment } from "../background/index-segment.ts";
|
|
|
53
54
|
import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
|
|
54
55
|
import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
|
|
55
56
|
import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
|
|
56
|
-
import { canPreferFork, createForkContextResolver,
|
|
57
|
+
import { canPreferFork, createForkContextResolver, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
|
|
57
58
|
import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
|
|
58
59
|
import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
|
|
59
60
|
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
@@ -112,9 +113,11 @@ import { MissionNotFoundError, updateMission } from "../../missions/store.ts";
|
|
|
112
113
|
import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
|
|
113
114
|
import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
|
|
114
115
|
import { resolveAuthorityDecision } from "../../policy/authority.ts";
|
|
115
|
-
import {
|
|
116
|
+
import { handleInspectorAction, INSPECTOR_ACTIONS } from "../../inspectors/actions.ts";
|
|
117
|
+
import { createBuiltinInspectorPlugins } from "../../inspectors/plugins.ts";
|
|
116
118
|
import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
|
|
117
|
-
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
119
|
+
import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowChildSettledNotification, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
|
|
120
|
+
import { formatIncrementalChildCompletion, scheduledCompletionTriggersTurn } from "../background/notify.ts";
|
|
118
121
|
import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
|
|
119
122
|
import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
|
|
120
123
|
import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
@@ -136,6 +139,7 @@ import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
|
|
|
136
139
|
import {
|
|
137
140
|
cleanupWorktrees,
|
|
138
141
|
createWorktrees,
|
|
142
|
+
preflightWorktreeSource,
|
|
139
143
|
withWorktreeTransaction,
|
|
140
144
|
type WorktreeSetupProgress,
|
|
141
145
|
diffWorktrees,
|
|
@@ -404,6 +408,7 @@ export interface SubagentParamsLike {
|
|
|
404
408
|
at?: string;
|
|
405
409
|
every?: string;
|
|
406
410
|
sessionOnly?: boolean;
|
|
411
|
+
quiet?: boolean;
|
|
407
412
|
on?: string | number;
|
|
408
413
|
timezone?: string;
|
|
409
414
|
overlap?: "skip";
|
|
@@ -466,7 +471,7 @@ function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): Run
|
|
|
466
471
|
|
|
467
472
|
type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
|
|
468
473
|
type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => Promise<void>;
|
|
469
|
-
type
|
|
474
|
+
type ThinkingOverrideForTask = () => AgentConfig["thinking"] | undefined;
|
|
470
475
|
|
|
471
476
|
interface ExecutionContextData {
|
|
472
477
|
params: SubagentParamsLike;
|
|
@@ -486,7 +491,7 @@ interface ExecutionContextData {
|
|
|
486
491
|
sessionDirForIndex: (idx?: number) => string;
|
|
487
492
|
sessionFileForIndex: (idx?: number) => string | undefined;
|
|
488
493
|
sessionFileForTask: ForkSessionFileForTask;
|
|
489
|
-
thinkingOverrideForTask:
|
|
494
|
+
thinkingOverrideForTask: ThinkingOverrideForTask;
|
|
490
495
|
artifactConfig: ArtifactConfig;
|
|
491
496
|
artifactsDir: string;
|
|
492
497
|
backgroundRequestedWhileClarifying: boolean;
|
|
@@ -2911,19 +2916,6 @@ function withAggregatedToolUsage(result: AgentToolResult<Details>): AgentToolRes
|
|
|
2911
2916
|
: result;
|
|
2912
2917
|
}
|
|
2913
2918
|
|
|
2914
|
-
function withForkThinkingNotes(
|
|
2915
|
-
result: AgentToolResult<Details>,
|
|
2916
|
-
downgrades: Map<number, string>,
|
|
2917
|
-
): AgentToolResult<Details> {
|
|
2918
|
-
if (downgrades.size === 0) return result;
|
|
2919
|
-
const children = [...downgrades.entries()]
|
|
2920
|
-
.sort(([a], [b]) => a - b)
|
|
2921
|
-
.map(([index, agent]) => `${agent} (child ${index})`)
|
|
2922
|
-
.join(", ");
|
|
2923
|
-
const note = `Note: fork context forced thinking off for ${children}. The forked transcript contained signed Anthropic thinking blocks that were sanitized, and Anthropic children cannot resume such a transcript with thinking enabled. Use context: "fresh" when an Anthropic child needs thinking.`;
|
|
2924
|
-
return { ...result, content: [...result.content, { type: "text", text: note }] };
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
2919
|
function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, contextSummary?: ContextSummary): AgentToolResult<Details> {
|
|
2928
2920
|
const message = error instanceof Error ? error.message : String(error);
|
|
2929
2921
|
return withResolvedContext(
|
|
@@ -2947,7 +2939,7 @@ function resolveStaticLaunchSummary(input: {
|
|
|
2947
2939
|
availableModels: ModelInfo[];
|
|
2948
2940
|
currentProvider?: string;
|
|
2949
2941
|
modelScope?: ModelScopeConfig;
|
|
2950
|
-
thinkingOverrideForTask:
|
|
2942
|
+
thinkingOverrideForTask: ThinkingOverrideForTask;
|
|
2951
2943
|
}): StaticLaunchSummary {
|
|
2952
2944
|
const agentConfig = input.agents.find((agent) => agent.name === input.agent);
|
|
2953
2945
|
const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
|
|
@@ -2962,7 +2954,7 @@ function resolveStaticLaunchSummary(input: {
|
|
|
2962
2954
|
agentConfig?.modelProvider ?? input.currentProvider,
|
|
2963
2955
|
modelScopes.length === 0 ? {} : { scope: modelScopes },
|
|
2964
2956
|
);
|
|
2965
|
-
const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask(
|
|
2957
|
+
const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask();
|
|
2966
2958
|
const thinking = externalRunner ? undefined : resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
|
|
2967
2959
|
return {
|
|
2968
2960
|
agent: input.agent,
|
|
@@ -2978,7 +2970,7 @@ function collectStaticLaunchSummaries(input: {
|
|
|
2978
2970
|
availableModels: ModelInfo[];
|
|
2979
2971
|
currentProvider?: string;
|
|
2980
2972
|
modelScope?: ModelScopeConfig;
|
|
2981
|
-
thinkingOverrideForTask:
|
|
2973
|
+
thinkingOverrideForTask: ThinkingOverrideForTask;
|
|
2982
2974
|
dynamicFanoutMaxItems?: number;
|
|
2983
2975
|
}): StaticLaunchSummary[] {
|
|
2984
2976
|
const summary = (agent: string, index: number, explicitModel?: string) => resolveStaticLaunchSummary({
|
|
@@ -3311,7 +3303,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
3311
3303
|
fast: params.fast,
|
|
3312
3304
|
modelOverrideFromParent,
|
|
3313
3305
|
modelOrigin,
|
|
3314
|
-
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(
|
|
3306
|
+
thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(),
|
|
3315
3307
|
thinkingCeiling: a.maxThinking,
|
|
3316
3308
|
maxSubagentDepth,
|
|
3317
3309
|
waitToolEnabled: deps.waitToolEnabled,
|
|
@@ -3846,7 +3838,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3846
3838
|
let childSessionControls: ForegroundChildSessionControls | undefined;
|
|
3847
3839
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3848
3840
|
if (foregroundControl) {
|
|
3849
|
-
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(
|
|
3841
|
+
const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask());
|
|
3850
3842
|
beginForegroundChild(foregroundControl, omitUndefinedProperties({
|
|
3851
3843
|
index: 0,
|
|
3852
3844
|
agent: params.agent!,
|
|
@@ -3878,6 +3870,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3878
3870
|
}
|
|
3879
3871
|
},
|
|
3880
3872
|
}));
|
|
3873
|
+
// Capture the owned mailbox before a child can start and finish between polling ticks.
|
|
3874
|
+
if (deps.childRuntime?.fanoutChild) deps.activateSupervisorTransport?.();
|
|
3881
3875
|
}
|
|
3882
3876
|
|
|
3883
3877
|
const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
|
|
@@ -3898,8 +3892,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3898
3892
|
const launched = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
|
|
3899
3893
|
permissions: deps.config.permissions,
|
|
3900
3894
|
runtimeSnapshotHost: deps.pi,
|
|
3895
|
+
hostAvailableBuiltins: getHostBuiltinToolNames(deps.pi),
|
|
3901
3896
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3902
|
-
llmIntentArbiter: createTaskMutationArbiter(ctx),
|
|
3897
|
+
llmIntentArbiter: createTaskMutationArbiter({ model: ctx.model, modelRegistry: ctx.modelRegistry, sessionId: ctx.sessionManager.getSessionId() }),
|
|
3903
3898
|
childRuntime: deps.childRuntime,
|
|
3904
3899
|
onChildSession: (controls) => { childSessionControls = controls; },
|
|
3905
3900
|
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
@@ -3936,7 +3931,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3936
3931
|
fast: params.fast,
|
|
3937
3932
|
modelOverrideFromParent,
|
|
3938
3933
|
modelOrigin,
|
|
3939
|
-
thinkingOverride: thinkingOverrideForTask(
|
|
3934
|
+
thinkingOverride: thinkingOverrideForTask(),
|
|
3940
3935
|
thinkingCeiling: agentConfig.maxThinking,
|
|
3941
3936
|
extensionBindings: params.extensionBindings,
|
|
3942
3937
|
availableModels,
|
|
@@ -4545,6 +4540,35 @@ export async function steerWorkflowChildByKey(input: {
|
|
|
4545
4540
|
}
|
|
4546
4541
|
}
|
|
4547
4542
|
|
|
4543
|
+
export async function preflightWorkflowWorktrees(input: {
|
|
4544
|
+
workflowDefaults: SubagentParamsLike;
|
|
4545
|
+
defaultWorktree?: boolean;
|
|
4546
|
+
calls: Array<{ key: string; params: Record<string, unknown> }>;
|
|
4547
|
+
ctxCwd: string;
|
|
4548
|
+
signal: AbortSignal;
|
|
4549
|
+
deadlineAt?: number;
|
|
4550
|
+
}): Promise<void> {
|
|
4551
|
+
const sources = new Map<string, string[]>();
|
|
4552
|
+
for (const { key, params } of input.calls) {
|
|
4553
|
+
// The workflow validates retained IDs/receipt references before admission; resolution follows it.
|
|
4554
|
+
if (params.resume !== undefined) continue;
|
|
4555
|
+
const effective = prepareWorkflowLaunchParams(input.workflowDefaults, params, "preflight", key);
|
|
4556
|
+
if ((effective.worktree ?? input.defaultWorktree) !== true) continue;
|
|
4557
|
+
// Match recursive execute's resolution, including relative default/child cwd.
|
|
4558
|
+
const cwd = resolveRequestedCwd(input.ctxCwd, effective.cwd);
|
|
4559
|
+
const keys = sources.get(cwd) ?? [];
|
|
4560
|
+
keys.push(key);
|
|
4561
|
+
sources.set(cwd, keys);
|
|
4562
|
+
}
|
|
4563
|
+
for (const [cwd, keys] of sources) {
|
|
4564
|
+
try { await preflightWorktreeSource(cwd, { signal: input.signal, deadlineAt: input.deadlineAt }); }
|
|
4565
|
+
catch (error) {
|
|
4566
|
+
throw new Error(`Worktree admission failed for ${keys.map((key) => `'${key}'`).join(", ")} at ${cwd}: ${error instanceof Error ? error.message : String(error)} Select the correct cwd or arrange an operator-approved commit/stash.`, { cause: error });
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
input.signal.throwIfAborted();
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4548
4572
|
export function prepareWorkflowLaunchParams(
|
|
4549
4573
|
workflowDefaults: SubagentParamsLike,
|
|
4550
4574
|
childParams: Record<string, unknown>,
|
|
@@ -4890,7 +4914,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4890
4914
|
} catch (error) {
|
|
4891
4915
|
const message = error instanceof Error ? error.message : String(error);
|
|
4892
4916
|
if (requestParams.action?.trim() === "validate") {
|
|
4893
|
-
const validation = validateWorkflowScript(requestParams.workflowScript ?? ""
|
|
4917
|
+
const validation = validateWorkflowScript(requestParams.workflowScript ?? "", {
|
|
4918
|
+
maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
|
|
4919
|
+
});
|
|
4894
4920
|
const invalidValidation = { ...validation, ok: false, errors: [...validation.errors, { message }] };
|
|
4895
4921
|
return {
|
|
4896
4922
|
content: [{ type: "text", text: JSON.stringify(invalidValidation) }],
|
|
@@ -4901,7 +4927,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4901
4927
|
return buildRequestedModeError(requestParams, message);
|
|
4902
4928
|
}
|
|
4903
4929
|
if (requestParams.action?.trim() === "validate") {
|
|
4904
|
-
const validation = validateWorkflowScript(requestParams.workflowScript ?? ""
|
|
4930
|
+
const validation = validateWorkflowScript(requestParams.workflowScript ?? "", {
|
|
4931
|
+
maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
|
|
4932
|
+
});
|
|
4905
4933
|
const payload = workflowPreflight ? { ...validation, preflight: workflowPreflight } : validation;
|
|
4906
4934
|
return {
|
|
4907
4935
|
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
@@ -4919,6 +4947,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4919
4947
|
workflowResource = { permit: workflowResourcePermit, ...consumed };
|
|
4920
4948
|
}
|
|
4921
4949
|
if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
|
|
4950
|
+
const spawnBudgetValidation = validateWorkflowScript(requestParams.workflowScript, {
|
|
4951
|
+
maxSubagentSpawnsPerRun: requestParams.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
|
|
4952
|
+
});
|
|
4953
|
+
const spawnBudgetErrors = spawnBudgetValidation.errors.filter((error) => error.kind === "spawn-budget");
|
|
4954
|
+
if (spawnBudgetErrors.length > 0) {
|
|
4955
|
+
return buildRequestedModeError(requestParams, `workflowScript validation failed before child launch; no children launched. ${spawnBudgetErrors.map((error) => error.message).join(" ")}`);
|
|
4956
|
+
}
|
|
4957
|
+
for (const warning of spawnBudgetValidation.warnings ?? []) console.warn(`[pi-subagents] ${warning.message}`);
|
|
4922
4958
|
const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
|
|
4923
4959
|
if (acceptanceErrors.length > 0) return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
|
|
4924
4960
|
const foregroundWorkflowRunId = encodeIndexSegment(_id);
|
|
@@ -5512,6 +5548,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5512
5548
|
try {
|
|
5513
5549
|
const workflow = await runWorkflowScript({
|
|
5514
5550
|
script: workflowScript,
|
|
5551
|
+
workflowRunId,
|
|
5515
5552
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5516
5553
|
timeoutMs: timeout,
|
|
5517
5554
|
signal: controller.signal,
|
|
@@ -5527,6 +5564,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5527
5564
|
},
|
|
5528
5565
|
...(workflowState ? { state: workflowState } : {}),
|
|
5529
5566
|
onTrace: updateTrace,
|
|
5567
|
+
onChildSettled: (notification) => {
|
|
5568
|
+
appendWorkflowEvent({
|
|
5569
|
+
type: "subagent.workflow.child_settled",
|
|
5570
|
+
childKey: notification.childKey,
|
|
5571
|
+
...(notification.childRunId ? { childRunId: notification.childRunId } : {}),
|
|
5572
|
+
outcome: notification.outcome,
|
|
5573
|
+
workflowRunning: notification.workflowRunning,
|
|
5574
|
+
});
|
|
5575
|
+
try {
|
|
5576
|
+
deps.pi.sendMessage(
|
|
5577
|
+
{
|
|
5578
|
+
customType: "subagent-incremental-child-notify",
|
|
5579
|
+
content: formatIncrementalChildCompletion(notification),
|
|
5580
|
+
display: notification.outcome !== "completed",
|
|
5581
|
+
},
|
|
5582
|
+
{ triggerTurn: scheduledCompletionTriggersTurn(requestParams.scheduleOrigin, notification.outcome) },
|
|
5583
|
+
);
|
|
5584
|
+
} catch (sendError) {
|
|
5585
|
+
console.error(`Failed to send incremental child completion notification for '${notification.childKey}':`, sendError);
|
|
5586
|
+
}
|
|
5587
|
+
deps.refreshResultDelivery?.();
|
|
5588
|
+
},
|
|
5530
5589
|
onLanePlan: (lanes) => {
|
|
5531
5590
|
status.workflowGraph = buildWorkflowLaneGraph(workflowRunId, lanes, status.workflowGraph);
|
|
5532
5591
|
applyWorkflowLaneTrace(status.workflowGraph, status.workflow?.trace ?? []);
|
|
@@ -5539,7 +5598,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5539
5598
|
persist({ tolerateStatusWriteFailure: true });
|
|
5540
5599
|
} });
|
|
5541
5600
|
},
|
|
5542
|
-
admit: (calls) => {
|
|
5601
|
+
admit: async (calls, admissionSignal) => {
|
|
5602
|
+
await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: parentCwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
|
|
5543
5603
|
const outputClaims = workflowChildOutputClaims({ ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
5544
5604
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
5545
5605
|
status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
@@ -5786,6 +5846,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5786
5846
|
try {
|
|
5787
5847
|
const workflow = await runWorkflowScript({
|
|
5788
5848
|
script: requestParams.workflowScript,
|
|
5849
|
+
workflowRunId: foregroundWorkflowRunId,
|
|
5789
5850
|
...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
|
|
5790
5851
|
globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
|
|
5791
5852
|
timeoutMs: timeout,
|
|
@@ -5804,7 +5865,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
5804
5865
|
}
|
|
5805
5866
|
sendWorkflowProgress();
|
|
5806
5867
|
},
|
|
5807
|
-
admit: (calls) => {
|
|
5868
|
+
admit: async (calls, admissionSignal) => {
|
|
5869
|
+
await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: ctx.cwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
|
|
5808
5870
|
const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: foregroundWorkflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
|
|
5809
5871
|
if (outputClaims.error) throw new Error(outputClaims.error);
|
|
5810
5872
|
claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
|
|
@@ -6062,16 +6124,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6062
6124
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
6063
6125
|
return handleHerdrProjectPaneAction(action as (typeof HERDR_PROJECT_PANE_ACTIONS)[number], paramsWithResolvedCwd, { cwd: requestCwd, state: deps.state, signal });
|
|
6064
6126
|
}
|
|
6065
|
-
if ((
|
|
6127
|
+
if ((INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
|
|
6066
6128
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
6067
6129
|
return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
|
|
6068
6130
|
}
|
|
6069
6131
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
6070
|
-
return
|
|
6132
|
+
return handleInspectorAction(action as (typeof INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
|
|
6071
6133
|
state: deps.state,
|
|
6072
6134
|
cwd: requestCwd,
|
|
6073
6135
|
...(deps.config.missions ? { missions: deps.config.missions } : {}),
|
|
6074
6136
|
...(deps.config.authorityPolicy ? { authorityPolicy: deps.config.authorityPolicy } : {}),
|
|
6137
|
+
plugins: createBuiltinInspectorPlugins(),
|
|
6075
6138
|
signal,
|
|
6076
6139
|
});
|
|
6077
6140
|
}
|
|
@@ -6702,65 +6765,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6702
6765
|
|
|
6703
6766
|
let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
6704
6767
|
let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
const forkThinkingDowngrades = new Map<number, string>();
|
|
6768
|
+
// Forked children keep their requested thinking level. Signed Anthropic thinking
|
|
6769
|
+
// blocks are stripped from the inherited transcript by the resolver (they are bound
|
|
6770
|
+
// to the parent session), which is not a reason to disable the child's own reasoning.
|
|
6709
6771
|
try {
|
|
6710
|
-
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
6711
|
-
const parentModel = requestParentModel;
|
|
6712
|
-
prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent, storedOrigin) => {
|
|
6713
|
-
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
6714
|
-
if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
|
|
6715
|
-
forkThinkingRequirements.set(index, true);
|
|
6716
|
-
return;
|
|
6717
|
-
}
|
|
6718
|
-
const effectiveStoredOrigin = storedOrigin === "configured" && modelOverride === undefined && agentConfig?.model === undefined
|
|
6719
|
-
? undefined
|
|
6720
|
-
: storedOrigin;
|
|
6721
|
-
const origin = resolveModelOrigin({
|
|
6722
|
-
fromParent: modelOverrideFromParent,
|
|
6723
|
-
storedOrigin: effectiveStoredOrigin,
|
|
6724
|
-
explicitModel: modelOverrideFromParent || effectiveStoredOrigin === "configured" ? undefined : modelOverride,
|
|
6725
|
-
agentModel: agentConfig?.model,
|
|
6726
|
-
parentModel,
|
|
6727
|
-
});
|
|
6728
|
-
const primaryModel = modelOverrideFromParent
|
|
6729
|
-
? modelOverride
|
|
6730
|
-
: resolveEffectiveSubagentModel(
|
|
6731
|
-
effectiveStoredOrigin === "configured" ? undefined : modelOverride,
|
|
6732
|
-
agentConfig?.model,
|
|
6733
|
-
parentModel,
|
|
6734
|
-
forkAvailableModels,
|
|
6735
|
-
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6736
|
-
{ source: origin === "explicit" ? "explicit" : "inherited" },
|
|
6737
|
-
);
|
|
6738
|
-
const candidates = buildModelCandidates(
|
|
6739
|
-
primaryModel,
|
|
6740
|
-
agentConfig?.fallbackModels,
|
|
6741
|
-
forkAvailableModels,
|
|
6742
|
-
agentConfig?.modelProvider ?? parentModel?.provider,
|
|
6743
|
-
{
|
|
6744
|
-
primaryModelFromParent: origin === "inherited",
|
|
6745
|
-
origin,
|
|
6746
|
-
},
|
|
6747
|
-
);
|
|
6748
|
-
forkThinkingRequirements.set(
|
|
6749
|
-
index,
|
|
6750
|
-
candidates.length === 0
|
|
6751
|
-
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
|
|
6752
|
-
);
|
|
6753
|
-
};
|
|
6754
6772
|
const pruneSession = contextPolicy.usesFork && deps.config.forkContext?.mode === "pruned"
|
|
6755
6773
|
? await createPrunedForkSessionWriter(ctx, deps.config.forkContext, signal)
|
|
6756
6774
|
: undefined;
|
|
6757
|
-
const forkContextResolver = createForkContextResolver(
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6775
|
+
const forkContextResolver = createForkContextResolver(
|
|
6776
|
+
ctx.sessionManager,
|
|
6777
|
+
contextPolicy.usesFork ? "fork" : undefined,
|
|
6778
|
+
pruneSession ? { pruneSession } : {},
|
|
6779
|
+
);
|
|
6761
6780
|
prepareForkSessionForIndex = forkContextResolver.prepareSessionForIndex;
|
|
6762
6781
|
forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
|
|
6763
|
-
forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
|
|
6764
6782
|
} catch (error) {
|
|
6765
6783
|
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
6766
6784
|
}
|
|
@@ -6863,23 +6881,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6863
6881
|
}
|
|
6864
6882
|
const sessionDirForIndex = (idx?: number) =>
|
|
6865
6883
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
6866
|
-
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0
|
|
6884
|
+
const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0) => {
|
|
6867
6885
|
if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
|
|
6868
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6869
6886
|
return forkSessionFileForIndex(idx);
|
|
6870
6887
|
};
|
|
6871
|
-
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0
|
|
6888
|
+
const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0) => {
|
|
6872
6889
|
if (!shouldForkAgent(contextPolicy, agentName)) return;
|
|
6873
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6874
6890
|
await prepareForkSessionForIndex(idx);
|
|
6875
6891
|
};
|
|
6876
|
-
const
|
|
6877
|
-
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
6878
|
-
prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
6879
|
-
const override = forkThinkingOverrideForIndex(idx);
|
|
6880
|
-
if (override === "off") forkThinkingDowngrades.set(idx, agentName);
|
|
6881
|
-
return override ?? delegatedThinkingOverride;
|
|
6882
|
-
};
|
|
6892
|
+
const thinkingOverrideForTask: ThinkingOverrideForTask = () => delegatedThinkingOverride;
|
|
6883
6893
|
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) =>
|
|
6884
6894
|
forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
6885
6895
|
const childSessionFileForIndex = (idx?: number) =>
|
|
@@ -6968,7 +6978,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
6968
6978
|
sessionDirForIndex,
|
|
6969
6979
|
sessionFileForIndex: childSessionFileForIndex,
|
|
6970
6980
|
sessionFileForTask: childSessionFileForTask,
|
|
6971
|
-
thinkingOverrideForTask
|
|
6981
|
+
thinkingOverrideForTask,
|
|
6972
6982
|
artifactConfig,
|
|
6973
6983
|
artifactsDir,
|
|
6974
6984
|
backgroundRequestedWhileClarifying,
|
|
@@ -7082,7 +7092,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
7082
7092
|
availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
7083
7093
|
currentProvider: requestParentModel?.provider,
|
|
7084
7094
|
modelScope,
|
|
7085
|
-
thinkingOverrideForTask
|
|
7095
|
+
thinkingOverrideForTask,
|
|
7086
7096
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
7087
7097
|
});
|
|
7088
7098
|
} catch (error) {
|
|
@@ -7173,7 +7183,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
7173
7183
|
const asyncResult = await runAsyncPath(execData, deps);
|
|
7174
7184
|
if (asyncResult) {
|
|
7175
7185
|
asyncLaunchFailed = asyncResult.isError === true;
|
|
7176
|
-
return attachMission(withRunFanoutBudget(withResolvedContext(
|
|
7186
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(asyncResult, contextPolicy.contextSummary), runFanoutBudget));
|
|
7177
7187
|
}
|
|
7178
7188
|
if (foregroundControl) {
|
|
7179
7189
|
writeNestedForegroundEvent("subagent.nested.started");
|
|
@@ -7183,11 +7193,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
7183
7193
|
if (hasSingle) {
|
|
7184
7194
|
const result = await runSinglePath(execData, deps);
|
|
7185
7195
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
7186
|
-
return attachMission(withRunFanoutBudget(withResolvedContext(
|
|
7196
|
+
return attachMission(withRunFanoutBudget(withResolvedContext(result, contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
|
|
7187
7197
|
}
|
|
7188
7198
|
} catch (error) {
|
|
7189
7199
|
asyncLaunchFailed = effectiveAsync;
|
|
7190
|
-
const errorResult =
|
|
7200
|
+
const errorResult = toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
7191
7201
|
if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
|
|
7192
7202
|
return attachMission(errorResult);
|
|
7193
7203
|
} finally {
|
|
@@ -243,6 +243,7 @@ export function reconcileDetachedWorkflowChildCompletion(input: {
|
|
|
243
243
|
cwd: next.cwd,
|
|
244
244
|
sessionId: next.sessionId ?? (typeof existing?.sessionId === "string" ? existing.sessionId : undefined),
|
|
245
245
|
completionOwnerId: next.completionOwnerId,
|
|
246
|
+
...(next.scheduleOrigin ? { scheduleOrigin: next.scheduleOrigin } : {}),
|
|
246
247
|
},
|
|
247
248
|
receipt,
|
|
248
249
|
receiptPath,
|
|
@@ -294,6 +295,7 @@ export function reconcileDetachedWorkflowChildCompletion(input: {
|
|
|
294
295
|
results,
|
|
295
296
|
sessionId: plan.status.sessionId,
|
|
296
297
|
completionOwnerId: plan.status.completionOwnerId,
|
|
298
|
+
...(plan.status.scheduleOrigin ? { scheduleOrigin: plan.status.scheduleOrigin } : {}),
|
|
297
299
|
timestamp: Date.now(),
|
|
298
300
|
triggerTurn: true,
|
|
299
301
|
});
|
|
@@ -6,6 +6,7 @@ import { readStatus } from "../../shared/utils.ts";
|
|
|
6
6
|
import { steeringReceipt, waitForSteeringAction } from "../background/steering.ts";
|
|
7
7
|
import { requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
|
|
8
8
|
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
9
|
+
import { workflowAsyncChildSteeringGuidance } from "../shared/workflow-async-child-guidance.ts";
|
|
9
10
|
|
|
10
11
|
export interface WorkflowForegroundSteeringTarget {
|
|
11
12
|
control: ForegroundRunControl;
|
|
@@ -85,7 +86,7 @@ export async function steerWorkflowRun(input: {
|
|
|
85
86
|
}
|
|
86
87
|
if (state.workflowControllers?.has(runId)) {
|
|
87
88
|
const route = resolveWorkflowForegroundSteeringTarget({ state, workflowRunId: runId, asyncDirRoot: path.dirname(asyncDir) });
|
|
88
|
-
if (!route.ok) return managementError(route.message);
|
|
89
|
+
if (!route.ok) return managementError([route.message, ...workflowAsyncChildSteeringGuidance(status, state)].join("\n"));
|
|
89
90
|
return steerWorkflowForegroundTarget({ target: route.target, message: input.message, mode: input.mode, index: input.index });
|
|
90
91
|
}
|
|
91
92
|
if (!state.currentSessionId) return managementError("Workflow steering requires an active parent session.");
|
|
@@ -105,11 +105,14 @@ function inferLevel(input: {
|
|
|
105
105
|
const inferredReadOnly = readOnlyTask || ((readOnlyAgent || input.acceptanceRole === "read-only") && !taskMayWrite);
|
|
106
106
|
const roleResolvesReadOnly = input.acceptanceRole !== undefined && inferredReadOnly;
|
|
107
107
|
const dynamicResolvesReadOnly = inferredReadOnly && !writeTask;
|
|
108
|
-
const
|
|
108
|
+
const riskyKeywordPattern = /\b(?:release|migration|migrate|security|data[- ]loss|destructive|post-review|fix pass)\b/;
|
|
109
|
+
const keywordRiskReadOnly = input.acceptanceRole === undefined
|
|
110
|
+
? intent.kind === "read-only" && !riskyKeywordPattern.test(task)
|
|
111
|
+
: inferredReadOnly;
|
|
109
112
|
const risky = Boolean(input.async && writeTask)
|
|
110
113
|
|| (Boolean(input.dynamic) && !roleResolvesReadOnly && !dynamicResolvesReadOnly)
|
|
111
114
|
|| (Boolean(input.dynamicGroup) && !roleResolvesReadOnly && !dynamicResolvesReadOnly)
|
|
112
|
-
|| (!keywordRiskReadOnly &&
|
|
115
|
+
|| (!keywordRiskReadOnly && riskyKeywordPattern.test(task));
|
|
113
116
|
|
|
114
117
|
if (risky) {
|
|
115
118
|
reasons.push(input.async ? "async write-capable or risky run" : "risky write-capable run");
|
|
@@ -108,6 +108,8 @@ export interface AsyncStatusWorkflowRow {
|
|
|
108
108
|
modelThinking?: string;
|
|
109
109
|
activity?: string;
|
|
110
110
|
startedAt?: number;
|
|
111
|
+
endedAt?: number;
|
|
112
|
+
durationMs?: number;
|
|
111
113
|
tokens?: number;
|
|
112
114
|
window?: number;
|
|
113
115
|
overflow?: number;
|
|
@@ -536,6 +538,8 @@ function projectLoadedWorkflowRow(step: AsyncJobStep, index: number, preflight?:
|
|
|
536
538
|
...(modelThinking ? { modelThinking } : {}),
|
|
537
539
|
...(activity ? { activity } : {}),
|
|
538
540
|
...(step.startedAt !== undefined ? { startedAt: step.startedAt } : {}),
|
|
541
|
+
...(step.endedAt !== undefined ? { endedAt: step.endedAt } : {}),
|
|
542
|
+
...(step.durationMs !== undefined ? { durationMs: step.durationMs } : {}),
|
|
539
543
|
...(step.tokens?.total !== undefined ? { tokens: step.tokens.total } : {}),
|
|
540
544
|
...(step.tokens?.window !== undefined ? { window: step.tokens.window } : {}),
|
|
541
545
|
...(preflight ? { preflight } : {}),
|
|
@@ -29,6 +29,8 @@ export interface SubagentCapabilityAudit {
|
|
|
29
29
|
effectiveMcpTools: string[];
|
|
30
30
|
agentAllowed: boolean;
|
|
31
31
|
agentRestrictionSources?: string[];
|
|
32
|
+
/** Builtin tools declared but unavailable on the host runtime. */
|
|
33
|
+
unavailableHostBuiltins?: string[];
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export interface RegisterSubagentCapabilityCeilingOptions {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type { ExtensionAPI
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { listBackgroundWorkProviders } from "../../api/background-work.ts";
|
|
3
3
|
import { ReadonlyDrainObservation } from "./readonly-drain-observation.ts";
|
|
4
|
-
import registerFanoutChildSubagentExtension from "../../extension/fanout-child.ts";
|
|
4
|
+
import registerFanoutChildSubagentExtension, { createChildSafeState } from "../../extension/fanout-child.ts";
|
|
5
5
|
import registerSubagentFastModeExtension from "./fast-mode-extension.ts";
|
|
6
6
|
import registerSubagentPromptRuntime from "./subagent-prompt-runtime.ts";
|
|
7
7
|
import type { ChildRuntimeConfig } from "./child-runtime-config.ts";
|
|
8
8
|
import type { ChildToolDiagnostic } from "./tool-availability.ts";
|
|
9
9
|
import type { ChildSessionLaunch } from "./child-session.ts";
|
|
10
|
+
import type { ArbiterModelContext } from "./llm-intent-arbiter.ts";
|
|
10
11
|
import type { ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
11
12
|
import { projectRuntimeAcknowledgedExtensions } from "./runtime-acknowledged-extensions.ts";
|
|
12
13
|
|
|
@@ -130,17 +131,22 @@ export function createChildHooks(config: ChildRuntimeConfig): ChildHookExtension
|
|
|
130
131
|
export function createCapturedChildHooks(config: ChildRuntimeConfig, runner = false) {
|
|
131
132
|
let diagnostic: ChildToolDiagnostic | undefined;
|
|
132
133
|
let acknowledgedIds: string[] | undefined;
|
|
133
|
-
let completionIntentContext:
|
|
134
|
+
let completionIntentContext: ArbiterModelContext | undefined;
|
|
135
|
+
let finalDrainHeld = false;
|
|
134
136
|
const capture: OwnedCapture = {
|
|
135
137
|
toolDiagnostic: (value) => { diagnostic = value; },
|
|
136
138
|
runtimeAcknowledgements: (ids) => { acknowledgedIds = ids; },
|
|
137
139
|
};
|
|
138
140
|
Object.assign(config, capture);
|
|
139
|
-
const hooks = childHooks(config, capture);
|
|
141
|
+
const hooks = childHooks(config, capture, (held) => { finalDrainHeld = held; });
|
|
140
142
|
if (runner) {
|
|
141
143
|
hooks.push({ name: "pi-subagents:completion-intent", factory: (pi) => pi.on("session_start", (_event, childCtx) => {
|
|
142
|
-
// Retain only attempt model services, not the live child session.
|
|
143
|
-
completionIntentContext = {
|
|
144
|
+
// Retain only attempt model services and the session id string, not the live child session.
|
|
145
|
+
completionIntentContext = {
|
|
146
|
+
model: childCtx.model,
|
|
147
|
+
modelRegistry: childCtx.modelRegistry,
|
|
148
|
+
sessionId: childCtx.sessionManager.getSessionId(),
|
|
149
|
+
};
|
|
144
150
|
}) });
|
|
145
151
|
const proof = promptProofs.get(hooks[0]!.factory);
|
|
146
152
|
if (proof) proof.factories = hooks.map((hook) => hook.factory);
|
|
@@ -150,18 +156,27 @@ export function createCapturedChildHooks(config: ChildRuntimeConfig, runner = fa
|
|
|
150
156
|
completionIntentContext: () => completionIntentContext,
|
|
151
157
|
toolDiagnostic: () => diagnostic,
|
|
152
158
|
runtimeAcknowledgedExtensions: () => acknowledgedIds ? projectRuntimeAcknowledgedExtensions(acknowledgedIds) : undefined,
|
|
159
|
+
finalDrainHeld: () => finalDrainHeld,
|
|
153
160
|
};
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
function childHooks(config: ChildRuntimeConfig, capture?: OwnedCapture): ChildHookExtension[] {
|
|
163
|
+
function childHooks(config: ChildRuntimeConfig, capture?: OwnedCapture, holdFinalDrain?: (held: boolean) => void): ChildHookExtension[] {
|
|
157
164
|
const snapshot = readonlyConfig(config, capture);
|
|
158
165
|
const proof: PromptProof | undefined = snapshot === undefined ? undefined : { config, snapshot, capture };
|
|
166
|
+
const runtime = Object.create(config) as ChildRuntimeConfig;
|
|
167
|
+
const ownedState = Object.getOwnPropertyDescriptor(config, "runtimeState");
|
|
168
|
+
if (!ownedState || !("value" in ownedState) || ownedState.value == null) {
|
|
169
|
+
Object.defineProperty(runtime, "runtimeState", { configurable: true, enumerable: true, writable: true, value: createChildSafeState() });
|
|
170
|
+
}
|
|
171
|
+
if (holdFinalDrain) {
|
|
172
|
+
Object.defineProperty(runtime, "holdFinalDrain", { configurable: true, enumerable: true, writable: true, value: holdFinalDrain });
|
|
173
|
+
}
|
|
159
174
|
const hooks: ChildHookExtension[] = [
|
|
160
175
|
{ name: "pi-subagents:prompt-runtime", factory: function promptRuntime(pi) {
|
|
161
|
-
if (!proof?.observeNext) return registerSubagentPromptRuntime(pi,
|
|
176
|
+
if (!proof?.observeNext) return registerSubagentPromptRuntime(pi, runtime);
|
|
162
177
|
proof.observation = proof.observeNext;
|
|
163
178
|
proof.observeNext = undefined;
|
|
164
|
-
registerSubagentPromptRuntime(pi,
|
|
179
|
+
registerSubagentPromptRuntime(pi, runtime, proof.observation);
|
|
165
180
|
} },
|
|
166
181
|
];
|
|
167
182
|
if (proof) {
|
|
@@ -169,6 +184,6 @@ function childHooks(config: ChildRuntimeConfig, capture?: OwnedCapture): ChildHo
|
|
|
169
184
|
promptProofs.set(hooks[0]!.factory, proof);
|
|
170
185
|
}
|
|
171
186
|
if (config.fast) hooks.push({ name: "pi-subagents:fast-mode", factory: (pi) => registerSubagentFastModeExtension(pi) });
|
|
172
|
-
if (config.fanoutChild) hooks.push({ name: "pi-subagents:fanout-child", factory: (pi) => registerFanoutChildSubagentExtension(pi,
|
|
187
|
+
if (config.fanoutChild) hooks.push({ name: "pi-subagents:fanout-child", factory: (pi) => registerFanoutChildSubagentExtension(pi, runtime) });
|
|
173
188
|
return hooks;
|
|
174
189
|
}
|