pi-subagents 0.57.0 → 0.58.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 +39 -0
- package/docs/agents.md +13 -6
- package/docs/extension-api.md +39 -0
- package/docs/models.md +3 -3
- package/docs/tool-reference.md +3 -2
- package/docs/workflows.md +24 -0
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -0
- package/skills/pi-subagents/references/execution-controls.md +1 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/agent-management.ts +23 -0
- package/src/agents/agent-serializer.ts +5 -0
- package/src/agents/agents.ts +38 -3
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +10 -2
- package/src/api/agents.ts +10 -5
- package/src/api/preflight.ts +26 -1
- package/src/extension/index.ts +15 -4
- package/src/extension/rpc.ts +35 -14
- package/src/extension/schemas.ts +2 -2
- package/src/extension/tool-description.ts +8 -4
- package/src/integrations/herdr-status.ts +5 -0
- package/src/runs/background/async-execution.ts +29 -12
- package/src/runs/background/async-resume.ts +6 -2
- package/src/runs/background/async-status.ts +2 -0
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/notify.ts +26 -3
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-watcher.ts +27 -10
- package/src/runs/background/subagent-runner.ts +71 -21
- package/src/runs/foreground/execution.ts +20 -1
- package/src/runs/foreground/subagent-executor.ts +57 -26
- package/src/runs/foreground/workflow-detach-reconcile.ts +128 -17
- package/src/runs/shared/completion-guard.ts +4 -3
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +386 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +155 -42
- package/src/runs/shared/model-exclusions.ts +12 -0
- package/src/runs/shared/model-fallback.ts +16 -4
- package/src/runs/shared/parallel-utils.ts +8 -1
- package/src/runs/shared/pi-args.ts +61 -6
- package/src/runs/shared/single-output.ts +17 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +85 -9
- package/src/shared/formatters.ts +6 -0
- package/src/shared/launch-contract.ts +4 -0
- package/src/shared/types.ts +42 -1
- package/src/shared/utils.ts +6 -29
- package/src/slash/subagents-admin.ts +3 -0
- package/src/tui/render.ts +14 -6
- package/src/workflows/scripted-workflow.ts +52 -5
- package/src/workflows/workflow-receipt.ts +39 -4
package/src/agents/agents.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
62
62
|
thinking?: string | false;
|
|
63
63
|
systemPromptMode: SystemPromptMode;
|
|
64
64
|
inheritProjectContext: boolean;
|
|
65
|
+
inheritGlobalContext: boolean;
|
|
65
66
|
inheritSkills: boolean;
|
|
66
67
|
defaultContext?: AgentDefaultContext;
|
|
67
68
|
acceptanceRole?: AcceptanceRole;
|
|
@@ -73,6 +74,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
73
74
|
mcpDirectTools?: string[];
|
|
74
75
|
extensions?: string[];
|
|
75
76
|
subagentOnlyExtensions?: string[];
|
|
77
|
+
mutationTools?: string[];
|
|
76
78
|
completionGuard?: boolean;
|
|
77
79
|
toolBudget?: ToolBudgetConfig;
|
|
78
80
|
}
|
|
@@ -89,6 +91,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
89
91
|
thinking?: string | false;
|
|
90
92
|
systemPromptMode?: SystemPromptMode;
|
|
91
93
|
inheritProjectContext?: boolean;
|
|
94
|
+
inheritGlobalContext?: boolean;
|
|
92
95
|
inheritSkills?: boolean;
|
|
93
96
|
defaultContext?: AgentDefaultContext | false;
|
|
94
97
|
acceptanceRole?: AcceptanceRole | false;
|
|
@@ -98,6 +101,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
98
101
|
tools?: string[] | false | "inherit";
|
|
99
102
|
extensions?: string[] | false;
|
|
100
103
|
subagentOnlyExtensions?: string[] | false;
|
|
104
|
+
mutationTools?: string[] | false;
|
|
101
105
|
completionGuard?: boolean;
|
|
102
106
|
toolBudget?: ToolBudgetConfig | false;
|
|
103
107
|
}
|
|
@@ -135,6 +139,7 @@ export interface AgentConfig {
|
|
|
135
139
|
thinking?: string | false;
|
|
136
140
|
systemPromptMode: SystemPromptMode;
|
|
137
141
|
inheritProjectContext: boolean;
|
|
142
|
+
inheritGlobalContext: boolean;
|
|
138
143
|
inheritSkills: boolean;
|
|
139
144
|
defaultContext?: AgentDefaultContext;
|
|
140
145
|
defaultAsync?: boolean;
|
|
@@ -152,6 +157,7 @@ export interface AgentConfig {
|
|
|
152
157
|
extensions?: string[];
|
|
153
158
|
extensionsFromDefault?: boolean;
|
|
154
159
|
subagentOnlyExtensions?: string[];
|
|
160
|
+
mutationTools?: string[];
|
|
155
161
|
output?: string;
|
|
156
162
|
outputMode?: OutputMode;
|
|
157
163
|
defaultReads?: string[];
|
|
@@ -710,6 +716,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
710
716
|
...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
|
|
711
717
|
systemPromptMode: agent.systemPromptMode,
|
|
712
718
|
inheritProjectContext: agent.inheritProjectContext,
|
|
719
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
713
720
|
inheritSkills: agent.inheritSkills,
|
|
714
721
|
...(agent.defaultContext !== undefined ? { defaultContext: agent.defaultContext } : {}),
|
|
715
722
|
...(agent.acceptanceRole !== undefined ? { acceptanceRole: agent.acceptanceRole } : {}),
|
|
@@ -721,6 +728,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
721
728
|
...(agent.mcpDirectTools ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),
|
|
722
729
|
...(!agent.extensionsFromDefault && agent.extensions ? { extensions: [...agent.extensions] } : {}),
|
|
723
730
|
...(agent.subagentOnlyExtensions ? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] } : {}),
|
|
731
|
+
...(agent.mutationTools ? { mutationTools: [...agent.mutationTools] } : {}),
|
|
724
732
|
...(agent.completionGuard !== undefined ? { completionGuard: agent.completionGuard } : {}),
|
|
725
733
|
...(agent.toolBudget !== undefined ? { toolBudget: agent.toolBudget } : {}),
|
|
726
734
|
};
|
|
@@ -741,6 +749,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
741
749
|
...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
|
|
742
750
|
...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
|
|
743
751
|
...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
|
|
752
|
+
...(override.inheritGlobalContext !== undefined ? { inheritGlobalContext: override.inheritGlobalContext } : {}),
|
|
744
753
|
...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),
|
|
745
754
|
...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),
|
|
746
755
|
...(override.acceptanceRole !== undefined ? { acceptanceRole: override.acceptanceRole } : {}),
|
|
@@ -750,6 +759,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
750
759
|
...(override.tools !== undefined ? { tools: Array.isArray(override.tools) ? [...override.tools] : override.tools } : {}),
|
|
751
760
|
...(override.extensions !== undefined ? { extensions: override.extensions === false ? false : [...override.extensions] } : {}),
|
|
752
761
|
...(override.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions] } : {}),
|
|
762
|
+
...(override.mutationTools !== undefined ? { mutationTools: override.mutationTools === false ? false : [...override.mutationTools] } : {}),
|
|
753
763
|
...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),
|
|
754
764
|
...(override.toolBudget !== undefined ? { toolBudget: override.toolBudget === false ? false : { ...override.toolBudget, ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}) } } : {}),
|
|
755
765
|
};
|
|
@@ -799,7 +809,7 @@ export function findNearestProjectRoot(cwd: string): string | null {
|
|
|
799
809
|
return findProjectRootCandidates(cwd)[0] ?? null;
|
|
800
810
|
}
|
|
801
811
|
|
|
802
|
-
function findConfiguredProjectRoot(cwd: string): string | null {
|
|
812
|
+
export function findConfiguredProjectRoot(cwd: string): string | null {
|
|
803
813
|
const candidates = findProjectRootCandidates(cwd);
|
|
804
814
|
const nearestRoot = candidates[0];
|
|
805
815
|
if (!nearestRoot) return null;
|
|
@@ -958,6 +968,14 @@ function parseBuiltinOverrideEntry(
|
|
|
958
968
|
}
|
|
959
969
|
}
|
|
960
970
|
|
|
971
|
+
if ("inheritGlobalContext" in input) {
|
|
972
|
+
if (typeof input.inheritGlobalContext === "boolean") {
|
|
973
|
+
override.inheritGlobalContext = input.inheritGlobalContext;
|
|
974
|
+
} else {
|
|
975
|
+
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritGlobalContext'; expected a boolean.`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
961
979
|
if ("inheritSkills" in input) {
|
|
962
980
|
if (typeof input.inheritSkills === "boolean") {
|
|
963
981
|
override.inheritSkills = input.inheritSkills;
|
|
@@ -1037,6 +1055,9 @@ function parseBuiltinOverrideEntry(
|
|
|
1037
1055
|
const subagentOnlyExtensions = parseOverrideStringArrayOrFalse(input.subagentOnlyExtensions, { filePath, name, field: "subagentOnlyExtensions" });
|
|
1038
1056
|
if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;
|
|
1039
1057
|
|
|
1058
|
+
const mutationTools = parseOverrideStringArrayOrFalse(input.mutationTools, { filePath, name, field: "mutationTools" });
|
|
1059
|
+
if (mutationTools !== undefined) override.mutationTools = mutationTools;
|
|
1060
|
+
|
|
1040
1061
|
return Object.keys(override).length > 0 ? override : undefined;
|
|
1041
1062
|
}
|
|
1042
1063
|
|
|
@@ -1272,6 +1293,7 @@ function applyBuiltinOverride(
|
|
|
1272
1293
|
if (override.thinking !== undefined) { if (override.thinking === false) delete next.thinking; else next.thinking = override.thinking; }
|
|
1273
1294
|
if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
|
|
1274
1295
|
if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
|
|
1296
|
+
if (override.inheritGlobalContext !== undefined) next.inheritGlobalContext = override.inheritGlobalContext;
|
|
1275
1297
|
if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;
|
|
1276
1298
|
if (override.defaultContext !== undefined) { if (override.defaultContext === false) delete next.defaultContext; else next.defaultContext = override.defaultContext; }
|
|
1277
1299
|
if (override.acceptanceRole !== undefined) { if (override.acceptanceRole === false) delete next.acceptanceRole; else next.acceptanceRole = override.acceptanceRole; }
|
|
@@ -1281,6 +1303,7 @@ function applyBuiltinOverride(
|
|
|
1281
1303
|
if (override.tools !== undefined) applyToolsOverride(next, override.tools);
|
|
1282
1304
|
if (override.extensions !== undefined) { if (override.extensions === false) delete next.extensions; else next.extensions = [...override.extensions]; }
|
|
1283
1305
|
if (override.subagentOnlyExtensions !== undefined) { if (override.subagentOnlyExtensions === false) delete next.subagentOnlyExtensions; else next.subagentOnlyExtensions = [...override.subagentOnlyExtensions]; }
|
|
1306
|
+
if (override.mutationTools !== undefined) { if (override.mutationTools === false) delete next.mutationTools; else next.mutationTools = [...override.mutationTools]; }
|
|
1284
1307
|
if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;
|
|
1285
1308
|
if (override.toolBudget !== undefined) { if (override.toolBudget === false) delete next.toolBudget; else next.toolBudget = override.toolBudget; }
|
|
1286
1309
|
|
|
@@ -1418,6 +1441,9 @@ function applyCustomAgentOverride(
|
|
|
1418
1441
|
if (override.inheritProjectContext !== undefined) {
|
|
1419
1442
|
fill("inheritProjectContext", ["inheritProjectContext"], override.inheritProjectContext);
|
|
1420
1443
|
}
|
|
1444
|
+
if (override.inheritGlobalContext !== undefined) {
|
|
1445
|
+
fill("inheritGlobalContext", ["inheritGlobalContext"], override.inheritGlobalContext);
|
|
1446
|
+
}
|
|
1421
1447
|
if (override.inheritSkills !== undefined) {
|
|
1422
1448
|
fill("inheritSkills", ["inheritSkills"], override.inheritSkills);
|
|
1423
1449
|
}
|
|
@@ -1449,6 +1475,9 @@ function applyCustomAgentOverride(
|
|
|
1449
1475
|
override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],
|
|
1450
1476
|
);
|
|
1451
1477
|
}
|
|
1478
|
+
if (override.mutationTools !== undefined) {
|
|
1479
|
+
fill("mutationTools", ["mutationTools"], override.mutationTools === false ? undefined : [...override.mutationTools]);
|
|
1480
|
+
}
|
|
1452
1481
|
if (override.completionGuard !== undefined) {
|
|
1453
1482
|
fill("completionGuard", ["completionGuard"], override.completionGuard);
|
|
1454
1483
|
}
|
|
@@ -1488,7 +1517,7 @@ function applyCustomAgentOverrides(
|
|
|
1488
1517
|
|
|
1489
1518
|
export function buildBuiltinOverrideConfig(
|
|
1490
1519
|
base: BuiltinAgentOverrideBase,
|
|
1491
|
-
draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
|
|
1520
|
+
draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritGlobalContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "mutationTools" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
|
|
1492
1521
|
): BuiltinAgentOverrideConfig | undefined {
|
|
1493
1522
|
const override: BuiltinAgentOverrideConfig = {};
|
|
1494
1523
|
|
|
@@ -1506,6 +1535,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
1506
1535
|
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
1507
1536
|
if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
|
|
1508
1537
|
if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
|
|
1538
|
+
if (draft.inheritGlobalContext !== base.inheritGlobalContext) override.inheritGlobalContext = draft.inheritGlobalContext;
|
|
1509
1539
|
if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;
|
|
1510
1540
|
if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;
|
|
1511
1541
|
if (draft.acceptanceRole !== base.acceptanceRole) override.acceptanceRole = draft.acceptanceRole ?? false;
|
|
@@ -1520,6 +1550,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
1520
1550
|
if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {
|
|
1521
1551
|
override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;
|
|
1522
1552
|
}
|
|
1553
|
+
if (!arraysEqual(draft.mutationTools, base.mutationTools)) override.mutationTools = draft.mutationTools ? [...draft.mutationTools] : false;
|
|
1523
1554
|
if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {
|
|
1524
1555
|
override.completionGuard = draft.completionGuard !== false;
|
|
1525
1556
|
}
|
|
@@ -1863,7 +1894,7 @@ function parseAgentRunnerFrontmatter(raw: string | undefined, agentName: string)
|
|
|
1863
1894
|
|
|
1864
1895
|
function validateExternalRunnerProfile(frontmatter: Record<string, string>, agentName: string, runner: AgentRunnerConfig | undefined): void {
|
|
1865
1896
|
if (runner?.type !== "external-cli" && runner?.type !== "external-job") return;
|
|
1866
|
-
const unsupported = ["tools", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
|
|
1897
|
+
const unsupported = ["tools", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "mutationTools", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
|
|
1867
1898
|
.filter((field) => frontmatter[field] !== undefined);
|
|
1868
1899
|
if (unsupported.length > 0) {
|
|
1869
1900
|
throw new Error(`Agent '${agentName}' uses runner.type='${runner.type}' and declares unsupported Pi-only fields: ${unsupported.join(", ")}.`);
|
|
@@ -1963,6 +1994,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1963
1994
|
: frontmatter.inheritProjectContext === "false"
|
|
1964
1995
|
? false
|
|
1965
1996
|
: defaultInheritProjectContext(localName);
|
|
1997
|
+
const inheritGlobalContext = frontmatter.inheritGlobalContext === "true";
|
|
1966
1998
|
const inheritSkills = frontmatter.inheritSkills === "true"
|
|
1967
1999
|
? true
|
|
1968
2000
|
: frontmatter.inheritSkills === "false"
|
|
@@ -2016,6 +2048,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
2016
2048
|
|
|
2017
2049
|
const extensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.extensions), filePath);
|
|
2018
2050
|
const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.subagentOnlyExtensions), filePath);
|
|
2051
|
+
const mutationTools = parseFrontmatterList(frontmatter.mutationTools);
|
|
2019
2052
|
let fast: boolean | undefined;
|
|
2020
2053
|
if (frontmatter.fast !== undefined) {
|
|
2021
2054
|
if (frontmatter.fast === "true") fast = true;
|
|
@@ -2072,6 +2105,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
2072
2105
|
...(frontmatter.thinking !== undefined ? { thinking: frontmatter.thinking === "false" ? false : frontmatter.thinking } : {}),
|
|
2073
2106
|
systemPromptMode,
|
|
2074
2107
|
inheritProjectContext,
|
|
2108
|
+
inheritGlobalContext,
|
|
2075
2109
|
inheritSkills,
|
|
2076
2110
|
...(defaultContext !== undefined ? { defaultContext } : {}),
|
|
2077
2111
|
...(defaultAsync !== undefined ? { defaultAsync } : {}),
|
|
@@ -2088,6 +2122,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
2088
2122
|
...(skillPath?.length ? { skillPath } : {}),
|
|
2089
2123
|
...(extensions !== undefined ? { extensions } : {}),
|
|
2090
2124
|
...(subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions } : {}),
|
|
2125
|
+
...(mutationTools?.length ? { mutationTools } : {}),
|
|
2091
2126
|
...(frontmatter.output !== undefined ? { output: frontmatter.output } : {}),
|
|
2092
2127
|
...(outputMode !== undefined ? { outputMode } : {}),
|
|
2093
2128
|
...(defaultReads?.length ? { defaultReads } : {}),
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { registerRuntimeAgent, type RuntimeAgentDefinition, type RuntimeAgentRegistration } from "./runtime-agent-registry.ts";
|
|
3
|
+
|
|
4
|
+
export const RUNTIME_AGENT_REGISTER_EVENT = "pi-subagents:runtime-agent-register:v1";
|
|
5
|
+
export const RUNTIME_AGENT_REGISTER_VERSION = 1;
|
|
6
|
+
|
|
7
|
+
export type RuntimeAgentRegistrationResult =
|
|
8
|
+
| { ok: true; registration: RuntimeAgentRegistration }
|
|
9
|
+
| { ok: false; error: Error };
|
|
10
|
+
|
|
11
|
+
export interface RuntimeAgentRegistrationRequest {
|
|
12
|
+
version: 1;
|
|
13
|
+
name: string;
|
|
14
|
+
definition: RuntimeAgentDefinition;
|
|
15
|
+
result?: RuntimeAgentRegistrationResult;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RegisterRuntimeAgentViaEventsInput {
|
|
19
|
+
pi: Pick<ExtensionAPI, "events">;
|
|
20
|
+
name: string;
|
|
21
|
+
definition: RuntimeAgentDefinition;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function errorFrom(value: unknown): Error {
|
|
25
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Register through the installed pi-subagents owner in this Pi process. */
|
|
29
|
+
export function registerAgentViaEvents(input: RegisterRuntimeAgentViaEventsInput): RuntimeAgentRegistration {
|
|
30
|
+
const request: RuntimeAgentRegistrationRequest = {
|
|
31
|
+
version: RUNTIME_AGENT_REGISTER_VERSION,
|
|
32
|
+
name: input.name,
|
|
33
|
+
definition: input.definition,
|
|
34
|
+
};
|
|
35
|
+
input.pi.events.emit(RUNTIME_AGENT_REGISTER_EVENT, request);
|
|
36
|
+
const result = request.result as unknown;
|
|
37
|
+
if (result === undefined) {
|
|
38
|
+
throw new Error("pi-subagents is not installed, not ready, or does not support runtime agent event registration.");
|
|
39
|
+
}
|
|
40
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
41
|
+
const candidate = result as Record<string, unknown>;
|
|
42
|
+
if (candidate.ok === true && candidate.registration && typeof candidate.registration === "object" && typeof (candidate.registration as { dispose?: unknown }).dispose === "function") {
|
|
43
|
+
return candidate.registration as RuntimeAgentRegistration;
|
|
44
|
+
}
|
|
45
|
+
if (candidate.ok === false && candidate.error instanceof Error) throw candidate.error;
|
|
46
|
+
}
|
|
47
|
+
throw new Error("pi-subagents returned a malformed runtime agent registration result.");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Install the process-local registration listener for the owning pi-subagents runtime. */
|
|
51
|
+
export function registerRuntimeAgentEventListener(pi: ExtensionAPI): () => void {
|
|
52
|
+
return pi.events.on(RUNTIME_AGENT_REGISTER_EVENT, (rawRequest) => {
|
|
53
|
+
if (!rawRequest || typeof rawRequest !== "object" || Array.isArray(rawRequest)) return;
|
|
54
|
+
const request = rawRequest as Record<string, unknown>;
|
|
55
|
+
if (request.result !== undefined) return;
|
|
56
|
+
try {
|
|
57
|
+
if (request.version !== RUNTIME_AGENT_REGISTER_VERSION) {
|
|
58
|
+
throw new Error(`Unsupported runtime agent registration event version '${String(request.version)}'.`);
|
|
59
|
+
}
|
|
60
|
+
const registration = registerRuntimeAgent({
|
|
61
|
+
pi,
|
|
62
|
+
name: request.name as string,
|
|
63
|
+
definition: request.definition as RuntimeAgentDefinition,
|
|
64
|
+
});
|
|
65
|
+
request.result = { ok: true, registration } satisfies RuntimeAgentRegistrationResult;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
request.result = { ok: false, error: errorFrom(error) } satisfies RuntimeAgentRegistrationResult;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -27,6 +27,7 @@ export interface RuntimeAgentDefinition {
|
|
|
27
27
|
thinking?: string | false;
|
|
28
28
|
systemPromptMode?: "append" | "replace";
|
|
29
29
|
inheritProjectContext?: boolean;
|
|
30
|
+
inheritGlobalContext?: boolean;
|
|
30
31
|
inheritSkills?: boolean;
|
|
31
32
|
defaultContext?: AgentDefaultContext;
|
|
32
33
|
defaultAsync?: boolean;
|
|
@@ -40,6 +41,7 @@ export interface RuntimeAgentDefinition {
|
|
|
40
41
|
skillPath?: readonly string[];
|
|
41
42
|
extensions?: readonly string[];
|
|
42
43
|
subagentOnlyExtensions?: readonly string[];
|
|
44
|
+
mutationTools?: readonly string[];
|
|
43
45
|
output?: string;
|
|
44
46
|
outputMode?: OutputMode;
|
|
45
47
|
defaultReads?: readonly string[];
|
|
@@ -204,9 +206,9 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
204
206
|
const definition = value as Record<string, unknown>;
|
|
205
207
|
const supported = new Set([
|
|
206
208
|
"description", "systemPrompt", "aliases", "tools", "mcpDirectTools", "model", "fallbackModels", "thinking",
|
|
207
|
-
"systemPromptMode", "inheritProjectContext", "inheritSkills", "defaultContext", "defaultAsync", "defaultTimeoutMs",
|
|
209
|
+
"systemPromptMode", "inheritProjectContext", "inheritGlobalContext", "inheritSkills", "defaultContext", "defaultAsync", "defaultTimeoutMs",
|
|
208
210
|
"defaultToolTimeoutMs", "defaultTurnBudget", "defaultAcceptance", "acceptanceRole", "runner", "skills", "skillPath",
|
|
209
|
-
"extensions", "subagentOnlyExtensions", "output", "outputMode", "defaultReads", "defaultProgress", "interactive",
|
|
211
|
+
"extensions", "subagentOnlyExtensions", "mutationTools", "output", "outputMode", "defaultReads", "defaultProgress", "interactive",
|
|
210
212
|
"maxSubagentDepth", "completionGuard", "toolBudget", "permissions",
|
|
211
213
|
]);
|
|
212
214
|
const unknown = Object.keys(definition).filter((key) => !supported.has(key));
|
|
@@ -227,6 +229,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
227
229
|
const model = validateOptionalString(definition.model, "Runtime agent definition model");
|
|
228
230
|
const fallbackModels = validateStringList(definition.fallbackModels, "Runtime agent definition fallbackModels");
|
|
229
231
|
const inheritProjectContext = validateBoolean(definition.inheritProjectContext, "Runtime agent definition inheritProjectContext");
|
|
232
|
+
const inheritGlobalContext = validateBoolean(definition.inheritGlobalContext, "Runtime agent definition inheritGlobalContext");
|
|
230
233
|
const inheritSkills = validateBoolean(definition.inheritSkills, "Runtime agent definition inheritSkills");
|
|
231
234
|
const defaultAsync = validateBoolean(definition.defaultAsync, "Runtime agent definition defaultAsync");
|
|
232
235
|
const defaultTimeoutMs = validatePositiveInteger(definition.defaultTimeoutMs, "Runtime agent definition defaultTimeoutMs");
|
|
@@ -238,6 +241,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
238
241
|
const skillPath = validateStringList(definition.skillPath, "Runtime agent definition skillPath");
|
|
239
242
|
const extensions = validateStringList(definition.extensions, "Runtime agent definition extensions");
|
|
240
243
|
const subagentOnlyExtensions = validateStringList(definition.subagentOnlyExtensions, "Runtime agent definition subagentOnlyExtensions");
|
|
244
|
+
const mutationTools = validateStringList(definition.mutationTools, "Runtime agent definition mutationTools");
|
|
241
245
|
const output = validateOptionalString(definition.output, "Runtime agent definition output");
|
|
242
246
|
const defaultReads = validateStringList(definition.defaultReads, "Runtime agent definition defaultReads");
|
|
243
247
|
const defaultProgress = validateBoolean(definition.defaultProgress, "Runtime agent definition defaultProgress");
|
|
@@ -257,6 +261,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
257
261
|
...(thinking !== undefined ? { thinking: thinking as string | false } : {}),
|
|
258
262
|
...(systemPromptMode !== undefined ? { systemPromptMode: systemPromptMode as "append" | "replace" } : {}),
|
|
259
263
|
...(inheritProjectContext !== undefined ? { inheritProjectContext } : {}),
|
|
264
|
+
...(inheritGlobalContext !== undefined ? { inheritGlobalContext } : {}),
|
|
260
265
|
...(inheritSkills !== undefined ? { inheritSkills } : {}),
|
|
261
266
|
...(defaultContext !== undefined ? { defaultContext: defaultContext as AgentDefaultContext } : {}),
|
|
262
267
|
...(defaultAsync !== undefined ? { defaultAsync } : {}),
|
|
@@ -270,6 +275,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
|
|
|
270
275
|
...(skillPath ? { skillPath } : {}),
|
|
271
276
|
...(extensions ? { extensions } : {}),
|
|
272
277
|
...(subagentOnlyExtensions ? { subagentOnlyExtensions } : {}),
|
|
278
|
+
...(mutationTools ? { mutationTools } : {}),
|
|
273
279
|
...(output ? { output } : {}),
|
|
274
280
|
...(outputMode !== undefined ? { outputMode: outputMode as OutputMode } : {}),
|
|
275
281
|
...(defaultReads ? { defaultReads } : {}),
|
|
@@ -334,6 +340,7 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
|
|
|
334
340
|
...(definition.thinking !== undefined ? { thinking: definition.thinking } : {}),
|
|
335
341
|
systemPromptMode: definition.systemPromptMode ?? defaultSystemPromptMode(name),
|
|
336
342
|
inheritProjectContext: definition.inheritProjectContext ?? defaultInheritProjectContext(name),
|
|
343
|
+
inheritGlobalContext: definition.inheritGlobalContext ?? false,
|
|
337
344
|
inheritSkills: definition.inheritSkills ?? defaultInheritSkills(),
|
|
338
345
|
...(definition.defaultContext !== undefined ? { defaultContext: definition.defaultContext } : {}),
|
|
339
346
|
...(definition.defaultAsync !== undefined ? { defaultAsync: definition.defaultAsync } : {}),
|
|
@@ -349,6 +356,7 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
|
|
|
349
356
|
...(definition.skillPath !== undefined ? { skillPath: [...definition.skillPath] } : {}),
|
|
350
357
|
...(definition.extensions !== undefined ? { extensions: [...definition.extensions] } : {}),
|
|
351
358
|
...(definition.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...definition.subagentOnlyExtensions] } : {}),
|
|
359
|
+
...(definition.mutationTools !== undefined ? { mutationTools: [...definition.mutationTools] } : {}),
|
|
352
360
|
...(definition.output !== undefined ? { output: definition.output } : {}),
|
|
353
361
|
...(definition.outputMode !== undefined ? { outputMode: definition.outputMode } : {}),
|
|
354
362
|
...(definition.defaultReads !== undefined ? { defaultReads: [...definition.defaultReads] } : {}),
|
package/src/api/agents.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RegisterRuntimeAgentInput, RuntimeAgentDefinition, RuntimeAgentRegistration } from "../agents/runtime-agent-registry.ts";
|
|
2
|
+
export { registerRuntimeAgent as registerAgent } from "../agents/runtime-agent-registry.ts";
|
|
3
|
+
export {
|
|
4
|
+
RUNTIME_AGENT_REGISTER_EVENT,
|
|
5
|
+
RUNTIME_AGENT_REGISTER_VERSION,
|
|
6
|
+
registerAgentViaEvents,
|
|
7
|
+
type RegisterRuntimeAgentViaEventsInput,
|
|
8
|
+
type RuntimeAgentRegistrationRequest,
|
|
9
|
+
type RuntimeAgentRegistrationResult,
|
|
10
|
+
} from "../agents/runtime-agent-events.ts";
|
|
2
11
|
|
|
3
12
|
export type { RegisterRuntimeAgentInput, RuntimeAgentDefinition, RuntimeAgentRegistration };
|
|
4
|
-
|
|
5
|
-
export function registerAgent(input: RegisterRuntimeAgentInput): RuntimeAgentRegistration {
|
|
6
|
-
return registerRuntimeAgent(input);
|
|
7
|
-
}
|
package/src/api/preflight.ts
CHANGED
|
@@ -42,8 +42,10 @@ export type SubagentLaunchContractReasonCode =
|
|
|
42
42
|
| "thinking_ceiling"
|
|
43
43
|
| "invalid_extension_bindings";
|
|
44
44
|
|
|
45
|
+
export type SubagentLaunchContractDiagnosticCode = SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning" | "workspace_scope_authority";
|
|
46
|
+
|
|
45
47
|
export interface SubagentLaunchContractDiagnostic {
|
|
46
|
-
code:
|
|
48
|
+
code: SubagentLaunchContractDiagnosticCode;
|
|
47
49
|
severity: "error" | "warning" | "host-required";
|
|
48
50
|
message: string;
|
|
49
51
|
}
|
|
@@ -157,6 +159,7 @@ export interface SubagentLaunchContract {
|
|
|
157
159
|
thinkingCeiling?: ThinkingLevel;
|
|
158
160
|
systemPromptMode: AgentConfig["systemPromptMode"];
|
|
159
161
|
inheritProjectContext: boolean;
|
|
162
|
+
inheritGlobalContext: boolean;
|
|
160
163
|
inheritSkills: boolean;
|
|
161
164
|
skills: SubagentLaunchContractSkills;
|
|
162
165
|
tools: SubagentLaunchContractTools;
|
|
@@ -204,6 +207,24 @@ function resolveLaunchContractContext(input: SubagentLaunchContractInput, agent:
|
|
|
204
207
|
});
|
|
205
208
|
}
|
|
206
209
|
|
|
210
|
+
function taskWorkspaceScopeAuthorityDiagnostic(task: string | undefined): SubagentLaunchContractDiagnostic | undefined {
|
|
211
|
+
if (!task) return undefined;
|
|
212
|
+
const text = task.replace(/\s+/g, " ").trim();
|
|
213
|
+
if (!text) return undefined;
|
|
214
|
+
const createsWorkspacePackage = /\b(?:add|create|introduce|make|set up)\b.{0,80}\b(?:new\s+)?(?:workspace\s+)?package\b/i.test(text)
|
|
215
|
+
|| /\b(?:new\s+)?package\b.{0,80}\b(?:workspace|monorepo)\b/i.test(text);
|
|
216
|
+
if (!createsWorkspacePackage) return undefined;
|
|
217
|
+
const packageOnlyAuthority = /\b(?:only|solely)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:package(?:\s+directory)?|packages\/[\w.-]+)\b/i.test(text)
|
|
218
|
+
|| /\b(?:do not|don't|must not)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:root|workspace|lockfile|metadata)\b/i.test(text)
|
|
219
|
+
|| /\bwithout\b.{0,40}\b(?:root|workspace|lockfile|metadata)\b.{0,40}\b(?:edit|change|modification|write)s?\b/i.test(text);
|
|
220
|
+
if (!packageOnlyAuthority) return undefined;
|
|
221
|
+
return {
|
|
222
|
+
code: "workspace_scope_authority",
|
|
223
|
+
severity: "warning",
|
|
224
|
+
message: "Task asks for a workspace package change while limiting authority to package-scope edits. New workspace packages often need root workspace metadata or lockfile changes, so confirm that authority before launch.",
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
207
228
|
function candidateList(inputAgent: string, selected: AgentConfig | undefined, cwd: string): SubagentLaunchContractAgentCandidate[] {
|
|
208
229
|
const all = discoverAgentsAll(cwd);
|
|
209
230
|
return [...all.builtin, ...all.package, ...all.user, ...all.project]
|
|
@@ -221,6 +242,8 @@ function candidateList(inputAgent: string, selected: AgentConfig | undefined, cw
|
|
|
221
242
|
|
|
222
243
|
export async function resolveSubagentLaunchContract(input: SubagentLaunchContractInput): Promise<SubagentLaunchContractResult> {
|
|
223
244
|
const diagnostics: SubagentLaunchContractDiagnostic[] = [];
|
|
245
|
+
const authorityDiagnostic = taskWorkspaceScopeAuthorityDiagnostic(input.task);
|
|
246
|
+
if (authorityDiagnostic) diagnostics.push(authorityDiagnostic);
|
|
224
247
|
const effectiveCwd = path.resolve(input.cwd);
|
|
225
248
|
try {
|
|
226
249
|
if (!fs.statSync(effectiveCwd).isDirectory()) {
|
|
@@ -400,6 +423,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
400
423
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
401
424
|
systemPromptMode: agent.systemPromptMode,
|
|
402
425
|
inheritProjectContext: agent.inheritProjectContext,
|
|
426
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
403
427
|
inheritSkills: agent.inheritSkills,
|
|
404
428
|
skills: {
|
|
405
429
|
requested: requestedSkills,
|
|
@@ -455,6 +479,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
455
479
|
systemPrompt: effectiveSystemPrompt,
|
|
456
480
|
systemPromptMode: agent.systemPromptMode,
|
|
457
481
|
inheritProjectContext: agent.inheritProjectContext,
|
|
482
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
458
483
|
inheritSkills: agent.inheritSkills,
|
|
459
484
|
skills: requestedSkills,
|
|
460
485
|
tools: toolPlan.effectiveToolAllowlist,
|
package/src/extension/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { keyText, type ExtensionAPI, type ExtensionContext, type ToolDefinition
|
|
|
21
21
|
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
|
|
22
22
|
import { discoverAgents, discoverAgentsAll, type AgentConfig, type AgentScope } from "../agents/agents.ts";
|
|
23
23
|
import { clearRuntimeAgentsForPi, listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.ts";
|
|
24
|
+
import { registerRuntimeAgentEventListener } from "../agents/runtime-agent-events.ts";
|
|
24
25
|
import { ensureAccessibleDir } from "../shared/accessible-dir.ts";
|
|
25
26
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
26
27
|
import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
@@ -37,6 +38,7 @@ import { getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, res
|
|
|
37
38
|
import { cleanupResultIndexes, missionObserverResultCandidateFiles } from "../runs/background/result-files.ts";
|
|
38
39
|
import { ASYNC_RETENTION_DELAY_MS, cleanupAsyncRetention } from "../runs/background/async-retention.ts";
|
|
39
40
|
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
41
|
+
import { createResultDeliveryOwnership } from "../runs/background/result-delivery-ownership.ts";
|
|
40
42
|
import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
|
|
41
43
|
import { registerSlashCommands } from "../slash/slash-commands.ts";
|
|
42
44
|
import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.ts";
|
|
@@ -466,7 +468,8 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
466
468
|
const supervisorChannel = createNativeSupervisorChannel(pi, state);
|
|
467
469
|
const waitSubscriptionManager = createWaitSubscriptionManager(pi, state);
|
|
468
470
|
const mainWatchdog = registerMainWatchdog(pi);
|
|
469
|
-
const
|
|
471
|
+
const resultDeliveryOwnership = createResultDeliveryOwnership(state);
|
|
472
|
+
const completionNotifier = registerSubagentNotify(pi, state, { batchConfig: config.completionBatch, ownership: resultDeliveryOwnership });
|
|
470
473
|
const fleetStatus = fleetViewEnabled
|
|
471
474
|
? new SubagentFleetStatus(state, async (itemKey) => {
|
|
472
475
|
const ctx = state.lastUiContext;
|
|
@@ -528,6 +531,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
528
531
|
10 * 60 * 1000,
|
|
529
532
|
{
|
|
530
533
|
notifier: completionNotifier,
|
|
534
|
+
ownership: resultDeliveryOwnership,
|
|
531
535
|
observeCompletion: (result) => scheduledRunManager.handleAsyncCompletion(result),
|
|
532
536
|
observedCompletionRunIds: () => scheduledRunManager.observedCompletionRunIds(),
|
|
533
537
|
hasDeliveryDemand: hasResultDeliveryDemand,
|
|
@@ -535,7 +539,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
535
539
|
resultScanLogging: config.resultScanLogging,
|
|
536
540
|
},
|
|
537
541
|
);
|
|
538
|
-
const { startResultWatcher, primeExistingResults, stopResultWatcher } = resultWatcher;
|
|
542
|
+
const { startResultWatcher, transitionResultDelivery, primeExistingResults, stopResultWatcher } = resultWatcher;
|
|
539
543
|
refreshResultDelivery = resultWatcher.refreshResultDelivery;
|
|
540
544
|
const asyncRetentionAbort = new AbortController();
|
|
541
545
|
const asyncRetentionTimer = setTimeout(async () => {
|
|
@@ -791,6 +795,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
791
795
|
fleetStatus?.refresh();
|
|
792
796
|
};
|
|
793
797
|
const eventUnsubscribes = [
|
|
798
|
+
registerRuntimeAgentEventListener(pi),
|
|
794
799
|
pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, asyncStartedHandler),
|
|
795
800
|
pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, asyncCompleteHandler),
|
|
796
801
|
pi.events.on(SUBAGENT_PROCESS_TERMINAL_EVENT, () => {
|
|
@@ -807,7 +812,9 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
807
812
|
if (event.toolName !== "subagent") return;
|
|
808
813
|
if (!ctx.hasUI) return;
|
|
809
814
|
state.lastUiContext = ctx;
|
|
815
|
+
const activeJobCount = state.asyncJobs.size;
|
|
810
816
|
restoreActiveJobs(ctx);
|
|
817
|
+
if (state.asyncJobs.size > activeJobCount) herdrStatusBridge.syncRuns();
|
|
811
818
|
fleetStatus?.setContext(ctx);
|
|
812
819
|
fleetStatus?.refresh();
|
|
813
820
|
if (state.asyncJobs.size > 0) {
|
|
@@ -853,11 +860,14 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
853
860
|
);
|
|
854
861
|
};
|
|
855
862
|
|
|
856
|
-
const resetSessionState = (ctx: ExtensionContext, recovering: boolean) => {
|
|
863
|
+
const resetSessionState = (ctx: ExtensionContext, recovering: boolean, previousSessionFile?: string) => {
|
|
857
864
|
state.widgetsSuspended = false;
|
|
858
865
|
state.baseCwd = ctx.cwd;
|
|
859
866
|
goalTurnId = 0;
|
|
867
|
+
const previousRuntimeSessionId = state.currentSessionId;
|
|
868
|
+
resultDeliveryOwnership.claimPredecessor(previousSessionFile, previousRuntimeSessionId);
|
|
860
869
|
state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
870
|
+
transitionResultDelivery();
|
|
861
871
|
state.parentSessionFile = ctx.sessionManager.getSessionFile();
|
|
862
872
|
state.trustedSessionFileRoot = state.parentSessionFile ? path.join(getAgentDir(), "sessions") : undefined;
|
|
863
873
|
state.trustedSessionRoots = [...new Set([
|
|
@@ -934,6 +944,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
934
944
|
clearTimeout(asyncRetentionTimer);
|
|
935
945
|
asyncRetentionAbort.abort();
|
|
936
946
|
stopResultWatcher();
|
|
947
|
+
resultDeliveryOwnership.clear();
|
|
937
948
|
completionNotifier.dispose();
|
|
938
949
|
mainWatchdog.dispose();
|
|
939
950
|
scheduledRunManager.stop();
|
|
@@ -1034,7 +1045,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
1034
1045
|
pi.on("session_start", (event, ctx) => {
|
|
1035
1046
|
installRuntime(ctx);
|
|
1036
1047
|
const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
|
|
1037
|
-
resetSessionState(ctx, recovering);
|
|
1048
|
+
resetSessionState(ctx, recovering, event.previousSessionFile);
|
|
1038
1049
|
herdrStatusBridge.sessionStarted({
|
|
1039
1050
|
hasUI: ctx.hasUI === true,
|
|
1040
1051
|
runs: activeHerdrRuns(),
|
package/src/extension/rpc.ts
CHANGED
|
@@ -557,7 +557,7 @@ function stopAsyncRun(
|
|
|
557
557
|
childId: stoppedChild.id,
|
|
558
558
|
status: "stopping",
|
|
559
559
|
ts,
|
|
560
|
-
reason: "
|
|
560
|
+
reason: "rpc",
|
|
561
561
|
source: "rpc",
|
|
562
562
|
asyncDir,
|
|
563
563
|
stepIndex: stoppedChild.index,
|
|
@@ -579,20 +579,21 @@ function stopAsyncRun(
|
|
|
579
579
|
if (initialStatus.mode === "workflow" && initialStatus.state === "running") {
|
|
580
580
|
if (child) {
|
|
581
581
|
const stopChild = options.state?.workflowChildStops?.get(initialRunId);
|
|
582
|
-
if (
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
582
|
+
if (stopChild) {
|
|
583
|
+
if (!stopChild(child.id, `Workflow child '${child.id}' stopped by RPC.`)) throw new SubagentRpcError("invalid_state", `Child '${childId}' in workflow ${initialRunId} is not available to stop.`);
|
|
584
|
+
emitChildStopping(initialRunId, location.asyncDir, child);
|
|
585
|
+
return {
|
|
586
|
+
runId: initialRunId,
|
|
587
|
+
asyncDir: location.asyncDir,
|
|
588
|
+
previousState: initialStatus.state,
|
|
589
|
+
state: "stopping",
|
|
590
|
+
childId: child.id,
|
|
591
|
+
message: `Stop requested for child ${child.id} in async run ${initialRunId}.`,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
593
594
|
}
|
|
594
595
|
const workflowController = options.state?.workflowControllers?.get(initialRunId);
|
|
595
|
-
if (workflowController) {
|
|
596
|
+
if (workflowController && !child) {
|
|
596
597
|
workflowController.abort(new Error("Workflow stopped by RPC."));
|
|
597
598
|
return {
|
|
598
599
|
runId: initialRunId,
|
|
@@ -602,7 +603,27 @@ function stopAsyncRun(
|
|
|
602
603
|
message: `Stop requested for async run ${initialRunId}.`,
|
|
603
604
|
};
|
|
604
605
|
}
|
|
605
|
-
|
|
606
|
+
try {
|
|
607
|
+
deliverStopRequest({
|
|
608
|
+
asyncDir: location.asyncDir,
|
|
609
|
+
pid: initialStatus.pid,
|
|
610
|
+
kill: options.kill,
|
|
611
|
+
now: options.now,
|
|
612
|
+
source: "rpc-stop",
|
|
613
|
+
...(child ? { targetIndex: child.index, childId: child.id } : {}),
|
|
614
|
+
});
|
|
615
|
+
} catch (error) {
|
|
616
|
+
throw new SubagentRpcError("execution_failed", error instanceof Error ? error.message : String(error));
|
|
617
|
+
}
|
|
618
|
+
if (child) emitChildStopping(initialRunId, location.asyncDir, child);
|
|
619
|
+
return {
|
|
620
|
+
runId: initialRunId,
|
|
621
|
+
asyncDir: location.asyncDir,
|
|
622
|
+
previousState: initialStatus.state,
|
|
623
|
+
state: "stopping",
|
|
624
|
+
...(child ? { childId: child.id } : {}),
|
|
625
|
+
message: child ? `Stop requested for child ${child.id} in async run ${initialRunId}.` : `Stop requested for async run ${initialRunId}.`,
|
|
626
|
+
};
|
|
606
627
|
}
|
|
607
628
|
|
|
608
629
|
let status;
|
package/src/extension/schemas.ts
CHANGED
|
@@ -289,7 +289,7 @@ const SubagentParamProperties = {
|
|
|
289
289
|
scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session to avoid persistent settings writes unless user/project is explicit." })),
|
|
290
290
|
target: Type.Optional(Type.String({ enum: ["main", "children", "child"], description: "Target for watchdog actions." })),
|
|
291
291
|
focus: Type.Optional(Type.Boolean({ description: "Focus the new Herdr pane for inspector.open or project.open." })),
|
|
292
|
-
thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [false] }], description: "Thinking level for action='watchdog.configure' (off/minimal/low/medium/high/xhigh/max, inherit, or false for off)." })),
|
|
292
|
+
thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [false] }], description: "Thinking level for action='watchdog.configure' only (off/minimal/low/medium/high/xhigh/max, inherit, or false for off). Ignored on dispatch; set per-run child thinking with a suffix on the model string, e.g. model: 'provider/id:high'." })),
|
|
293
293
|
at: Type.Optional(Type.String({ description: "One-shot trigger for action='schedule.create': a relative delay such as '+10m' or an ISO timestamp with timezone." })),
|
|
294
294
|
every: Type.Optional(Type.String({ description: "Fixed recurring interval for action='schedule.create', such as '30m', '6h', '2d', or '2w'." })),
|
|
295
295
|
on: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "integer" }], description: "Calendar selector reserved for a later schedule slice." })),
|
|
@@ -347,7 +347,7 @@ const SubagentParamProperties = {
|
|
|
347
347
|
})),
|
|
348
348
|
outputMode: Type.Optional(OutputModeOverride),
|
|
349
349
|
skill: Type.Optional(SkillOverride),
|
|
350
|
-
model: Type.Optional(Type.String({ description: "Default child model override. Full provider/id values are accepted; bare ids resolve from the active registry." })),
|
|
350
|
+
model: Type.Optional(Type.String({ description: "Default child model override. Full provider/id values are accepted; bare ids resolve from the active registry. Append a thinking suffix (off/minimal/low/medium/high/xhigh/max, e.g. 'provider/id:low') to set the child's thinking level for the run; the suffix wins over the agent's thinking default." })),
|
|
351
351
|
fast: Type.Optional(Type.Boolean({ description: "Opt into priority service tier for supported native OpenAI-Codex child models. Default false. This can increase quota or cost." })),
|
|
352
352
|
outputSchema: Type.Optional(JsonSchemaObject),
|
|
353
353
|
agentContract: Type.Optional(AgentContractOverride),
|