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.
Files changed (55) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/docs/agents.md +13 -6
  3. package/docs/extension-api.md +39 -0
  4. package/docs/models.md +3 -3
  5. package/docs/tool-reference.md +3 -2
  6. package/docs/workflows.md +24 -0
  7. package/package.json +1 -1
  8. package/skills/pi-subagents/SKILL.md +2 -0
  9. package/skills/pi-subagents/references/execution-controls.md +1 -1
  10. package/skills/pi-subagents/references/management-authoring-rpc.md +1 -0
  11. package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
  12. package/src/agents/agent-management.ts +23 -0
  13. package/src/agents/agent-serializer.ts +5 -0
  14. package/src/agents/agents.ts +38 -3
  15. package/src/agents/runtime-agent-events.ts +70 -0
  16. package/src/agents/runtime-agent-registry.ts +10 -2
  17. package/src/api/agents.ts +10 -5
  18. package/src/api/preflight.ts +26 -1
  19. package/src/extension/index.ts +15 -4
  20. package/src/extension/rpc.ts +35 -14
  21. package/src/extension/schemas.ts +2 -2
  22. package/src/extension/tool-description.ts +8 -4
  23. package/src/integrations/herdr-status.ts +5 -0
  24. package/src/runs/background/async-execution.ts +29 -12
  25. package/src/runs/background/async-resume.ts +6 -2
  26. package/src/runs/background/async-status.ts +2 -0
  27. package/src/runs/background/chain-append.ts +2 -0
  28. package/src/runs/background/notify.ts +26 -3
  29. package/src/runs/background/result-delivery-ownership.ts +45 -0
  30. package/src/runs/background/result-watcher.ts +27 -10
  31. package/src/runs/background/subagent-runner.ts +71 -21
  32. package/src/runs/foreground/execution.ts +20 -1
  33. package/src/runs/foreground/subagent-executor.ts +57 -26
  34. package/src/runs/foreground/workflow-detach-reconcile.ts +128 -17
  35. package/src/runs/shared/completion-guard.ts +4 -3
  36. package/src/runs/shared/dynamic-fanout.ts +3 -3
  37. package/src/runs/shared/fast-mode-extension.ts +5 -5
  38. package/src/runs/shared/launch-cwd.ts +16 -0
  39. package/src/runs/shared/long-running-guard.ts +2 -1
  40. package/src/runs/shared/mcp-config-sources.ts +386 -0
  41. package/src/runs/shared/mcp-direct-tool-allowlist.ts +155 -42
  42. package/src/runs/shared/model-exclusions.ts +12 -0
  43. package/src/runs/shared/model-fallback.ts +16 -4
  44. package/src/runs/shared/parallel-utils.ts +8 -1
  45. package/src/runs/shared/pi-args.ts +61 -6
  46. package/src/runs/shared/single-output.ts +17 -0
  47. package/src/runs/shared/subagent-prompt-runtime.ts +85 -9
  48. package/src/shared/formatters.ts +6 -0
  49. package/src/shared/launch-contract.ts +4 -0
  50. package/src/shared/types.ts +42 -1
  51. package/src/shared/utils.ts +6 -29
  52. package/src/slash/subagents-admin.ts +3 -0
  53. package/src/tui/render.ts +14 -6
  54. package/src/workflows/scripted-workflow.ts +52 -5
  55. package/src/workflows/workflow-receipt.ts +39 -4
@@ -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 { registerRuntimeAgent, type RegisterRuntimeAgentInput, type RuntimeAgentDefinition, type RuntimeAgentRegistration } from "../agents/runtime-agent-registry.ts";
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
- }
@@ -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: SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning";
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,
@@ -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 completionNotifier = registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
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(),
@@ -557,7 +557,7 @@ function stopAsyncRun(
557
557
  childId: stoppedChild.id,
558
558
  status: "stopping",
559
559
  ts,
560
- reason: "user",
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 (!stopChild) throw new SubagentRpcError("invalid_state", `Workflow ${initialRunId} is not controlled by this extension runtime; child stop is unavailable.`);
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
- };
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
- throw new SubagentRpcError("invalid_state", `Workflow ${initialRunId} is not controlled by this extension runtime; reload recovery cannot stop it safely.`);
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;
@@ -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),