@ian-pascoe/pi-minimal-subagents 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ filter in `~/.pi/agent/settings.json` using the repository-relative path:
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
From this package checkout, load the source directly with
|
|
32
|
-
`pi -e ./src/index.ts`. Requires Node `>=22.19.0` and Pi `>=0.
|
|
32
|
+
`pi -e ./src/index.ts`. Requires Node `>=22.19.0` and Pi `>=0.99.0`.
|
|
33
33
|
|
|
34
34
|
## Configuration
|
|
35
35
|
|
|
@@ -164,7 +164,7 @@ example:
|
|
|
164
164
|
"minimalSubagents": {
|
|
165
165
|
"baseToolset": ["context_*"],
|
|
166
166
|
"readToolset": ["read", "grep", "find", "ls"],
|
|
167
|
-
"modifyToolset": ["bash", "edit", "write", "
|
|
167
|
+
"modifyToolset": ["bash", "edit", "write", "lsp_*", "dap_*"]
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
```
|
|
@@ -203,18 +203,26 @@ Pattern expansion happens when a Child Agent is created. `/reload` applies
|
|
|
203
203
|
settings to future launches without changing existing Launch Contracts. If a tool
|
|
204
204
|
in an existing contract later disappears, normal restoration dependency checks
|
|
205
205
|
still apply; the saved grant is not silently rewritten. Toolsets configure names,
|
|
206
|
-
not tool operations: granting
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Child Agents load Pi's built-in `codemode`, `tool-search`, and `mcp`
|
|
210
|
-
unless settings disable them (`-builtin:<name>`); each child connects
|
|
211
|
-
servers.
|
|
206
|
+
not tool operations: granting a multifunction tool grants that tool's available
|
|
207
|
+
operations, regardless of preset name.
|
|
208
|
+
|
|
209
|
+
Child Agents load Pi's built-in `llama.cpp`, `codemode`, `tool-search`, and `mcp`
|
|
210
|
+
extensions unless settings disable them (`-builtin:<name>`); each child connects
|
|
211
|
+
its own MCP servers. Pi does not export the llama.cpp factory, so children load it
|
|
212
|
+
from Pi's installed `dist/extensions/llama/index.js`; when that file is absent, a
|
|
213
|
+
llama.cpp launch model is reported as a missing dependency. Child `codemode`
|
|
214
|
+
scripts get no `models` API, so they cannot call models outside the Launch
|
|
215
|
+
Contract. A tool's exposure still decides whether it is declared or reachable only
|
|
212
216
|
through `codemode` or `tool_search`. Granted `codemode`- or `deferred`-exposed
|
|
213
217
|
tools are callable but not declared, so a child that needs them also needs
|
|
214
218
|
`codemode` or `tool_search` in its grant. Ungranted tools are unreachable in the
|
|
215
219
|
child. MCP tools are checked at launch only by name, because children register
|
|
216
220
|
them after connecting; a tool whose server disappeared fails when called. Closing
|
|
217
|
-
a child runtime shuts its extensions down, closing its MCP connections.
|
|
221
|
+
a child runtime shuts its extensions down, closing its MCP connections. A reopened
|
|
222
|
+
child re-declares the tools it last declared itself, including tools loaded
|
|
223
|
+
through `tool_search`; as in Pi, a tool that registers only after the child opens,
|
|
224
|
+
such as an MCP tool, is not re-declared. Declarations inherited from the parent's
|
|
225
|
+
context are not restored.
|
|
218
226
|
|
|
219
227
|
## Capabilities and persistence
|
|
220
228
|
|
|
@@ -252,7 +260,11 @@ tools while the Launch Contract continues to record the originally granted
|
|
|
252
260
|
capability names.
|
|
253
261
|
|
|
254
262
|
`agent_message` reports whether a message was delivered through an active
|
|
255
|
-
parent wait, queued for the recipient, or failed.
|
|
263
|
+
parent wait, queued for the recipient, or failed. A failed delivery and a
|
|
264
|
+
partially failed `subagent_delete` return an error result (`isError: true`) that
|
|
265
|
+
still carries the declared structured output, so the model sees an error while
|
|
266
|
+
codemode scripts receive the `failures` or `error` data instead of a data-less
|
|
267
|
+
rejection. `subagent_wait` can return an
|
|
256
268
|
intermediate Wait Event containing a Coordination Message before the child turn
|
|
257
269
|
settles. That event claims only its message, so later unconsumed messages and the
|
|
258
270
|
terminal result retain automatic fallback. If the turn has already settled, one
|
|
@@ -315,6 +327,10 @@ process-local fork handoff is lost, recovery reads only the destination's
|
|
|
315
327
|
selected branch and proceeds only when its canonical `parentSession` proves the
|
|
316
328
|
source file; it never substitutes the source session's newer head.
|
|
317
329
|
|
|
330
|
+
## Tool annotations
|
|
331
|
+
|
|
332
|
+
The coordinator tools declare MCP-style `annotations`, which Pi reports through `pi.getAllTools()` for permission extensions and does not send to model providers. `subagent` is destructive and open-world because a Child Agent can use any tool it is granted. `agent_message` is not read-only but is non-destructive and closed-world. `subagent_cancel` is non-destructive and idempotent; `subagent_delete` is destructive and idempotent. `subagent_status` and `subagent_wait` are read-only (`subagent_wait` consumes queued deliveries, so it is not idempotent).
|
|
333
|
+
|
|
318
334
|
## Status and TUI
|
|
319
335
|
|
|
320
336
|
In TUI mode, `/subagents` or `/subagents status` opens a large, centered,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ian-pascoe/pi-minimal-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Persistent nested subagents with bounded delegation for Pi",
|
|
6
6
|
"keywords": [
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"provenance": true
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ian-pascoe/pi-utils": "^0.3.
|
|
34
|
+
"@ian-pascoe/pi-utils": "^0.3.1",
|
|
35
35
|
"minimatch": "^10.2.6"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@earendil-works/pi-agent-core": "
|
|
39
|
-
"@earendil-works/pi-ai": "
|
|
40
|
-
"@earendil-works/pi-coding-agent": "
|
|
41
|
-
"@earendil-works/pi-tui": "
|
|
38
|
+
"@earendil-works/pi-agent-core": ">=0.99.0",
|
|
39
|
+
"@earendil-works/pi-ai": ">=0.99.0",
|
|
40
|
+
"@earendil-works/pi-coding-agent": ">=0.99.0",
|
|
41
|
+
"@earendil-works/pi-tui": ">=0.99.0",
|
|
42
42
|
"typebox": "*"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import {
|
|
5
|
+
createCodemodeExtension,
|
|
6
|
+
createMcpExtension,
|
|
7
|
+
createToolSearchExtension,
|
|
8
|
+
DefaultResourceLoader,
|
|
9
|
+
type ExtensionFactory,
|
|
10
|
+
getPackageDir,
|
|
11
|
+
SettingsManager,
|
|
12
|
+
} from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { Type } from "typebox";
|
|
14
|
+
import { Value } from "typebox/value";
|
|
4
15
|
import { canonicalPath } from "./minimal-subagents-paths.js";
|
|
5
16
|
import type { ProjectContextMode } from "./minimal-subagents-types.js";
|
|
6
17
|
|
|
@@ -17,19 +28,52 @@ type InlineExtension = NonNullable<
|
|
|
17
28
|
ConstructorParameters<typeof DefaultResourceLoader>[0]["extensionFactories"]
|
|
18
29
|
>[number];
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
const ExtensionModuleSchema = Type.Object({
|
|
32
|
+
default: Type.Function([Type.Unknown()], Type.Unknown()),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Built-in extension name, and the provider ID it registers. */
|
|
36
|
+
const LLAMA = "llama.cpp";
|
|
37
|
+
|
|
38
|
+
/** Pi ships a file-backed llama.cpp factory but does not export it; undefined when it is absent. */
|
|
39
|
+
function piLlamaExtensionPath(): string | undefined {
|
|
40
|
+
const path = join(getPackageDir(), "dist", "extensions", "llama", "index.js");
|
|
41
|
+
return existsSync(path) ? path : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function llamaExtension(): InlineExtension[] {
|
|
45
|
+
const path = piLlamaExtensionPath();
|
|
46
|
+
if (!path) return [];
|
|
47
|
+
const factory: ExtensionFactory = async (pi) => {
|
|
48
|
+
const module: unknown = await import(pathToFileURL(path).href);
|
|
49
|
+
if (!Value.Check(ExtensionModuleSchema, module))
|
|
50
|
+
throw new Error(`Pi's built-in llama.cpp file has no extension factory (${path})`);
|
|
51
|
+
await module.default(pi);
|
|
52
|
+
};
|
|
53
|
+
return [{ name: LLAMA, factory, builtin: true }];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Whether a child can register a provider the root has; llama.cpp needs Pi's unexported factory. */
|
|
57
|
+
export function childProviderAvailable(provider: string): boolean {
|
|
58
|
+
return provider !== LLAMA || piLlamaExtensionPath() !== undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Child scripts must not call models outside their Launch Contract. */
|
|
62
|
+
export const CHILD_CODEMODE_MODELS = false;
|
|
63
|
+
|
|
64
|
+
/** Pi's built-in extensions in Pi's order, so children match the root's built-in surface. */
|
|
21
65
|
export function childBuiltinExtensions(): InlineExtension[] {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
return [
|
|
67
|
+
...llamaExtension(),
|
|
68
|
+
{
|
|
69
|
+
name: "codemode",
|
|
70
|
+
factory: createCodemodeExtension({ models: CHILD_CODEMODE_MODELS }),
|
|
71
|
+
builtin: true,
|
|
72
|
+
replaceable: true,
|
|
73
|
+
},
|
|
74
|
+
{ name: "tool-search", factory: createToolSearchExtension(), builtin: true, replaceable: true },
|
|
75
|
+
{ name: "mcp", factory: createMcpExtension(), builtin: true, replaceable: true },
|
|
76
|
+
];
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
function createDefaultChildResourceLoaderOptions(
|
|
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { unlink } from "node:fs/promises";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
6
|
-
import type
|
|
6
|
+
import { getCurrentSystemMessage, type Model, type Usage } from "@earendil-works/pi-ai";
|
|
7
7
|
import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
|
|
8
8
|
import {
|
|
9
9
|
AgentSession,
|
|
@@ -34,7 +34,11 @@ import {
|
|
|
34
34
|
DEFAULT_MAX_SUBAGENT_DEPTH,
|
|
35
35
|
getSubagentDepth,
|
|
36
36
|
} from "./minimal-subagents-capabilities.js";
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
CHILD_CODEMODE_MODELS,
|
|
39
|
+
childProviderAvailable,
|
|
40
|
+
createChildResourceLoader,
|
|
41
|
+
} from "./minimal-subagents-child-resources.js";
|
|
38
42
|
import {
|
|
39
43
|
CHILD_IDENTITY_ENTRY_TYPE,
|
|
40
44
|
FORK_CLONE_ENTRY_TYPE,
|
|
@@ -164,6 +168,20 @@ function installChildToolCapabilityPolicy(
|
|
|
164
168
|
session.setActiveToolsByName(session.getActiveToolNames().filter((name) => !isScriptOnly(name)));
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Passing `tools` skips Pi's transcript restore, so re-declare the tools a reopened child last
|
|
173
|
+
* declared itself (such as through `tool_search`); the capability policy still clamps them.
|
|
174
|
+
* Declarations inherited from the parent predate the child and are not restored.
|
|
175
|
+
*/
|
|
176
|
+
function restoreChildDeclaredTools(session: AgentSession, agent: PersistedAgent): void {
|
|
177
|
+
const { messages } = session.sessionManager.buildSessionContext();
|
|
178
|
+
const createdAt = Date.parse(agent.created_at);
|
|
179
|
+
if (!messages.some((message) => message.role === "system" && message.timestamp >= createdAt))
|
|
180
|
+
return;
|
|
181
|
+
const declared = getCurrentSystemMessage(messages);
|
|
182
|
+
if (declared) session.setActiveToolsByName((declared.toolsAdded ?? []).map(({ name }) => name));
|
|
183
|
+
}
|
|
184
|
+
|
|
167
185
|
/** Moves one verified child session file to trash and reports command unavailability. */
|
|
168
186
|
export interface SessionFileTrashCapability {
|
|
169
187
|
moveSessionFile(sessionFile: string): Promise<Error | undefined>;
|
|
@@ -201,6 +219,8 @@ export interface PiAgentSessionFactoryOptions {
|
|
|
201
219
|
agentDir: string;
|
|
202
220
|
extensions: readonly Pick<Extension, "path" | "resolvedPath" | "sourceInfo" | "hidden">[];
|
|
203
221
|
flagValues: ReadonlyMap<string, boolean | string>;
|
|
222
|
+
/** Whether child codemode scripts get the `models` API, so observers can reproduce it. */
|
|
223
|
+
codemodeModels: boolean;
|
|
204
224
|
},
|
|
205
225
|
) => ChildSessionObserver | undefined;
|
|
206
226
|
}
|
|
@@ -1109,9 +1129,12 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
1109
1129
|
requireEligibleModel: boolean,
|
|
1110
1130
|
): Promise<string[]> {
|
|
1111
1131
|
const missing: string[] = [];
|
|
1112
|
-
|
|
1132
|
+
const model = this.modelById.get(agent.launch_contract.model);
|
|
1133
|
+
if (!model) missing.push(agent.launch_contract.model);
|
|
1113
1134
|
else if (requireEligibleModel && !this.eligibleModelIds.has(agent.launch_contract.model)) {
|
|
1114
1135
|
missing.push(agent.launch_contract.model);
|
|
1136
|
+
} else if (!childProviderAvailable(model.provider)) {
|
|
1137
|
+
missing.push(agent.launch_contract.model);
|
|
1115
1138
|
}
|
|
1116
1139
|
const discoveredTools = await this.discoverChildToolNames(agent);
|
|
1117
1140
|
for (const toolName of agent.launch_contract.ordinary_tools) {
|
|
@@ -1217,6 +1240,7 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
1217
1240
|
throw new Error(`Minimal subagents child tool loading failed: ${missingTools.join(", ")}`);
|
|
1218
1241
|
}
|
|
1219
1242
|
installChildToolCapabilityPolicy(session, allowedToolNames, runtimeToolAdapters);
|
|
1243
|
+
restoreChildDeclaredTools(session, agent);
|
|
1220
1244
|
await session.bindExtensions({ mode: "print" });
|
|
1221
1245
|
const registeredNames = new Set(session.getAllTools().map((tool) => tool.name));
|
|
1222
1246
|
const missingCoordinatorTools = coordinatorTools
|
|
@@ -1244,6 +1268,7 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
1244
1268
|
hidden,
|
|
1245
1269
|
})),
|
|
1246
1270
|
flagValues: new Map(resourceLoader.getExtensions().runtime.flagValues),
|
|
1271
|
+
codemodeModels: CHILD_CODEMODE_MODELS,
|
|
1247
1272
|
}),
|
|
1248
1273
|
);
|
|
1249
1274
|
}
|
|
@@ -130,13 +130,31 @@ function structuredToolResult<TDetails extends CoordinatorToolResultDetails>(
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Report a failed operation that still carries declared output: the model sees an error and
|
|
135
|
+
* codemode scripts receive `structuredContent` instead of a data-less rejection.
|
|
136
|
+
*/
|
|
137
|
+
function failedStructuredToolResult<TDetails extends CoordinatorToolResultDetails>(
|
|
138
|
+
prefix: string,
|
|
139
|
+
result: TDetails,
|
|
140
|
+
options: { troubleshootingHint: boolean },
|
|
141
|
+
): AgentToolResult<TDetails> & { isError: true } {
|
|
142
|
+
const success = structuredToolResult(result);
|
|
143
|
+
const text = `${prefix}:\n${textOf(success)}`;
|
|
144
|
+
return {
|
|
145
|
+
...success,
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text" as const,
|
|
149
|
+
text: options.troubleshootingHint ? withTroubleshootingHint(text) : text,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function textOf(result: AgentToolResult<unknown>): string {
|
|
157
|
+
return result.content.map((part) => (part.type === "text" ? part.text : "")).join("\n");
|
|
140
158
|
}
|
|
141
159
|
|
|
142
160
|
function callerSourceTurnId(
|
|
@@ -164,6 +182,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
164
182
|
promptSnippet: "Spawn a persistent child with a prefix-free root-child ID",
|
|
165
183
|
promptGuidelines: modelRolePromptGuidelines,
|
|
166
184
|
parameters: options.schemas.subagent,
|
|
185
|
+
annotations: {
|
|
186
|
+
readOnlyHint: false,
|
|
187
|
+
destructiveHint: true,
|
|
188
|
+
idempotentHint: false,
|
|
189
|
+
openWorldHint: true,
|
|
190
|
+
},
|
|
167
191
|
async execute(_toolCallId, parameters, _signal, _onUpdate, context) {
|
|
168
192
|
return runCoordinatorToolActivity(options, async () => {
|
|
169
193
|
const result = await options.coordinator.spawn(
|
|
@@ -191,6 +215,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
191
215
|
"Send one mid-turn coordination message to a direct parent, direct sibling, or direct child. The result says whether it was delivered through an active wait, queued for the recipient, or failed.",
|
|
192
216
|
promptSnippet: "Coordinate required mid-turn action with one adjacent agent",
|
|
193
217
|
parameters: options.schemas.agent_message,
|
|
218
|
+
annotations: {
|
|
219
|
+
readOnlyHint: false,
|
|
220
|
+
destructiveHint: false,
|
|
221
|
+
idempotentHint: false,
|
|
222
|
+
openWorldHint: false,
|
|
223
|
+
},
|
|
194
224
|
async execute(toolCallId, parameters) {
|
|
195
225
|
return runCoordinatorToolActivity(options, async () => {
|
|
196
226
|
const result = await options.coordinator.sendAgentMessage(
|
|
@@ -201,7 +231,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
201
231
|
},
|
|
202
232
|
callerSourceTurnId(options.coordinator, options.callerId, toolCallId),
|
|
203
233
|
);
|
|
204
|
-
return
|
|
234
|
+
return result.disposition === "failed"
|
|
235
|
+
? failedStructuredToolResult("Minimal subagents message delivery failed", result, {
|
|
236
|
+
// The coordinator already appends the hint to `error`.
|
|
237
|
+
troubleshootingHint: false,
|
|
238
|
+
})
|
|
239
|
+
: structuredToolResult(result);
|
|
205
240
|
});
|
|
206
241
|
},
|
|
207
242
|
...createCoordinatorToolRendering("agent_message"),
|
|
@@ -214,6 +249,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
214
249
|
"Wait for one direct child's oldest observable turn, or select an exact retained turn_id. An active child may first return event=message; later unconsumed items still fall back automatically. An already settled turn returns event=turn once with queued messages in messages. Timeout returns event=timeout with detailed child status and never cancels the child.",
|
|
215
250
|
promptSnippet: "Wait for one direct child's exact turn",
|
|
216
251
|
parameters: options.schemas.subagent_wait,
|
|
252
|
+
annotations: {
|
|
253
|
+
readOnlyHint: true,
|
|
254
|
+
destructiveHint: false,
|
|
255
|
+
idempotentHint: false,
|
|
256
|
+
openWorldHint: false,
|
|
257
|
+
},
|
|
217
258
|
async execute(_toolCallId, parameters, signal, onUpdate) {
|
|
218
259
|
const startedAt = Date.now();
|
|
219
260
|
const updateWaitingResult = () =>
|
|
@@ -262,6 +303,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
262
303
|
"List direct children when agent_id is omitted, or inspect one direct child's launch contract, result, usage, dependencies, and bounded recent activity including message text and reasoning.",
|
|
263
304
|
promptSnippet: "Inspect direct child state",
|
|
264
305
|
parameters: options.schemas.subagent_status,
|
|
306
|
+
annotations: {
|
|
307
|
+
readOnlyHint: true,
|
|
308
|
+
destructiveHint: false,
|
|
309
|
+
idempotentHint: true,
|
|
310
|
+
openWorldHint: false,
|
|
311
|
+
},
|
|
265
312
|
async execute(_toolCallId, parameters) {
|
|
266
313
|
return runCoordinatorToolActivity(options, () =>
|
|
267
314
|
structuredToolResult(options.coordinator.status(options.callerId, parameters.agent_id)),
|
|
@@ -277,6 +324,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
277
324
|
"Abort active work for one direct child while preserving sessions for later continuation. Recursive cancellation includes its subtree and defaults to true.",
|
|
278
325
|
promptSnippet: "Cancel active subagent turns without deleting sessions",
|
|
279
326
|
parameters: options.schemas.subagent_cancel,
|
|
327
|
+
annotations: {
|
|
328
|
+
readOnlyHint: false,
|
|
329
|
+
destructiveHint: false,
|
|
330
|
+
idempotentHint: true,
|
|
331
|
+
openWorldHint: false,
|
|
332
|
+
},
|
|
280
333
|
async execute(_toolCallId, parameters) {
|
|
281
334
|
return runCoordinatorToolActivity(options, async () =>
|
|
282
335
|
structuredToolResult(
|
|
@@ -298,6 +351,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
298
351
|
"Delete one direct child's persistent session and retain durable ID tombstones. Recursive deletion includes its subtree and defaults to true.",
|
|
299
352
|
promptSnippet: "Delete subagent sessions and tombstone their IDs",
|
|
300
353
|
parameters: options.schemas.subagent_delete,
|
|
354
|
+
annotations: {
|
|
355
|
+
readOnlyHint: false,
|
|
356
|
+
destructiveHint: true,
|
|
357
|
+
idempotentHint: true,
|
|
358
|
+
openWorldHint: false,
|
|
359
|
+
},
|
|
301
360
|
async execute(_toolCallId, parameters) {
|
|
302
361
|
return runCoordinatorToolActivity(options, async () => {
|
|
303
362
|
const result = await options.coordinator.delete(
|
|
@@ -309,7 +368,9 @@ export function createCoordinatorToolDefinitions(
|
|
|
309
368
|
options.onAttention?.(
|
|
310
369
|
`Minimal subagents deletion partially failed for ${parameters.agent_id}`,
|
|
311
370
|
);
|
|
312
|
-
|
|
371
|
+
return failedStructuredToolResult("Minimal subagents deletion partially failed", result, {
|
|
372
|
+
troubleshootingHint: true,
|
|
373
|
+
});
|
|
313
374
|
}
|
|
314
375
|
return structuredToolResult(result);
|
|
315
376
|
});
|