@oh-my-pi/pi-coding-agent 15.1.3 → 15.1.5
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 +24 -0
- package/dist/types/async/job-manager.d.ts +3 -2
- package/dist/types/main.d.ts +11 -2
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +13 -1
- package/dist/types/modes/acp/acp-mode.d.ts +3 -1
- package/dist/types/plan-mode/approved-plan.d.ts +6 -4
- package/dist/types/session/agent-session.d.ts +6 -2
- package/dist/types/session/client-bridge.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +2 -0
- package/dist/types/tools/ast-grep.d.ts +2 -0
- package/dist/types/tools/render-utils.d.ts +13 -3
- package/package.json +7 -7
- package/src/async/job-manager.ts +111 -13
- package/src/cli/update-cli.ts +1 -5
- package/src/eval/js/shared/runtime.ts +82 -2
- package/src/extensibility/typebox.ts +44 -17
- package/src/main.ts +215 -148
- package/src/modes/acp/acp-agent.ts +115 -32
- package/src/modes/acp/acp-client-bridge.ts +2 -1
- package/src/modes/acp/acp-event-mapper.ts +208 -32
- package/src/modes/acp/acp-mode.ts +11 -3
- package/src/modes/components/tree-selector.ts +26 -7
- package/src/plan-mode/approved-plan.ts +21 -9
- package/src/prompts/agents/oracle.md +56 -0
- package/src/prompts/tools/ask.md +4 -3
- package/src/sdk.ts +3 -1
- package/src/session/agent-session.ts +186 -54
- package/src/session/client-bridge.ts +3 -0
- package/src/task/agents.ts +2 -0
- package/src/tools/ast-edit.ts +19 -11
- package/src/tools/ast-grep.ts +14 -10
- package/src/tools/render-utils.ts +26 -12
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* `@sinclair/typebox` directly in their own package.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { areJsonValuesEqual } from "@oh-my-pi/pi-ai/utils/schema";
|
|
24
|
+
import { areJsonValuesEqual, zodToWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
25
25
|
import {
|
|
26
26
|
type ZodArray,
|
|
27
27
|
type ZodEnum,
|
|
@@ -104,19 +104,46 @@ interface ObjectOpts extends Meta {
|
|
|
104
104
|
// Helpers
|
|
105
105
|
// ---------------------------------------------------------------------------
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Stamp a non-enumerable `toJSON()` on a schema so `JSON.stringify(schema)`
|
|
109
|
+
* yields a clean draft 2020-12 JSON Schema — matching real TypeBox semantics
|
|
110
|
+
* where the schema object IS already a JSON Schema. Without this, an extension
|
|
111
|
+
* author who serialises the schema across any JSON boundary (worker
|
|
112
|
+
* postMessage, MCP transport, config persistence, network hop, structuredClone
|
|
113
|
+
* fallback) ships the raw Zod internals (`def`, `_zod`, object-shaped `enum`,
|
|
114
|
+
* `"type":"enum"`) — neither valid JSON Schema nor parseable Zod. See
|
|
115
|
+
* issue #1101 for the symptoms when this leaks into a tool's `input_schema`.
|
|
116
|
+
*
|
|
117
|
+
* Idempotent: re-stamping the same instance is a no-op.
|
|
118
|
+
*/
|
|
119
|
+
function wire<T extends ZodType>(schema: T): T {
|
|
120
|
+
if (!Object.hasOwn(schema as object, "toJSON")) {
|
|
121
|
+
Object.defineProperty(schema as object, "toJSON", {
|
|
122
|
+
value: function toJSON(this: ZodType) {
|
|
123
|
+
return zodToWireSchema(this);
|
|
124
|
+
},
|
|
125
|
+
enumerable: false,
|
|
126
|
+
writable: true,
|
|
127
|
+
configurable: true,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return schema;
|
|
131
|
+
}
|
|
132
|
+
|
|
107
133
|
function withMeta<T extends ZodType>(schema: T, opts: Meta | undefined): T {
|
|
108
|
-
if (!opts) return schema;
|
|
109
134
|
let out: ZodType = schema;
|
|
110
|
-
if (
|
|
111
|
-
|
|
135
|
+
if (opts) {
|
|
136
|
+
if (typeof opts.description === "string") out = out.describe(opts.description);
|
|
137
|
+
if ("default" in opts) out = out.default(opts.default as never) as unknown as ZodType;
|
|
112
138
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
const metadata: Record<string, unknown> = {};
|
|
140
|
+
for (const key in opts) {
|
|
141
|
+
if (key === "description" || key === "default" || key === "additionalProperties") continue;
|
|
142
|
+
metadata[key] = opts[key];
|
|
143
|
+
}
|
|
144
|
+
if (Object.keys(metadata).length > 0) out = out.meta(metadata);
|
|
117
145
|
}
|
|
118
|
-
|
|
119
|
-
return out as T;
|
|
146
|
+
return wire(out as T);
|
|
120
147
|
}
|
|
121
148
|
|
|
122
149
|
// ---------------------------------------------------------------------------
|
|
@@ -313,7 +340,8 @@ function tRecord<V extends ZodType>(key: ZodType, value: V, opts?: Meta): ZodTyp
|
|
|
313
340
|
}
|
|
314
341
|
|
|
315
342
|
function tOptional<E extends ZodType>(schema: E, _opts?: Meta): ZodOptional<E> {
|
|
316
|
-
|
|
343
|
+
if (isOptional(schema)) return wire(schema as unknown as ZodOptional<E>);
|
|
344
|
+
return wire(schema.optional() as ZodOptional<E>);
|
|
317
345
|
}
|
|
318
346
|
|
|
319
347
|
function tNullable<E extends ZodType>(schema: E, opts?: Meta): ZodType {
|
|
@@ -322,27 +350,26 @@ function tNullable<E extends ZodType>(schema: E, opts?: Meta): ZodType {
|
|
|
322
350
|
|
|
323
351
|
function tReadonly<E extends ZodType>(schema: E): E {
|
|
324
352
|
// TypeBox's `Type.Readonly` is purely a marker; runtime parsing is identical.
|
|
325
|
-
return schema;
|
|
353
|
+
return wire(schema);
|
|
326
354
|
}
|
|
327
355
|
|
|
328
356
|
function tPartial<P extends ZodRawShape>(obj: ZodObject<P>): ZodObject<P> {
|
|
329
|
-
return obj.partial() as unknown as ZodObject<P
|
|
357
|
+
return wire(obj.partial() as unknown as ZodObject<P>);
|
|
330
358
|
}
|
|
331
359
|
|
|
332
360
|
function tRequired<P extends ZodRawShape>(obj: ZodObject<P>): ZodObject<P> {
|
|
333
|
-
return obj.required() as unknown as ZodObject<P
|
|
361
|
+
return wire(obj.required() as unknown as ZodObject<P>);
|
|
334
362
|
}
|
|
335
363
|
|
|
336
364
|
function tPick<P extends ZodRawShape, K extends keyof P>(obj: ZodObject<P>, keys: readonly K[]): ZodObject<Pick<P, K>> {
|
|
337
365
|
const mask = Object.fromEntries(keys.map(k => [k as string, true]));
|
|
338
|
-
return obj.pick(mask as never) as unknown as ZodObject<Pick<P, K
|
|
366
|
+
return wire(obj.pick(mask as never) as unknown as ZodObject<Pick<P, K>>);
|
|
339
367
|
}
|
|
340
368
|
|
|
341
369
|
function tOmit<P extends ZodRawShape, K extends keyof P>(obj: ZodObject<P>, keys: readonly K[]): ZodObject<Omit<P, K>> {
|
|
342
370
|
const mask = Object.fromEntries(keys.map(k => [k as string, true]));
|
|
343
|
-
return obj.omit(mask as never) as unknown as ZodObject<Omit<P, K
|
|
371
|
+
return wire(obj.omit(mask as never) as unknown as ZodObject<Omit<P, K>>);
|
|
344
372
|
}
|
|
345
|
-
|
|
346
373
|
function tComposite(objects: readonly ZodObject<ZodRawShape>[], opts?: Meta): ZodObject<ZodRawShape> {
|
|
347
374
|
// `Type.Composite([...])` flattens every object schema into one object schema
|
|
348
375
|
// rather than producing an intersection. Mirror that via repeated `extend`.
|
package/src/main.ts
CHANGED
|
@@ -49,8 +49,14 @@ import type { MCPManager } from "./mcp";
|
|
|
49
49
|
import { InteractiveMode, runAcpMode, runPrintMode, runRpcMode } from "./modes";
|
|
50
50
|
import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
|
|
51
51
|
import type { SubmittedUserInput } from "./modes/types";
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
type CreateAgentSessionOptions,
|
|
54
|
+
type CreateAgentSessionResult,
|
|
55
|
+
createAgentSession,
|
|
56
|
+
discoverAuthStorage,
|
|
57
|
+
} from "./sdk";
|
|
53
58
|
import type { AgentSession } from "./session/agent-session";
|
|
59
|
+
import type { AuthStorage } from "./session/auth-storage";
|
|
54
60
|
import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
|
|
55
61
|
import { resolvePromptInput } from "./system-prompt";
|
|
56
62
|
import type { LspStartupServerInfo } from "./tools";
|
|
@@ -102,9 +108,9 @@ const RPC_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
|
|
102
108
|
"memories.enabled",
|
|
103
109
|
];
|
|
104
110
|
|
|
105
|
-
function applyRpcDefaultSettingOverrides(): void {
|
|
111
|
+
function applyRpcDefaultSettingOverrides(targetSettings: Settings = settings): void {
|
|
106
112
|
for (const settingPath of RPC_DEFAULTED_SETTING_PATHS) {
|
|
107
|
-
|
|
113
|
+
targetSettings.override(settingPath, getDefault(settingPath));
|
|
108
114
|
}
|
|
109
115
|
}
|
|
110
116
|
|
|
@@ -160,6 +166,73 @@ export async function submitInteractiveInput(
|
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
|
|
169
|
+
function applyExtensionFlagValues(session: AgentSession, rawArgs: string[]): Map<string, boolean | string> {
|
|
170
|
+
const extensionRunner = session.extensionRunner;
|
|
171
|
+
if (!extensionRunner) {
|
|
172
|
+
return new Map();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const extFlags = extensionRunner.getFlags();
|
|
176
|
+
if (extFlags.size > 0) {
|
|
177
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
178
|
+
const arg = rawArgs[i];
|
|
179
|
+
if (!arg.startsWith("--")) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const flagName = arg.slice(2);
|
|
183
|
+
const extFlag = extFlags.get(flagName);
|
|
184
|
+
if (!extFlag) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (extFlag.type === "boolean") {
|
|
188
|
+
extensionRunner.setFlagValue(flagName, true);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (i + 1 < rawArgs.length) {
|
|
192
|
+
extensionRunner.setFlagValue(flagName, rawArgs[++i]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return extensionRunner.getFlagValues();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type AcpSessionFactory = (cwd: string) => Promise<AgentSession>;
|
|
201
|
+
|
|
202
|
+
interface AcpSessionFactoryOptions {
|
|
203
|
+
baseOptions: CreateAgentSessionOptions;
|
|
204
|
+
settings: Settings;
|
|
205
|
+
sessionDir?: string;
|
|
206
|
+
authStorage: AuthStorage;
|
|
207
|
+
modelRegistry: ModelRegistry;
|
|
208
|
+
parsedArgs: Pick<Args, "apiKey">;
|
|
209
|
+
rawArgs: string[];
|
|
210
|
+
createSession: (options: CreateAgentSessionOptions) => Promise<CreateAgentSessionResult>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function createAcpSessionFactory(args: AcpSessionFactoryOptions): AcpSessionFactory {
|
|
214
|
+
return async cwd => {
|
|
215
|
+
const nextSettings = await args.settings.cloneForCwd(cwd);
|
|
216
|
+
const nextSessionManager = SessionManager.create(cwd, args.sessionDir);
|
|
217
|
+
const agentId = `acp:${nextSessionManager.getSessionId()}`;
|
|
218
|
+
const { session: nextSession } = await args.createSession({
|
|
219
|
+
...args.baseOptions,
|
|
220
|
+
cwd,
|
|
221
|
+
sessionManager: nextSessionManager,
|
|
222
|
+
settings: nextSettings,
|
|
223
|
+
authStorage: args.authStorage,
|
|
224
|
+
modelRegistry: args.modelRegistry,
|
|
225
|
+
agentId,
|
|
226
|
+
hasUI: false,
|
|
227
|
+
});
|
|
228
|
+
if (args.parsedArgs.apiKey && !args.baseOptions.model && nextSession.model) {
|
|
229
|
+
args.authStorage.setRuntimeApiKey(nextSession.model.provider, args.parsedArgs.apiKey);
|
|
230
|
+
}
|
|
231
|
+
applyExtensionFlagValues(nextSession, args.rawArgs);
|
|
232
|
+
return nextSession;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
163
236
|
async function runInteractiveMode(
|
|
164
237
|
session: AgentSession,
|
|
165
238
|
version: string,
|
|
@@ -290,7 +363,11 @@ async function flushChangelogVersion(): Promise<void> {
|
|
|
290
363
|
}
|
|
291
364
|
}
|
|
292
365
|
|
|
293
|
-
async function createSessionManager(
|
|
366
|
+
async function createSessionManager(
|
|
367
|
+
parsed: Args,
|
|
368
|
+
cwd: string,
|
|
369
|
+
activeSettings: Settings = settings,
|
|
370
|
+
): Promise<SessionManager | undefined> {
|
|
294
371
|
if (parsed.fork) {
|
|
295
372
|
if (parsed.noSession) {
|
|
296
373
|
throw new Error("--fork requires session persistence");
|
|
@@ -343,7 +420,7 @@ async function createSessionManager(parsed: Args, cwd: string): Promise<SessionM
|
|
|
343
420
|
// session exists. When a prior session is resumed, mark parsed.continue so
|
|
344
421
|
// buildSessionOptions restores the session's model/thinking instead of
|
|
345
422
|
// overriding them with CLI defaults.
|
|
346
|
-
if (
|
|
423
|
+
if (activeSettings.get("autoResume")) {
|
|
347
424
|
const manager = await SessionManager.continueRecent(cwd, parsed.sessionDir);
|
|
348
425
|
if (manager.getEntries().length > 0) {
|
|
349
426
|
parsed.continue = true;
|
|
@@ -437,6 +514,7 @@ async function buildSessionOptions(
|
|
|
437
514
|
scopedModels: ScopedModel[],
|
|
438
515
|
sessionManager: SessionManager | undefined,
|
|
439
516
|
modelRegistry: ModelRegistry,
|
|
517
|
+
activeSettings: Settings,
|
|
440
518
|
): Promise<{ options: CreateAgentSessionOptions }> {
|
|
441
519
|
const options: CreateAgentSessionOptions = {
|
|
442
520
|
cwd: parsed.cwd ?? getProjectDir(),
|
|
@@ -459,7 +537,7 @@ async function buildSessionOptions(
|
|
|
459
537
|
// - supports --provider <name> --model <pattern>
|
|
460
538
|
// - supports --model <provider>/<pattern>
|
|
461
539
|
const modelMatchPreferences = {
|
|
462
|
-
usageOrder:
|
|
540
|
+
usageOrder: activeSettings.getStorage()?.getModelUsageOrder(),
|
|
463
541
|
};
|
|
464
542
|
if (parsed.model) {
|
|
465
543
|
const resolved = resolveCliModel({
|
|
@@ -482,7 +560,7 @@ async function buildSessionOptions(
|
|
|
482
560
|
}
|
|
483
561
|
} else if (resolved.model) {
|
|
484
562
|
options.model = resolved.model;
|
|
485
|
-
|
|
563
|
+
activeSettings.overrideModelRoles({
|
|
486
564
|
default: resolved.selector ?? `${resolved.model.provider}/${resolved.model.id}`,
|
|
487
565
|
});
|
|
488
566
|
if (!parsed.thinking && resolved.thinkingLevel) {
|
|
@@ -490,13 +568,13 @@ async function buildSessionOptions(
|
|
|
490
568
|
}
|
|
491
569
|
}
|
|
492
570
|
} else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {
|
|
493
|
-
const remembered =
|
|
571
|
+
const remembered = activeSettings.getModelRole("default");
|
|
494
572
|
if (remembered) {
|
|
495
573
|
const rememberedSpec = resolveModelRoleValue(
|
|
496
574
|
remembered,
|
|
497
575
|
scopedModels.map(scopedModel => scopedModel.model),
|
|
498
576
|
{
|
|
499
|
-
settings,
|
|
577
|
+
settings: activeSettings,
|
|
500
578
|
matchPreferences: modelMatchPreferences,
|
|
501
579
|
modelRegistry,
|
|
502
580
|
},
|
|
@@ -534,7 +612,7 @@ async function buildSessionOptions(
|
|
|
534
612
|
|
|
535
613
|
// Scoped models for Ctrl+P cycling - fill in default thinking levels when not explicit
|
|
536
614
|
if (scopedModels.length > 0) {
|
|
537
|
-
const defaultThinkingLevel =
|
|
615
|
+
const defaultThinkingLevel = activeSettings.get("defaultThinkingLevel");
|
|
538
616
|
options.scopedModels = scopedModels.map(scopedModel => ({
|
|
539
617
|
model: scopedModel.model,
|
|
540
618
|
thinkingLevel: scopedModel.explicitThinkingLevel
|
|
@@ -571,7 +649,7 @@ async function buildSessionOptions(
|
|
|
571
649
|
options.skills = [];
|
|
572
650
|
} else if (parsed.skills && parsed.skills.length > 0) {
|
|
573
651
|
// Override includeSkills for this session
|
|
574
|
-
|
|
652
|
+
activeSettings.override("skills.includeSkills", parsed.skills as string[]);
|
|
575
653
|
}
|
|
576
654
|
|
|
577
655
|
// Rules
|
|
@@ -593,7 +671,18 @@ async function buildSessionOptions(
|
|
|
593
671
|
return { options };
|
|
594
672
|
}
|
|
595
673
|
|
|
596
|
-
|
|
674
|
+
interface RunRootCommandDependencies {
|
|
675
|
+
createAgentSession?: typeof createAgentSession;
|
|
676
|
+
discoverAuthStorage?: typeof discoverAuthStorage;
|
|
677
|
+
runAcpMode?: typeof runAcpMode;
|
|
678
|
+
settings?: Settings;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export async function runRootCommand(
|
|
682
|
+
parsed: Args,
|
|
683
|
+
rawArgs: string[],
|
|
684
|
+
deps: RunRootCommandDependencies = {},
|
|
685
|
+
): Promise<void> {
|
|
597
686
|
logger.startTiming();
|
|
598
687
|
|
|
599
688
|
// Initialize theme early with defaults (CLI commands need symbols)
|
|
@@ -606,7 +695,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
606
695
|
const notifs: (InteractiveModeNotify | null)[] = [];
|
|
607
696
|
|
|
608
697
|
// Create AuthStorage and ModelRegistry upfront
|
|
609
|
-
const authStorage = await logger.time("discoverModels", discoverAuthStorage);
|
|
698
|
+
const authStorage = await logger.time("discoverModels", deps.discoverAuthStorage ?? discoverAuthStorage);
|
|
610
699
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
611
700
|
|
|
612
701
|
if (parsedArgs.version) {
|
|
@@ -668,9 +757,9 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
668
757
|
pluginPreloadPromise.catch(() => {});
|
|
669
758
|
|
|
670
759
|
const cwd = getProjectDir();
|
|
671
|
-
const settingsInstance = await logger.time("settings:init", Settings.init, { cwd });
|
|
760
|
+
const settingsInstance = deps.settings ?? (await logger.time("settings:init", Settings.init, { cwd }));
|
|
672
761
|
if (parsedArgs.mode === "rpc" || parsedArgs.mode === "rpc-ui" || parsedArgs.mode === "acp") {
|
|
673
|
-
applyRpcDefaultSettingOverrides();
|
|
762
|
+
applyRpcDefaultSettingOverrides(settingsInstance);
|
|
674
763
|
}
|
|
675
764
|
if (parsedArgs.noPty || parsedArgs.mode === "rpc-ui") {
|
|
676
765
|
Bun.env.PI_NO_PTY = "1";
|
|
@@ -684,7 +773,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
684
773
|
return { pipedInput, fileText: undefined, fileImages: undefined };
|
|
685
774
|
}
|
|
686
775
|
const processed = await processFileArguments(parsedArgs.fileArgs, {
|
|
687
|
-
autoResizeImages:
|
|
776
|
+
autoResizeImages: settingsInstance.get("images.autoResize"),
|
|
688
777
|
});
|
|
689
778
|
return { pipedInput, fileText: processed.text, fileImages: processed.images };
|
|
690
779
|
});
|
|
@@ -699,14 +788,14 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
699
788
|
const mode = parsedArgs.mode || "text";
|
|
700
789
|
|
|
701
790
|
// Initialize discovery system with settings for provider persistence
|
|
702
|
-
logger.time("initializeWithSettings", initializeWithSettings,
|
|
791
|
+
logger.time("initializeWithSettings", initializeWithSettings, settingsInstance);
|
|
703
792
|
|
|
704
793
|
// Apply model role overrides from CLI args or env vars (ephemeral, not persisted)
|
|
705
794
|
const smolModel = parsedArgs.smol ?? $env.PI_SMOL_MODEL;
|
|
706
795
|
const slowModel = parsedArgs.slow ?? $env.PI_SLOW_MODEL;
|
|
707
796
|
const planModel = parsedArgs.plan ?? $env.PI_PLAN_MODEL;
|
|
708
797
|
if (smolModel || slowModel || planModel) {
|
|
709
|
-
|
|
798
|
+
settingsInstance.overrideModelRoles({
|
|
710
799
|
smol: smolModel,
|
|
711
800
|
slow: slowModel,
|
|
712
801
|
plan: planModel,
|
|
@@ -717,16 +806,16 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
717
806
|
"initTheme:final",
|
|
718
807
|
initTheme,
|
|
719
808
|
isInteractive,
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
809
|
+
settingsInstance.get("symbolPreset"),
|
|
810
|
+
settingsInstance.get("colorBlindMode"),
|
|
811
|
+
settingsInstance.get("theme.dark"),
|
|
812
|
+
settingsInstance.get("theme.light"),
|
|
724
813
|
);
|
|
725
814
|
|
|
726
815
|
let scopedModels: ScopedModel[] = [];
|
|
727
|
-
const modelPatterns = parsedArgs.models ??
|
|
816
|
+
const modelPatterns = parsedArgs.models ?? settingsInstance.get("enabledModels");
|
|
728
817
|
const modelMatchPreferences = {
|
|
729
|
-
usageOrder:
|
|
818
|
+
usageOrder: settingsInstance.getStorage()?.getModelUsageOrder(),
|
|
730
819
|
};
|
|
731
820
|
if (modelPatterns && modelPatterns.length > 0) {
|
|
732
821
|
scopedModels = await logger.time(
|
|
@@ -739,7 +828,13 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
739
828
|
}
|
|
740
829
|
|
|
741
830
|
// Create session manager based on CLI flags
|
|
742
|
-
let sessionManager = await logger.time(
|
|
831
|
+
let sessionManager = await logger.time(
|
|
832
|
+
"createSessionManager",
|
|
833
|
+
createSessionManager,
|
|
834
|
+
parsedArgs,
|
|
835
|
+
cwd,
|
|
836
|
+
settingsInstance,
|
|
837
|
+
);
|
|
743
838
|
|
|
744
839
|
// Handle --resume (no value): show session picker
|
|
745
840
|
if (parsedArgs.resume === true && !parsedArgs.fork) {
|
|
@@ -759,7 +854,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
759
854
|
await pluginPreloadPromise;
|
|
760
855
|
|
|
761
856
|
// Background marketplace auto-update — never blocks startup.
|
|
762
|
-
const autoUpdate =
|
|
857
|
+
const autoUpdate = settingsInstance.get("marketplace.autoUpdate");
|
|
763
858
|
if (autoUpdate !== "off") {
|
|
764
859
|
void (async () => {
|
|
765
860
|
try {
|
|
@@ -793,6 +888,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
793
888
|
scopedModels,
|
|
794
889
|
sessionManager,
|
|
795
890
|
modelRegistry,
|
|
891
|
+
settingsInstance,
|
|
796
892
|
);
|
|
797
893
|
sessionOptions.authStorage = authStorage;
|
|
798
894
|
sessionOptions.modelRegistry = modelRegistry;
|
|
@@ -812,140 +908,111 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
812
908
|
}
|
|
813
909
|
}
|
|
814
910
|
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
createAgentSession,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (parsedArgs.apiKey && !sessionOptions.model && session.model) {
|
|
825
|
-
authStorage.setRuntimeApiKey(session.model.provider, parsedArgs.apiKey);
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
if (modelFallbackMessage) {
|
|
829
|
-
notifs.push({ kind: "warn", message: modelFallbackMessage });
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
const modelRegistryError = modelRegistry.getError();
|
|
833
|
-
if (modelRegistryError) {
|
|
834
|
-
notifs.push({ kind: "error", message: modelRegistryError.message });
|
|
835
|
-
}
|
|
911
|
+
const createAgentSessionImpl = deps.createAgentSession ?? createAgentSession;
|
|
912
|
+
const createSession = async (options: CreateAgentSessionOptions): Promise<CreateAgentSessionResult> => {
|
|
913
|
+
const result = await logger.time("createAgentSession", createAgentSessionImpl, options);
|
|
914
|
+
// Kick off background model discovery only after createAgentSession finishes its parallel
|
|
915
|
+
// discovery arms; running these concurrently contends for the event loop and stretches
|
|
916
|
+
// every parallel arm by ~30ms.
|
|
917
|
+
modelRegistry.refreshInBackground();
|
|
918
|
+
return result;
|
|
919
|
+
};
|
|
836
920
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
855
|
-
if (i + 1 < rawArgs.length) {
|
|
856
|
-
session.extensionRunner.setFlagValue(flagName, rawArgs[++i]);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
921
|
+
if (mode === "acp") {
|
|
922
|
+
const createAcpSession = createAcpSessionFactory({
|
|
923
|
+
baseOptions: sessionOptions,
|
|
924
|
+
settings: settingsInstance,
|
|
925
|
+
sessionDir: parsedArgs.sessionDir,
|
|
926
|
+
authStorage,
|
|
927
|
+
modelRegistry,
|
|
928
|
+
parsedArgs,
|
|
929
|
+
rawArgs,
|
|
930
|
+
createSession,
|
|
931
|
+
});
|
|
932
|
+
await (deps.runAcpMode ?? runAcpMode)(createAcpSession);
|
|
933
|
+
} else {
|
|
934
|
+
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } =
|
|
935
|
+
await createSession(sessionOptions);
|
|
936
|
+
if (parsedArgs.apiKey && !sessionOptions.model && session.model) {
|
|
937
|
+
authStorage.setRuntimeApiKey(session.model.provider, parsedArgs.apiKey);
|
|
859
938
|
}
|
|
860
|
-
}
|
|
861
939
|
|
|
862
|
-
if (!isInteractive && parsedArgs.mode !== "acp" && !session.model) {
|
|
863
940
|
if (modelFallbackMessage) {
|
|
864
|
-
|
|
865
|
-
} else {
|
|
866
|
-
process.stderr.write(`${chalk.red("No models available.")}\n`);
|
|
941
|
+
notifs.push({ kind: "warn", message: modelFallbackMessage });
|
|
867
942
|
}
|
|
868
|
-
process.stderr.write(`${chalk.yellow("\nSet an API key environment variable:")}\n`);
|
|
869
|
-
process.stderr.write(" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\n");
|
|
870
|
-
process.stderr.write(`${chalk.yellow(`\nOr create ${ModelsConfigFile.path()}`)}\n`);
|
|
871
|
-
process.exit(1);
|
|
872
|
-
}
|
|
873
943
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
const nextSessionManager = SessionManager.create(cwd, parsedArgs.sessionDir);
|
|
878
|
-
const { session: nextSession } = await createAgentSession({
|
|
879
|
-
...sessionOptions,
|
|
880
|
-
cwd,
|
|
881
|
-
sessionManager: nextSessionManager,
|
|
882
|
-
settings: nextSettings,
|
|
883
|
-
authStorage,
|
|
884
|
-
modelRegistry,
|
|
885
|
-
hasUI: false,
|
|
886
|
-
});
|
|
887
|
-
if (nextSession.extensionRunner) {
|
|
888
|
-
for (const [flagName, value] of extensionFlagValues) {
|
|
889
|
-
nextSession.extensionRunner.setFlagValue(flagName, value);
|
|
890
|
-
}
|
|
944
|
+
const modelRegistryError = modelRegistry.getError();
|
|
945
|
+
if (modelRegistryError) {
|
|
946
|
+
notifs.push({ kind: "error", message: modelRegistryError.message });
|
|
891
947
|
}
|
|
892
|
-
return nextSession;
|
|
893
|
-
};
|
|
894
948
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels;
|
|
904
|
-
if (scopedModelsForDisplay.length > 0) {
|
|
905
|
-
const modelList = scopedModelsForDisplay
|
|
906
|
-
.map(scopedModel => {
|
|
907
|
-
const thinkingStr = !scopedModel.thinkingLevel ? `:${scopedModel.thinkingLevel}` : "";
|
|
908
|
-
return `${scopedModel.model.id}${thinkingStr}`;
|
|
909
|
-
})
|
|
910
|
-
.join(", ");
|
|
911
|
-
process.stdout.write(`${chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Ctrl+P to cycle)")}`)}\n`);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
if ($env.PI_TIMING) {
|
|
915
|
-
logger.printTimings();
|
|
916
|
-
if ($env.PI_TIMING === "x") {
|
|
917
|
-
process.exit(0);
|
|
949
|
+
applyExtensionFlagValues(session, rawArgs);
|
|
950
|
+
|
|
951
|
+
if (!isInteractive && !session.model) {
|
|
952
|
+
if (modelFallbackMessage) {
|
|
953
|
+
process.stderr.write(`${chalk.red(modelFallbackMessage)}\n`);
|
|
954
|
+
} else {
|
|
955
|
+
process.stderr.write(`${chalk.red("No models available.")}\n`);
|
|
918
956
|
}
|
|
957
|
+
process.stderr.write(`${chalk.yellow("\nSet an API key environment variable:")}\n`);
|
|
958
|
+
process.stderr.write(" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\n");
|
|
959
|
+
process.stderr.write(`${chalk.yellow(`\nOr create ${ModelsConfigFile.path()}`)}\n`);
|
|
960
|
+
process.exit(1);
|
|
919
961
|
}
|
|
920
962
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
VERSION
|
|
925
|
-
changelogMarkdown,
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
963
|
+
if (mode === "rpc" || mode === "rpc-ui") {
|
|
964
|
+
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined);
|
|
965
|
+
} else if (isInteractive) {
|
|
966
|
+
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
|
|
967
|
+
const changelogMarkdown = await logger.time("main:getChangelogForDisplay", getChangelogForDisplay, parsedArgs);
|
|
968
|
+
|
|
969
|
+
const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels;
|
|
970
|
+
if (scopedModelsForDisplay.length > 0) {
|
|
971
|
+
const modelList = scopedModelsForDisplay
|
|
972
|
+
.map(scopedModel => {
|
|
973
|
+
const thinkingStr = !scopedModel.thinkingLevel ? `:${scopedModel.thinkingLevel}` : "";
|
|
974
|
+
return `${scopedModel.model.id}${thinkingStr}`;
|
|
975
|
+
})
|
|
976
|
+
.join(", ");
|
|
977
|
+
process.stdout.write(`${chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Ctrl+P to cycle)")}`)}\n`);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if ($env.PI_TIMING) {
|
|
981
|
+
logger.printTimings();
|
|
982
|
+
if ($env.PI_TIMING === "x") {
|
|
983
|
+
process.exit(0);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
logger.endTiming();
|
|
988
|
+
await runInteractiveMode(
|
|
989
|
+
session,
|
|
990
|
+
VERSION,
|
|
991
|
+
changelogMarkdown,
|
|
992
|
+
notifs,
|
|
993
|
+
versionCheckPromise,
|
|
994
|
+
parsedArgs.messages,
|
|
995
|
+
setToolUIContext,
|
|
996
|
+
lspServers,
|
|
997
|
+
mcpManager,
|
|
998
|
+
eventBus,
|
|
999
|
+
initialMessage,
|
|
1000
|
+
initialImages,
|
|
1001
|
+
);
|
|
1002
|
+
} else {
|
|
1003
|
+
await runPrintMode(session, {
|
|
1004
|
+
mode,
|
|
1005
|
+
messages: parsedArgs.messages,
|
|
1006
|
+
initialMessage,
|
|
1007
|
+
initialImages,
|
|
1008
|
+
});
|
|
1009
|
+
if ($env.PI_TIMING) {
|
|
1010
|
+
logger.printTimings();
|
|
1011
|
+
}
|
|
1012
|
+
await session.dispose();
|
|
1013
|
+
stopThemeWatcher();
|
|
1014
|
+
await postmortem.quit(0);
|
|
945
1015
|
}
|
|
946
|
-
await session.dispose();
|
|
947
|
-
stopThemeWatcher();
|
|
948
|
-
await postmortem.quit(0);
|
|
949
1016
|
}
|
|
950
1017
|
}
|
|
951
1018
|
|