@gotgenes/pi-subagents 10.2.0 → 11.0.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/CHANGELOG.md +36 -0
- package/docs/architecture/architecture.md +114 -59
- package/docs/plans/0229-agent-born-complete.md +564 -0
- package/docs/plans/0231-push-exec-registry-to-runner.md +245 -0
- package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +38 -0
- package/docs/retro/0229-agent-born-complete.md +47 -0
- package/docs/retro/0231-push-exec-registry-to-runner.md +71 -0
- package/package.json +1 -1
- package/src/index.ts +17 -15
- package/src/lifecycle/agent-manager.ts +49 -112
- package/src/lifecycle/agent-runner.ts +31 -23
- package/src/lifecycle/agent.ts +166 -39
- package/src/observation/record-observer.ts +1 -2
- package/src/tools/agent-tool.ts +2 -2
- package/src/tools/background-spawner.ts +7 -5
- package/src/tools/foreground-runner.ts +11 -9
- package/src/types.ts +13 -0
|
@@ -8,27 +8,22 @@
|
|
|
8
8
|
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import type { Model } from "@earendil-works/pi-ai";
|
|
11
|
-
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import { AgentTypeRegistry } from "#src/config/agent-types";
|
|
13
11
|
import { debugLog } from "#src/debug";
|
|
14
|
-
import { Agent } from "#src/lifecycle/agent";
|
|
12
|
+
import { Agent, type AgentLifecycleObserver } from "#src/lifecycle/agent";
|
|
15
13
|
import type { AgentRunner } from "#src/lifecycle/agent-runner";
|
|
16
14
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
17
15
|
import type { WorktreeManager } from "#src/lifecycle/worktree";
|
|
18
16
|
|
|
19
|
-
import { NotificationState } from "#src/observation/notification-state";
|
|
20
17
|
import { subscribeAgentObserver } from "#src/observation/record-observer";
|
|
21
18
|
import type { RunConfig } from "#src/runtime";
|
|
22
|
-
import type { AgentInvocation, IsolationMode,
|
|
23
|
-
|
|
24
|
-
export type CompactionInfo = { reason: "manual" | "threshold" | "overflow"; tokensBefore: number };
|
|
19
|
+
import type { AgentInvocation, CompactionInfo, IsolationMode, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
|
|
25
20
|
|
|
26
21
|
/** Observer interface for agent lifecycle notifications. */
|
|
27
22
|
export interface AgentManagerObserver {
|
|
28
23
|
onAgentStarted(record: Agent): void;
|
|
29
24
|
onAgentCompleted(record: Agent): void;
|
|
30
25
|
onAgentCompacted(record: Agent, info: CompactionInfo): void;
|
|
31
|
-
/** Fires synchronously after a background agent record is created (before
|
|
26
|
+
/** Fires synchronously after a background agent record is created (before run). */
|
|
32
27
|
onAgentCreated(record: Agent): void;
|
|
33
28
|
}
|
|
34
29
|
|
|
@@ -38,30 +33,12 @@ const DEFAULT_MAX_CONCURRENT = 4;
|
|
|
38
33
|
export interface AgentManagerOptions {
|
|
39
34
|
runner: AgentRunner;
|
|
40
35
|
worktrees: WorktreeManager;
|
|
41
|
-
exec: ShellExec;
|
|
42
|
-
registry: AgentTypeRegistry;
|
|
43
36
|
/** Injected getter for the concurrency limit - owned by SettingsManager. */
|
|
44
37
|
getMaxConcurrent?: () => number;
|
|
45
38
|
getRunConfig?: () => RunConfig;
|
|
46
39
|
observer?: AgentManagerObserver;
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
interface SpawnArgs {
|
|
50
|
-
snapshot: ParentSnapshot;
|
|
51
|
-
type: SubagentType;
|
|
52
|
-
prompt: string;
|
|
53
|
-
options: AgentSpawnConfig;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ParentSessionInfo {
|
|
57
|
-
/** Path to the parent session's JSONL file (for deriving the subagent session directory). */
|
|
58
|
-
parentSessionFile?: string;
|
|
59
|
-
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
60
|
-
parentSessionId?: string;
|
|
61
|
-
/** Tool call ID for background notification wiring. When set, spawn attaches NotificationState. */
|
|
62
|
-
toolCallId?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
42
|
export interface AgentSpawnConfig {
|
|
66
43
|
description: string;
|
|
67
44
|
model?: Model<any>;
|
|
@@ -82,8 +59,8 @@ export interface AgentSpawnConfig {
|
|
|
82
59
|
invocation?: AgentInvocation;
|
|
83
60
|
/** Parent abort signal - when aborted, the subagent is also stopped. */
|
|
84
61
|
signal?: AbortSignal;
|
|
85
|
-
/**
|
|
86
|
-
|
|
62
|
+
/** Per-agent lifecycle observer — replaces onSessionCreated callback. */
|
|
63
|
+
observer?: AgentLifecycleObserver;
|
|
87
64
|
/** Parent session identity - grouped fields that travel together from the tool boundary. */
|
|
88
65
|
parentSession?: ParentSessionInfo;
|
|
89
66
|
}
|
|
@@ -94,20 +71,16 @@ export class AgentManager {
|
|
|
94
71
|
private readonly observer?: AgentManagerObserver;
|
|
95
72
|
private readonly runner: AgentRunner;
|
|
96
73
|
private readonly worktrees: WorktreeManager;
|
|
97
|
-
private readonly exec: ShellExec;
|
|
98
|
-
private readonly registry: AgentTypeRegistry;
|
|
99
74
|
private readonly _getMaxConcurrent: () => number;
|
|
100
75
|
private getRunConfig?: () => RunConfig;
|
|
101
76
|
|
|
102
|
-
/** Queue of background
|
|
103
|
-
private queue:
|
|
77
|
+
/** Queue of background agent IDs waiting to start. */
|
|
78
|
+
private queue: string[] = [];
|
|
104
79
|
/** Number of currently running background agents. */
|
|
105
80
|
private runningBackground = 0;
|
|
106
81
|
constructor(options: AgentManagerOptions) {
|
|
107
82
|
this.runner = options.runner;
|
|
108
83
|
this.worktrees = options.worktrees;
|
|
109
|
-
this.exec = options.exec;
|
|
110
|
-
this.registry = options.registry;
|
|
111
84
|
this.observer = options.observer;
|
|
112
85
|
this.getRunConfig = options.getRunConfig;
|
|
113
86
|
this._getMaxConcurrent = options.getMaxConcurrent ?? (() => DEFAULT_MAX_CONCURRENT);
|
|
@@ -124,6 +97,25 @@ export class AgentManager {
|
|
|
124
97
|
this.drainQueue();
|
|
125
98
|
}
|
|
126
99
|
|
|
100
|
+
/** Compose a per-agent lifecycle observer from manager and spawn-config concerns. */
|
|
101
|
+
private buildObserver(options: AgentSpawnConfig): AgentLifecycleObserver {
|
|
102
|
+
return {
|
|
103
|
+
onStarted: (agent) => {
|
|
104
|
+
if (options.isBackground) this.runningBackground++;
|
|
105
|
+
this.observer?.onAgentStarted(agent);
|
|
106
|
+
},
|
|
107
|
+
onSessionCreated: options.observer?.onSessionCreated
|
|
108
|
+
? (agent, session) => options.observer!.onSessionCreated!(agent, session)
|
|
109
|
+
: undefined,
|
|
110
|
+
onRunFinished: (agent) => {
|
|
111
|
+
if (options.isBackground) this.finalizeBackgroundRun(agent);
|
|
112
|
+
},
|
|
113
|
+
onCompacted: (agent, info) => {
|
|
114
|
+
this.observer?.onAgentCompacted(agent, info);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
127
119
|
/**
|
|
128
120
|
* Spawn an agent and return its ID immediately (for background use).
|
|
129
121
|
* If the concurrency limit is reached, the agent is queued.
|
|
@@ -135,92 +127,45 @@ export class AgentManager {
|
|
|
135
127
|
options: AgentSpawnConfig,
|
|
136
128
|
): string {
|
|
137
129
|
const id = randomUUID().slice(0, 17);
|
|
138
|
-
const abortController = new AbortController();
|
|
139
130
|
const record = new Agent({
|
|
140
131
|
id,
|
|
141
132
|
type,
|
|
142
133
|
description: options.description,
|
|
143
134
|
status: options.isBackground ? "queued" : "running",
|
|
144
135
|
startedAt: Date.now(),
|
|
145
|
-
abortController,
|
|
146
136
|
invocation: options.invocation,
|
|
137
|
+
// Run config
|
|
138
|
+
snapshot,
|
|
139
|
+
prompt,
|
|
140
|
+
model: options.model,
|
|
141
|
+
maxTurns: options.maxTurns,
|
|
142
|
+
isolated: options.isolated,
|
|
143
|
+
thinkingLevel: options.thinkingLevel,
|
|
144
|
+
isolation: options.isolation,
|
|
145
|
+
parentSession: options.parentSession,
|
|
146
|
+
signal: options.signal,
|
|
147
|
+
// Shared deps
|
|
148
|
+
runner: this.runner,
|
|
149
|
+
worktrees: this.worktrees,
|
|
150
|
+
observer: this.buildObserver(options),
|
|
151
|
+
getRunConfig: this.getRunConfig,
|
|
147
152
|
});
|
|
148
153
|
this.agents.set(id, record);
|
|
149
154
|
|
|
150
|
-
if (options.parentSession?.toolCallId) {
|
|
151
|
-
record.notification = new NotificationState(options.parentSession.toolCallId);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
155
|
if (options.isBackground) {
|
|
155
156
|
this.observer?.onAgentCreated(record);
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
const args: SpawnArgs = { snapshot, type, prompt, options };
|
|
159
|
-
|
|
160
159
|
if (options.isBackground && !options.bypassQueue && this.runningBackground >= this._getMaxConcurrent()) {
|
|
161
160
|
// Queue it - will be started when a running agent completes
|
|
162
|
-
this.queue.push(
|
|
161
|
+
this.queue.push(id);
|
|
163
162
|
return id;
|
|
164
163
|
}
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
// up the record so callers don't see an orphan in `listAgents()`.
|
|
168
|
-
try {
|
|
169
|
-
record.setupWorktree(this.worktrees, options.isolation);
|
|
170
|
-
record.promise = this.startAgent(id, record, args);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
this.agents.delete(id);
|
|
173
|
-
throw err;
|
|
174
|
-
}
|
|
165
|
+
record.promise = record.run();
|
|
175
166
|
return id;
|
|
176
167
|
}
|
|
177
168
|
|
|
178
|
-
/** Actually start an agent (called immediately or from queue drain). */
|
|
179
|
-
private async startAgent(id: string, record: Agent, { snapshot, type, prompt, options }: SpawnArgs): Promise<void> {
|
|
180
|
-
record.markRunning(Date.now());
|
|
181
|
-
if (options.isBackground) this.runningBackground++;
|
|
182
|
-
this.observer?.onAgentStarted(record);
|
|
183
|
-
|
|
184
|
-
record.setOnRunFinished(
|
|
185
|
-
options.isBackground ? () => this.finalizeBackgroundRun(record) : undefined,
|
|
186
|
-
);
|
|
187
|
-
record.wireSignal(options.signal, () => this.abort(id));
|
|
188
|
-
|
|
189
|
-
const runConfig = this.getRunConfig?.();
|
|
190
|
-
try {
|
|
191
|
-
const result = await this.runner.run(snapshot, type, prompt, {
|
|
192
|
-
context: {
|
|
193
|
-
exec: this.exec,
|
|
194
|
-
registry: this.registry,
|
|
195
|
-
cwd: record.worktreeState?.path,
|
|
196
|
-
parentSession: options.parentSession,
|
|
197
|
-
},
|
|
198
|
-
model: options.model,
|
|
199
|
-
maxTurns: options.maxTurns,
|
|
200
|
-
defaultMaxTurns: runConfig?.defaultMaxTurns,
|
|
201
|
-
graceTurns: runConfig?.graceTurns,
|
|
202
|
-
isolated: options.isolated,
|
|
203
|
-
thinkingLevel: options.thinkingLevel,
|
|
204
|
-
signal: record.abortController!.signal,
|
|
205
|
-
onSessionCreated: (session) => {
|
|
206
|
-
// Capture the session file path early so it's available for display
|
|
207
|
-
// before the run completes (e.g. in background agent status messages).
|
|
208
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- sessionManager is typed as always present but Pi SDK may not provide it
|
|
209
|
-
const outputFile = session.sessionManager?.getSessionFile?.() ?? undefined;
|
|
210
|
-
record.execution = { session, outputFile };
|
|
211
|
-
record.flushPendingSteers(session);
|
|
212
|
-
record.attachObserver(subscribeAgentObserver(session, record, {
|
|
213
|
-
onCompact: (r, info) => this.observer?.onAgentCompacted(r, info),
|
|
214
|
-
}));
|
|
215
|
-
options.onSessionCreated?.(session, record);
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
record.completeRun(result, this.worktrees);
|
|
219
|
-
} catch (err) {
|
|
220
|
-
record.failRun(err, this.worktrees);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
169
|
/** Decrement background counter, notify observer (crash-safe), and drain the queue. */
|
|
225
170
|
private finalizeBackgroundRun(record: Agent): void {
|
|
226
171
|
this.runningBackground--;
|
|
@@ -231,18 +176,10 @@ export class AgentManager {
|
|
|
231
176
|
/** Start queued agents up to the concurrency limit. */
|
|
232
177
|
private drainQueue() {
|
|
233
178
|
while (this.queue.length > 0 && this.runningBackground < this._getMaxConcurrent()) {
|
|
234
|
-
const
|
|
235
|
-
const record = this.agents.get(
|
|
179
|
+
const id = this.queue.shift()!;
|
|
180
|
+
const record = this.agents.get(id);
|
|
236
181
|
if (record?.status !== "queued") continue;
|
|
237
|
-
|
|
238
|
-
record.setupWorktree(this.worktrees, next.args.options.isolation);
|
|
239
|
-
record.promise = this.startAgent(next.id, record, next.args);
|
|
240
|
-
} catch (err) {
|
|
241
|
-
// Late failure (e.g. strict worktree-isolation) - surface on the record
|
|
242
|
-
// so the user/agent can see it via /agents, then keep draining.
|
|
243
|
-
record.markError(err);
|
|
244
|
-
this.observer?.onAgentCompleted(record);
|
|
245
|
-
}
|
|
182
|
+
record.promise = record.run();
|
|
246
183
|
}
|
|
247
184
|
}
|
|
248
185
|
|
|
@@ -310,7 +247,7 @@ export class AgentManager {
|
|
|
310
247
|
|
|
311
248
|
// Remove from queue if queued
|
|
312
249
|
if (record.status === "queued") {
|
|
313
|
-
this.queue = this.queue.filter(
|
|
250
|
+
this.queue = this.queue.filter(qid => qid !== id);
|
|
314
251
|
record.markStopped();
|
|
315
252
|
return true;
|
|
316
253
|
}
|
|
@@ -358,8 +295,8 @@ export class AgentManager {
|
|
|
358
295
|
abortAll(): number {
|
|
359
296
|
let count = 0;
|
|
360
297
|
// Clear queued agents first
|
|
361
|
-
for (const
|
|
362
|
-
const record = this.agents.get(
|
|
298
|
+
for (const id of this.queue) {
|
|
299
|
+
const record = this.agents.get(id);
|
|
363
300
|
if (record) {
|
|
364
301
|
record.markStopped();
|
|
365
302
|
count++;
|
|
@@ -9,14 +9,13 @@ import {
|
|
|
9
9
|
type SettingsManager,
|
|
10
10
|
} from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import type { AgentConfigLookup } from "#src/config/agent-types";
|
|
12
|
-
import type { ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
13
12
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
14
13
|
import { registerChildSession, unregisterChildSession } from "#src/lifecycle/permission-bridge";
|
|
15
14
|
import { extractAssistantContent } from "#src/session/content-items";
|
|
16
15
|
import { extractText } from "#src/session/context";
|
|
17
16
|
import type { EnvInfo } from "#src/session/env";
|
|
18
17
|
import { type AssemblerIO, assembleSessionConfig } from "#src/session/session-config";
|
|
19
|
-
import type { ShellExec, SubagentType, ThinkingLevel } from "#src/types";
|
|
18
|
+
import type { ParentSessionInfo, ShellExec, SubagentType, ThinkingLevel } from "#src/types";
|
|
20
19
|
|
|
21
20
|
/** Names of tools registered by this extension that subagents must NOT inherit. */
|
|
22
21
|
const EXCLUDED_TOOL_NAMES = ["subagent", "get_subagent_result", "steer_subagent"];
|
|
@@ -114,19 +113,27 @@ export interface SessionFactoryIO {
|
|
|
114
113
|
*/
|
|
115
114
|
export type RunnerIO = EnvironmentIO & SessionFactoryIO;
|
|
116
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Dependencies owned by the runner — injected at construction time.
|
|
118
|
+
*
|
|
119
|
+
* Groups the IO boundary with the two static domain deps (exec, registry)
|
|
120
|
+
* that every run() call needs but that do not vary per call.
|
|
121
|
+
*/
|
|
122
|
+
export interface RunnerDeps {
|
|
123
|
+
io: RunnerIO;
|
|
124
|
+
exec: ShellExec;
|
|
125
|
+
registry: AgentConfigLookup;
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
// ── Public interfaces ─────────────────────────────────────────────────────────
|
|
118
129
|
|
|
119
130
|
/**
|
|
120
|
-
*
|
|
131
|
+
* Per-call execution context — fields that vary per spawn.
|
|
121
132
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
133
|
+
* Static dependencies (exec, registry) live on RunnerDeps; this interface
|
|
134
|
+
* carries only the two per-call fields that AgentManager supplies at spawn time.
|
|
124
135
|
*/
|
|
125
136
|
export interface RunContext {
|
|
126
|
-
/** Shell-exec callback for detectEnv - injected from pi.exec(). */
|
|
127
|
-
exec: ShellExec;
|
|
128
|
-
/** Agent config lookup - provides resolveAgentConfig and getToolNamesForType. */
|
|
129
|
-
registry: AgentConfigLookup;
|
|
130
137
|
/** Override working directory (e.g. for worktree isolation). */
|
|
131
138
|
cwd?: string;
|
|
132
139
|
/** Parent session identity (file path + session ID). */
|
|
@@ -182,15 +189,16 @@ export interface AgentRunner {
|
|
|
182
189
|
}
|
|
183
190
|
|
|
184
191
|
/**
|
|
185
|
-
* Concrete AgentRunner backed by
|
|
192
|
+
* Concrete AgentRunner backed by RunnerDeps.
|
|
186
193
|
*
|
|
187
|
-
* Captures
|
|
194
|
+
* Captures IO, exec, and registry at construction time so AgentManager
|
|
195
|
+
* remains unaware of runner-internal dependencies.
|
|
188
196
|
*/
|
|
189
197
|
export class ConcreteAgentRunner implements AgentRunner {
|
|
190
|
-
constructor(private readonly
|
|
198
|
+
constructor(private readonly deps: RunnerDeps) {}
|
|
191
199
|
|
|
192
200
|
run(snapshot: ParentSnapshot, type: SubagentType, prompt: string, options: RunOptions): Promise<RunResult> {
|
|
193
|
-
return runAgent(snapshot, type, prompt, options, this.
|
|
201
|
+
return runAgent(snapshot, type, prompt, options, this.deps);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
resume(session: AgentSession, prompt: string, options?: ResumeOptions): Promise<string> {
|
|
@@ -253,11 +261,11 @@ export async function runAgent(
|
|
|
253
261
|
type: SubagentType,
|
|
254
262
|
prompt: string,
|
|
255
263
|
options: RunOptions,
|
|
256
|
-
|
|
264
|
+
deps: RunnerDeps,
|
|
257
265
|
): Promise<RunResult> {
|
|
258
266
|
// Resolve working directory upfront - needed for detectEnv before assembly.
|
|
259
267
|
const effectiveCwd = options.context.cwd ?? snapshot.cwd;
|
|
260
|
-
const env = await io.detectEnv(
|
|
268
|
+
const env = await deps.io.detectEnv(deps.exec, effectiveCwd);
|
|
261
269
|
|
|
262
270
|
// Assemble session configuration (synchronous, no SDK objects).
|
|
263
271
|
const cfg = assembleSessionConfig(
|
|
@@ -275,11 +283,11 @@ export async function runAgent(
|
|
|
275
283
|
thinkingLevel: options.thinkingLevel,
|
|
276
284
|
},
|
|
277
285
|
env,
|
|
278
|
-
|
|
279
|
-
io.assemblerIO,
|
|
286
|
+
deps.registry,
|
|
287
|
+
deps.io.assemblerIO,
|
|
280
288
|
);
|
|
281
289
|
|
|
282
|
-
const agentDir = io.getAgentDir();
|
|
290
|
+
const agentDir = deps.io.getAgentDir();
|
|
283
291
|
|
|
284
292
|
// Load extensions/skills: true → load; false → don't.
|
|
285
293
|
// Suppress AGENTS.md/CLAUDE.md and APPEND_SYSTEM.md - upstream's
|
|
@@ -287,7 +295,7 @@ export async function runAgent(
|
|
|
287
295
|
// would defeat prompt_mode: replace and isolated: true. Parent context, if
|
|
288
296
|
// wanted, reaches the subagent via prompt_mode: append (parentSystemPrompt
|
|
289
297
|
// is embedded in systemPromptOverride) or inherit_context (conversation).
|
|
290
|
-
const loader = io.createResourceLoader({
|
|
298
|
+
const loader = deps.io.createResourceLoader({
|
|
291
299
|
cwd: cfg.effectiveCwd,
|
|
292
300
|
agentDir,
|
|
293
301
|
noExtensions: !cfg.extensions,
|
|
@@ -303,15 +311,15 @@ export async function runAgent(
|
|
|
303
311
|
// Create a persisted SessionManager so transcripts are written in Pi's
|
|
304
312
|
// official JSONL format. Falls back to a temp directory when the parent
|
|
305
313
|
// session is not persisted (e.g. headless/API mode).
|
|
306
|
-
const sessionDir = io.deriveSessionDir(options.context.parentSession?.parentSessionFile, cfg.effectiveCwd);
|
|
307
|
-
const sessionManager = io.createSessionManager(cfg.effectiveCwd, sessionDir);
|
|
314
|
+
const sessionDir = deps.io.deriveSessionDir(options.context.parentSession?.parentSessionFile, cfg.effectiveCwd);
|
|
315
|
+
const sessionManager = deps.io.createSessionManager(cfg.effectiveCwd, sessionDir);
|
|
308
316
|
sessionManager.newSession({ parentSession: options.context.parentSession?.parentSessionId });
|
|
309
317
|
|
|
310
|
-
const { session } = await io.createSession({
|
|
318
|
+
const { session } = await deps.io.createSession({
|
|
311
319
|
cwd: cfg.effectiveCwd,
|
|
312
320
|
agentDir,
|
|
313
321
|
sessionManager,
|
|
314
|
-
settingsManager: io.createSettingsManager(cfg.effectiveCwd, agentDir),
|
|
322
|
+
settingsManager: deps.io.createSettingsManager(cfg.effectiveCwd, agentDir),
|
|
315
323
|
modelRegistry: snapshot.modelRegistry,
|
|
316
324
|
model: cfg.model,
|
|
317
325
|
tools: cfg.toolNames,
|