pi-subagents 0.66.0 → 0.68.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 +138 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +43 -15
- package/docs/configuration.md +67 -23
- package/docs/extension-api.md +38 -19
- package/docs/missions.md +10 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +22 -12
- package/docs/standalone-background.md +59 -0
- package/docs/tool-reference.md +33 -20
- package/docs/watchdog.md +39 -10
- package/docs/workflows.md +37 -13
- package/index.ts +5 -2
- package/inspector-runner.mjs +2 -2
- package/package.json +4 -2
- package/prompts/parallel-review.md +1 -1
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +32 -0
- package/skills/pi-subagents/SKILL.md +32 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +11 -9
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +190 -71
- package/src/agents/builtin-names.ts +1 -0
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +94 -59
- package/src/api/required-child-extensions.ts +6 -0
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +66 -4
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +62 -39
- package/src/extension/public-execution.ts +7 -5
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +81 -81
- package/src/extension/tool-description.ts +30 -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 +44 -31
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +348 -176
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +17 -12
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +23 -10
- package/src/runs/background/binary-bootstrap.ts +38 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +105 -7
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +81 -33
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +12 -33
- package/src/runs/background/runner-child-launch.ts +6 -1
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +51 -18
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +441 -304
- package/src/runs/background/subagent-wait.ts +176 -28
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +134 -248
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +374 -178
- 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 +38 -11
- package/src/runs/shared/async-status-projection.ts +127 -33
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +28 -5
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +8 -1
- package/src/runs/shared/child-session.ts +127 -52
- package/src/runs/shared/child-tool-plan.ts +142 -11
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
- 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-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +45 -15
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +68 -8
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +74 -30
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -7
- package/src/slash/subagents-admin.ts +24 -13
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +168 -37
- package/src/watchdog/child-status.ts +28 -28
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +21 -1
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +10 -2
- package/src/watchdog/register-main.ts +39 -35
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +123 -74
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +100 -27
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +3 -0
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +5 -3
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +279 -10
- package/src/workflows/workflow-checklist.ts +2 -2
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/runner-server-preload.mjs +0 -13
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
|
@@ -22,6 +22,10 @@ function parseStepBody(agent: string, sectionBody: string): ChainStepConfig {
|
|
|
22
22
|
const key = keyValue.trim().toLowerCase();
|
|
23
23
|
const rawValue = rawValueValue.trim();
|
|
24
24
|
|
|
25
|
+
if (key === "machine") {
|
|
26
|
+
if (rawValue) step.machine = rawValue;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
25
29
|
if (key === "output") {
|
|
26
30
|
if (rawValue === "false") step.output = false;
|
|
27
31
|
else if (rawValue) step.output = rawValue;
|
|
@@ -257,6 +261,7 @@ export function serializeChain(config: ChainConfig): string {
|
|
|
257
261
|
for (let i = 0; i < config.steps.length; i++) {
|
|
258
262
|
const step = config.steps[i]!;
|
|
259
263
|
lines.push(`## ${step.agent}`);
|
|
264
|
+
if (step.machine) lines.push(`machine: ${step.machine}`);
|
|
260
265
|
if (step.output === false) lines.push("output: false");
|
|
261
266
|
else if (step.output) lines.push(`output: ${step.output}`);
|
|
262
267
|
if (step.phase) lines.push(`phase: ${step.phase}`);
|
|
@@ -24,7 +24,6 @@ export interface RuntimeAgentDefinition {
|
|
|
24
24
|
allowNestedSubagents?: boolean;
|
|
25
25
|
mcpDirectTools?: readonly string[];
|
|
26
26
|
model?: string;
|
|
27
|
-
fallbackModels?: readonly string[];
|
|
28
27
|
thinking?: string | false;
|
|
29
28
|
systemPromptMode?: "append" | "replace";
|
|
30
29
|
inheritProjectContext?: boolean;
|
|
@@ -37,6 +36,7 @@ export interface RuntimeAgentDefinition {
|
|
|
37
36
|
defaultAcceptance?: AcceptanceInput;
|
|
38
37
|
acceptanceRole?: AcceptanceRole;
|
|
39
38
|
runner?: AgentRunnerConfig;
|
|
39
|
+
machine?: string;
|
|
40
40
|
skills?: readonly string[];
|
|
41
41
|
skillPath?: readonly string[];
|
|
42
42
|
extensions?: readonly string[];
|
|
@@ -199,9 +199,9 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
199
199
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Runtime agent definition must be an object.");
|
|
200
200
|
const definition = value as Record<string, unknown>;
|
|
201
201
|
const supported = new Set([
|
|
202
|
-
"description", "systemPrompt", "aliases", "tools", "excludeTools", "allowNestedSubagents", "mcpDirectTools", "model", "
|
|
202
|
+
"description", "systemPrompt", "aliases", "tools", "excludeTools", "allowNestedSubagents", "mcpDirectTools", "model", "thinking",
|
|
203
203
|
"systemPromptMode", "inheritProjectContext", "inheritGlobalContext", "inheritSkills", "defaultContext", "defaultAsync", "defaultTimeoutMs",
|
|
204
|
-
"defaultToolTimeoutMs", "defaultAcceptance", "acceptanceRole", "runner", "skills", "skillPath",
|
|
204
|
+
"defaultToolTimeoutMs", "defaultAcceptance", "acceptanceRole", "runner", "machine", "skills", "skillPath",
|
|
205
205
|
"extensions", "subagentOnlyExtensions", "mutationTools", "output", "outputMode", "defaultReads", "defaultProgress", "interactive",
|
|
206
206
|
"maxSubagentDepth", "completionGuard", "toolBudget", "permissions",
|
|
207
207
|
]);
|
|
@@ -223,7 +223,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
223
223
|
const allowNestedSubagents = validateBoolean(definition.allowNestedSubagents, "Runtime agent definition allowNestedSubagents");
|
|
224
224
|
const mcpDirectTools = validateStringList(definition.mcpDirectTools, "Runtime agent definition mcpDirectTools");
|
|
225
225
|
const model = validateOptionalString(definition.model, "Runtime agent definition model");
|
|
226
|
-
|
|
226
|
+
if ((definition as Record<string, unknown>).fallbackModels !== undefined) throw new Error("Runtime agent definition fallbackModels was removed; configure one model instead.");
|
|
227
227
|
const inheritProjectContext = validateBoolean(definition.inheritProjectContext, "Runtime agent definition inheritProjectContext");
|
|
228
228
|
const inheritGlobalContext = validateBoolean(definition.inheritGlobalContext, "Runtime agent definition inheritGlobalContext");
|
|
229
229
|
const inheritSkills = validateBoolean(definition.inheritSkills, "Runtime agent definition inheritSkills");
|
|
@@ -237,6 +237,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
237
237
|
const extensions = validateStringList(definition.extensions, "Runtime agent definition extensions");
|
|
238
238
|
const subagentOnlyExtensions = validateStringList(definition.subagentOnlyExtensions, "Runtime agent definition subagentOnlyExtensions");
|
|
239
239
|
const mutationTools = validateStringList(definition.mutationTools, "Runtime agent definition mutationTools");
|
|
240
|
+
const machine = validateOptionalString(definition.machine, "Runtime agent definition machine");
|
|
240
241
|
const output = validateOptionalString(definition.output, "Runtime agent definition output");
|
|
241
242
|
const defaultReads = validateStringList(definition.defaultReads, "Runtime agent definition defaultReads");
|
|
242
243
|
const defaultProgress = validateBoolean(definition.defaultProgress, "Runtime agent definition defaultProgress");
|
|
@@ -254,7 +255,6 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
254
255
|
...(allowNestedSubagents !== undefined ? { allowNestedSubagents } : {}),
|
|
255
256
|
...(mcpDirectTools ? { mcpDirectTools } : {}),
|
|
256
257
|
...(model ? { model } : {}),
|
|
257
|
-
...(fallbackModels ? { fallbackModels } : {}),
|
|
258
258
|
...(thinking !== undefined ? { thinking: thinking as string | false } : {}),
|
|
259
259
|
...(systemPromptMode !== undefined ? { systemPromptMode: systemPromptMode as "append" | "replace" } : {}),
|
|
260
260
|
...(inheritProjectContext !== undefined ? { inheritProjectContext } : {}),
|
|
@@ -272,6 +272,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
272
272
|
...(extensions ? { extensions } : {}),
|
|
273
273
|
...(subagentOnlyExtensions ? { subagentOnlyExtensions } : {}),
|
|
274
274
|
...(mutationTools ? { mutationTools } : {}),
|
|
275
|
+
...(machine ? { machine } : {}),
|
|
275
276
|
...(output ? { output } : {}),
|
|
276
277
|
...(outputMode !== undefined ? { outputMode: outputMode as OutputMode } : {}),
|
|
277
278
|
...(defaultReads ? { defaultReads } : {}),
|
|
@@ -334,7 +335,6 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
|
|
|
334
335
|
...(definition.allowNestedSubagents !== undefined ? { allowNestedSubagents: definition.allowNestedSubagents } : {}),
|
|
335
336
|
...(definition.mcpDirectTools !== undefined ? { mcpDirectTools: [...definition.mcpDirectTools] } : {}),
|
|
336
337
|
...(definition.model !== undefined ? { model: definition.model } : {}),
|
|
337
|
-
...(definition.fallbackModels !== undefined ? { fallbackModels: [...definition.fallbackModels] } : {}),
|
|
338
338
|
...(definition.thinking !== undefined ? { thinking: definition.thinking } : {}),
|
|
339
339
|
systemPromptMode: definition.systemPromptMode ?? defaultSystemPromptMode(name),
|
|
340
340
|
inheritProjectContext: definition.inheritProjectContext ?? defaultInheritProjectContext(name),
|
|
@@ -354,6 +354,7 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
|
|
|
354
354
|
...(definition.extensions !== undefined ? { extensions: [...definition.extensions] } : {}),
|
|
355
355
|
...(definition.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...definition.subagentOnlyExtensions] } : {}),
|
|
356
356
|
...(definition.mutationTools !== undefined ? { mutationTools: [...definition.mutationTools] } : {}),
|
|
357
|
+
...(definition.machine !== undefined ? { machine: definition.machine } : {}),
|
|
357
358
|
...(definition.output !== undefined ? { output: definition.output } : {}),
|
|
358
359
|
...(definition.outputMode !== undefined ? { outputMode: definition.outputMode } : {}),
|
|
359
360
|
...(definition.defaultReads !== undefined ? { defaultReads: [...definition.defaultReads] } : {}),
|
package/src/api/delegation.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { IntercomBridgeConfig } from "../shared/types.ts";
|
|
2
|
+
|
|
1
3
|
// This is the established extension-to-extension transport. The structured
|
|
2
4
|
// delegation API intentionally reuses it instead of adding a second event
|
|
3
5
|
// protocol. Unstructured legacy direct payloads are rejected.
|
|
@@ -35,6 +37,8 @@ export interface SubagentDelegationRequest {
|
|
|
35
37
|
toolBudget?: SubagentDelegationToolBudget;
|
|
36
38
|
skill?: string | string[] | boolean;
|
|
37
39
|
artifacts?: boolean;
|
|
40
|
+
/** Per-launch bridge config; replaces the global `intercomBridge` config. Pass the same value to preflight to compare digests. */
|
|
41
|
+
intercomBridge?: IntercomBridgeConfig;
|
|
38
42
|
result: SubagentDelegationResultRequest;
|
|
39
43
|
}
|
|
40
44
|
|
package/src/api/preflight.ts
CHANGED
|
@@ -3,30 +3,37 @@ import * as path from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { discoverAgentSnapshot, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig, type AgentDiscoveryAllResult, type AgentScope, type AgentSource } from "../agents/agents.ts";
|
|
5
5
|
import { resolveExecutionAgentScope } from "../agents/agent-scope.ts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { buildModelCandidates, inheritsParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type AvailableModelInfo, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
6
|
+
import { normalizeSkillInput, resolveSkillsWithFallback } from "../agents/skills.ts";
|
|
7
|
+
import { inheritsParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, resolveModelSelection, type AvailableModelInfo, type ParentModel } from "../runs/shared/model-resolution.ts";
|
|
9
8
|
import { resolveModelScopesForAgent } from "../runs/shared/model-scope.ts";
|
|
10
9
|
import { applyThinkingSuffix, resolvePiLaunchToolPlan, type PiLaunchToolPlan } from "../runs/shared/child-tool-plan.ts";
|
|
11
|
-
import {
|
|
10
|
+
import { buildEffectiveSystemPrompt } from "../runs/shared/effective-system-prompt.ts";
|
|
11
|
+
import { normalizeSingleOutputOverride, resolveSingleOutputPath } from "../runs/shared/single-output.ts";
|
|
12
12
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
13
13
|
import { resolveEffectiveThinking } from "../shared/model-info.ts";
|
|
14
14
|
import { assertThinkingWithinCeiling, intersectThinkingCeilings, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
|
|
15
|
-
import { SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, type ArtifactDirPreference, type ArtifactPaths, type JsonSchemaObject, type OutputMode } from "../shared/types.ts";
|
|
15
|
+
import { SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, type ArtifactDirPreference, type ArtifactPaths, type IntercomBridgeConfig, type IntercomBridgeMode, type JsonSchemaObject, type OutputMode } from "../shared/types.ts";
|
|
16
16
|
import { capabilityCeilingAgentRestrictionMessage, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "../runs/shared/capability-ceiling.ts";
|
|
17
17
|
import { resolvePermissionRules } from "../runs/shared/permissions.ts";
|
|
18
18
|
import type { ResolvedMcpDirectToolSelection } from "../runs/shared/mcp-direct-tool-allowlist.ts";
|
|
19
19
|
import { resolveStepBehavior } from "../shared/settings.ts";
|
|
20
20
|
import { canPreferForkFromSnapshot, resolveSubagentLaunchContext } from "../shared/fork-context.ts";
|
|
21
21
|
import { loadConfig } from "../extension/config.ts";
|
|
22
|
-
import {
|
|
22
|
+
import { applyIntercomBridgeToAgent, resolveIntercomBridge, validateIntercomBridgeConfig } from "../intercom/intercom-bridge.ts";
|
|
23
|
+
import { AGENT_DEFINITION_PROJECTION_VERSION, resolveLaunchBinding, stableJsonDigest } from "../shared/launch-contract.ts";
|
|
23
24
|
import { DIRS, TEMP_ROOT_DIR } from "../shared/types.ts";
|
|
24
25
|
import { processTerminalCandidatePath, processTerminalPath } from "../runs/background/process-terminal.ts";
|
|
25
26
|
import { resultFilePath } from "../runs/background/result-files.ts";
|
|
26
27
|
import { nestedResultsPath } from "../runs/shared/nested-events.ts";
|
|
27
28
|
import { normalizeExtensionBindings, type ExtensionBindings } from "../runs/shared/extension-bindings.ts";
|
|
29
|
+
import { resolveRequiredChildExtensions } from "../shared/required-child-extensions.ts";
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
// v3: the contract reports the resolved Intercom bridge state and binds its
|
|
32
|
+
// prompt and tools into launchContractDigest, matching execution (#2127).
|
|
33
|
+
export const SUBAGENT_LAUNCH_CONTRACT_VERSION = 3 as const;
|
|
34
|
+
|
|
35
|
+
/** Stands in for the parent session target when the host does not supply one; only custom templates that name the session read it. */
|
|
36
|
+
const PREFLIGHT_ORCHESTRATOR_TARGET = "preflight";
|
|
30
37
|
|
|
31
38
|
export type SubagentLaunchContractReasonCode =
|
|
32
39
|
| "missing_agent"
|
|
@@ -38,7 +45,8 @@ export type SubagentLaunchContractReasonCode =
|
|
|
38
45
|
| "unsupported_mode"
|
|
39
46
|
| "restricted_agent"
|
|
40
47
|
| "thinking_ceiling"
|
|
41
|
-
| "invalid_extension_bindings"
|
|
48
|
+
| "invalid_extension_bindings"
|
|
49
|
+
| "invalid_intercom_bridge";
|
|
42
50
|
|
|
43
51
|
export type SubagentLaunchContractDiagnosticCode = SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning" | "workspace_scope_authority";
|
|
44
52
|
|
|
@@ -65,11 +73,13 @@ export interface SubagentLaunchContractInput {
|
|
|
65
73
|
skill?: string | string[] | boolean;
|
|
66
74
|
output?: string | boolean;
|
|
67
75
|
outputMode?: OutputMode;
|
|
68
|
-
outputSchema?: JsonSchemaObject;
|
|
76
|
+
outputSchema?: JsonSchemaObject | false;
|
|
69
77
|
extensionBindings?: ExtensionBindings;
|
|
70
78
|
artifacts?: boolean;
|
|
71
79
|
artifactDir?: ArtifactDirPreference;
|
|
72
80
|
parentSessionFile?: string | null;
|
|
81
|
+
/** Parent session whose host-required child extension snapshot is preflighted. */
|
|
82
|
+
parentSessionId?: string;
|
|
73
83
|
/** Current parent leaf required before an implicit `defaultContext: fork` stays `fork`. */
|
|
74
84
|
parentLeafId?: string | null;
|
|
75
85
|
sessionRoot?: string;
|
|
@@ -80,8 +90,23 @@ export interface SubagentLaunchContractInput {
|
|
|
80
90
|
nestedRootRunId?: string;
|
|
81
91
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
82
92
|
inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
93
|
+
/** Builtin tool names the host runtime provides; used to intersect agent-declared tools. */
|
|
94
|
+
hostAvailableBuiltins?: readonly string[];
|
|
95
|
+
/** Per-launch bridge config; replaces the global `intercomBridge` config exactly as the tool and delegation overrides do. */
|
|
96
|
+
intercomBridge?: IntercomBridgeConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Supervisor session target the host will hand to the child. Only a custom
|
|
99
|
+
* bridge instruction file that names the session needs it; the default
|
|
100
|
+
* template is session-independent.
|
|
101
|
+
*/
|
|
102
|
+
orchestratorTarget?: string;
|
|
83
103
|
}
|
|
84
104
|
|
|
105
|
+
/** Bridge activation before tool capability ceilings are applied. */
|
|
106
|
+
export type SubagentLaunchContractIntercomBridge =
|
|
107
|
+
| { active: true; mode: Exclude<IntercomBridgeMode, "off"> }
|
|
108
|
+
| { active: false; mode: IntercomBridgeMode };
|
|
109
|
+
|
|
85
110
|
export interface SubagentLaunchContractAgentCandidate {
|
|
86
111
|
name: string;
|
|
87
112
|
localName?: string;
|
|
@@ -122,6 +147,7 @@ export interface SubagentLaunchContractTools {
|
|
|
122
147
|
toolExtensionPaths: string[];
|
|
123
148
|
runtimeExtensions: string[];
|
|
124
149
|
configuredExtensions: string[];
|
|
150
|
+
requiredExtensionIds: string[];
|
|
125
151
|
extensionArgs: string[];
|
|
126
152
|
disableAmbientExtensions: boolean;
|
|
127
153
|
fanoutAuthorized: boolean;
|
|
@@ -153,7 +179,6 @@ export interface SubagentLaunchContract {
|
|
|
153
179
|
agent: SubagentLaunchContractAgent;
|
|
154
180
|
context: "fresh" | "fork";
|
|
155
181
|
model?: string;
|
|
156
|
-
modelCandidates: string[];
|
|
157
182
|
thinking?: string;
|
|
158
183
|
thinkingCeiling?: ThinkingLevel;
|
|
159
184
|
systemPromptMode: AgentConfig["systemPromptMode"];
|
|
@@ -162,6 +187,7 @@ export interface SubagentLaunchContract {
|
|
|
162
187
|
inheritSkills: boolean;
|
|
163
188
|
skills: SubagentLaunchContractSkills;
|
|
164
189
|
tools: SubagentLaunchContractTools;
|
|
190
|
+
intercomBridge: SubagentLaunchContractIntercomBridge;
|
|
165
191
|
roots: SubagentLaunchContractRoots;
|
|
166
192
|
protocol: {
|
|
167
193
|
lifecycleArtifactVersion: number;
|
|
@@ -257,6 +283,15 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
257
283
|
if (input.artifactDir !== undefined && input.artifactDir !== "project" && input.artifactDir !== "session" && input.artifactDir !== "temp") {
|
|
258
284
|
return { ok: false, code: "invalid_artifact_dir", message: `Unsupported artifactDir '${String(input.artifactDir)}'; expected 'project', 'session', or 'temp'.`, diagnostics };
|
|
259
285
|
}
|
|
286
|
+
const bridgeOverride = input.intercomBridge === undefined ? undefined : validateIntercomBridgeConfig({ value: input.intercomBridge, label: "intercomBridge" });
|
|
287
|
+
if (bridgeOverride && !bridgeOverride.ok) {
|
|
288
|
+
return { ok: false, code: "invalid_intercom_bridge", message: bridgeOverride.error, diagnostics };
|
|
289
|
+
}
|
|
290
|
+
// Execution always derives a non-empty target, so an empty one here would
|
|
291
|
+
// silently deactivate the bridge and break parity instead of proving it.
|
|
292
|
+
if (input.orchestratorTarget !== undefined && (typeof input.orchestratorTarget !== "string" || !input.orchestratorTarget.trim())) {
|
|
293
|
+
return { ok: false, code: "invalid_intercom_bridge", message: "orchestratorTarget must be a non-empty string when provided.", diagnostics };
|
|
294
|
+
}
|
|
260
295
|
const scope = resolveExecutionAgentScope(input.agentScope);
|
|
261
296
|
const parentProvider = input.preferredProvider ?? input.parentModel?.provider;
|
|
262
297
|
const discovery = discoverAgentSnapshot(effectiveCwd, scope, parentProvider, { includeChains: false });
|
|
@@ -276,20 +311,32 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
276
311
|
if (!resolvedAgent.agent) {
|
|
277
312
|
return { ok: false, code: "missing_agent", message: formatUnknownAgentError(input.agent, unknownAgentDiagnosticContext(discovered)), diagnostics };
|
|
278
313
|
}
|
|
279
|
-
const
|
|
314
|
+
const definitionAgent = resolvedAgent.agent;
|
|
280
315
|
let extensionBindings: ExtensionBindings | undefined;
|
|
281
316
|
try {
|
|
282
317
|
extensionBindings = normalizeExtensionBindings(input.extensionBindings)?.value;
|
|
283
318
|
} catch (error) {
|
|
284
319
|
return { ok: false, code: "invalid_extension_bindings", message: error instanceof Error ? error.message : String(error), diagnostics };
|
|
285
320
|
}
|
|
286
|
-
if (extensionBindings !== undefined && (
|
|
287
|
-
return { ok: false, code: "unsupported_mode", message: `extensionBindings is not supported for runner.type='${
|
|
321
|
+
if (extensionBindings !== undefined && (definitionAgent.runner?.type === "external-cli" || definitionAgent.runner?.type === "external-job")) {
|
|
322
|
+
return { ok: false, code: "unsupported_mode", message: `extensionBindings is not supported for runner.type='${definitionAgent.runner.type}'.`, diagnostics };
|
|
288
323
|
}
|
|
289
|
-
const context = resolveLaunchContractContext(input,
|
|
324
|
+
const context = resolveLaunchContractContext(input, definitionAgent);
|
|
290
325
|
if (context === "fork") {
|
|
291
|
-
diagnostics.push({ code: "host_required", severity: "host-required", message: "Exact fork session branching
|
|
326
|
+
diagnostics.push({ code: "host_required", severity: "host-required", message: "Exact fork session branching requires Pi host session snapshots." });
|
|
327
|
+
}
|
|
328
|
+
// Execution rewrites the discovered agent through the bridge before any
|
|
329
|
+
// other launch resolution, so preflight must hash the same rewritten agent.
|
|
330
|
+
const bridge = resolveIntercomBridge({
|
|
331
|
+
config: loadConfig().intercomBridge,
|
|
332
|
+
...(bridgeOverride ? { override: bridgeOverride.value } : {}),
|
|
333
|
+
context,
|
|
334
|
+
orchestratorTarget: input.orchestratorTarget ?? PREFLIGHT_ORCHESTRATOR_TARGET,
|
|
335
|
+
});
|
|
336
|
+
if (bridge.active && bridge.interpolatesOrchestratorTarget && input.orchestratorTarget === undefined) {
|
|
337
|
+
diagnostics.push({ code: "host_required", severity: "host-required", message: "The intercomBridge instruction file names the supervisor session; supply orchestratorTarget to bind the exact child prompt." });
|
|
292
338
|
}
|
|
339
|
+
const agent = applyIntercomBridgeToAgent(definitionAgent, bridge);
|
|
293
340
|
const effectiveCapabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
|
|
294
341
|
const restrictionMessage = capabilityCeilingAgentRestrictionMessage(agent.name, effectiveCapabilityCeiling);
|
|
295
342
|
if (restrictionMessage) return { ok: false, code: "restricted_agent", message: restrictionMessage, diagnostics };
|
|
@@ -301,6 +348,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
301
348
|
...(input.outputMode !== undefined ? { outputMode: input.outputMode } : {}),
|
|
302
349
|
...(skillInput !== undefined ? { skills: skillInput } : {}),
|
|
303
350
|
...(input.model !== undefined ? { model: input.model } : {}),
|
|
351
|
+
...(input.outputSchema !== undefined ? { outputSchema: input.outputSchema } : {}),
|
|
304
352
|
});
|
|
305
353
|
const requestedSkills = behavior.skills === false ? [] : behavior.skills;
|
|
306
354
|
const resolvedSkills = resolveSkillsWithFallback(
|
|
@@ -316,6 +364,9 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
316
364
|
if (resolvedSkills.missing.length > 0) diagnostics.push({ code: "missing_skill", severity: "error", message: `Missing skills: ${resolvedSkills.missing.join(", ")}` });
|
|
317
365
|
|
|
318
366
|
const externalRunner = agent.runner?.type === "external-cli" || agent.runner?.type === "external-job";
|
|
367
|
+
if (externalRunner && behavior.outputSchema) {
|
|
368
|
+
return { ok: false, code: "unsupported_mode", message: `Agent '${agent.name}' uses runner.type='${agent.runner?.type}' and does not support: structured output.`, diagnostics };
|
|
369
|
+
}
|
|
319
370
|
const availableModels = normalizeAvailableModels(input.availableModels);
|
|
320
371
|
const preferredProvider = agent.modelProvider ?? input.preferredProvider ?? input.parentModel?.provider;
|
|
321
372
|
const modelScopes = resolveModelScopesForAgent(discovered.modelScope, agent.name, input.parentModel);
|
|
@@ -332,19 +383,14 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
332
383
|
input.thinkingCeiling,
|
|
333
384
|
input.inheritedThinkingCeiling,
|
|
334
385
|
);
|
|
335
|
-
const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel,
|
|
336
|
-
const modelCandidates = externalRunner
|
|
337
|
-
? []
|
|
338
|
-
: buildModelCandidates(primaryModel, agent.fallbackModels, availableModels, preferredProvider, {
|
|
386
|
+
const model = externalRunner ? undefined : applyThinkingSuffix(resolveModelSelection(primaryModel, availableModels, preferredProvider, {
|
|
339
387
|
scope: modelScopes,
|
|
340
388
|
primaryModelFromParent: modelOrigin === "inherited" || inheritsParentModel(input.model, agent.model, input.parentModel),
|
|
341
389
|
origin: modelOrigin,
|
|
342
|
-
})
|
|
343
|
-
.map((candidate) => applyThinkingSuffix(candidate, effectiveThinkingConfig, input.thinking !== undefined) ?? candidate);
|
|
390
|
+
}).model, effectiveThinkingConfig, input.thinking !== undefined);
|
|
344
391
|
if (!externalRunner) {
|
|
345
392
|
try {
|
|
346
393
|
assertThinkingWithinCeiling({ model, configThinking: effectiveThinkingConfig, ceiling: thinkingCeiling, agent: agent.name, runId });
|
|
347
|
-
for (const candidate of modelCandidates) assertThinkingWithinCeiling({ model: candidate, configThinking: effectiveThinkingConfig, ceiling: thinkingCeiling, agent: agent.name, runId });
|
|
348
394
|
} catch (error) {
|
|
349
395
|
const message = error instanceof Error ? error.message : String(error);
|
|
350
396
|
diagnostics.push({ code: "thinking_ceiling", severity: "error", message });
|
|
@@ -354,6 +400,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
354
400
|
let toolPlan: PiLaunchToolPlan;
|
|
355
401
|
const permissionRules = resolvePermissionRules(loadConfig().permissions, agent.permissions);
|
|
356
402
|
const fast = input.fast ?? agent.fast;
|
|
403
|
+
const requiredExtensions = externalRunner ? [] : resolveRequiredChildExtensions(input.parentSessionId);
|
|
357
404
|
try {
|
|
358
405
|
toolPlan = resolvePiLaunchToolPlan({
|
|
359
406
|
tools: agent.tools,
|
|
@@ -361,16 +408,17 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
361
408
|
allowNestedSubagents: agent.allowNestedSubagents,
|
|
362
409
|
extensions: agent.extensions,
|
|
363
410
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
411
|
+
requiredExtensions,
|
|
364
412
|
mcpDirectTools: agent.mcpDirectTools,
|
|
365
413
|
cwd: effectiveCwd,
|
|
366
414
|
requireReadTool: resolvedSkills.resolved.length > 0,
|
|
367
|
-
structuredOutput: Boolean(
|
|
415
|
+
structuredOutput: Boolean(behavior.outputSchema),
|
|
368
416
|
fast,
|
|
369
417
|
model,
|
|
370
|
-
modelCandidates,
|
|
371
418
|
capabilityCeiling: effectiveCapabilityCeiling,
|
|
372
419
|
agentName: agent.name,
|
|
373
420
|
permissionRules,
|
|
421
|
+
hostAvailableBuiltins: input.hostAvailableBuiltins,
|
|
374
422
|
});
|
|
375
423
|
} catch (error) {
|
|
376
424
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -399,17 +447,23 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
399
447
|
if (resolvedSkills.missing.length > 0) {
|
|
400
448
|
return { ok: false, code: "missing_skill", message: `Missing skills: ${resolvedSkills.missing.join(", ")}`, diagnostics };
|
|
401
449
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
450
|
+
const effectiveThinking = resolveEffectiveThinking(model, effectiveThinkingConfig);
|
|
451
|
+
const binding = resolveLaunchBinding({
|
|
452
|
+
agent,
|
|
453
|
+
task: input.task ?? "",
|
|
454
|
+
model,
|
|
455
|
+
...(fast !== undefined ? { fast } : {}),
|
|
456
|
+
...(effectiveThinking ? { thinking: effectiveThinking } : {}),
|
|
457
|
+
systemPrompt: buildEffectiveSystemPrompt({ agent, resolvedSkills: resolvedSkills.resolved, cwd: effectiveCwd, ...(outputPath ? { outputPath } : {}) }),
|
|
458
|
+
skills: requestedSkills,
|
|
459
|
+
toolPlan,
|
|
460
|
+
...(outputPath ? { outputPath } : {}),
|
|
461
|
+
outputMode: behavior.outputMode,
|
|
462
|
+
...(behavior.outputSchema ? { structuredOutputSchema: behavior.outputSchema } : {}),
|
|
463
|
+
...(extensionBindings ? { extensionBindings } : {}),
|
|
464
|
+
});
|
|
410
465
|
const candidates = candidateList(input.agent, agent, discovery.all);
|
|
411
466
|
const shadowedCandidates = candidates.filter((candidate) => !candidate.selected);
|
|
412
|
-
const definitionDigest = agentDefinitionDigest(agent);
|
|
413
467
|
const contractBase: Omit<SubagentLaunchContract, "digest"> = {
|
|
414
468
|
version: SUBAGENT_LAUNCH_CONTRACT_VERSION,
|
|
415
469
|
runId,
|
|
@@ -420,13 +474,12 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
420
474
|
source: agent.source,
|
|
421
475
|
filePath: agent.filePath,
|
|
422
476
|
definitionProjectionVersion: AGENT_DEFINITION_PROJECTION_VERSION,
|
|
423
|
-
definitionDigest,
|
|
477
|
+
definitionDigest: binding.definitionDigest,
|
|
424
478
|
shadowedCandidates,
|
|
425
479
|
},
|
|
426
480
|
context,
|
|
427
481
|
...(model ? { model } : {}),
|
|
428
|
-
|
|
429
|
-
...(resolveEffectiveThinking(model, effectiveThinkingConfig) ? { thinking: resolveEffectiveThinking(model, effectiveThinkingConfig) } : {}),
|
|
482
|
+
...(effectiveThinking ? { thinking: effectiveThinking } : {}),
|
|
430
483
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
431
484
|
systemPromptMode: agent.systemPromptMode,
|
|
432
485
|
inheritProjectContext: agent.inheritProjectContext,
|
|
@@ -450,12 +503,15 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
450
503
|
toolExtensionPaths: toolPlan.toolExtensionPaths,
|
|
451
504
|
runtimeExtensions: toolPlan.runtimeExtensions,
|
|
452
505
|
configuredExtensions: toolPlan.configuredExtensions,
|
|
453
|
-
|
|
506
|
+
requiredExtensionIds: toolPlan.requiredExtensions.map(({ id }) => id),
|
|
507
|
+
// Required paths are private launch authority; preflight exposes their safe IDs above.
|
|
508
|
+
extensionArgs: toolPlan.extensionArgs.filter((extensionPath) => !requiredExtensions.some(({ path }) => path === extensionPath)),
|
|
454
509
|
disableAmbientExtensions: toolPlan.disableAmbientExtensions,
|
|
455
510
|
fanoutAuthorized: toolPlan.fanoutAuthorized,
|
|
456
511
|
...(toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {}),
|
|
457
512
|
...(toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}),
|
|
458
513
|
},
|
|
514
|
+
intercomBridge: bridge.active && bridge.mode !== "off" ? { active: true, mode: bridge.mode } : { active: false, mode: bridge.mode },
|
|
459
515
|
roots: {
|
|
460
516
|
cwd: effectiveCwd,
|
|
461
517
|
...(sessionRoot ? { sessionRoot } : {}),
|
|
@@ -477,28 +533,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
477
533
|
packageVersion: packageVersion(),
|
|
478
534
|
},
|
|
479
535
|
diagnostics,
|
|
480
|
-
launchContractDigest:
|
|
481
|
-
task: input.task ?? "",
|
|
482
|
-
definitionDigest,
|
|
483
|
-
...(model ? { model } : {}),
|
|
484
|
-
modelCandidates,
|
|
485
|
-
...(fast !== undefined ? { fast } : {}),
|
|
486
|
-
...(resolveEffectiveThinking(model, effectiveThinkingConfig) ? { thinking: resolveEffectiveThinking(model, effectiveThinkingConfig) } : {}),
|
|
487
|
-
systemPrompt: effectiveSystemPrompt,
|
|
488
|
-
systemPromptMode: agent.systemPromptMode,
|
|
489
|
-
inheritProjectContext: agent.inheritProjectContext,
|
|
490
|
-
inheritGlobalContext: agent.inheritGlobalContext,
|
|
491
|
-
inheritSkills: agent.inheritSkills,
|
|
492
|
-
skills: requestedSkills,
|
|
493
|
-
tools: toolPlan.effectiveToolAllowlist,
|
|
494
|
-
...(toolPlan.excludeTools.length > 0 ? { excludeTools: toolPlan.excludeTools } : {}),
|
|
495
|
-
extensions: toolPlan.extensionArgs,
|
|
496
|
-
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
497
|
-
...(outputPath ? { outputPath } : {}),
|
|
498
|
-
outputMode: behavior.outputMode,
|
|
499
|
-
...(input.outputSchema ? { structuredOutputSchema: input.outputSchema } : {}),
|
|
500
|
-
...(extensionBindings ? { extensionBindings } : {}),
|
|
501
|
-
}),
|
|
536
|
+
launchContractDigest: binding.launchContractDigest,
|
|
502
537
|
};
|
|
503
538
|
return { ok: true, contract: { ...contractBase, digest: digestContract(contractBase) } };
|
|
504
539
|
}
|
package/src/api/shared-types.ts
CHANGED
package/src/extension/config.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { FLEET_KEYBINDING_ACTIONS, type ArtifactDirPreference, type ExtensionCon
|
|
|
6
6
|
import { validateMissionStoreConfig } from "../missions/store.ts";
|
|
7
7
|
import { validateAuthorityPolicy } from "../policy/authority.ts";
|
|
8
8
|
import { getAgentDir } from "../shared/utils.ts";
|
|
9
|
-
import { DEFAULT_MODEL_EXCLUSION_TTL_MS, MAX_MODEL_EXCLUSION_TTL_MS, setDefaultTTL } from "../runs/shared/model-exclusions.ts";
|
|
10
9
|
import { validatePermissionConfig } from "../runs/shared/permissions.ts";
|
|
11
10
|
import { MAX_ABANDONED_SLOT_RELEASE_AFTER_MS, MIN_ABANDONED_SLOT_RELEASE_AFTER_MS } from "../runs/background/active-async-capacity.ts";
|
|
12
11
|
import { normalizeWorktreeBranchPrefix } from "../runs/shared/worktree.ts";
|
|
@@ -97,17 +96,6 @@ function validateCapacityConfig(value: unknown): void {
|
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
/** Validate the user-controlled TTL policy before it reaches the exclusion store. */
|
|
101
|
-
// TEST:test/unit/pi-coding-agent-dir.test.ts[loads and applies model exclusion TTL config]
|
|
102
|
-
function validateModelExclusionsConfig(value: unknown): void {
|
|
103
|
-
if (value === undefined) return;
|
|
104
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("config.modelExclusions must be a JSON object");
|
|
105
|
-
const defaultTtlMs = (value as Record<string, unknown>).defaultTtlMs;
|
|
106
|
-
if (defaultTtlMs !== undefined && (typeof defaultTtlMs !== "number" || !Number.isFinite(defaultTtlMs) || defaultTtlMs <= 0 || defaultTtlMs > MAX_MODEL_EXCLUSION_TTL_MS)) {
|
|
107
|
-
throw new Error(`config.modelExclusions.defaultTtlMs must be a finite positive number no greater than ${MAX_MODEL_EXCLUSION_TTL_MS}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
99
|
function validateOrcaProgressTabsConfig(value: unknown): void {
|
|
112
100
|
if (value === undefined) return;
|
|
113
101
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("config.orcaProgressTabs must be a JSON object");
|
|
@@ -154,6 +142,13 @@ function validateConfig(config: Record<string, unknown>): void {
|
|
|
154
142
|
throw new Error('config.defaultSubagentContext must be "fresh" or "fork"');
|
|
155
143
|
}
|
|
156
144
|
validateForkContextConfig(config.forkContext);
|
|
145
|
+
if (config.checkpointBeforeDeadlineMs !== undefined
|
|
146
|
+
&& (typeof config.checkpointBeforeDeadlineMs !== "number"
|
|
147
|
+
|| !Number.isInteger(config.checkpointBeforeDeadlineMs)
|
|
148
|
+
|| config.checkpointBeforeDeadlineMs <= 0
|
|
149
|
+
|| config.checkpointBeforeDeadlineMs > 2_147_483_647)) {
|
|
150
|
+
throw new Error("config.checkpointBeforeDeadlineMs must be a positive integer no larger than 2147483647");
|
|
151
|
+
}
|
|
157
152
|
if (config.foregroundDetachShortcut !== undefined
|
|
158
153
|
&& (typeof config.foregroundDetachShortcut !== "string" || !isValidKeyId(config.foregroundDetachShortcut))) {
|
|
159
154
|
throw new Error("config.foregroundDetachShortcut must be a valid keybinding string such as \"ctrl+b\"");
|
|
@@ -177,7 +172,7 @@ function validateConfig(config: Record<string, unknown>): void {
|
|
|
177
172
|
validateFleetKeybindingsConfig(config.fleetKeybindings);
|
|
178
173
|
validateArtifactConfig(config.artifactConfig);
|
|
179
174
|
validateCapacityConfig(config.capacity);
|
|
180
|
-
|
|
175
|
+
if (config.modelExclusions !== undefined) throw new Error("config.modelExclusions was removed; model failures are no longer persisted or used for automatic switching");
|
|
181
176
|
validateModelResponseAliases(config.modelResponseAliases);
|
|
182
177
|
validateMainWindowRendererConfig(config.mainWindowRenderer);
|
|
183
178
|
validateOrcaProgressTabsConfig(config.orcaProgressTabs);
|
|
@@ -210,28 +205,6 @@ export function updateConfig(updater: (config: ExtensionConfig) => ExtensionConf
|
|
|
210
205
|
return next;
|
|
211
206
|
}
|
|
212
207
|
|
|
213
|
-
/**
|
|
214
|
-
* Resolve the default TTL that the process-wide exclusion store should use.
|
|
215
|
-
*
|
|
216
|
-
* @param config Extension configuration after validation.
|
|
217
|
-
* @returns The configured TTL in milliseconds, or the built-in 24-hour default.
|
|
218
|
-
*/
|
|
219
|
-
// TEST:test/unit/pi-coding-agent-dir.test.ts[loads and applies model exclusion TTL config]
|
|
220
|
-
export function resolveModelExclusionTTL(config: Pick<ExtensionConfig, "modelExclusions">): number {
|
|
221
|
-
return config.modelExclusions?.defaultTtlMs ?? DEFAULT_MODEL_EXCLUSION_TTL_MS;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Apply the configured exclusion policy to the process-wide model store.
|
|
226
|
-
*
|
|
227
|
-
* @param config Extension configuration after validation.
|
|
228
|
-
* @returns Nothing.
|
|
229
|
-
*/
|
|
230
|
-
// TEST:test/unit/pi-coding-agent-dir.test.ts[loads and applies model exclusion TTL config]
|
|
231
|
-
export function applyModelExclusionsConfig(config: Pick<ExtensionConfig, "modelExclusions">): void {
|
|
232
|
-
setDefaultTTL(resolveModelExclusionTTL(config), { shortenExisting: config.modelExclusions?.defaultTtlMs !== undefined });
|
|
233
|
-
}
|
|
234
|
-
|
|
235
208
|
export function resolveAsyncByDefault(config: Pick<ExtensionConfig, "asyncByDefault">): boolean {
|
|
236
209
|
return config.asyncByDefault !== false;
|
|
237
210
|
}
|
|
@@ -242,12 +215,12 @@ export function loadConfig(): ExtensionConfig {
|
|
|
242
215
|
return readConfigForUpdate(configPath);
|
|
243
216
|
} catch (error) {
|
|
244
217
|
if (error instanceof PrunedForkConfigError) throw error;
|
|
245
|
-
// Explicit route identity and
|
|
218
|
+
// Explicit route identity, worktree, and checkpoint policies must not be silently
|
|
246
219
|
// discarded and replaced by the built-in defaults after validation fails.
|
|
247
220
|
try {
|
|
248
221
|
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8")) as unknown;
|
|
249
222
|
if (raw && typeof raw === "object" && !Array.isArray(raw)
|
|
250
|
-
&& (Object.hasOwn(raw, "worktreeProvider") || Object.hasOwn(raw, "worktreeBranchPrefix") || Object.hasOwn(raw, "modelResponseAliases"))) throw error;
|
|
223
|
+
&& (Object.hasOwn(raw, "worktreeProvider") || Object.hasOwn(raw, "worktreeBranchPrefix") || Object.hasOwn(raw, "modelResponseAliases") || Object.hasOwn(raw, "modelExclusions") || Object.hasOwn(raw, "checkpointBeforeDeadlineMs"))) throw error;
|
|
251
224
|
} catch (readError) {
|
|
252
225
|
if (readError === error) throw error;
|
|
253
226
|
}
|