@narumitw/pi-subagents 2.1.0 → 2.1.2
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/README.md +23 -12
- package/dist/chunks/{chunk-PABJJYP6.ts → chunk-2LMJU25E.ts} +12 -41
- package/dist/chunks/chunk-2LMJU25E.ts.map +7 -0
- package/dist/chunks/{chunk-FXI45N3J.ts → chunk-6NSJVPXX.ts} +4 -14
- package/dist/chunks/{chunk-FXI45N3J.ts.map → chunk-6NSJVPXX.ts.map} +2 -2
- package/dist/chunks/{chunk-VBDGNNLM.ts → chunk-NLT67IZS.ts} +137 -29
- package/dist/chunks/chunk-NLT67IZS.ts.map +7 -0
- package/dist/chunks/{chunk-DIHBUR2E.ts → chunk-YU53SHA7.ts} +2 -2
- package/dist/chunks/{completion-delivery-JVLNQRWX.ts → completion-delivery-RSJU6BXL.ts} +3 -3
- package/dist/chunks/{config-ui-DDKERQHI.ts → config-ui-ABHYNGQ7.ts} +4 -4
- package/dist/chunks/{consult-PQ6PRAKC.ts → consult-LJU3IQY5.ts} +2 -2
- package/dist/chunks/{persistence-XHPJBZL7.ts → persistence-UY3PY6E5.ts} +2 -2
- package/dist/chunks/{registry-XDXPECWF.ts → registry-BT54L6CY.ts} +2 -2
- package/dist/index.ts +322 -60
- package/dist/index.ts.map +3 -3
- package/docs/async-runtime-protocol.md +17 -2
- package/package.json +2 -2
- package/src/completion-requirement.ts +201 -30
- package/src/consult-registration.ts +4 -12
- package/src/session-guidance-contract.ts +399 -0
- package/src/stateful-guidance.ts +3 -20
- package/src/stateful-registration.ts +4 -31
- package/src/subagents-extension.ts +56 -66
- package/dist/chunks/chunk-PABJJYP6.ts.map +0 -7
- package/dist/chunks/chunk-VBDGNNLM.ts.map +0 -7
- /package/dist/chunks/{chunk-DIHBUR2E.ts.map → chunk-YU53SHA7.ts.map} +0 -0
- /package/dist/chunks/{completion-delivery-JVLNQRWX.ts.map → completion-delivery-RSJU6BXL.ts.map} +0 -0
- /package/dist/chunks/{config-ui-DDKERQHI.ts.map → config-ui-ABHYNGQ7.ts.map} +0 -0
- /package/dist/chunks/{consult-PQ6PRAKC.ts.map → consult-LJU3IQY5.ts.map} +0 -0
- /package/dist/chunks/{persistence-XHPJBZL7.ts.map → persistence-UY3PY6E5.ts.map} +0 -0
- /package/dist/chunks/{registry-XDXPECWF.ts.map → registry-BT54L6CY.ts.map} +0 -0
|
@@ -14,7 +14,12 @@ Omitting the field or using `background` preserves prior behavior and does not c
|
|
|
14
14
|
`AgentRegistry` owns requirement transitions with the child turn and persisted completion outbox.
|
|
15
15
|
Tool-result `details.agent.completionRequirements` provides fork-sensitive branch evidence.
|
|
16
16
|
Session restoration retains exact requirements found on the active branch and treats sessions without visible subagent state as a possible compacted continuation.
|
|
17
|
-
The
|
|
17
|
+
The successful lifecycle tool result and delivered completion message are the ordinary model-visible requirement handoff.
|
|
18
|
+
When a resume changes a pending run to cancelled and interrupted while its stale handoff remains in model context, `before_agent_start` appends one hidden versioned transition after that handoff.
|
|
19
|
+
This append-only transition also applies when leading summaries retain the stale handoff, prevents duplicate publication on later turns, and participates in fork-sensitive branch reconstruction.
|
|
20
|
+
If leading compaction or branch summaries remove the handoff, the `context` hook restores one canonical hidden `pi-subagent-required-completions` fallback immediately after the summaries.
|
|
21
|
+
A branch-local custom session entry records the exact restored boundary so reload and tree navigation reconstruct the correct historical fallback.
|
|
22
|
+
The fallback remains at that fixed boundary for the leading-summary epoch, while a later completion or cancellation transition supersedes it at the conversation tail.
|
|
18
23
|
`CompletionDeliveryBroker` owns exact completion visibility acknowledgement and asks the registry to mark the corresponding requirement visible.
|
|
19
24
|
No timer, waiter, or UI object owns requirement truth.
|
|
20
25
|
|
|
@@ -31,10 +36,20 @@ The runtime bounds retained requirement records per agent and rejects a sixty-fi
|
|
|
31
36
|
## Parent behavior
|
|
32
37
|
|
|
33
38
|
Pending and available requirements remain final-answer dependencies.
|
|
34
|
-
|
|
39
|
+
A newly established canonical fallback omits requirements already visible at that boundary.
|
|
40
|
+
A fallback retained from an earlier request remains historical prefix context after visibility changes, and the later completion message supplies the superseding state.
|
|
35
41
|
Cancelled requirements are terminal and must be reported rather than silently treated as successful evidence.
|
|
36
42
|
A failed, partial, interrupted, stale, or cancelled child never satisfies mutating acceptance or independent-verification requirements merely because its turn settled.
|
|
37
43
|
|
|
44
|
+
## Prompt-cache boundary
|
|
45
|
+
|
|
46
|
+
Provider-visible subagent tool definitions and prompt metadata remain stable within one configured tool-surface epoch.
|
|
47
|
+
A versioned hidden session-guidance message carries the bounded agent catalog, completion delivery, capacity, cwd policy, and consultation resource policy without placing mutable values in leading tool metadata.
|
|
48
|
+
The initial guidance contract is persisted once before the first agent turn when no equivalent retained contract exists.
|
|
49
|
+
A successfully applied live policy change appends a superseding guidance contract without triggering a model turn.
|
|
50
|
+
Compaction restores missing guidance and required-completion fallbacks in deterministic order after leading summaries.
|
|
51
|
+
These rules preserve normalized cache-eligible prefixes across ordinary turns but do not guarantee a provider-reported cache hit.
|
|
52
|
+
|
|
38
53
|
## Budget termination
|
|
39
54
|
|
|
40
55
|
Omitted limits use runtime or agent policy and are recorded as runtime-sourced telemetry.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-subagents",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Pi extension for delegating work to specialized isolated subagents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"typescript": "7.0.2"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@narumitw/pi-tui-kit": "^0.58.
|
|
56
|
+
"@narumitw/pi-tui-kit": "^0.58.1",
|
|
57
57
|
"proper-lockfile": "^4.1.2"
|
|
58
58
|
},
|
|
59
59
|
"repository": {
|
|
@@ -14,6 +14,9 @@ export const CompletionRequirementModeSchema = StringEnum(COMPLETION_REQUIREMENT
|
|
|
14
14
|
|
|
15
15
|
export const COMPLETION_REQUIREMENT_VERSION = "pi-subagents:completion-requirement:v1" as const;
|
|
16
16
|
export const COMPLETION_REQUIREMENT_CONTEXT_TYPE = "pi-subagent-required-completions";
|
|
17
|
+
export const COMPLETION_REQUIREMENT_TRANSITION_TYPE = "pi-subagent-required-completion-transition";
|
|
18
|
+
export const COMPLETION_REQUIREMENT_TRANSITION_VERSION =
|
|
19
|
+
"pi-subagents:completion-requirement-transition:v1" as const;
|
|
17
20
|
const MAX_REQUIREMENTS_PER_AGENT = 20;
|
|
18
21
|
export const MAX_UNRESOLVED_REQUIRED_COMPLETIONS = 64;
|
|
19
22
|
|
|
@@ -159,6 +162,24 @@ export function completionRequirementsFromBranch(
|
|
|
159
162
|
}
|
|
160
163
|
continue;
|
|
161
164
|
}
|
|
165
|
+
if (
|
|
166
|
+
message.role === "custom" &&
|
|
167
|
+
message.customType === COMPLETION_REQUIREMENT_TRANSITION_TYPE
|
|
168
|
+
) {
|
|
169
|
+
const details = message.details;
|
|
170
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) continue;
|
|
171
|
+
const detailRecord = details as Record<string, unknown>;
|
|
172
|
+
if (detailRecord.version !== COMPLETION_REQUIREMENT_TRANSITION_VERSION) continue;
|
|
173
|
+
const requirements = Array.isArray(detailRecord.records) ? detailRecord.records : [];
|
|
174
|
+
for (const requirement of requirements) {
|
|
175
|
+
if (!isCompletionRequirementRecord(requirement) || requirement.state !== "cancelled") {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
observedState = true;
|
|
179
|
+
records.set(completionRequirementKey(requirement), { ...requirement });
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
162
183
|
if (message.role !== "custom" || message.customType !== "pi-subagent-completion") continue;
|
|
163
184
|
const details = message.details;
|
|
164
185
|
if (!details || typeof details !== "object" || Array.isArray(details)) continue;
|
|
@@ -187,22 +208,98 @@ export function pendingRequiredCompletionCount(
|
|
|
187
208
|
).length;
|
|
188
209
|
}
|
|
189
210
|
|
|
211
|
+
export function createRequiredCompletionTransition(
|
|
212
|
+
messages: ContextEvent["messages"],
|
|
213
|
+
agents: readonly ManagedAgent[],
|
|
214
|
+
) {
|
|
215
|
+
const summarized = leadingSummaryBoundary(messages) > 0;
|
|
216
|
+
const state = modelRequirementState(agents);
|
|
217
|
+
const visible = completionRequirementsFromBranch(messages).records;
|
|
218
|
+
const records = state.records.filter((record) => {
|
|
219
|
+
if (record.state !== "cancelled") return false;
|
|
220
|
+
const retained = visible.get(completionRequirementKey(record));
|
|
221
|
+
if (modelRequirementStateMatches(retained, record)) return false;
|
|
222
|
+
return !summarized || retained !== undefined;
|
|
223
|
+
});
|
|
224
|
+
if (records.length === 0) return undefined;
|
|
225
|
+
return {
|
|
226
|
+
role: "custom" as const,
|
|
227
|
+
customType: COMPLETION_REQUIREMENT_TRANSITION_TYPE,
|
|
228
|
+
content: requiredCompletionTransitionContent(records, state.omittedCancelled),
|
|
229
|
+
display: false,
|
|
230
|
+
details: {
|
|
231
|
+
version: COMPLETION_REQUIREMENT_TRANSITION_VERSION,
|
|
232
|
+
records: records.map((record) => ({ ...record })),
|
|
233
|
+
},
|
|
234
|
+
timestamp: 0,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
190
238
|
export function reconcileRequiredCompletionContext(
|
|
191
239
|
messages: ContextEvent["messages"],
|
|
192
240
|
agents: readonly ManagedAgent[],
|
|
241
|
+
restorationPredecessors: readonly string[] = [],
|
|
242
|
+
restoredBoundaryContent?: string,
|
|
193
243
|
): ContextEvent["messages"] {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
);
|
|
244
|
+
const existing = messages.filter(isRequiredCompletionContextMessage);
|
|
245
|
+
const withoutPrior = messages.filter((message) => !isRequiredCompletionContextMessage(message));
|
|
246
|
+
const state = modelRequirementState(agents);
|
|
247
|
+
const summaryBoundary = leadingSummaryBoundary(withoutPrior);
|
|
248
|
+
const currentContent =
|
|
249
|
+
state.records.length > 0 && !hasModelVisibleRequirementState(withoutPrior, state.records)
|
|
250
|
+
? requiredCompletionContextContent(state.records, state.omittedCancelled)
|
|
251
|
+
: undefined;
|
|
252
|
+
const content = summaryBoundary > 0 ? (restoredBoundaryContent ?? currentContent) : undefined;
|
|
253
|
+
let insertionBoundary = summaryBoundary;
|
|
254
|
+
while (insertionBoundary < withoutPrior.length) {
|
|
255
|
+
const predecessor = withoutPrior[insertionBoundary];
|
|
256
|
+
if (
|
|
257
|
+
predecessor?.role !== "custom" ||
|
|
258
|
+
!restorationPredecessors.includes(predecessor.customType)
|
|
259
|
+
) {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
insertionBoundary += 1;
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
content !== undefined &&
|
|
266
|
+
existing.length === 1 &&
|
|
267
|
+
messages[insertionBoundary] === existing[0] &&
|
|
268
|
+
existing[0]?.content === content &&
|
|
269
|
+
hasCompletionRequirementContextVersion(existing[0])
|
|
270
|
+
) {
|
|
271
|
+
return messages;
|
|
272
|
+
}
|
|
273
|
+
if (existing.length === 0 && content === undefined) return messages;
|
|
274
|
+
if (content === undefined) return withoutPrior;
|
|
275
|
+
return [
|
|
276
|
+
...withoutPrior.slice(0, insertionBoundary),
|
|
277
|
+
{
|
|
278
|
+
role: "custom",
|
|
279
|
+
customType: COMPLETION_REQUIREMENT_CONTEXT_TYPE,
|
|
280
|
+
content,
|
|
281
|
+
display: false,
|
|
282
|
+
details: { version: COMPLETION_REQUIREMENT_VERSION },
|
|
283
|
+
timestamp: 0,
|
|
284
|
+
},
|
|
285
|
+
...withoutPrior.slice(insertionBoundary),
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
interface ModelRequirementRecord {
|
|
290
|
+
state: CompletionRequirementState;
|
|
291
|
+
runId: string;
|
|
292
|
+
generation: number;
|
|
293
|
+
terminalState: AgentLifecycleState | undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function modelRequirementState(agents: readonly ManagedAgent[]): {
|
|
297
|
+
records: CompletionRequirementRecord[];
|
|
298
|
+
omittedCancelled: number;
|
|
299
|
+
} {
|
|
198
300
|
const allRecords = agents
|
|
199
301
|
.flatMap((agent) =>
|
|
200
|
-
(agent.completionRequirements ?? []).map((requirement) => ({
|
|
201
|
-
state: requirement.state,
|
|
202
|
-
runId: requirement.runId,
|
|
203
|
-
generation: requirement.generation,
|
|
204
|
-
terminalState: requirement.terminalState,
|
|
205
|
-
})),
|
|
302
|
+
(agent.completionRequirements ?? []).map((requirement) => ({ ...requirement })),
|
|
206
303
|
)
|
|
207
304
|
.filter((record) => record.state !== "visible");
|
|
208
305
|
const unresolved = allRecords.filter(
|
|
@@ -211,34 +308,108 @@ export function reconcileRequiredCompletionContext(
|
|
|
211
308
|
const cancelled = allRecords.filter((record) => record.state === "cancelled");
|
|
212
309
|
const cancelledSlots = Math.max(0, MAX_UNRESOLVED_REQUIRED_COMPLETIONS - unresolved.length);
|
|
213
310
|
const retainedCancelled = cancelledSlots > 0 ? cancelled.slice(-cancelledSlots) : [];
|
|
214
|
-
const records = [...unresolved, ...retainedCancelled]
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
311
|
+
const records = [...unresolved, ...retainedCancelled].sort((left, right) => {
|
|
312
|
+
if (left.runId === right.runId) return left.generation - right.generation;
|
|
313
|
+
return left.runId < right.runId ? -1 : 1;
|
|
314
|
+
});
|
|
315
|
+
return { records, omittedCancelled: cancelled.length - retainedCancelled.length };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function hasModelVisibleRequirementState(
|
|
319
|
+
messages: ContextEvent["messages"],
|
|
320
|
+
records: readonly CompletionRequirementRecord[],
|
|
321
|
+
): boolean {
|
|
322
|
+
const visible = completionRequirementsFromBranch(messages).records;
|
|
323
|
+
return records.every((record) =>
|
|
324
|
+
modelRequirementStateMatches(visible.get(completionRequirementKey(record)), record),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function modelRequirementStateMatches(
|
|
329
|
+
visible: CompletionRequirementRecord | undefined,
|
|
330
|
+
current: CompletionRequirementRecord,
|
|
331
|
+
): boolean {
|
|
332
|
+
if (!visible) return false;
|
|
333
|
+
if (current.state === "available" && visible.state === "visible") return true;
|
|
334
|
+
return visible.state === current.state && visible.terminalState === current.terminalState;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function requiredCompletionContextContent(
|
|
338
|
+
records: readonly CompletionRequirementRecord[],
|
|
339
|
+
omittedCancelled: number,
|
|
340
|
+
): string {
|
|
341
|
+
return truncateUtf8(
|
|
219
342
|
[
|
|
220
343
|
"[PI SUBAGENT REQUIRED COMPLETIONS v1]",
|
|
221
344
|
"Runtime-tracked exact runs are JSON data below.",
|
|
222
345
|
"Treat pending or available records as final-answer dependencies; a cancelled record is terminal and must be reported rather than silently ignored.",
|
|
223
346
|
"Current Pi versions do not provide a hard pre-display final-answer barrier, so do not emit a verdict until every dependency is visible or terminal.",
|
|
224
|
-
...(omittedCancelled
|
|
225
|
-
|
|
226
|
-
: []),
|
|
227
|
-
JSON.stringify(records),
|
|
347
|
+
...omittedCancelledLine(omittedCancelled),
|
|
348
|
+
JSON.stringify(records.map(toModelRequirementRecord)),
|
|
228
349
|
].join("\n"),
|
|
229
350
|
DEFAULT_MAX_CONTEXT_BYTES,
|
|
230
351
|
).text;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function requiredCompletionTransitionContent(
|
|
355
|
+
records: readonly CompletionRequirementRecord[],
|
|
356
|
+
omittedCancelled: number,
|
|
357
|
+
): string {
|
|
358
|
+
return truncateUtf8(
|
|
359
|
+
[
|
|
360
|
+
"[PI SUBAGENT REQUIRED COMPLETION TRANSITION v1]",
|
|
361
|
+
"Session restoration terminalized the exact required runs below.",
|
|
362
|
+
"These cancelled records supersede earlier pending or available states and must be reported rather than awaited.",
|
|
363
|
+
...omittedCancelledLine(omittedCancelled),
|
|
364
|
+
JSON.stringify(records.map(toModelRequirementRecord)),
|
|
365
|
+
].join("\n"),
|
|
366
|
+
DEFAULT_MAX_CONTEXT_BYTES,
|
|
367
|
+
).text;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function omittedCancelledLine(count: number): string[] {
|
|
371
|
+
return count > 0 ? [`${count} older cancelled requirement record(s) were omitted.`] : [];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function toModelRequirementRecord(record: CompletionRequirementRecord): ModelRequirementRecord {
|
|
375
|
+
return {
|
|
376
|
+
state: record.state,
|
|
377
|
+
runId: record.runId,
|
|
378
|
+
generation: record.generation,
|
|
379
|
+
terminalState: record.terminalState,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
type RequiredCompletionContextMessage = Extract<
|
|
384
|
+
ContextEvent["messages"][number],
|
|
385
|
+
{ role: "custom" }
|
|
386
|
+
> & { content: string };
|
|
387
|
+
|
|
388
|
+
function isRequiredCompletionContextMessage(
|
|
389
|
+
message: ContextEvent["messages"][number],
|
|
390
|
+
): message is RequiredCompletionContextMessage {
|
|
391
|
+
return message.role === "custom" && message.customType === COMPLETION_REQUIREMENT_CONTEXT_TYPE;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function hasCompletionRequirementContextVersion(
|
|
395
|
+
message: RequiredCompletionContextMessage,
|
|
396
|
+
): boolean {
|
|
397
|
+
return (
|
|
398
|
+
typeof message.details === "object" &&
|
|
399
|
+
message.details !== null &&
|
|
400
|
+
!Array.isArray(message.details) &&
|
|
401
|
+
(message.details as Record<string, unknown>).version === COMPLETION_REQUIREMENT_VERSION
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function leadingSummaryBoundary(messages: ContextEvent["messages"]): number {
|
|
406
|
+
let index = 0;
|
|
407
|
+
while (index < messages.length) {
|
|
408
|
+
const role = messages[index]?.role;
|
|
409
|
+
if (role !== "compactionSummary" && role !== "branchSummary") break;
|
|
410
|
+
index += 1;
|
|
411
|
+
}
|
|
412
|
+
return index;
|
|
242
413
|
}
|
|
243
414
|
|
|
244
415
|
export function isCompletionRequirementRecord(
|
|
@@ -3,10 +3,6 @@ import { cachedModuleLoader, throwIfAborted } from "./cached-module-loader.js";
|
|
|
3
3
|
import type { RegisterSubagentConsultOptions } from "./consult.js";
|
|
4
4
|
import { renderConsultCall, renderConsultResult } from "./consult-render.js";
|
|
5
5
|
import { type ConsultDetails, SubagentConsultParams } from "./consult-tool.js";
|
|
6
|
-
import {
|
|
7
|
-
DEFAULT_CONSULT_RESOURCE_POLICY,
|
|
8
|
-
DEFAULT_CONSULTATION_CWD_POLICY,
|
|
9
|
-
} from "./settings/inspection.js";
|
|
10
6
|
|
|
11
7
|
interface ConsultExecutionModule {
|
|
12
8
|
executeSubagentConsult: typeof import("./consult.js").executeSubagentConsult;
|
|
@@ -20,7 +16,7 @@ export function registerSubagentConsult(
|
|
|
20
16
|
pi: ExtensionAPI,
|
|
21
17
|
options: RegisterSubagentConsultOptions,
|
|
22
18
|
dependencies: ConsultRegistrationDependencies = {},
|
|
23
|
-
):
|
|
19
|
+
): void {
|
|
24
20
|
const loadExecution = cachedModuleLoader(
|
|
25
21
|
dependencies.loadExecution ?? (() => import("./consult.js")),
|
|
26
22
|
);
|
|
@@ -41,12 +37,12 @@ export function registerSubagentConsult(
|
|
|
41
37
|
pi.on("session_start", () => cancelAndWaitForWork("Subagent consultation session replaced"));
|
|
42
38
|
pi.on("session_shutdown", () => cancelAndWaitForWork("Subagent consultation session shut down"));
|
|
43
39
|
|
|
44
|
-
const
|
|
45
|
-
|
|
40
|
+
const description =
|
|
41
|
+
"Run one ephemeral subagent synchronously under enforced read-only tool and resource policies and return its answer. The child can use only the effective subset of Pi's built-in read, grep, find, and ls tools. Shell commands, file writes, extension tools, detached lifecycle operations, and persistent agent state are disabled. The current working-directory policy, trusted-target resource policy, and available agent definitions are published in the pi-subagents session-guidance message. Allowed targets without effective trust inherit no target or project resources. This is not a filesystem sandbox.";
|
|
46
42
|
const definition: ToolDefinition<typeof SubagentConsultParams, ConsultDetails> = {
|
|
47
43
|
name: "subagent_consult",
|
|
48
44
|
label: "Consult Read-only Subagent",
|
|
49
|
-
description
|
|
45
|
+
description,
|
|
50
46
|
promptSnippet: "Consult one constrained read-only subagent and wait for its answer",
|
|
51
47
|
promptGuidelines: [
|
|
52
48
|
"Use subagent_consult only for bounded read-only evidence gathering when an independent perspective is worth making the main agent wait.",
|
|
@@ -103,10 +99,6 @@ export function registerSubagentConsult(
|
|
|
103
99
|
if (event.toolName !== "subagent_consult") return;
|
|
104
100
|
if ((event.details as ConsultDetails | undefined)?.isError) return { isError: true };
|
|
105
101
|
});
|
|
106
|
-
return (catalog: string) => {
|
|
107
|
-
definition.description = catalog ? `${baseDescription()}\n\n${catalog}` : baseDescription();
|
|
108
|
-
pi.registerTool<typeof SubagentConsultParams, ConsultDetails>(definition);
|
|
109
|
-
};
|
|
110
102
|
}
|
|
111
103
|
|
|
112
104
|
function combineAbortSignals(
|