@narumitw/pi-subagents 2.1.0 → 2.1.1

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 (31) hide show
  1. package/README.md +21 -12
  2. package/dist/chunks/{chunk-PABJJYP6.ts → chunk-2LMJU25E.ts} +12 -41
  3. package/dist/chunks/chunk-2LMJU25E.ts.map +7 -0
  4. package/dist/chunks/{chunk-FXI45N3J.ts → chunk-6NSJVPXX.ts} +4 -14
  5. package/dist/chunks/{chunk-FXI45N3J.ts.map → chunk-6NSJVPXX.ts.map} +2 -2
  6. package/dist/chunks/{chunk-VBDGNNLM.ts → chunk-NLT67IZS.ts} +137 -29
  7. package/dist/chunks/chunk-NLT67IZS.ts.map +7 -0
  8. package/dist/chunks/{chunk-DIHBUR2E.ts → chunk-YU53SHA7.ts} +2 -2
  9. package/dist/chunks/{completion-delivery-JVLNQRWX.ts → completion-delivery-RSJU6BXL.ts} +3 -3
  10. package/dist/chunks/{config-ui-DDKERQHI.ts → config-ui-ABHYNGQ7.ts} +4 -4
  11. package/dist/chunks/{consult-PQ6PRAKC.ts → consult-LJU3IQY5.ts} +2 -2
  12. package/dist/chunks/{persistence-XHPJBZL7.ts → persistence-UY3PY6E5.ts} +2 -2
  13. package/dist/chunks/{registry-XDXPECWF.ts → registry-BT54L6CY.ts} +2 -2
  14. package/dist/index.ts +263 -60
  15. package/dist/index.ts.map +3 -3
  16. package/docs/async-runtime-protocol.md +16 -2
  17. package/package.json +1 -1
  18. package/src/completion-requirement.ts +201 -30
  19. package/src/consult-registration.ts +4 -12
  20. package/src/session-guidance-contract.ts +309 -0
  21. package/src/stateful-guidance.ts +3 -20
  22. package/src/stateful-registration.ts +4 -31
  23. package/src/subagents-extension.ts +56 -66
  24. package/dist/chunks/chunk-PABJJYP6.ts.map +0 -7
  25. package/dist/chunks/chunk-VBDGNNLM.ts.map +0 -7
  26. /package/dist/chunks/{chunk-DIHBUR2E.ts.map → chunk-YU53SHA7.ts.map} +0 -0
  27. /package/dist/chunks/{completion-delivery-JVLNQRWX.ts.map → completion-delivery-RSJU6BXL.ts.map} +0 -0
  28. /package/dist/chunks/{config-ui-DDKERQHI.ts.map → config-ui-ABHYNGQ7.ts.map} +0 -0
  29. /package/dist/chunks/{consult-PQ6PRAKC.ts.map → consult-LJU3IQY5.ts.map} +0 -0
  30. /package/dist/chunks/{persistence-XHPJBZL7.ts.map → persistence-UY3PY6E5.ts.map} +0 -0
  31. /package/dist/chunks/{registry-XDXPECWF.ts.map → registry-BT54L6CY.ts.map} +0 -0
