pi-fast-subagent 0.9.1 → 0.9.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 +10 -2
- package/agents/scout.md +4 -3
- package/index.ts +127 -4
- package/package.json +1 -1
- package/render.ts +16 -4
- package/runner.ts +71 -12
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-fast-subagent
|
|
2
2
|
|
|
3
|
-
In-process subagent delegation for [pi](https://github.com/badlogic/pi-mono).
|
|
3
|
+
In-process subagent delegation for [pi](https://github.com/badlogic/pi-mono) with max visibility.
|
|
4
4
|
|
|
5
5
|
Runs subagents with `createAgentSession()` in same process instead of spawning `pi` subprocesses. This removes subprocess cold-start and reuses pi auth/model registry.
|
|
6
6
|
|
|
@@ -272,6 +272,14 @@ Cancel a specific job directly:
|
|
|
272
272
|
/fast-subagent:bg-cancel sa_ab12cd34
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
+
### `/fast-subagent:debug-model`
|
|
276
|
+
|
|
277
|
+
Show cached/inferred main-model state used for model inheritance troubleshooting.
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
/fast-subagent:debug-model
|
|
281
|
+
```
|
|
282
|
+
|
|
275
283
|
## Keyboard Shortcuts
|
|
276
284
|
|
|
277
285
|
| Shortcut | Action |
|
|
@@ -289,7 +297,7 @@ Goal: keep this extension **small and focused** — aligned with pi's philosophy
|
|
|
289
297
|
|
|
290
298
|
- Async/background isolation not supported in-process
|
|
291
299
|
- Git worktree isolation not supported
|
|
292
|
-
- Nested subagent
|
|
300
|
+
- Nested subagent spawning disabled by default (`maxDepth: 0`); opt in per agent via frontmatter
|
|
293
301
|
|
|
294
302
|
## Tool Reference
|
|
295
303
|
|
package/agents/scout.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scout
|
|
3
3
|
description: Explores codebases, maps structure, traces data flow, answers how things work across many files
|
|
4
|
-
model:
|
|
4
|
+
model: openai-codex/gpt-5.4-mini
|
|
5
5
|
|
|
6
6
|
# tools: which tools this agent can use.
|
|
7
7
|
# (omit) → all tools: builtins + every parent extension (default)
|
|
@@ -36,5 +36,6 @@ Output style:
|
|
|
36
36
|
- use sections
|
|
37
37
|
- include file paths
|
|
38
38
|
- include short bullets
|
|
39
|
-
-
|
|
40
|
-
- do not
|
|
39
|
+
- describe what code does, not whether it is good or bad
|
|
40
|
+
- do not rate, review, or analyze quality
|
|
41
|
+
- do not propose code changes
|
package/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "./format.js";
|
|
28
28
|
import { defaultLoaderPool } from "./loader-pool.js";
|
|
29
29
|
import { renderSubagentCall, renderSubagentResult } from "./render.js";
|
|
30
|
-
import { getCurrentDepth, mapConcurrent, runAgent } from "./runner.js";
|
|
30
|
+
import { DEPTH_ENV, getCurrentDepth, mapConcurrent, resolveModelObject, runAgent } from "./runner.js";
|
|
31
31
|
import { SubagentParams } from "./schemas.js";
|
|
32
32
|
import type { AgentRowStatus, OnUpdate, RunResult, SubagentDetails, ToolCallEntry } from "./types.js";
|
|
33
33
|
|
|
@@ -36,6 +36,9 @@ import type { AgentRowStatus, OnUpdate, RunResult, SubagentDetails, ToolCallEntr
|
|
|
36
36
|
let _bgManager: BackgroundJobManager | null = null;
|
|
37
37
|
let _onBgJobComplete: ((job: BackgroundSubagentJob) => void) | null = null;
|
|
38
38
|
let _setBgStatus: ((text: string | undefined) => void) | null = null;
|
|
39
|
+
let _currentMainModel: string | undefined;
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
let _currentMainModelObject: any | undefined;
|
|
39
42
|
|
|
40
43
|
function getBgManager(): BackgroundJobManager {
|
|
41
44
|
if (!_bgManager) _bgManager = new BackgroundJobManager({
|
|
@@ -49,6 +52,54 @@ function refreshBgStatus(): void {
|
|
|
49
52
|
_setBgStatus?.(running.length > 0 ? `⧗ ${running.length} bg agent${running.length > 1 ? "s" : ""}` : undefined);
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
function rememberMainModel(model: ExtensionContext["model"]): void {
|
|
56
|
+
if (!model) return;
|
|
57
|
+
_currentMainModel = `${model.provider}/${model.id}`;
|
|
58
|
+
_currentMainModelObject = model;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function rememberMainModelReference(modelRef: string | undefined, ctx: ExtensionContext): void {
|
|
62
|
+
if (!modelRef) return;
|
|
63
|
+
_currentMainModel = modelRef;
|
|
64
|
+
_currentMainModelObject = resolveModelObject(ctx.modelRegistry, modelRef) ?? _currentMainModelObject;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function rememberPayloadModel(payload: unknown, ctx: ExtensionContext): void {
|
|
68
|
+
const modelId = typeof payload === "object" && payload && "model" in payload && typeof (payload as { model?: unknown }).model === "string"
|
|
69
|
+
? (payload as { model: string }).model
|
|
70
|
+
: undefined;
|
|
71
|
+
if (!modelId) return;
|
|
72
|
+
|
|
73
|
+
const lower = modelId.toLowerCase();
|
|
74
|
+
const matches = ctx.modelRegistry.getAll().filter((m) => m.id.toLowerCase() === lower || `${m.provider}/${m.id}`.toLowerCase() === lower);
|
|
75
|
+
if (matches.length === 1) rememberMainModel(matches[0]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isMainSessionEvent(): boolean {
|
|
79
|
+
return !process.env[DEPTH_ENV];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function rememberLatestSessionModel(ctx: ExtensionContext): void {
|
|
83
|
+
const scan = (entries: ReturnType<ExtensionContext["sessionManager"]["getEntries"]>): boolean => {
|
|
84
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
85
|
+
const e = entries[i]!;
|
|
86
|
+
if (e.type === "model_change") {
|
|
87
|
+
rememberMainModelReference(`${(e as any).provider}/${(e as any).modelId}`, ctx);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
if (!scan(ctx.sessionManager.getEntries())) scan(ctx.sessionManager.getBranch());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getInheritedMainModel(ctx: ExtensionContext): { modelRef?: string; deps: { modelRegistry: ExtensionContext["modelRegistry"]; modelObject?: unknown } } {
|
|
97
|
+
rememberMainModel(ctx.model);
|
|
98
|
+
if (!_currentMainModelObject && _currentMainModel) rememberMainModelReference(_currentMainModel, ctx);
|
|
99
|
+
if (_currentMainModelObject) return { modelRef: _currentMainModel, deps: { modelRegistry: ctx.modelRegistry, modelObject: _currentMainModelObject } };
|
|
100
|
+
return { modelRef: _currentMainModel, deps: { modelRegistry: ctx.modelRegistry } };
|
|
101
|
+
}
|
|
102
|
+
|
|
52
103
|
// ─── Foreground detach registry ─────────────────────────────────────────────
|
|
53
104
|
|
|
54
105
|
interface ForegroundDetachEntry {
|
|
@@ -84,8 +135,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
84
135
|
);
|
|
85
136
|
};
|
|
86
137
|
|
|
138
|
+
pi.on("model_select", async (event) => {
|
|
139
|
+
if (!isMainSessionEvent()) return;
|
|
140
|
+
rememberMainModel(event.model);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
pi.on("before_provider_request", async (event, ctx) => {
|
|
144
|
+
if (!isMainSessionEvent()) return;
|
|
145
|
+
if (ctx.model) rememberMainModel(ctx.model);
|
|
146
|
+
else rememberPayloadModel(event.payload, ctx);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
pi.on("turn_start", async (_event, ctx) => {
|
|
150
|
+
if (!isMainSessionEvent()) return;
|
|
151
|
+
rememberMainModel(ctx.model);
|
|
152
|
+
});
|
|
153
|
+
|
|
87
154
|
pi.on("session_start", async (_event, ctx) => {
|
|
88
155
|
_setBgStatus = (text) => ctx.ui.setStatus(BG_STATUS_KEY, text);
|
|
156
|
+
if (!isMainSessionEvent()) return;
|
|
157
|
+
// Seed _currentMainModel from ctx.model or session entries.
|
|
158
|
+
if (ctx.model) rememberMainModel(ctx.model);
|
|
159
|
+
else rememberLatestSessionModel(ctx);
|
|
89
160
|
|
|
90
161
|
// Warm one extension-capable loader after startup so first `tools: all`
|
|
91
162
|
// subagent call reuses loaded extensions instead of blocking.
|
|
@@ -100,6 +171,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
100
171
|
getBgManager().shutdown();
|
|
101
172
|
_bgManager = null;
|
|
102
173
|
_setBgStatus = null;
|
|
174
|
+
_currentMainModel = undefined;
|
|
175
|
+
_currentMainModelObject = undefined;
|
|
103
176
|
defaultLoaderPool.clear();
|
|
104
177
|
});
|
|
105
178
|
|
|
@@ -124,6 +197,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
124
197
|
},
|
|
125
198
|
});
|
|
126
199
|
|
|
200
|
+
// ─── /fast-subagent:debug-model ──────────────────────────────────────────
|
|
201
|
+
pi.registerCommand("fast-subagent:debug-model", {
|
|
202
|
+
description: "Debug: show cached main model and session entries.",
|
|
203
|
+
async handler(_args, ctx) {
|
|
204
|
+
const entries = ctx.sessionManager.getEntries();
|
|
205
|
+
const modelEntries = entries.filter((e) => e.type === "model_change");
|
|
206
|
+
const branch = ctx.sessionManager.getBranch();
|
|
207
|
+
const branchModelEntries = branch.filter((e) => e.type === "model_change");
|
|
208
|
+
if (!_currentMainModelObject && _currentMainModel) rememberMainModelReference(_currentMainModel, ctx);
|
|
209
|
+
const registryMatch = resolveModelObject(ctx.modelRegistry, _currentMainModel);
|
|
210
|
+
const lines = [
|
|
211
|
+
`_currentMainModel: ${_currentMainModel ?? "(undefined)"}`,
|
|
212
|
+
`_currentMainModelObject: ${_currentMainModelObject ? `${_currentMainModelObject.provider}/${_currentMainModelObject.id}` : "(undefined)"}`,
|
|
213
|
+
`ctx.model: ${ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "(undefined)"}`,
|
|
214
|
+
`resolveModelObject(_currentMainModel): ${registryMatch ? `${registryMatch.provider}/${registryMatch.id}` : "(undefined)"}`,
|
|
215
|
+
`getEntries() total: ${entries.length}`,
|
|
216
|
+
`getEntries() model_change: ${modelEntries.map((e) => `${(e as any).provider}/${(e as any).modelId}`).join(", ") || "none"}`,
|
|
217
|
+
`getBranch() total: ${branch.length}`,
|
|
218
|
+
`getBranch() model_change: ${branchModelEntries.map((e) => `${(e as any).provider}/${(e as any).modelId}`).join(", ") || "none"}`,
|
|
219
|
+
];
|
|
220
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
127
224
|
// ─── /fast-subagent:agent ─────────────────────────────────────────────────
|
|
128
225
|
pi.registerCommand("fast-subagent:agent", {
|
|
129
226
|
description: "List available subagents. Usage: /fast-subagent:agent [name] — show details for a specific agent.",
|
|
@@ -417,11 +514,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
417
514
|
const { agent, error } = findAgent(params.agent);
|
|
418
515
|
if (error || !agent) return { content: [{ type: "text", text: error ?? "Not found" }] };
|
|
419
516
|
|
|
517
|
+
// Determine if this agent should inherit the main session's model.
|
|
518
|
+
const shouldInherit = !params.model && (!agent.model || agent.model === "inherit");
|
|
519
|
+
let inheritedModel: string | undefined;
|
|
520
|
+
let inheritDeps: { modelRegistry: ExtensionContext["modelRegistry"]; modelObject?: unknown } = { modelRegistry: ctx.modelRegistry };
|
|
521
|
+
if (shouldInherit) {
|
|
522
|
+
let inherited = getInheritedMainModel(ctx);
|
|
523
|
+
if (!inherited.modelRef) {
|
|
524
|
+
rememberLatestSessionModel(ctx);
|
|
525
|
+
inherited = getInheritedMainModel(ctx);
|
|
526
|
+
}
|
|
527
|
+
inheritedModel = inherited.modelRef;
|
|
528
|
+
inheritDeps = inherited.deps;
|
|
529
|
+
}
|
|
530
|
+
const effectiveModel = params.model ?? (shouldInherit ? inheritedModel : (agent.model === "inherit" ? undefined : agent.model));
|
|
531
|
+
|
|
420
532
|
if (params.background) {
|
|
421
533
|
const bgAbort = new AbortController();
|
|
422
534
|
const handle: BackgroundHandleLike = { abort: () => bgAbort.abort() };
|
|
423
535
|
const resultPromise: Promise<BackgroundJobResult> = runAgent(
|
|
424
|
-
agent, params.task, cwd,
|
|
536
|
+
agent, params.task, cwd, effectiveModel, bgAbort.signal, undefined, undefined, inheritDeps,
|
|
425
537
|
).then((r) => ({ summary: r.output, exitCode: r.exitCode, error: r.error, model: r.model }));
|
|
426
538
|
const jobId = getBgManager().adoptHandle(agent.name, params.task, cwd, handle, resultPromise);
|
|
427
539
|
return { content: [{ type: "text", text: `Background job started: ${jobId}\nTo check status, ask me to poll job ${jobId}.` }] };
|
|
@@ -441,7 +553,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
441
553
|
: undefined;
|
|
442
554
|
|
|
443
555
|
const agentRunPromise: Promise<RunResult> = runAgent(
|
|
444
|
-
agent, params.task, cwd,
|
|
556
|
+
agent, params.task, cwd, effectiveModel, agentAbort.signal, wrappedOnUpdate, undefined, inheritDeps,
|
|
445
557
|
);
|
|
446
558
|
|
|
447
559
|
const bgResultPromise: Promise<BackgroundJobResult> = agentRunPromise
|
|
@@ -546,7 +658,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
546
658
|
parallelAgents[i]!.responseText = (partial.content?.[0] as any)?.text || parallelAgents[i]!.responseText;
|
|
547
659
|
emitParallel(true);
|
|
548
660
|
};
|
|
549
|
-
const
|
|
661
|
+
const shouldInherit = !t.model && (!agent.model || agent.model === "inherit");
|
|
662
|
+
const inherited = shouldInherit ? getInheritedMainModel(ctx) : undefined;
|
|
663
|
+
const result = await runAgent(
|
|
664
|
+
agent,
|
|
665
|
+
t.task,
|
|
666
|
+
t.cwd ?? cwd,
|
|
667
|
+
t.model ?? inherited?.modelRef,
|
|
668
|
+
signal,
|
|
669
|
+
agentOnUpdate,
|
|
670
|
+
parentDepth,
|
|
671
|
+
inherited?.deps ?? { modelRegistry: ctx.modelRegistry },
|
|
672
|
+
);
|
|
550
673
|
parallelAgents[i]!.status = result.exitCode === 0 ? "done" : "error";
|
|
551
674
|
parallelAgents[i]!.durMs = Date.now() - agentStart;
|
|
552
675
|
parallelAgents[i]!.toolCalls = result.toolCalls;
|
package/package.json
CHANGED
package/render.ts
CHANGED
|
@@ -334,11 +334,22 @@ export function renderSubagentResult(
|
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
} else {
|
|
337
|
-
// Render events chronologically
|
|
337
|
+
// Render events chronologically. Coalesce text deltas to avoid one-line-per-token output.
|
|
338
338
|
let lastWasText = false;
|
|
339
|
+
let textBuffer = "";
|
|
339
340
|
const agentLabel = `${details.agentName ?? "Agent"}:`;
|
|
341
|
+
|
|
342
|
+
const flushTextBuffer = () => {
|
|
343
|
+
if (!textBuffer) return;
|
|
344
|
+
for (const line of textBuffer.split("\n")) {
|
|
345
|
+
for (const w of wrapLine(indent + line, width)) out.push(w);
|
|
346
|
+
}
|
|
347
|
+
textBuffer = "";
|
|
348
|
+
};
|
|
349
|
+
|
|
340
350
|
for (const evt of events) {
|
|
341
351
|
if (evt.type === "tool_start") {
|
|
352
|
+
if (lastWasText) flushTextBuffer();
|
|
342
353
|
lastWasText = false;
|
|
343
354
|
const call = `${evt.toolName}(${evt.argSummary})`;
|
|
344
355
|
toolLineMap.set(evt.toolCallId, out.length);
|
|
@@ -348,10 +359,9 @@ export function renderSubagentResult(
|
|
|
348
359
|
out.push(truncateToWidth(theme.fg("toolTitle", agentLabel), width, "..."));
|
|
349
360
|
lastWasText = true;
|
|
350
361
|
}
|
|
351
|
-
|
|
352
|
-
for (const w of wrapLine(indent + line, width)) out.push(w);
|
|
353
|
-
}
|
|
362
|
+
textBuffer += evt.text;
|
|
354
363
|
} else if (evt.type === "tool_end") {
|
|
364
|
+
if (lastWasText) flushTextBuffer();
|
|
355
365
|
lastWasText = false;
|
|
356
366
|
const toolLineIdx = toolLineMap.get(evt.toolCallId);
|
|
357
367
|
const dur = evt.durMs != null
|
|
@@ -368,6 +378,8 @@ export function renderSubagentResult(
|
|
|
368
378
|
}
|
|
369
379
|
}
|
|
370
380
|
}
|
|
381
|
+
|
|
382
|
+
if (lastWasText) flushTextBuffer();
|
|
371
383
|
}
|
|
372
384
|
|
|
373
385
|
return out;
|
package/runner.ts
CHANGED
|
@@ -66,6 +66,62 @@ export function getCurrentDepth(): number {
|
|
|
66
66
|
|
|
67
67
|
export interface RunAgentDeps {
|
|
68
68
|
loaderPool?: LoaderPool;
|
|
69
|
+
modelRegistry?: ModelRegistry;
|
|
70
|
+
/** Pass a Model object directly to bypass registry lookup (used for model: inherit). */
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
modelObject?: any;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
function modelRef(model: any): string {
|
|
77
|
+
return `${model.provider}/${model.id}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve provider/modelId into a Model object.
|
|
82
|
+
*
|
|
83
|
+
* `ModelRegistry.find()` only returns bundled/custom registry entries. Pi itself
|
|
84
|
+
* can run ad-hoc provider model IDs (for example via `--model
|
|
85
|
+
* anthropic/claude-sonnet-4-6`) by cloning provider config from a known model.
|
|
86
|
+
* Do same here so `model: inherit` can use main session's exact model ID even
|
|
87
|
+
* before pi's bundled registry knows it.
|
|
88
|
+
*/
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
export function resolveModelObject(modelRegistry: ModelRegistry, modelReference: string | undefined): any | undefined {
|
|
91
|
+
const ref = modelReference?.trim();
|
|
92
|
+
if (!ref) return undefined;
|
|
93
|
+
|
|
94
|
+
const allModels = modelRegistry.getAll();
|
|
95
|
+
const lowerRef = ref.toLowerCase();
|
|
96
|
+
const exact = allModels.find((m) => modelRef(m).toLowerCase() === lowerRef || m.id.toLowerCase() === lowerRef);
|
|
97
|
+
if (exact) return exact;
|
|
98
|
+
|
|
99
|
+
const slash = ref.indexOf("/");
|
|
100
|
+
if (slash === -1) return undefined;
|
|
101
|
+
|
|
102
|
+
const providerRef = ref.slice(0, slash).trim();
|
|
103
|
+
const modelId = ref.slice(slash + 1).trim();
|
|
104
|
+
if (!providerRef || !modelId) return undefined;
|
|
105
|
+
|
|
106
|
+
const providerModels = allModels.filter((m) => m.provider.toLowerCase() === providerRef.toLowerCase());
|
|
107
|
+
if (providerModels.length === 0) return undefined;
|
|
108
|
+
const provider = providerModels[0]!.provider;
|
|
109
|
+
|
|
110
|
+
const found = modelRegistry.find(provider, modelId);
|
|
111
|
+
if (found) return found;
|
|
112
|
+
|
|
113
|
+
// Unknown model ID for known provider: clone closest provider model so auth,
|
|
114
|
+
// API, baseUrl, headers, compat, cost shape, and token defaults survive.
|
|
115
|
+
const idLower = modelId.toLowerCase();
|
|
116
|
+
const family = ["opus", "sonnet", "haiku", "gpt", "gemini", "kimi", "glm", "grok"].find((token) => idLower.includes(token));
|
|
117
|
+
const familyModels = family ? providerModels.filter((m) => m.id.toLowerCase().includes(family)) : [];
|
|
118
|
+
const base = (familyModels[0] ?? providerModels[0])!;
|
|
119
|
+
return {
|
|
120
|
+
...base,
|
|
121
|
+
provider,
|
|
122
|
+
id: modelId,
|
|
123
|
+
name: modelId,
|
|
124
|
+
};
|
|
69
125
|
}
|
|
70
126
|
|
|
71
127
|
export async function runAgent(
|
|
@@ -92,7 +148,8 @@ export async function runAgent(
|
|
|
92
148
|
}
|
|
93
149
|
|
|
94
150
|
const bootStartedAt = Date.now();
|
|
95
|
-
const { authStorage, modelRegistry } = getAuth();
|
|
151
|
+
const { authStorage, modelRegistry: defaultRegistry } = getAuth();
|
|
152
|
+
const modelRegistry = deps.modelRegistry ?? defaultRegistry;
|
|
96
153
|
const agentDir = getAgentDir();
|
|
97
154
|
const noExtensions = !agentNeedsExtensions(agent.tools);
|
|
98
155
|
const coldLoader = !pool.isWarm(cwd, agentDir, noExtensions);
|
|
@@ -114,6 +171,8 @@ export async function runAgent(
|
|
|
114
171
|
});
|
|
115
172
|
await allowUiPaint(coldLoader);
|
|
116
173
|
|
|
174
|
+
const createPrevEnvDepth = process.env[DEPTH_ENV];
|
|
175
|
+
process.env[DEPTH_ENV] = String(depth + 1);
|
|
117
176
|
const loaderLease = await pool.acquire(
|
|
118
177
|
cwd,
|
|
119
178
|
agentDir,
|
|
@@ -130,10 +189,13 @@ export async function runAgent(
|
|
|
130
189
|
authStorage,
|
|
131
190
|
modelRegistry,
|
|
132
191
|
resourceLoader: loaderLease.loader,
|
|
192
|
+
...(deps.modelObject ? { model: deps.modelObject } : {}),
|
|
133
193
|
});
|
|
134
194
|
session = created.session;
|
|
135
195
|
} catch (e) {
|
|
136
196
|
loaderLease.release();
|
|
197
|
+
if (createPrevEnvDepth === undefined) delete process.env[DEPTH_ENV];
|
|
198
|
+
else process.env[DEPTH_ENV] = createPrevEnvDepth;
|
|
137
199
|
return {
|
|
138
200
|
output: "",
|
|
139
201
|
exitCode: 1,
|
|
@@ -142,17 +204,14 @@ export async function runAgent(
|
|
|
142
204
|
usage: { input: 0, output: 0, cost: 0, turns: 0 },
|
|
143
205
|
};
|
|
144
206
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (model) await session.setModel(model);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
207
|
+
if (createPrevEnvDepth === undefined) delete process.env[DEPTH_ENV];
|
|
208
|
+
else process.env[DEPTH_ENV] = createPrevEnvDepth;
|
|
209
|
+
|
|
210
|
+
// Resolve and apply model. Force this even when createAgentSession received
|
|
211
|
+
// modelObject so restore/default fallback cannot silently win.
|
|
212
|
+
const modelStr = modelOverride ?? (agent.model === "inherit" ? undefined : agent.model);
|
|
213
|
+
const model = deps.modelObject ?? resolveModelObject(modelRegistry, modelStr);
|
|
214
|
+
if (model) await session.setModel(model);
|
|
156
215
|
|
|
157
216
|
// Apply tools allowlist.
|
|
158
217
|
// "all" → no restriction (everything registered stays active)
|