@ian-pascoe/pi-minimal-subagents 0.1.1 → 0.2.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 +58 -5
- package/package.json +1 -1
- package/src/minimal-subagents-capabilities.ts +7 -0
- package/src/minimal-subagents-config.ts +182 -65
- package/src/minimal-subagents-context.ts +7 -1
- package/src/minimal-subagents-coordinator.ts +938 -114
- package/src/minimal-subagents-delivery-ledger.ts +529 -0
- package/src/minimal-subagents-extension.ts +382 -64
- package/src/minimal-subagents-fork-lifecycle.ts +22 -12
- package/src/minimal-subagents-message-envelope.ts +20 -0
- package/src/minimal-subagents-registry-wire.ts +269 -0
- package/src/minimal-subagents-registry.ts +1505 -132
- package/src/minimal-subagents-render-contract.ts +301 -0
- package/src/minimal-subagents-rendering.ts +513 -372
- package/src/minimal-subagents-session-wire.ts +42 -0
- package/src/minimal-subagents-sessions.ts +427 -101
- package/src/minimal-subagents-shutdown.ts +1 -1
- package/src/minimal-subagents-tool-schemas.ts +21 -7
- package/src/minimal-subagents-tools.ts +70 -21
- package/src/minimal-subagents-types.ts +65 -14
- package/src/minimal-subagents-ui.ts +8 -5
package/README.md
CHANGED
|
@@ -130,11 +130,64 @@ children: `subagent`, `agent_message`, `subagent_wait`, `subagent_status`,
|
|
|
130
130
|
only the three adjacent-coordination tools: `agent_message`, `subagent_wait`,
|
|
131
131
|
and `subagent_status`.
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
The `subagent` `tools` argument distinguishes capability presets from exact
|
|
134
|
+
lists: `"read"` grants `read`, `grep`, `find`, and `ls`; `"modify"` adds
|
|
135
|
+
`bash`, `edit`, and `write`; an array such as `["read"]` grants exactly the
|
|
136
|
+
named ordinary tool and does not expand a preset. Coordinator tools are
|
|
137
|
+
injected separately according to delegation and must not appear in `tools`;
|
|
138
|
+
misuse returns an actionable error. Use the string preset when a child needs
|
|
139
|
+
the complete read-only discovery bundle.
|
|
140
|
+
|
|
141
|
+
`agent_message` reports whether a message was delivered through an active
|
|
142
|
+
parent wait, queued for the recipient, or failed. `subagent_wait` can return an
|
|
143
|
+
intermediate Wait Event containing a Coordination Message before the child turn
|
|
144
|
+
settles; call it again for the terminal turn result. Pass optional `turn_id` to
|
|
145
|
+
address an older retained turn exactly. Without it, waits select the oldest
|
|
146
|
+
observable claimed or pending turn before the active/latest turn. A caller may
|
|
147
|
+
have only one outstanding wait for the same source turn; a concurrent duplicate
|
|
148
|
+
is rejected instead of competing for one Wait Event.
|
|
149
|
+
|
|
150
|
+
The persisted Delivery Ledger records Coordination Messages, terminal results,
|
|
151
|
+
globally increasing sequence, and wait ownership before delivery. Existing
|
|
152
|
+
items retain their sequence; gaps from skipped malformed records are valid.
|
|
153
|
+
Claims can name only active, latest, or retained turns. Once a wait returns an
|
|
154
|
+
intermediate message, that wait path owns the rest of the source turn across
|
|
155
|
+
reloads, forks, and newer turns. Automatic fallback retains its ordered queue
|
|
156
|
+
reservation, treats idle notifications as advisory, and rechecks actual
|
|
157
|
+
recipient idleness before injecting a message. Destination-session Delivery
|
|
158
|
+
Evidence settles and compacts ledger items, preventing duplicate delivery and
|
|
159
|
+
unbounded checkpoint growth. The pure Delivery Ledger state machine retains at
|
|
160
|
+
most 20 pending wait-only terminal results per source agent; Coordination
|
|
161
|
+
Messages are not removed by that terminal-retention limit. Delivered messages
|
|
162
|
+
include stable delivery, source-agent, and source-turn identities in persisted
|
|
163
|
+
details.
|
|
164
|
+
|
|
165
|
+
Deleting a child first verifies its session header and persistent identity,
|
|
166
|
+
then uses the optional `trash` command when available and falls back to
|
|
167
|
+
unlinking its session file. Deletion prunes pending delivery state and retained
|
|
168
|
+
recent-message projections sourced from the complete deleted subtree. Restore
|
|
169
|
+
and clone perform the same ownership check and reopen the recorded child-session
|
|
170
|
+
leaf.
|
|
171
|
+
|
|
172
|
+
Registry replay and Delivery Evidence are scoped to the Root Agent's active
|
|
173
|
+
session-tree branch. Registry writes use V2 records with complete field,
|
|
174
|
+
identity, sequence, hierarchy, adjacency, destination, ordinary-tool ceiling,
|
|
175
|
+
and coordinator-tool exclusion validation. Every available V2 agent has a
|
|
176
|
+
selected leaf; only unavailable recovery placeholders may omit it. Valid V1
|
|
177
|
+
records and checkpoints migrate during replay; invalid owned records are
|
|
178
|
+
skipped with semantic diagnostic codes rather than disabling the extension.
|
|
179
|
+
Persisted message activity carries an explicit `recorded_at` from the
|
|
180
|
+
coordinator clock.
|
|
181
|
+
|
|
182
|
+
`/tree` abandons old process-local work and restores the selected branch. Fork
|
|
183
|
+
preparation is read-only; only confirmed fork shutdown interrupts work and
|
|
184
|
+
clones the selected branch, so another extension can cancel a fork without
|
|
185
|
+
freezing coordinator tools. Each clone records a new generation-specific
|
|
186
|
+
identity/provenance pair, and the destination appends ownership for that clone's
|
|
187
|
+
current session ID rather than reusing inherited ownership. If a
|
|
188
|
+
process-local fork handoff is lost, recovery reads only the destination's
|
|
189
|
+
selected branch and proceeds only when its canonical `parentSession` proves the
|
|
190
|
+
source file; it never substitutes the source session's newer head.
|
|
138
191
|
|
|
139
192
|
## Status and TUI
|
|
140
193
|
|
package/package.json
CHANGED
|
@@ -73,6 +73,13 @@ export function resolveOrdinaryToolSelection(
|
|
|
73
73
|
? MODIFY_TOOL_BUNDLE
|
|
74
74
|
: selection;
|
|
75
75
|
const uniqueRequested = [...new Set(requested)];
|
|
76
|
+
const coordinatorTools = new Set<string>(COORDINATOR_TOOL_NAMES);
|
|
77
|
+
const requestedCoordinatorTools = uniqueRequested.filter((name) => coordinatorTools.has(name));
|
|
78
|
+
if (requestedCoordinatorTools.length > 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Minimal subagents ordinary tool selection: coordinator tools are injected separately and must not appear in tools: ${requestedCoordinatorTools.join(", ")}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
76
83
|
const available = new Set(context.availableTools);
|
|
77
84
|
const ceiling = new Set(context.capabilityCeiling);
|
|
78
85
|
const missing = uniqueRequested.filter((name) => !available.has(name));
|
|
@@ -1,8 +1,34 @@
|
|
|
1
|
+
import type { JsonValue } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { type Static, Type } from "typebox";
|
|
4
|
+
import { Value } from "typebox/value";
|
|
1
5
|
import { DEFAULT_MAX_SUBAGENT_DEPTH, THINKING_LEVELS } from "./minimal-subagents-capabilities.js";
|
|
2
6
|
|
|
3
7
|
const MODEL_ROLE_NAME_MAX_LENGTH = 64;
|
|
4
8
|
const MODEL_ROLE_HINT_MAX_LENGTH = 500;
|
|
5
9
|
|
|
10
|
+
const JsonValueSchema = Type.Unsafe<JsonValue>({});
|
|
11
|
+
const SettingsDocumentSchema = Type.Object({
|
|
12
|
+
minimalSubagents: Type.Optional(JsonValueSchema),
|
|
13
|
+
});
|
|
14
|
+
const MinimalSubagentsSettingsSchema = Type.Object({
|
|
15
|
+
maxSubagentDepth: Type.Optional(JsonValueSchema),
|
|
16
|
+
modelRoles: Type.Optional(JsonValueSchema),
|
|
17
|
+
});
|
|
18
|
+
const JsonObjectSchema = Type.Record(Type.String(), JsonValueSchema);
|
|
19
|
+
const PositiveSafeIntegerSchema = Type.Integer({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER });
|
|
20
|
+
const MaxSubagentDepthSettingSchema = Type.Union([PositiveSafeIntegerSchema, Type.Null()]);
|
|
21
|
+
const ShorthandModelRoleSchema = Type.String();
|
|
22
|
+
const ExpandedModelRoleSchema = Type.Object(
|
|
23
|
+
{
|
|
24
|
+
model: Type.String(),
|
|
25
|
+
hint: Type.Optional(Type.String()),
|
|
26
|
+
},
|
|
27
|
+
{ additionalProperties: false },
|
|
28
|
+
);
|
|
29
|
+
const ModelRoleEntriesSchema = Type.Record(Type.String(), JsonValueSchema);
|
|
30
|
+
const ModelRolesSettingSchema = Type.Union([ModelRoleEntriesSchema, Type.Null()]);
|
|
31
|
+
|
|
6
32
|
type ModelRoleThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
7
33
|
|
|
8
34
|
/** Describes one user-authored advisory model role shown to subagent callers. */
|
|
@@ -20,71 +46,153 @@ export interface ResolvedMinimalSubagentsConfig {
|
|
|
20
46
|
warnings: string[];
|
|
21
47
|
}
|
|
22
48
|
|
|
49
|
+
interface MinimalSubagentsSettingsDocument {
|
|
50
|
+
minimalSubagents?: JsonValue;
|
|
51
|
+
}
|
|
52
|
+
|
|
23
53
|
interface MinimalSubagentsConfigInput {
|
|
24
|
-
globalSettings:
|
|
25
|
-
projectSettings:
|
|
54
|
+
globalSettings: MinimalSubagentsSettingsDocument;
|
|
55
|
+
projectSettings: MinimalSubagentsSettingsDocument;
|
|
26
56
|
eligibleModelIds: readonly string[];
|
|
27
57
|
}
|
|
28
58
|
|
|
59
|
+
type PiSettingsDocument = ReturnType<SettingsManager["getGlobalSettings"]>;
|
|
60
|
+
type MinimalSubagentsSettingsDocumentInput = PiSettingsDocument | MinimalSubagentsSettingsDocument;
|
|
61
|
+
|
|
29
62
|
interface MinimalSubagentsSettingsReader {
|
|
30
|
-
getGlobalSettings():
|
|
31
|
-
getProjectSettings():
|
|
63
|
+
getGlobalSettings(): MinimalSubagentsSettingsDocumentInput;
|
|
64
|
+
getProjectSettings(): MinimalSubagentsSettingsDocumentInput;
|
|
32
65
|
}
|
|
33
66
|
|
|
34
67
|
type SettingsScope = "global" | "project";
|
|
35
68
|
|
|
36
69
|
interface ScopedSettingValue {
|
|
37
70
|
scope: SettingsScope;
|
|
38
|
-
value:
|
|
71
|
+
value: ModelRoleWireValue;
|
|
39
72
|
}
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
interface ParsedMinimalSubagentsSettings {
|
|
75
|
+
maxSubagentDepth?: MaxSubagentDepthWireValue;
|
|
76
|
+
modelRoles?: ModelRolesWireValue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type MaxSubagentDepthWireValue =
|
|
80
|
+
| { kind: "depth"; value: number }
|
|
81
|
+
| { kind: "reset" }
|
|
82
|
+
| { kind: "invalid" };
|
|
83
|
+
|
|
84
|
+
type ModelRoleWireValue =
|
|
85
|
+
| { kind: "delete" }
|
|
86
|
+
| { kind: "shorthand"; model: string }
|
|
87
|
+
| { kind: "expanded"; fields: Static<typeof ExpandedModelRoleSchema> }
|
|
88
|
+
| { kind: "malformed-expanded"; fields: Record<string, JsonValue> }
|
|
89
|
+
| { kind: "invalid" };
|
|
90
|
+
|
|
91
|
+
type ModelRolesWireValue =
|
|
92
|
+
| { kind: "reset" }
|
|
93
|
+
| { kind: "entries"; entries: ReadonlyMap<string, ModelRoleWireValue> }
|
|
94
|
+
| { kind: "invalid" };
|
|
95
|
+
|
|
96
|
+
function parseMaxSubagentDepthWireValue(value: JsonValue): MaxSubagentDepthWireValue {
|
|
97
|
+
if (!Value.Check(MaxSubagentDepthSettingSchema, value)) return { kind: "invalid" };
|
|
98
|
+
return value === null ? { kind: "reset" } : { kind: "depth", value };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseModelRoleWireValue(value: JsonValue): ModelRoleWireValue {
|
|
102
|
+
if (value === null) return { kind: "delete" };
|
|
103
|
+
if (Value.Check(ShorthandModelRoleSchema, value)) return { kind: "shorthand", model: value };
|
|
104
|
+
if (Value.Check(JsonObjectSchema, value)) {
|
|
105
|
+
return Value.Check(ExpandedModelRoleSchema, value)
|
|
106
|
+
? { kind: "expanded", fields: value }
|
|
107
|
+
: { kind: "malformed-expanded", fields: value };
|
|
108
|
+
}
|
|
109
|
+
return { kind: "invalid" };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isExpandedModelRoleWireValue(
|
|
113
|
+
value: ModelRoleWireValue,
|
|
114
|
+
): value is Extract<ModelRoleWireValue, { kind: "expanded" | "malformed-expanded" }> {
|
|
115
|
+
return value.kind === "expanded" || value.kind === "malformed-expanded";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function parseModelRolesWireValue(value: JsonValue): ModelRolesWireValue {
|
|
119
|
+
if (!Value.Check(ModelRolesSettingSchema, value)) return { kind: "invalid" };
|
|
120
|
+
if (value === null) return { kind: "reset" };
|
|
121
|
+
return {
|
|
122
|
+
kind: "entries",
|
|
123
|
+
entries: new Map(
|
|
124
|
+
Object.entries(value).map(([name, roleValue]) => [name, parseModelRoleWireValue(roleValue)]),
|
|
125
|
+
),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parsePiSettingsDocument(
|
|
130
|
+
settings: MinimalSubagentsSettingsDocumentInput,
|
|
131
|
+
): MinimalSubagentsSettingsDocument {
|
|
132
|
+
if (!Value.Check(SettingsDocumentSchema, settings)) return {};
|
|
133
|
+
const parsed: MinimalSubagentsSettingsDocument = {};
|
|
134
|
+
if (settings.minimalSubagents !== undefined) {
|
|
135
|
+
parsed.minimalSubagents = settings.minimalSubagents;
|
|
136
|
+
}
|
|
137
|
+
return parsed;
|
|
43
138
|
}
|
|
44
139
|
|
|
45
140
|
function readMinimalSubagentsSettings(
|
|
46
|
-
settings:
|
|
141
|
+
settings: MinimalSubagentsSettingsDocument,
|
|
47
142
|
scope: SettingsScope,
|
|
48
143
|
warnings: string[],
|
|
49
|
-
):
|
|
50
|
-
if (!
|
|
51
|
-
|
|
144
|
+
): ParsedMinimalSubagentsSettings {
|
|
145
|
+
if (!Value.Check(SettingsDocumentSchema, settings)) return {};
|
|
146
|
+
const minimalSubagents = settings.minimalSubagents;
|
|
147
|
+
if (minimalSubagents === undefined) return {};
|
|
148
|
+
if (Value.Check(MinimalSubagentsSettingsSchema, minimalSubagents)) {
|
|
149
|
+
const parsed: ParsedMinimalSubagentsSettings = {};
|
|
150
|
+
if (minimalSubagents.maxSubagentDepth !== undefined) {
|
|
151
|
+
parsed.maxSubagentDepth = parseMaxSubagentDepthWireValue(minimalSubagents.maxSubagentDepth);
|
|
152
|
+
}
|
|
153
|
+
if (minimalSubagents.modelRoles !== undefined) {
|
|
154
|
+
parsed.modelRoles = parseModelRolesWireValue(minimalSubagents.modelRoles);
|
|
155
|
+
}
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
52
158
|
warnings.push(`${scope} minimalSubagents: expected an object`);
|
|
53
159
|
return {};
|
|
54
160
|
}
|
|
55
161
|
|
|
56
162
|
function mergeModelRoleEntries(
|
|
57
|
-
globalValue:
|
|
58
|
-
projectValue:
|
|
163
|
+
globalValue: ModelRolesWireValue | undefined,
|
|
164
|
+
projectValue: ModelRolesWireValue | undefined,
|
|
59
165
|
warnings: string[],
|
|
60
166
|
): Map<string, ScopedSettingValue> {
|
|
61
167
|
const entries = new Map<string, ScopedSettingValue>();
|
|
62
|
-
if (globalValue
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
entries.set(name, { scope: "global", value });
|
|
66
|
-
}
|
|
67
|
-
} else if (globalValue !== null) {
|
|
68
|
-
warnings.push("global minimalSubagents.modelRoles: expected an object or null");
|
|
168
|
+
if (globalValue?.kind === "entries") {
|
|
169
|
+
for (const [name, value] of globalValue.entries) {
|
|
170
|
+
entries.set(name, { scope: "global", value });
|
|
69
171
|
}
|
|
172
|
+
} else if (globalValue?.kind === "invalid") {
|
|
173
|
+
warnings.push("global minimalSubagents.modelRoles: expected an object or null");
|
|
70
174
|
}
|
|
71
|
-
if (projectValue ===
|
|
175
|
+
if (projectValue?.kind === "reset") return new Map();
|
|
72
176
|
if (projectValue === undefined) return entries;
|
|
73
|
-
if (
|
|
177
|
+
if (projectValue.kind === "invalid") {
|
|
74
178
|
warnings.push("project minimalSubagents.modelRoles: expected an object or null");
|
|
75
179
|
return entries;
|
|
76
180
|
}
|
|
181
|
+
if (projectValue.kind !== "entries") return entries;
|
|
77
182
|
|
|
78
|
-
for (const [name, value] of
|
|
79
|
-
if (value ===
|
|
183
|
+
for (const [name, value] of projectValue.entries) {
|
|
184
|
+
if (value.kind === "delete") {
|
|
80
185
|
entries.delete(name);
|
|
81
186
|
continue;
|
|
82
187
|
}
|
|
83
188
|
const inherited = entries.get(name)?.value;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
189
|
+
const mergedValue =
|
|
190
|
+
inherited !== undefined &&
|
|
191
|
+
isExpandedModelRoleWireValue(inherited) &&
|
|
192
|
+
isExpandedModelRoleWireValue(value)
|
|
193
|
+
? parseModelRoleWireValue({ ...inherited.fields, ...value.fields })
|
|
194
|
+
: value;
|
|
195
|
+
entries.set(name, { scope: "project", value: mergedValue });
|
|
88
196
|
}
|
|
89
197
|
return entries;
|
|
90
198
|
}
|
|
@@ -142,69 +250,78 @@ function parseModelRoles(
|
|
|
142
250
|
}
|
|
143
251
|
|
|
144
252
|
const value = entry.value;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
253
|
+
const expandedRoleObject = isExpandedModelRoleWireValue(value) ? value.fields : undefined;
|
|
254
|
+
if (expandedRoleObject !== undefined) {
|
|
255
|
+
const invalidFields = Object.keys(expandedRoleObject).filter(
|
|
256
|
+
(key) => key !== "model" && key !== "hint",
|
|
257
|
+
);
|
|
258
|
+
if (invalidFields.length > 0) {
|
|
259
|
+
warnings.push(`${path}: unknown field: ${invalidFields.join(", ")}`);
|
|
149
260
|
continue;
|
|
150
261
|
}
|
|
151
|
-
} else if (
|
|
262
|
+
} else if (value.kind !== "shorthand") {
|
|
152
263
|
warnings.push(`${path}: expected a model string or expanded role object`);
|
|
153
264
|
continue;
|
|
154
265
|
}
|
|
155
266
|
|
|
156
|
-
const
|
|
157
|
-
|
|
267
|
+
const modelValue =
|
|
268
|
+
expandedRoleObject === undefined && value.kind === "shorthand"
|
|
269
|
+
? value.model
|
|
270
|
+
: expandedRoleObject?.model;
|
|
271
|
+
if (
|
|
272
|
+
!Value.Check(ShorthandModelRoleSchema, modelValue) ||
|
|
273
|
+
modelValue.length === 0 ||
|
|
274
|
+
modelValue !== modelValue.trim()
|
|
275
|
+
) {
|
|
158
276
|
warnings.push(`${path}: model must be a non-empty trimmed string`);
|
|
159
277
|
continue;
|
|
160
278
|
}
|
|
161
|
-
const resolvedModel = resolveModelRoleReference(
|
|
279
|
+
const resolvedModel = resolveModelRoleReference(modelValue, eligibleModels, path, warnings);
|
|
162
280
|
if (resolvedModel === undefined) continue;
|
|
163
281
|
|
|
164
|
-
const
|
|
282
|
+
const hintValue = expandedRoleObject?.hint;
|
|
165
283
|
if (
|
|
166
|
-
|
|
167
|
-
(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
/[\r\n]/.test(
|
|
171
|
-
|
|
284
|
+
hintValue !== undefined &&
|
|
285
|
+
(!Value.Check(ShorthandModelRoleSchema, hintValue) ||
|
|
286
|
+
hintValue.length === 0 ||
|
|
287
|
+
hintValue !== hintValue.trim() ||
|
|
288
|
+
/[\r\n]/.test(hintValue) ||
|
|
289
|
+
hintValue.length > MODEL_ROLE_HINT_MAX_LENGTH)
|
|
172
290
|
) {
|
|
173
291
|
warnings.push(`${path}.hint: expected trimmed single-line text up to 500 characters`);
|
|
174
292
|
continue;
|
|
175
293
|
}
|
|
176
|
-
|
|
294
|
+
const role: MinimalSubagentsModelRole = {
|
|
177
295
|
name,
|
|
178
296
|
model: resolvedModel.model,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
297
|
+
};
|
|
298
|
+
if (resolvedModel.thinkingLevel !== undefined) {
|
|
299
|
+
role.thinkingLevel = resolvedModel.thinkingLevel;
|
|
300
|
+
}
|
|
301
|
+
if (hintValue !== undefined && Value.Check(ShorthandModelRoleSchema, hintValue)) {
|
|
302
|
+
role.hint = hintValue;
|
|
303
|
+
}
|
|
304
|
+
roles.push(role);
|
|
184
305
|
}
|
|
185
306
|
return roles;
|
|
186
307
|
}
|
|
187
308
|
|
|
188
309
|
function resolveMaxSubagentDepth(
|
|
189
|
-
globalValue:
|
|
190
|
-
projectValue:
|
|
310
|
+
globalValue: MaxSubagentDepthWireValue | undefined,
|
|
311
|
+
projectValue: MaxSubagentDepthWireValue | undefined,
|
|
191
312
|
warnings: string[],
|
|
192
313
|
): number {
|
|
193
314
|
let resolvedDepth = DEFAULT_MAX_SUBAGENT_DEPTH;
|
|
194
|
-
if (globalValue
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
);
|
|
201
|
-
}
|
|
315
|
+
if (globalValue?.kind === "depth") {
|
|
316
|
+
resolvedDepth = globalValue.value;
|
|
317
|
+
} else if (globalValue?.kind === "invalid") {
|
|
318
|
+
warnings.push(
|
|
319
|
+
"global minimalSubagents.maxSubagentDepth: expected a positive safe integer or null",
|
|
320
|
+
);
|
|
202
321
|
}
|
|
203
322
|
if (projectValue === undefined) return resolvedDepth;
|
|
204
|
-
if (projectValue ===
|
|
205
|
-
if (
|
|
206
|
-
return Number(projectValue);
|
|
207
|
-
}
|
|
323
|
+
if (projectValue.kind === "reset") return DEFAULT_MAX_SUBAGENT_DEPTH;
|
|
324
|
+
if (projectValue.kind === "depth") return projectValue.value;
|
|
208
325
|
warnings.push(
|
|
209
326
|
"project minimalSubagents.maxSubagentDepth: expected a positive safe integer or null",
|
|
210
327
|
);
|
|
@@ -242,8 +359,8 @@ export function resolveMinimalSubagentsSettings(
|
|
|
242
359
|
eligibleModelIds: readonly string[],
|
|
243
360
|
): ResolvedMinimalSubagentsConfig {
|
|
244
361
|
return resolveMinimalSubagentsConfig({
|
|
245
|
-
globalSettings: settings.getGlobalSettings(),
|
|
246
|
-
projectSettings: settings.getProjectSettings(),
|
|
362
|
+
globalSettings: parsePiSettingsDocument(settings.getGlobalSettings()),
|
|
363
|
+
projectSettings: parsePiSettingsDocument(settings.getProjectSettings()),
|
|
247
364
|
eligibleModelIds,
|
|
248
365
|
});
|
|
249
366
|
}
|
|
@@ -11,11 +11,17 @@ export function snapshotCommittedContext(
|
|
|
11
11
|
return structuredClone(committed);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** Carries the selected caller messages and whether child preparation should compact them. */
|
|
15
|
+
export interface ImportedSubagentContext {
|
|
16
|
+
messages: AgentMessage[];
|
|
17
|
+
compact: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
/** Select the imported message snapshot and defer expensive compact preparation to the child turn. */
|
|
15
21
|
export function assembleImportedContext(
|
|
16
22
|
mode: SessionContextMode,
|
|
17
23
|
committedMessages: AgentMessage[],
|
|
18
|
-
):
|
|
24
|
+
): ImportedSubagentContext {
|
|
19
25
|
if (mode === "omit") return { messages: [], compact: false };
|
|
20
26
|
return { messages: committedMessages, compact: mode === "compact" };
|
|
21
27
|
}
|