@@ -1,17 +1,4 @@
1
- import type { CompletionDelivery } from "./agents/types.js";
2
-
3
- export function createSpawnPromptGuidelines(
4
- completionDelivery: CompletionDelivery,
5
- blockingEnabled = true,
6
- ): string[] {
7
- const deliveryGuidance =
8
- completionDelivery === "auto-resume"
9
- ? blockingEnabled
10
- ? "With subagent_spawn completion delivery set to auto-resume, prefer one subagent_spawn for broad asynchronous research or consequential independent review that covers related branches even when the final answer depends on its result; do not choose blocking parallel fan-out merely to keep delegation in the same turn."
11
- : "With subagent_spawn completion delivery set to auto-resume, prefer one subagent_spawn for broad asynchronous research or consequential independent review that covers related branches even when the final answer depends on its result."
12
- : blockingEnabled
13
- ? "With subagent_spawn completion delivery set to next-turn (the default), prefer one subagent_spawn for broad asynchronous research or consequential independent review only when the current response does not depend on its result; when it does, use subagent_spawn only with useful overlap and call subagent_await after that overlap is complete. Do not migrate new work to the deprecated subagent tool."
14
- : "With subagent_spawn completion delivery set to next-turn (the default), use subagent_spawn only when the current response does not depend on its result; complete final-answer-dependent work directly because an idle root is not awakened.";
1
+ export function createSpawnPromptGuidelines(blockingEnabled = true): string[] {
15
2
  return [
16
3
  "Do not use subagent_spawn for simple or critical-path work that the main agent can perform directly. The main agent retains overall planning, immediate critical-path work, integration, final verification, and the final answer.",
17
4
  "Before one ordinary subagent_spawn, identify concrete useful non-overlapping main-agent work you can start immediately and a supported completion integration path. If none exists, perform the task directly instead of calling subagent_spawn.",
@@ -21,12 +8,8 @@ export function createSpawnPromptGuidelines(
21
8
  "For an ordinary subagent_spawn, omit contract; use a delegation contract only when explicit acceptance, authority, evidence, or admission semantics are required.",
22
9
  "Do not set subagent_spawn contract enforcement to enforce with requestedAuthority readPaths, writePaths, network, or secrets; those guarantees are unsupported and reject before child launch, while capabilities and tools remain enforceable.",
23
10
  "If subagent_spawn rejects an unsupported guarantee, retry once with those fields removed or enforcement set to audit only when they were advisory; when any field is a required security boundary, stop instead of weakening it.",
24
- deliveryGuidance,
25
- ...(completionDelivery === "auto-resume"
26
- ? [
27
- 'Track every final-answer-dependent subagent_spawn by setting completionRequirement to "required" and retaining its returned agentId or taskPath; treat interim output as progress, and synthesize only after every corresponding completion message is visible or terminal.',
28
- ]
29
- : []),
11
+ "Read the current pi-subagents session-guidance message before choosing detached completion behavior. With next-turn delivery, use subagent_spawn only when the current response does not depend on its result unless useful overlap ends with an intentional subagent_await. With auto-resume delivery, final-answer-dependent detached work may continue asynchronously.",
12
+ 'Track every final-answer-dependent subagent_spawn by setting completionRequirement to "required" and retaining its returned agentId or taskPath; treat interim output as progress, and synthesize only after every corresponding completion message is visible or terminal.',
30
13
  "Keep ordinary review in the main agent with a review skill and deterministic checks; use subagent_spawn for detached review only when consequential independent verification has concrete parallel value.",
31
14
  "Use a single subagent_spawn for a bounded implementation slice with clear ownership only when it can run beside the identified main-agent work.",
32
15
  "Use a single subagent_spawn without concurrent main-agent work only for an explicit user-requested specialist model, tool profile, or isolation boundary.",
@@ -20,7 +20,6 @@ import type { CompletionDeliveryBroker } from "./completion-delivery.js";
20
20
  import {
21
21
  CompletionRequirementModeSchema,
22
22
  completionRequirementsFromBranch,
23
- reconcileRequiredCompletionContext,
24
23
  } from "./completion-requirement.js";
25
24
  import type { ContextMode } from "./context.js";
26
25
  import type { CreateStatefulTransportOptions } from "./create-stateful-transport.js";
@@ -267,8 +266,6 @@ export interface StatefulSubagentRuntimeStatus {
267
266
  export interface StatefulSubagentController {
268
267
  getCompletionDelivery(): CompletionDelivery;
269
268
  setCompletionDelivery(value: CompletionDelivery): void;
270
- setAgentCatalog(value: string): void;
271
- refreshSettingsGuidance(): void;
272
269
  getRuntimeStatus(): StatefulSubagentRuntimeStatus;
273
270
  listAgents(includeClosed?: boolean): ManagedAgent[];
274
271
  listRunInspection(includeClosed?: boolean): AgentRunInspectionSummary[];
@@ -293,10 +290,8 @@ export function registerStatefulSubagents(
293
290
  const transportKind = resolveStatefulTransportKind(settings.transport);
294
291
  let completionDelivery = resolveCompletionDelivery(settings.completionDelivery);
295
292
  let runtimeLimits = resolveStatefulLimits(settings);
296
- let agentCatalog = "";
297
293
  let completionBroker: CompletionDeliveryBroker | undefined;
298
294
  let peerBroker: import("./peer-communication.js").PeerCommunicationBroker | undefined;
299
- let refreshSpawnToolRegistration: (() => void) | undefined;
300
295
  let registry: AgentRegistry | undefined;
301
296
  let persistence: AgentPersistence | undefined;
302
297
  let sweepTimer: NodeJS.Timeout | undefined;
@@ -350,14 +345,6 @@ export function registerStatefulSubagents(
350
345
  setCompletionDelivery(value) {
351
346
  completionDelivery = value;
352
347
  completionBroker?.setDelivery(value);
353
- refreshSpawnToolRegistration?.();
354
- },
355
- setAgentCatalog(value) {
356
- agentCatalog = value;
357
- refreshSpawnToolRegistration?.();
358
- },
359
- refreshSettingsGuidance() {
360
- refreshSpawnToolRegistration?.();
361
348
  },
362
349
  getRuntimeStatus() {
363
350
  const counts = registry?.inspectionCounts() ?? { activeAgents: 0, retainedAgents: 0 };
@@ -617,7 +604,6 @@ export function registerStatefulSubagents(
617
604
  if (completion.recipientId === "root") sessionBroker.enqueue(completion);
618
605
  }
619
606
  runtimeLimits = nextLimits;
620
- refreshSpawnToolRegistration?.();
621
607
  const sweepEveryMs = Math.max(
622
608
  1_000,
623
609
  Math.min(sessionSettings.idleTtlMs ?? 60 * 60 * 1000, 60_000),
@@ -642,9 +628,6 @@ export function registerStatefulSubagents(
642
628
 
643
629
  pi.on("context", (event) => {
644
630
  completionBroker?.onParentContext(event.messages);
645
- const agents = registry?.list() ?? [];
646
- const messages = reconcileRequiredCompletionContext(event.messages, agents);
647
- if (messages !== event.messages) return { messages };
648
631
  });
649
632
 
650
633
  pi.on("agent_settled", () => {
@@ -687,14 +670,13 @@ export function registerStatefulSubagents(
687
670
  await transition;
688
671
  });
689
672
 
690
- const baseSpawnDescription = () =>
691
- `Start an addressable background subagent with an opaque agentId and canonical taskPath, plus an optional thinking level and execution budgets chosen for the task difficulty, return immediately with an agentId, and receive its completion asynchronously. Detached capacity: ${runtimeLimits.maxAgents} retained agents, ${runtimeLimits.maxActiveTurns} active turns, ${runtimeLimits.maxChildrenPerAgent} direct children per agent, and depth ${runtimeLimits.maxDepth}. Working-directory target policy: ${dependencies.getSettings?.()?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY}. This controls launch targets and protected project resources, not filesystem access or sandboxing.`;
692
673
  const spawnTool = defineTool({
693
674
  name: "subagent_spawn",
694
675
  label: "Spawn Subagent",
695
- description: appendAgentCatalog(baseSpawnDescription(), agentCatalog),
676
+ description:
677
+ "Start an addressable background subagent with an opaque agentId and canonical taskPath, an optional thinking level and execution budgets chosen for the task difficulty, and asynchronous completion delivery. The current bounded capacity, completion policy, working-directory policy, and available agent definitions are published in the pi-subagents session-guidance message. Working-directory policy controls launch targets and protected project resources, not filesystem access or sandboxing.",
696
678
  promptSnippet: "Start a reusable detached subagent; completion is delivered asynchronously",
697
- promptGuidelines: createSpawnPromptGuidelines(completionDelivery, blockingEnabled),
679
+ promptGuidelines: createSpawnPromptGuidelines(blockingEnabled),
698
680
  parameters: grammarSafeToolObject({
699
681
  agent: Type.String({ minLength: 1 }),
700
682
  taskName: Type.Optional(
@@ -975,12 +957,7 @@ export function registerStatefulSubagents(
975
957
  }
976
958
  },
977
959
  });
978
- refreshSpawnToolRegistration = () => {
979
- spawnTool.description = appendAgentCatalog(baseSpawnDescription(), agentCatalog);
980
- spawnTool.promptGuidelines = createSpawnPromptGuidelines(completionDelivery, blockingEnabled);
981
- pi.registerTool(spawnTool);
982
- };
983
- refreshSpawnToolRegistration();
960
+ pi.registerTool(spawnTool);
984
961
 
985
962
  pi.registerTool({
986
963
  name: "subagent_send",
@@ -1303,10 +1280,6 @@ async function cleanupClosedWorkspaces(
1303
1280
  }
1304
1281
  }
1305
1282
 
1306
- function appendAgentCatalog(baseDescription: string, catalog: string): string {
1307
- return catalog ? `${baseDescription}\n\n${catalog}` : baseDescription;
1308
- }
1309
-
1310
1283
  function result(agent: ManagedAgent, text: string) {
1311
1284
  return {
1312
1285
  content: [{ type: "text" as const, text }],
@@ -39,6 +39,10 @@ import {
39
39
  import { MAX_BLOCKING_PARALLEL_CONCURRENCY } from "./limits.js";
40
40
  import { SubagentParams } from "./params.js";
41
41
  import { renderSubagentCall, renderSubagentResult } from "./render.js";
42
+ import {
43
+ registerSubagentSessionGuidance,
44
+ type SubagentSessionGuidanceSnapshot,
45
+ } from "./session-guidance-contract.js";
42
46
  import {
43
47
  consumeSubagentSettingsNotice,
44
48
  DEFAULT_CONSULT_RESOURCE_POLICY,
@@ -80,11 +84,10 @@ export default function (pi: ExtensionAPI, dependencies: SubagentsDependencies =
80
84
  let currentSettings: SubagentSettings | undefined = settings;
81
85
  let currentCatalog = "";
82
86
  const blockingEnabled = settings?.blocking?.enabled !== false;
83
- const refreshBlockingCatalog = blockingEnabled
84
- ? registerBlockingSubagent(pi, () => currentSettings, loadBlockingExecution)
85
- : () => undefined;
86
- let refreshStatefulCatalog: (catalog: string) => void = () => undefined;
87
- let refreshConsultCatalog: (catalog: string) => void = () => undefined;
87
+ const statefulEnabled = settings?.stateful?.enabled !== false;
88
+ if (blockingEnabled) {
89
+ registerBlockingSubagent(pi, () => currentSettings, statefulEnabled, loadBlockingExecution);
90
+ }
88
91
 
89
92
  pi.on("session_start", async (event, ctx) => {
90
93
  // Preserve a one-shot migration notice from extension load while refreshing
@@ -101,9 +104,6 @@ export default function (pi: ExtensionAPI, dependencies: SubagentsDependencies =
101
104
  currentCatalog = formatAgentCatalog(
102
105
  discoverAgentCatalog(ctx.cwd, ctx.isProjectTrusted(), refreshedSettings),
103
106
  ).text;
104
- refreshBlockingCatalog(currentCatalog);
105
- refreshStatefulCatalog(currentCatalog);
106
- refreshConsultCatalog(currentCatalog);
107
107
  await usageRecording.startSession({
108
108
  enabled: resolveUsageRecordingEnabled(currentSettings?.usageRecording),
109
109
  surfaceArm: usageSurfaceArm(blockingEnabled, statefulRuntime.getRuntimeStatus().enabled),
@@ -121,7 +121,6 @@ export default function (pi: ExtensionAPI, dependencies: SubagentsDependencies =
121
121
  loadTransport: dependencies.loadStatefulTransport,
122
122
  usageRecording,
123
123
  });
124
- refreshStatefulCatalog = statefulRuntime.setAgentCatalog;
125
124
  const getBlockingEnabled = () => blockingEnabled;
126
125
  const getMaxParallelTasks = () => resolveBlockingMaxParallelTasks(currentSettings);
127
126
  const getConsultResourcePolicy = () =>
@@ -144,16 +143,34 @@ export default function (pi: ExtensionAPI, dependencies: SubagentsDependencies =
144
143
  dependencies.inspect,
145
144
  );
146
145
  if (blockingEnabled) {
147
- refreshConsultCatalog = registerSubagentConsult(
148
- pi,
149
- { getSettings: () => currentSettings },
150
- dependencies.consult,
151
- );
146
+ registerSubagentConsult(pi, { getSettings: () => currentSettings }, dependencies.consult);
152
147
  }
148
+ const sessionGuidance = registerSubagentSessionGuidance(
149
+ pi,
150
+ (): SubagentSessionGuidanceSnapshot => {
151
+ const runtimeStatus = statefulRuntime.getRuntimeStatus();
152
+ return {
153
+ blockingEnabled,
154
+ statefulEnabled: runtimeStatus.enabled,
155
+ completionDelivery: runtimeStatus.completionDelivery,
156
+ blockingMaxParallelTasks: resolveBlockingMaxParallelTasks(currentSettings),
157
+ statefulLimits: runtimeStatus.limits,
158
+ consultationCwdPolicy: getConsultationCwdPolicy(),
159
+ delegationCwdPolicy: getDelegationCwdPolicy(),
160
+ consultResourcePolicy: getConsultResourcePolicy(),
161
+ agentCatalog: currentCatalog,
162
+ };
163
+ },
164
+ () => statefulRuntime.listAgents(),
165
+ );
153
166
  registerSubagentConfigCommand(
154
167
  pi,
155
168
  {
156
169
  ...statefulRuntime,
170
+ setCompletionDelivery(value) {
171
+ statefulRuntime.setCompletionDelivery(value);
172
+ sessionGuidance.publish();
173
+ },
157
174
  getBlockingEnabled,
158
175
  getMaxParallelTasks,
159
176
  getConsultResourcePolicy,
@@ -169,47 +186,32 @@ export default function (pi: ExtensionAPI, dependencies: SubagentsDependencies =
169
186
  };
170
187
  },
171
188
  setMaxParallelTasks(value: number) {
172
- const previousSettings = currentSettings;
173
189
  currentSettings = {
174
190
  ...(currentSettings ?? {}),
175
191
  blocking: { ...(currentSettings?.blocking ?? {}), maxParallelTasks: value },
176
192
  };
177
- try {
178
- refreshBlockingCatalog(currentCatalog);
179
- } catch (applyError) {
180
- currentSettings = previousSettings;
181
- try {
182
- refreshBlockingCatalog(currentCatalog);
183
- } catch (rollbackError) {
184
- throw new AggregateError(
185
- [applyError, rollbackError],
186
- "Failed to apply and roll back the parallel-worker limit",
187
- );
188
- }
189
- throw applyError;
190
- }
193
+ sessionGuidance.publish();
191
194
  },
192
195
  setConsultResourcePolicy(value: ConsultResourcePolicy) {
193
196
  currentSettings = {
194
197
  ...(currentSettings ?? {}),
195
198
  consult: { ...(currentSettings?.consult ?? {}), resources: value },
196
199
  };
197
- refreshConsultCatalog(currentCatalog);
200
+ sessionGuidance.publish();
198
201
  },
199
202
  setConsultationCwdPolicy(value: ConsultationCwdPolicy) {
200
203
  currentSettings = {
201
204
  ...(currentSettings ?? {}),
202
205
  cwdPolicy: { ...(currentSettings?.cwdPolicy ?? {}), consultation: value },
203
206
  };
204
- refreshConsultCatalog(currentCatalog);
207
+ sessionGuidance.publish();
205
208
  },
206
209
  setDelegationCwdPolicy(value: DelegationCwdPolicy) {
207
210
  currentSettings = {
208
211
  ...(currentSettings ?? {}),
209
212
  cwdPolicy: { ...(currentSettings?.cwdPolicy ?? {}), delegation: value },
210
213
  };
211
- refreshBlockingCatalog(currentCatalog);
212
- statefulRuntime.refreshSettingsGuidance();
214
+ sessionGuidance.publish();
213
215
  },
214
216
  },
215
217
  configOwner,
@@ -228,9 +230,9 @@ function usageSurfaceArm(blockingEnabled: boolean, statefulEnabled: boolean): Us
228
230
  function registerBlockingSubagent(
229
231
  pi: ExtensionAPI,
230
232
  getSettings: () => SubagentSettings | undefined,
233
+ statefulEnabled: boolean,
231
234
  loadExecution: () => Promise<BlockingExecutionModule>,
232
- ): (catalog: string) => void {
233
- let catalog = "";
235
+ ): void {
234
236
  let deprecationWarningShown = false;
235
237
  const activeControllers = new Set<AbortController>();
236
238
  const activeWork = new Set<Promise<unknown>>();
@@ -245,26 +247,24 @@ function registerBlockingSubagent(
245
247
  return cancelAndWaitForWork("Blocking subagent session replaced");
246
248
  });
247
249
  pi.on("session_shutdown", () => cancelAndWaitForWork("Blocking subagent session shut down"));
248
- const statefulEnabled = () => getSettings()?.stateful?.enabled !== false;
249
250
  const deprecationAlternatives = () =>
250
- statefulEnabled()
251
+ statefulEnabled
251
252
  ? "Prefer the main agent for tightly coupled work, subagent_spawn for detached work, subagent_await for an intentional retained-agent join, or subagent_consult for bounded synchronous read-only evidence."
252
253
  : "Prefer the main agent for tightly coupled work or subagent_consult for bounded synchronous read-only evidence; enable the background workflow before using detached alternatives.";
253
- const baseDescription = () =>
254
- [
255
- "Deprecated compatibility tool: do not choose subagent for new work.",
256
- deprecationAlternatives(),
257
- "Run specialized subagents as a blocking operation with isolated contexts.",
258
- "The call blocks the main agent until every worker and optional aggregator finishes, so queued steering waits.",
259
- "Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder), workflow (named dependency tasks with optional capability routing), or panel (independent reviewers plus evidence-preserving synthesis).",
260
- "Parallel mode may include an aggregator fan-in step; workflow mode validates dependencies, authority, artifacts, scope conflicts, retries, and hedging before scheduling. Use subagent_consult instead for one synchronous child that must be executor-constrained to read-only tools.",
261
- 'Default agent scope is "user" (from ~/.pi/agent/agents).',
262
- `To enable project-local agents in ${CONFIG_DIR_NAME}/agents, pass agentScope: "both" (or "project") as a top-level argument for that call.`,
263
- `Maximum parallel worker tasks per call: ${resolveBlockingMaxParallelTasks(getSettings())}. Parallel execution starts at most ${MAX_BLOCKING_PARALLEL_CONCURRENCY} workers at once.`,
264
- `Working-directory target policy: ${getSettings()?.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY}. This controls launch targets and protected project resources, not filesystem access or sandboxing.`,
265
- ].join(" ");
266
- const promptGuidelines = () => [
267
- statefulEnabled()
254
+ const baseDescription = [
255
+ "Deprecated compatibility tool: do not choose subagent for new work.",
256
+ deprecationAlternatives(),
257
+ "Run specialized subagents as a blocking operation with isolated contexts.",
258
+ "The call blocks the main agent until every worker and optional aggregator finishes, so queued steering waits.",
259
+ "Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder), workflow (named dependency tasks with optional capability routing), or panel (independent reviewers plus evidence-preserving synthesis).",
260
+ "Parallel mode may include an aggregator fan-in step; workflow mode validates dependencies, authority, artifacts, scope conflicts, retries, and hedging before scheduling. Use subagent_consult instead for one synchronous child that must be executor-constrained to read-only tools.",
261
+ 'Default agent scope is "user" (from ~/.pi/agent/agents).',
262
+ `To enable project-local agents in ${CONFIG_DIR_NAME}/agents, pass agentScope: "both" (or "project") as a top-level argument for that call.`,
263
+ `Configured parallel limits never exceed ${MAX_BLOCKING_PARALLEL_CONCURRENCY} concurrently started workers. The current call limit, working-directory policy, and available agent definitions are published in the pi-subagents session-guidance message.`,
264
+ "Working-directory policy controls launch targets and protected project resources, not filesystem access or sandboxing.",
265
+ ].join(" ");
266
+ const promptGuidelines = [
267
+ statefulEnabled
268
268
  ? "The subagent tool is deprecated for new work; prefer the main agent, subagent_spawn with supported completion delivery, subagent_await for an intentional retained-agent join, or subagent_consult for bounded synchronous read-only evidence."
269
269
  : "The subagent tool is deprecated for new work; prefer the main agent or subagent_consult for bounded synchronous read-only evidence, and enable the background workflow before using detached alternatives.",
270
270
  "Use deprecated subagent only for an existing caller or an explicit user request whose blocking chain, fan-in, panel, or workflow semantics do not yet have a detached replacement.",
@@ -275,7 +275,7 @@ function registerBlockingSubagent(
275
275
  "Keep ordinary planning in the main agent, or use explicit workflow mode when a genuine dependency graph requires caller-authored orchestration.",
276
276
  "Keep ordinary review in the main agent with a review skill and deterministic checks; reserve panel mode or custom verifier agents for consequential independent verification.",
277
277
  "A compatibility subagent call blocks the main agent from processing queued steering until it returns; use it only when the explicit legacy workflow justifies making Pi unavailable.",
278
- `If a blocking parallel subagent call is genuinely required, keep tasks independent, stay within the configured max ${resolveBlockingMaxParallelTasks(getSettings())}, and avoid write-heavy implementation touching the same files or shared state.`,
278
+ "If a blocking parallel subagent call is genuinely required, keep tasks independent, stay within the configured maximum from the current pi-subagents session-guidance message, and avoid write-heavy implementation touching the same files or shared state.",
279
279
  "For parallel subagent calls, omit the aggregator key entirely unless a fan-in step is required; do not send null, empty strings, or an empty object for unused optional fields.",
280
280
  "Use workflow mode for explicit dependencies or capability routing; declare read/write or ownership scopes, require structured-v2 artifacts when downstream tasks consume them, and use retry or hedging only with the required side-effect contract.",
281
281
  "Use panel mode only for consequential review or research that benefits from at least two independent reviewers and one bounded synthesis; agreement is not proof, dissent and blocking objections remain visible, and simple or latency-sensitive work should not use a panel.",
@@ -286,10 +286,10 @@ function registerBlockingSubagent(
286
286
  const definition: ToolDefinition<typeof SubagentParams, SubagentDetails> = {
287
287
  name: "subagent",
288
288
  label: "Blocking Subagent · Deprecated",
289
- description: appendAgentCatalog(baseDescription(), catalog),
289
+ description: baseDescription,
290
290
  promptSnippet:
291
291
  "Deprecated blocking subagent compatibility tool; prefer detached or read-only alternatives.",
292
- promptGuidelines: promptGuidelines(),
292
+ promptGuidelines,
293
293
  parameters: SubagentParams,
294
294
 
295
295
  async execute(toolCallId, params, signal, onUpdate, ctx) {
@@ -303,7 +303,7 @@ function registerBlockingSubagent(
303
303
  if (!deprecationWarningShown && ctx.hasUI) {
304
304
  deprecationWarningShown = true;
305
305
  ctx.ui.notify(
306
- statefulEnabled()
306
+ statefulEnabled
307
307
  ? "subagent is deprecated for new work. Prefer the main agent, subagent_spawn with completion delivery, subagent_await for an intentional join, or subagent_consult for synchronous read-only evidence."
308
308
  : "subagent is deprecated for new work. Prefer the main agent or subagent_consult; enable the background workflow before using detached alternatives.",
309
309
  "warning",
@@ -352,14 +352,4 @@ function registerBlockingSubagent(
352
352
  if ((event.details as (SubagentDetails & { isError?: boolean }) | undefined)?.isError)
353
353
  return { isError: true };
354
354
  });
355
- return (nextCatalog: string) => {
356
- catalog = nextCatalog;
357
- definition.description = appendAgentCatalog(baseDescription(), catalog);
358
- definition.promptGuidelines = promptGuidelines();
359
- pi.registerTool<typeof SubagentParams, SubagentDetails>(definition);
360
- };
361
- }
362
-
363
- function appendAgentCatalog(baseDescription: string, catalog: string): string {
364
- return catalog ? `${baseDescription}\n\n${catalog}` : baseDescription;
365
355
  }