@oh-my-pi/pi-coding-agent 15.10.1 → 15.10.3
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 +113 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +7 -1
- package/dist/types/cli/startup-cwd.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-provider-priority.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +4 -1
- package/dist/types/config/settings.d.ts +7 -2
- package/dist/types/debug/report-bundle.d.ts +3 -0
- package/dist/types/edit/file-snapshot-store.d.ts +18 -10
- package/dist/types/edit/index.d.ts +0 -1
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +4 -1
- package/dist/types/lsp/client.d.ts +10 -0
- package/dist/types/lsp/index.d.ts +0 -5
- package/dist/types/main.d.ts +14 -9
- package/dist/types/mcp/tool-bridge.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +0 -9
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/late-diagnostics-message.d.ts +20 -0
- package/dist/types/modes/components/read-tool-group.d.ts +6 -0
- package/dist/types/modes/components/session-selector.d.ts +16 -7
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +0 -18
- package/dist/types/modes/controllers/event-controller.d.ts +17 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/magic-keywords.d.ts +1 -1
- package/dist/types/modes/markdown-prose.d.ts +1 -1
- package/dist/types/modes/types.d.ts +7 -0
- package/dist/types/modes/workflow.d.ts +3 -3
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/messages.d.ts +11 -8
- package/dist/types/session/session-manager.d.ts +5 -2
- package/dist/types/session/yield-queue.d.ts +10 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/tools/eval-render.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +8 -0
- package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
- package/dist/types/tools/github-cache.d.ts +12 -0
- package/dist/types/tools/index.d.ts +31 -0
- package/dist/types/tools/path-utils.d.ts +13 -1
- package/dist/types/tools/read.d.ts +2 -1
- package/dist/types/tools/render-utils.d.ts +3 -1
- package/dist/types/tools/renderers.d.ts +0 -15
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/tools/write.d.ts +0 -2
- package/dist/types/tools/yield.d.ts +8 -0
- package/dist/types/tui/code-cell.d.ts +0 -2
- package/dist/types/tui/hyperlink.d.ts +5 -7
- package/dist/types/tui/output-block.d.ts +0 -18
- package/package.json +9 -9
- package/src/cli/args.ts +3 -1
- package/src/cli/dry-balance-cli.ts +2 -4
- package/src/cli/gallery-cli.ts +4 -0
- package/src/cli/gallery-fixtures/codeintel.ts +0 -1
- package/src/cli/gallery-fixtures/fs.ts +68 -1
- package/src/cli/gallery-fixtures/types.ts +8 -1
- package/src/cli/startup-cwd.ts +68 -0
- package/src/commands/launch.ts +3 -0
- package/src/commit/agentic/agent.ts +1 -0
- package/src/commit/model-selection.ts +3 -2
- package/src/config/model-provider-priority.ts +55 -0
- package/src/config/model-registry.ts +4 -22
- package/src/config/model-resolver.ts +39 -7
- package/src/config/settings.ts +86 -41
- package/src/debug/index.ts +8 -0
- package/src/debug/raw-sse-buffer.ts +7 -4
- package/src/debug/report-bundle.ts +9 -0
- package/src/edit/file-snapshot-store.ts +33 -1
- package/src/edit/hashline/diff.ts +86 -0
- package/src/edit/hashline/execute.ts +14 -1
- package/src/edit/hashline/filesystem.ts +2 -1
- package/src/edit/index.ts +31 -17
- package/src/edit/renderer.ts +116 -31
- package/src/eval/__tests__/llm-bridge.test.ts +20 -0
- package/src/eval/js/context-manager.ts +32 -15
- package/src/eval/js/shared/prelude.txt +26 -10
- package/src/eval/llm-bridge.ts +14 -3
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/executor.ts +23 -11
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +10 -1
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/lsp/client.ts +23 -11
- package/src/lsp/config.ts +11 -1
- package/src/lsp/index.ts +189 -61
- package/src/main.ts +144 -78
- package/src/mcp/tool-bridge.ts +2 -0
- package/src/memories/index.ts +2 -2
- package/src/modes/components/assistant-message.ts +3 -15
- package/src/modes/components/custom-editor.ts +143 -111
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/model-selector.ts +59 -13
- package/src/modes/components/oauth-selector.ts +33 -7
- package/src/modes/components/plan-review-overlay.ts +26 -5
- package/src/modes/components/read-tool-group.ts +415 -35
- package/src/modes/components/session-selector.ts +89 -35
- package/src/modes/components/status-line.ts +19 -4
- package/src/modes/components/tips.txt +1 -1
- package/src/modes/components/tool-execution.ts +7 -49
- package/src/modes/components/transcript-container.ts +108 -32
- package/src/modes/components/user-message.ts +1 -1
- package/src/modes/controllers/event-controller.ts +32 -1
- package/src/modes/controllers/input-controller.ts +56 -9
- package/src/modes/interactive-mode.ts +107 -20
- package/src/modes/magic-keywords.ts +1 -1
- package/src/modes/markdown-prose.ts +1 -1
- package/src/modes/theme/shimmer.ts +20 -9
- package/src/modes/types.ts +7 -0
- package/src/modes/utils/ui-helpers.ts +26 -5
- package/src/modes/workflow.ts +10 -10
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/plan-mode-active.md +56 -72
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/eval.md +5 -2
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +85 -10
- package/src/session/agent-session.ts +42 -15
- package/src/session/auth-storage.ts +2 -0
- package/src/session/messages.ts +21 -14
- package/src/session/session-manager.ts +98 -25
- package/src/session/yield-queue.ts +20 -2
- package/src/task/executor.ts +72 -36
- package/src/task/render.ts +3 -4
- package/src/tiny/title-client.ts +6 -1
- package/src/tools/bash.ts +7 -7
- package/src/tools/browser/tab-supervisor.ts +13 -1
- package/src/tools/browser/tab-worker.ts +33 -4
- package/src/tools/eval-render.ts +4 -23
- package/src/tools/eval.ts +13 -2
- package/src/tools/find.ts +148 -99
- package/src/tools/gh-cache-invalidation.ts +200 -0
- package/src/tools/github-cache.ts +25 -0
- package/src/tools/index.ts +32 -0
- package/src/tools/inspect-image.ts +2 -2
- package/src/tools/path-utils.ts +47 -24
- package/src/tools/plan-mode-guard.ts +52 -7
- package/src/tools/read.ts +41 -20
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/renderers.ts +0 -15
- package/src/tools/search.ts +38 -3
- package/src/tools/ssh.ts +0 -1
- package/src/tools/todo.ts +1 -0
- package/src/tools/write.ts +5 -14
- package/src/tools/yield.ts +10 -1
- package/src/tui/code-cell.ts +1 -6
- package/src/tui/hyperlink.ts +13 -23
- package/src/tui/output-block.ts +2 -97
- package/src/utils/commit-message-generator.ts +2 -2
- package/src/utils/enhanced-paste.ts +30 -2
- package/src/web/search/providers/codex.ts +37 -8
package/src/main.ts
CHANGED
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
* This file handles CLI argument parsing and translates them into
|
|
5
5
|
* createAgentSession() options. The SDK does the heavy lifting.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from "node:fs/promises";
|
|
7
|
+
import * as fsSync from "node:fs";
|
|
9
8
|
import * as os from "node:os";
|
|
10
|
-
import * as path from "node:path";
|
|
11
9
|
import { createInterface } from "node:readline/promises";
|
|
12
10
|
import { EventLoopKeepalive } from "@oh-my-pi/pi-agent-core";
|
|
13
11
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
@@ -28,9 +26,16 @@ import { processFileArguments } from "./cli/file-processor";
|
|
|
28
26
|
import { buildInitialMessage } from "./cli/initial-message";
|
|
29
27
|
import { runListModelsCommand } from "./cli/list-models";
|
|
30
28
|
import { selectSession } from "./cli/session-picker";
|
|
29
|
+
import { applyStartupCwd } from "./cli/startup-cwd";
|
|
31
30
|
import { findConfigFile } from "./config";
|
|
32
31
|
import { ModelRegistry, ModelsConfigFile } from "./config/model-registry";
|
|
33
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
getModelMatchPreferences,
|
|
34
|
+
resolveCliModel,
|
|
35
|
+
resolveModelRoleValue,
|
|
36
|
+
resolveModelScope,
|
|
37
|
+
type ScopedModel,
|
|
38
|
+
} from "./config/model-resolver";
|
|
34
39
|
import { getDefault, type SettingPath, Settings, settings } from "./config/settings";
|
|
35
40
|
import { initializeWithSettings } from "./discovery";
|
|
36
41
|
import {
|
|
@@ -166,7 +171,8 @@ export async function submitInteractiveInput(
|
|
|
166
171
|
|
|
167
172
|
try {
|
|
168
173
|
using _keepalive = new EventLoopKeepalive();
|
|
169
|
-
// Continue shortcuts submit an already-started
|
|
174
|
+
// Continue shortcuts submit an already-started synthetic developer prompt with
|
|
175
|
+
// no optimistic user message.
|
|
170
176
|
if (!input.started && !mode.markPendingSubmissionStarted(input)) {
|
|
171
177
|
return;
|
|
172
178
|
}
|
|
@@ -177,6 +183,8 @@ export async function submitInteractiveInput(
|
|
|
177
183
|
display: input.display ?? false,
|
|
178
184
|
attribution: "agent",
|
|
179
185
|
});
|
|
186
|
+
} else if (input.synthetic) {
|
|
187
|
+
await session.prompt(input.text, { synthetic: true, expandPromptTemplates: false });
|
|
180
188
|
} else {
|
|
181
189
|
await session.prompt(input.text, { images: input.images });
|
|
182
190
|
}
|
|
@@ -344,11 +352,11 @@ async function runInteractiveMode(
|
|
|
344
352
|
}
|
|
345
353
|
}
|
|
346
354
|
|
|
347
|
-
type
|
|
355
|
+
type SessionPromptResult = "accepted" | "declined" | "unavailable";
|
|
348
356
|
|
|
349
|
-
type
|
|
357
|
+
type SessionPrompt = (session: SessionInfo) => Promise<SessionPromptResult>;
|
|
350
358
|
|
|
351
|
-
async function promptForkSession(session: SessionInfo): Promise<
|
|
359
|
+
async function promptForkSession(session: SessionInfo): Promise<SessionPromptResult> {
|
|
352
360
|
if (!process.stdin.isTTY) {
|
|
353
361
|
return "unavailable";
|
|
354
362
|
}
|
|
@@ -362,6 +370,68 @@ async function promptForkSession(session: SessionInfo): Promise<ForkSessionPromp
|
|
|
362
370
|
}
|
|
363
371
|
}
|
|
364
372
|
|
|
373
|
+
async function promptMoveSession(session: SessionInfo): Promise<SessionPromptResult> {
|
|
374
|
+
if (!process.stdin.isTTY) {
|
|
375
|
+
return "unavailable";
|
|
376
|
+
}
|
|
377
|
+
const message = `Session's directory no longer exists (${session.cwd}). Move (re-root) it into the current directory? [Y/n] `;
|
|
378
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
379
|
+
try {
|
|
380
|
+
const answer = (await rl.question(message)).trim().toLowerCase();
|
|
381
|
+
return answer === "" || answer === "y" || answer === "yes" ? "accepted" : "declined";
|
|
382
|
+
} finally {
|
|
383
|
+
rl.close();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Friendly CLI failure raised by {@link createSessionManager} when the user's
|
|
389
|
+
* session-resolution flags (`--resume`/`--fork`/cross-project prompts) cannot
|
|
390
|
+
* be satisfied. {@link runRootCommand} catches it and prints a clean stderr
|
|
391
|
+
* message instead of letting it surface as `[Uncaught Exception]`
|
|
392
|
+
* (see issue #2084).
|
|
393
|
+
*/
|
|
394
|
+
export class SessionResolutionError extends Error {
|
|
395
|
+
readonly hint?: string;
|
|
396
|
+
constructor(message: string, hint?: string) {
|
|
397
|
+
super(message);
|
|
398
|
+
this.name = "SessionResolutionError";
|
|
399
|
+
this.hint = hint;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
type MissingCwdMoveResult =
|
|
404
|
+
| { status: "not-needed" }
|
|
405
|
+
| { status: "declined" }
|
|
406
|
+
| { status: "moved"; manager: SessionManager };
|
|
407
|
+
|
|
408
|
+
async function moveMissingCwdSessionIfNeeded(
|
|
409
|
+
sessionArg: string,
|
|
410
|
+
session: SessionInfo,
|
|
411
|
+
cwd: string,
|
|
412
|
+
sessionDir: string | undefined,
|
|
413
|
+
askToMoveSession: SessionPrompt,
|
|
414
|
+
): Promise<MissingCwdMoveResult> {
|
|
415
|
+
const sourceCwd = session.cwd;
|
|
416
|
+
if (!sourceCwd || fsSync.existsSync(sourceCwd)) {
|
|
417
|
+
return { status: "not-needed" };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const movePromptResult = await askToMoveSession(session);
|
|
421
|
+
if (movePromptResult === "unavailable") {
|
|
422
|
+
throw new SessionResolutionError(
|
|
423
|
+
`Session "${sessionArg}" belongs to a directory that no longer exists (${sourceCwd}); run interactively to move it into the current project.`,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
if (movePromptResult === "declined") {
|
|
427
|
+
return { status: "declined" };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const manager = await SessionManager.open(session.path, sessionDir);
|
|
431
|
+
await manager.moveTo(cwd, sessionDir);
|
|
432
|
+
return { status: "moved", manager };
|
|
433
|
+
}
|
|
434
|
+
|
|
365
435
|
async function getChangelogForDisplay(parsed: Args): Promise<string | undefined> {
|
|
366
436
|
if (parsed.continue || parsed.resume) {
|
|
367
437
|
return undefined;
|
|
@@ -407,11 +477,12 @@ export async function createSessionManager(
|
|
|
407
477
|
parsed: Args,
|
|
408
478
|
cwd: string,
|
|
409
479
|
activeSettings: Settings = settings,
|
|
410
|
-
askToForkSession:
|
|
480
|
+
askToForkSession: SessionPrompt = promptForkSession,
|
|
481
|
+
askToMoveSession: SessionPrompt = promptMoveSession,
|
|
411
482
|
): Promise<SessionManager | undefined> {
|
|
412
483
|
if (parsed.fork) {
|
|
413
484
|
if (parsed.noSession) {
|
|
414
|
-
throw new
|
|
485
|
+
throw new SessionResolutionError("--fork requires session persistence");
|
|
415
486
|
}
|
|
416
487
|
const forkSource = parsed.fork;
|
|
417
488
|
if (forkSource.includes("/") || forkSource.includes("\\") || forkSource.endsWith(".jsonl")) {
|
|
@@ -419,7 +490,10 @@ export async function createSessionManager(
|
|
|
419
490
|
}
|
|
420
491
|
const match = await resolveResumableSession(forkSource, cwd, parsed.sessionDir);
|
|
421
492
|
if (!match) {
|
|
422
|
-
throw new
|
|
493
|
+
throw new SessionResolutionError(
|
|
494
|
+
`Session "${forkSource}" not found.`,
|
|
495
|
+
"Run `omp --resume` without an argument to pick from recent sessions, or `omp` to start a new one.",
|
|
496
|
+
);
|
|
423
497
|
}
|
|
424
498
|
return await SessionManager.forkFrom(match.session.path, cwd, parsed.sessionDir);
|
|
425
499
|
}
|
|
@@ -434,15 +508,46 @@ export async function createSessionManager(
|
|
|
434
508
|
}
|
|
435
509
|
const match = await resolveResumableSession(sessionArg, cwd, parsed.sessionDir);
|
|
436
510
|
if (!match) {
|
|
437
|
-
throw new
|
|
511
|
+
throw new SessionResolutionError(
|
|
512
|
+
`Session "${sessionArg}" not found.`,
|
|
513
|
+
"Run `omp --resume` without an argument to pick from recent sessions, or `omp` to start a new one.",
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
if (match.scope === "local") {
|
|
517
|
+
const moveResult = await moveMissingCwdSessionIfNeeded(
|
|
518
|
+
sessionArg,
|
|
519
|
+
match.session,
|
|
520
|
+
cwd,
|
|
521
|
+
parsed.sessionDir,
|
|
522
|
+
askToMoveSession,
|
|
523
|
+
);
|
|
524
|
+
if (moveResult.status === "moved") {
|
|
525
|
+
return moveResult.manager;
|
|
526
|
+
}
|
|
527
|
+
if (moveResult.status === "declined") {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
438
530
|
}
|
|
439
531
|
if (match.scope === "global") {
|
|
440
532
|
const normalizedCwd = normalizePathForComparison(cwd);
|
|
441
533
|
const normalizedMatchCwd = normalizePathForComparison(match.session.cwd || cwd);
|
|
442
534
|
if (normalizedCwd !== normalizedMatchCwd) {
|
|
535
|
+
const moveResult = await moveMissingCwdSessionIfNeeded(
|
|
536
|
+
sessionArg,
|
|
537
|
+
match.session,
|
|
538
|
+
cwd,
|
|
539
|
+
parsed.sessionDir,
|
|
540
|
+
askToMoveSession,
|
|
541
|
+
);
|
|
542
|
+
if (moveResult.status === "moved") {
|
|
543
|
+
return moveResult.manager;
|
|
544
|
+
}
|
|
545
|
+
if (moveResult.status === "declined") {
|
|
546
|
+
return undefined;
|
|
547
|
+
}
|
|
443
548
|
const forkPromptResult = await askToForkSession(match.session);
|
|
444
549
|
if (forkPromptResult === "unavailable") {
|
|
445
|
-
throw new
|
|
550
|
+
throw new SessionResolutionError(
|
|
446
551
|
`Session "${sessionArg}" is in another project (${match.session.cwd}); run interactively to fork it into the current project.`,
|
|
447
552
|
);
|
|
448
553
|
}
|
|
@@ -480,56 +585,6 @@ export async function createSessionManager(
|
|
|
480
585
|
return undefined;
|
|
481
586
|
}
|
|
482
587
|
|
|
483
|
-
async function maybeAutoChdir(parsed: Args): Promise<void> {
|
|
484
|
-
if (parsed.allowHome || parsed.cwd) {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const home = os.homedir();
|
|
489
|
-
if (!home) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const normalizePath = normalizePathForComparison;
|
|
494
|
-
|
|
495
|
-
const cwd = normalizePath(getProjectDir());
|
|
496
|
-
const normalizedHome = normalizePath(home);
|
|
497
|
-
if (cwd !== normalizedHome) {
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const isDirectory = async (p: string) => {
|
|
502
|
-
try {
|
|
503
|
-
const s = await fs.stat(p);
|
|
504
|
-
return s.isDirectory();
|
|
505
|
-
} catch {
|
|
506
|
-
return false;
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
const candidates = [path.join(home, "tmp"), "/tmp", "/var/tmp"];
|
|
511
|
-
for (const candidate of candidates) {
|
|
512
|
-
try {
|
|
513
|
-
if (!(await isDirectory(candidate))) {
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
setProjectDir(candidate);
|
|
517
|
-
return;
|
|
518
|
-
} catch {
|
|
519
|
-
// Try next candidate.
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
try {
|
|
524
|
-
const fallback = os.tmpdir();
|
|
525
|
-
if (fallback && normalizePath(fallback) !== cwd && (await isDirectory(fallback))) {
|
|
526
|
-
setProjectDir(fallback);
|
|
527
|
-
}
|
|
528
|
-
} catch {
|
|
529
|
-
// Ignore fallback errors.
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
588
|
/** Discover SYSTEM.md file if no CLI system prompt was provided */
|
|
534
589
|
function discoverSystemPromptFile(): string | undefined {
|
|
535
590
|
// Check project-local first (.omp/SYSTEM.md, .pi/SYSTEM.md legacy)
|
|
@@ -586,9 +641,7 @@ async function buildSessionOptions(
|
|
|
586
641
|
// Model from CLI
|
|
587
642
|
// - supports --provider <name> --model <pattern>
|
|
588
643
|
// - supports --model <provider>/<pattern>
|
|
589
|
-
const modelMatchPreferences =
|
|
590
|
-
usageOrder: activeSettings.getStorage()?.getModelUsageOrder(),
|
|
591
|
-
};
|
|
644
|
+
const modelMatchPreferences = getModelMatchPreferences(activeSettings);
|
|
592
645
|
if (parsed.model) {
|
|
593
646
|
const resolved = resolveCliModel({
|
|
594
647
|
cliProvider: parsed.provider,
|
|
@@ -745,7 +798,7 @@ export async function runRootCommand(
|
|
|
745
798
|
await logger.time("initTheme:initial", initTheme);
|
|
746
799
|
|
|
747
800
|
const parsedArgs = parsed;
|
|
748
|
-
await logger.time("
|
|
801
|
+
await logger.time("applyStartupCwd", applyStartupCwd, parsedArgs);
|
|
749
802
|
|
|
750
803
|
const notifs: (InteractiveModeNotify | null)[] = [];
|
|
751
804
|
|
|
@@ -880,9 +933,7 @@ export async function runRootCommand(
|
|
|
880
933
|
|
|
881
934
|
let scopedModels: ScopedModel[] = [];
|
|
882
935
|
const modelPatterns = parsedArgs.models ?? settingsInstance.get("enabledModels");
|
|
883
|
-
const modelMatchPreferences =
|
|
884
|
-
usageOrder: settingsInstance.getStorage()?.getModelUsageOrder(),
|
|
885
|
-
};
|
|
936
|
+
const modelMatchPreferences = getModelMatchPreferences(settingsInstance);
|
|
886
937
|
if (modelPatterns && modelPatterns.length > 0) {
|
|
887
938
|
scopedModels = await logger.time(
|
|
888
939
|
"resolveModelScope",
|
|
@@ -893,14 +944,29 @@ export async function runRootCommand(
|
|
|
893
944
|
);
|
|
894
945
|
}
|
|
895
946
|
|
|
896
|
-
// Create session manager based on CLI flags
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
947
|
+
// Create session manager based on CLI flags. SessionResolutionError signals a
|
|
948
|
+
// user-facing failure (unknown --resume/--fork id, non-interactive fork
|
|
949
|
+
// prompt, --fork with --no-session): print + exit cleanly instead of letting
|
|
950
|
+
// it surface as `[Uncaught Exception]` (see issue #2084).
|
|
951
|
+
let sessionManager: SessionManager | undefined;
|
|
952
|
+
try {
|
|
953
|
+
sessionManager = await logger.time(
|
|
954
|
+
"createSessionManager",
|
|
955
|
+
createSessionManager,
|
|
956
|
+
parsedArgs,
|
|
957
|
+
cwd,
|
|
958
|
+
settingsInstance,
|
|
959
|
+
);
|
|
960
|
+
} catch (error: unknown) {
|
|
961
|
+
if (error instanceof SessionResolutionError) {
|
|
962
|
+
process.stderr.write(`${chalk.red(`Error: ${error.message}`)}\n`);
|
|
963
|
+
if (error.hint) {
|
|
964
|
+
process.stderr.write(`${chalk.dim(error.hint)}\n`);
|
|
965
|
+
}
|
|
966
|
+
process.exit(1);
|
|
967
|
+
}
|
|
968
|
+
throw error;
|
|
969
|
+
}
|
|
904
970
|
|
|
905
971
|
// User declined the cross-project fork prompt — exit cleanly with a friendly
|
|
906
972
|
// message rather than letting the decline bubble up as an uncaught exception
|
package/src/mcp/tool-bridge.ts
CHANGED
|
@@ -220,6 +220,7 @@ export class MCPTool implements CustomTool<TSchema, MCPToolDetails> {
|
|
|
220
220
|
readonly mcpToolName: string;
|
|
221
221
|
/** Server name */
|
|
222
222
|
readonly mcpServerName: string;
|
|
223
|
+
readonly approval = "write" as const;
|
|
223
224
|
/** Render completed MCP calls with the result header replacing the pending call header. */
|
|
224
225
|
readonly mergeCallAndResult = true;
|
|
225
226
|
|
|
@@ -305,6 +306,7 @@ export class DeferredMCPTool implements CustomTool<TSchema, MCPToolDetails> {
|
|
|
305
306
|
readonly mcpToolName: string;
|
|
306
307
|
/** Server name */
|
|
307
308
|
readonly mcpServerName: string;
|
|
309
|
+
readonly approval = "write" as const;
|
|
308
310
|
/** Render completed MCP calls with the result header replacing the pending call header. */
|
|
309
311
|
readonly mergeCallAndResult = true;
|
|
310
312
|
|
package/src/memories/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { type ApiKey, clampThinkingLevelForModel, completeSimple, Effort, type M
|
|
|
7
7
|
import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient, prompt } from "@oh-my-pi/pi-utils";
|
|
8
8
|
|
|
9
9
|
import type { ModelRegistry } from "../config/model-registry";
|
|
10
|
-
import { resolveModelRoleValue } from "../config/model-resolver";
|
|
10
|
+
import { getModelMatchPreferences, resolveModelRoleValue } from "../config/model-resolver";
|
|
11
11
|
import type { Settings } from "../config/settings";
|
|
12
12
|
import consolidationTemplate from "../prompts/memories/consolidation.md" with { type: "text" };
|
|
13
13
|
import readPathTemplate from "../prompts/memories/read-path.md" with { type: "text" };
|
|
@@ -1088,7 +1088,7 @@ async function resolveMemoryModel(options: {
|
|
|
1088
1088
|
if (requestedModel) {
|
|
1089
1089
|
const resolved = resolveModelRoleValue(requestedModel, modelRegistry.getAll(), {
|
|
1090
1090
|
settings: session.settings,
|
|
1091
|
-
matchPreferences:
|
|
1091
|
+
matchPreferences: getModelMatchPreferences(session.settings),
|
|
1092
1092
|
modelRegistry,
|
|
1093
1093
|
});
|
|
1094
1094
|
if (resolved.model) return resolved.model;
|
|
@@ -4,7 +4,7 @@ import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
|
4
4
|
import { settings } from "../../config/settings";
|
|
5
5
|
import type { AssistantThinkingRenderer } from "../../extensibility/extensions/types";
|
|
6
6
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveAbortLabel, shouldRenderAbortReason } from "../../session/messages";
|
|
8
8
|
import { resolveImageOptions } from "../../tools/render-utils";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -74,18 +74,6 @@ export class AssistantMessageComponent extends Container {
|
|
|
74
74
|
return this.#transcriptBlockFinalized;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
/**
|
|
78
|
-
* Assistant text/thinking streams in append-only: earlier rendered rows never
|
|
79
|
-
* re-layout, new content only grows the block at the bottom. The transcript
|
|
80
|
-
* reports this so the renderer may commit scrolled-off head rows of a long
|
|
81
|
-
* streamed reply to native scrollback instead of dropping them (see
|
|
82
|
-
* `NativeScrollbackLiveRegion#getNativeScrollbackCommitSafeEnd`). Volatile
|
|
83
|
-
* blocks (tool previews that collapse) intentionally do not implement this.
|
|
84
|
-
*/
|
|
85
|
-
isTranscriptBlockAppendOnly(): boolean {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
77
|
markTranscriptBlockFinalized(): void {
|
|
90
78
|
this.#transcriptBlockFinalized = true;
|
|
91
79
|
}
|
|
@@ -252,7 +240,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
252
240
|
// But only if there are no tool calls (tool execution components will show the error)
|
|
253
241
|
const hasToolCalls = message.content.some(c => c.type === "toolCall");
|
|
254
242
|
if (!hasToolCalls) {
|
|
255
|
-
if (message.stopReason === "aborted" &&
|
|
243
|
+
if (message.stopReason === "aborted" && shouldRenderAbortReason(message.errorMessage)) {
|
|
256
244
|
const abortMessage = resolveAbortLabel(message.errorMessage);
|
|
257
245
|
if (hasVisibleContent) {
|
|
258
246
|
this.#contentContainer.addChild(new Spacer(1));
|
|
@@ -268,7 +256,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
268
256
|
}
|
|
269
257
|
if (
|
|
270
258
|
message.errorMessage &&
|
|
271
|
-
|
|
259
|
+
shouldRenderAbortReason(message.errorMessage) &&
|
|
272
260
|
message.stopReason !== "aborted" &&
|
|
273
261
|
message.stopReason !== "error"
|
|
274
262
|
) {
|