@oh-my-pi/pi-coding-agent 17.1.8 → 17.2.1
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 +110 -0
- package/dist/{CHANGELOG-vt8ene9g.md → CHANGELOG-dj46zzrm.md} +110 -0
- package/dist/cli.js +10009 -9654
- package/dist/{template-dys3vk5b.js → template-8vdv6xb4.js} +1 -1
- package/dist/types/advisor/runtime.d.ts +8 -2
- package/dist/types/advisor/transcript-recorder.d.ts +15 -0
- package/dist/types/auto-thinking/classifier.d.ts +9 -2
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/session-picker.d.ts +14 -7
- package/dist/types/collab/guest.d.ts +21 -2
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/model-discovery.d.ts +12 -0
- package/dist/types/config/settings-schema.d.ts +73 -8
- package/dist/types/cursor-bridge-tools.d.ts +54 -0
- package/dist/types/cursor.d.ts +193 -8
- package/dist/types/edit/edit-clipboard.d.ts +19 -0
- package/dist/types/edit/hashline/diff.d.ts +9 -1
- package/dist/types/edit/index.d.ts +8 -1
- package/dist/types/edit/streaming.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +19 -2
- package/dist/types/extensibility/extensions/types.d.ts +25 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +25 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/local-protocol.d.ts +3 -2
- package/dist/types/internal-urls/security-protocol.d.ts +15 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/launch/terminal-output-worker-client.d.ts +5 -0
- package/dist/types/launch/terminal-output-worker-protocol.d.ts +14 -0
- package/dist/types/launch/terminal-output-worker.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +33 -4
- package/dist/types/modes/components/codex-reset-fireworks.d.ts +51 -0
- package/dist/types/modes/components/read-tool-group.d.ts +12 -0
- package/dist/types/modes/components/session-selector.d.ts +6 -0
- package/dist/types/modes/components/status-line/component.d.ts +23 -0
- package/dist/types/modes/components/tool-execution.d.ts +16 -7
- package/dist/types/modes/components/usage-row.d.ts +2 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +26 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +4 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +16 -0
- package/dist/types/modes/theme/theme.d.ts +12 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/sdk.d.ts +18 -0
- package/dist/types/security/auth.d.ts +30 -0
- package/dist/types/security/cloud.d.ts +79 -0
- package/dist/types/security/comparison.d.ts +49 -0
- package/dist/types/security/contracts/ids.d.ts +15 -0
- package/dist/types/security/contracts/index.d.ts +4 -0
- package/dist/types/security/contracts/schemas.d.ts +660 -0
- package/dist/types/security/contracts/types.d.ts +234 -0
- package/dist/types/security/contracts/validation.d.ts +5 -0
- package/dist/types/security/coordinator.d.ts +100 -0
- package/dist/types/security/importers/codex-security.d.ts +7 -0
- package/dist/types/security/importers/index.d.ts +2 -0
- package/dist/types/security/importers/sarif.d.ts +9 -0
- package/dist/types/security/index.d.ts +13 -0
- package/dist/types/security/preflight.d.ts +61 -0
- package/dist/types/security/provenance.d.ts +24 -0
- package/dist/types/security/publication.d.ts +78 -0
- package/dist/types/security/remediation.d.ts +27 -0
- package/dist/types/security/resource-output.d.ts +8 -0
- package/dist/types/security/sarif.d.ts +2 -0
- package/dist/types/security/store.d.ts +40 -0
- package/dist/types/session/agent-session-events.d.ts +2 -0
- package/dist/types/session/agent-session-types.d.ts +36 -1
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/claude-session-store.d.ts +13 -0
- package/dist/types/session/codex-session-store.d.ts +14 -0
- package/dist/types/session/credential-pin.d.ts +58 -0
- package/dist/types/session/foreign-session-import.d.ts +15 -0
- package/dist/types/session/foreign-session-jsonl.d.ts +9 -0
- package/dist/types/session/foreign-session-store.d.ts +23 -0
- package/dist/types/session/session-advisors.d.ts +47 -2
- package/dist/types/session/session-entries.d.ts +19 -1
- package/dist/types/session/session-handoff.d.ts +4 -1
- package/dist/types/session/session-loader.d.ts +2 -0
- package/dist/types/session/session-maintenance.d.ts +1 -1
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-tools.d.ts +25 -1
- package/dist/types/session/turn-recovery.d.ts +10 -4
- package/dist/types/slash-commands/helpers/security.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/executor.d.ts +14 -2
- package/dist/types/task/render.d.ts +7 -0
- package/dist/types/thinking.d.ts +18 -6
- package/dist/types/tools/acp-bridge.d.ts +28 -5
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +17 -1
- package/dist/types/tools/hub/launch.d.ts +5 -1
- package/dist/types/tools/index.d.ts +10 -2
- package/dist/types/tools/path-utils.d.ts +19 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/tools/security-scan.d.ts +96 -0
- package/dist/types/tools/xdev.d.ts +2 -1
- package/dist/types/utils/changelog.d.ts +19 -0
- package/dist/types/utils/git.d.ts +10 -0
- package/dist/types/utils/jj.d.ts +11 -4
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/scripts/security-compare.ts +40 -0
- package/src/advisor/runtime.ts +138 -12
- package/src/advisor/transcript-recorder.ts +56 -0
- package/src/auto-thinking/classifier.ts +48 -10
- package/src/cli/args.ts +6 -0
- package/src/cli/flag-tables.ts +2 -0
- package/src/cli/gallery-fixtures/fs.ts +25 -0
- package/src/cli/session-picker.ts +34 -17
- package/src/cli/ttsr-cli.ts +19 -1
- package/src/cli.ts +9 -0
- package/src/collab/guest.ts +37 -4
- package/src/collab/host.ts +1 -0
- package/src/commands/launch.ts +6 -0
- package/src/config/model-discovery.ts +32 -5
- package/src/config/model-registry.ts +107 -25
- package/src/config/settings-schema.ts +77 -6
- package/src/config/settings.ts +25 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +463 -12
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/hashline/diff.ts +49 -10
- package/src/edit/hashline/execute.ts +38 -6
- package/src/edit/hashline/filesystem.ts +27 -3
- package/src/edit/index.ts +12 -2
- package/src/edit/renderer.ts +4 -4
- package/src/edit/streaming.ts +17 -2
- package/src/eval/py/prelude.py +7 -3
- package/src/export/html/template.js +1 -1
- package/src/extensibility/extensions/runner.ts +70 -0
- package/src/extensibility/extensions/types.ts +30 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +65 -13
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/local-protocol.ts +3 -2
- package/src/internal-urls/mcp-protocol.ts +4 -1
- package/src/internal-urls/router.ts +4 -1
- package/src/internal-urls/security-protocol.ts +261 -0
- package/src/internal-urls/types.ts +1 -1
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/lsp/client.ts +18 -3
- package/src/lsp/index.ts +7 -0
- package/src/lsp/render.ts +1 -1
- package/src/main.ts +127 -56
- package/src/mcp/manager.ts +120 -18
- package/src/modes/acp/acp-agent.ts +15 -9
- package/src/modes/components/chat-transcript-builder.ts +30 -11
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/read-tool-group.ts +159 -17
- package/src/modes/components/session-selector.ts +19 -3
- package/src/modes/components/status-line/component.ts +381 -54
- package/src/modes/components/tool-execution.ts +78 -31
- package/src/modes/components/usage-row.ts +10 -5
- package/src/modes/controllers/event-controller.ts +26 -4
- package/src/modes/controllers/mcp-command-controller.ts +68 -3
- package/src/modes/controllers/selector-controller.ts +94 -36
- package/src/modes/controllers/tan-command-controller.ts +14 -0
- package/src/modes/interactive-mode.ts +31 -17
- package/src/modes/rpc/host-uris.ts +6 -0
- package/src/modes/rpc/rpc-client.ts +18 -0
- package/src/modes/rpc/rpc-mode.ts +16 -1
- package/src/modes/rpc/rpc-types.ts +11 -0
- package/src/modes/theme/theme.ts +151 -6
- package/src/modes/types.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +38 -21
- package/src/prompts/agents/security-reviewer.md +75 -0
- package/src/prompts/security/scan-coordinator.md +7 -0
- package/src/prompts/security/scan-request.md +21 -0
- package/src/prompts/security/validate-request.md +8 -0
- package/src/prompts/system/auto-thinking-difficulty.md +4 -2
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +6 -0
- package/src/prompts/system/xdev-mount-notice.md +1 -1
- package/src/prompts/tools/bash.md +15 -17
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/glob.md +7 -6
- package/src/prompts/tools/grep.md +7 -6
- package/src/prompts/tools/security-publish.md +1 -0
- package/src/prompts/tools/security-scan.md +1 -0
- package/src/sdk.ts +212 -21
- package/src/security/auth.ts +98 -0
- package/src/security/cloud.ts +686 -0
- package/src/security/comparison.ts +255 -0
- package/src/security/contracts/ids.ts +111 -0
- package/src/security/contracts/index.ts +4 -0
- package/src/security/contracts/schemas.ts +201 -0
- package/src/security/contracts/types.ts +254 -0
- package/src/security/contracts/validation.ts +65 -0
- package/src/security/coordinator.ts +708 -0
- package/src/security/importers/codex-security.ts +387 -0
- package/src/security/importers/index.ts +2 -0
- package/src/security/importers/sarif.ts +357 -0
- package/src/security/index.ts +13 -0
- package/src/security/preflight.ts +405 -0
- package/src/security/provenance.ts +106 -0
- package/src/security/publication.ts +326 -0
- package/src/security/remediation.ts +93 -0
- package/src/security/resource-output.ts +50 -0
- package/src/security/sarif.ts +78 -0
- package/src/security/store.ts +430 -0
- package/src/session/agent-session-events.ts +1 -0
- package/src/session/agent-session-types.ts +40 -1
- package/src/session/agent-session.ts +302 -175
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/model-controls.ts +11 -3
- package/src/session/session-advisors.ts +140 -16
- package/src/session/session-entries.ts +21 -1
- package/src/session/session-handoff.ts +20 -3
- package/src/session/session-loader.ts +25 -9
- package/src/session/session-maintenance.ts +59 -11
- package/src/session/session-manager.ts +61 -0
- package/src/session/session-tools.ts +107 -5
- package/src/session/settings-stream-fn.ts +1 -0
- package/src/session/turn-recovery.ts +97 -51
- package/src/slash-commands/builtin-registry.ts +152 -3
- package/src/slash-commands/helpers/security.ts +451 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/agents.ts +2 -0
- package/src/task/executor.ts +20 -4
- package/src/task/render.ts +20 -4
- package/src/task/structured-subagent.ts +6 -4
- package/src/thinking.ts +29 -10
- package/src/tools/acp-bridge.ts +52 -8
- package/src/tools/browser.ts +5 -0
- package/src/tools/builtin-names.ts +1 -0
- package/src/tools/checkpoint.ts +0 -13
- package/src/tools/grep.ts +64 -8
- package/src/tools/hub/launch.ts +10 -15
- package/src/tools/index.ts +37 -5
- package/src/tools/path-utils.ts +91 -0
- package/src/tools/read.ts +3 -0
- package/src/tools/security-scan.ts +287 -0
- package/src/tools/write.ts +9 -3
- package/src/tools/xdev.ts +39 -14
- package/src/utils/changelog.ts +144 -4
- package/src/utils/git.ts +61 -14
- package/src/utils/jj.ts +19 -13
- package/src/web/search/providers/codex.ts +34 -0
- package/src/web/search/types.ts +1 -1
|
@@ -6,16 +6,26 @@ import type { SessionInfo } from "../session/session-listing";
|
|
|
6
6
|
import { SessionManager } from "../session/session-manager";
|
|
7
7
|
import { FileSessionStorage } from "../session/session-storage";
|
|
8
8
|
|
|
9
|
+
/** Presentation and capability controls for the standalone session picker. */
|
|
10
|
+
export interface SessionPickerOptions {
|
|
11
|
+
allSessions?: SessionInfo[];
|
|
12
|
+
title?: string;
|
|
13
|
+
scopeLabel?: string | false;
|
|
14
|
+
showCwd?: boolean;
|
|
15
|
+
allowDelete?: boolean;
|
|
16
|
+
allowGlobalScope?: boolean;
|
|
17
|
+
historySearch?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
/**
|
|
10
21
|
* Show the TUI session selector and return the selected session, or null if
|
|
11
|
-
* cancelled.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* is loaded lazily via `SessionManager.listAll`.
|
|
22
|
+
* cancelled. The default OMP picker supports deletion, transcript-history
|
|
23
|
+
* search, and an all-projects scope; foreign import pickers disable those
|
|
24
|
+
* source-owned capabilities.
|
|
15
25
|
*/
|
|
16
26
|
export async function selectSession(
|
|
17
27
|
sessions: SessionInfo[],
|
|
18
|
-
options
|
|
28
|
+
options: SessionPickerOptions = {},
|
|
19
29
|
): Promise<SessionInfo | null> {
|
|
20
30
|
const { promise, resolve } = Promise.withResolvers<SessionInfo | null>();
|
|
21
31
|
const ui = new TUI(new ProcessTerminal());
|
|
@@ -26,11 +36,13 @@ export async function selectSession(
|
|
|
26
36
|
// session-list prefix never sees. Best-effort: a missing/locked history.db
|
|
27
37
|
// must not break the picker.
|
|
28
38
|
let historyMatcher: ((query: string) => string[]) | undefined;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
if (options.historySearch !== false) {
|
|
40
|
+
try {
|
|
41
|
+
const history = HistoryStorage.open();
|
|
42
|
+
historyMatcher = (query: string) => history.matchingSessionIds(query);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.warn("History storage unavailable for session ranking", { error: String(error) });
|
|
45
|
+
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
const showSelector = () => {
|
|
@@ -58,16 +70,21 @@ export async function selectSession(
|
|
|
58
70
|
}
|
|
59
71
|
},
|
|
60
72
|
{
|
|
61
|
-
onDelete:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
onDelete:
|
|
74
|
+
options.allowDelete === false
|
|
75
|
+
? undefined
|
|
76
|
+
: async (session: SessionInfo) => {
|
|
77
|
+
await storage.deleteSessionWithArtifacts(session.path);
|
|
78
|
+
return true;
|
|
79
|
+
},
|
|
66
80
|
historyMatcher,
|
|
67
|
-
loadAllSessions: () => SessionManager.listAll(storage),
|
|
68
|
-
allSessions: options
|
|
81
|
+
loadAllSessions: options.allowGlobalScope === false ? undefined : () => SessionManager.listAll(storage),
|
|
82
|
+
allSessions: options.allSessions,
|
|
69
83
|
getTerminalRows: () => ui.terminal.rows,
|
|
70
84
|
fillHeight: true,
|
|
85
|
+
title: options.title,
|
|
86
|
+
scopeLabel: options.scopeLabel,
|
|
87
|
+
showCwd: options.showCwd,
|
|
71
88
|
},
|
|
72
89
|
);
|
|
73
90
|
return selector;
|
package/src/cli/ttsr-cli.ts
CHANGED
|
@@ -94,12 +94,18 @@ interface TestReport {
|
|
|
94
94
|
evaluated: number;
|
|
95
95
|
triggered: RuleMatchDetail[];
|
|
96
96
|
notTriggered: RuleMatchDetail[];
|
|
97
|
+
/**
|
|
98
|
+
* Set when a file path was supplied but the match source was inferred as
|
|
99
|
+
* `text` (extension absent from {@link SOURCE_FILE_EXT}), so callers can
|
|
100
|
+
* surface why a source file was evaluated against a prose context.
|
|
101
|
+
*/
|
|
102
|
+
inferenceNote?: string;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
const STDIN_MARKER = "-";
|
|
100
106
|
/** Extensions treated as source files for default tool-context inference. */
|
|
101
107
|
const SOURCE_FILE_EXT =
|
|
102
|
-
/^\.(ts|tsx|js|jsx|mjs|cjs|rs|py|go|java|kt|swift|c|cc|cpp|h|hpp|rb|php|lua|css|scss|html|json|ya?ml|toml|md|mdc)$/i;
|
|
108
|
+
/^\.(ts|tsx|js|jsx|mjs|cjs|rs|py|go|java|kt|swift|c|cc|cpp|h|hpp|rb|php|lua|css|scss|html|json|ya?ml|toml|md|mdc|cs|razor|cshtml|fs|fsx|vb|sh|bash|sql|zig|dart|scala|ex|exs|proto|tf)$/i;
|
|
103
109
|
|
|
104
110
|
const BINARY_PROBE_BYTES = 8192;
|
|
105
111
|
const DEFAULT_MAX_SCAN_BYTES = 5 * 1024 * 1024;
|
|
@@ -342,6 +348,14 @@ async function runTest(args: TtsrTestArgs, json: boolean, cwd: string): Promise<
|
|
|
342
348
|
args.source ?? (filePath && SOURCE_FILE_EXT.test(path.extname(filePath)) ? "tool" : "text");
|
|
343
349
|
const tool = args.tool ?? (source === "tool" ? "edit" : undefined);
|
|
344
350
|
|
|
351
|
+
// A supplied source file whose extension is unknown falls through to the
|
|
352
|
+
// text (prose) context, where tool-scoped rules can never match. Surface
|
|
353
|
+
// that so a false negative reads as a context mismatch, not a bad regex.
|
|
354
|
+
const inferenceNote =
|
|
355
|
+
!args.source && filePath && source === "text"
|
|
356
|
+
? `inferred --source text from '${path.extname(filePath) || filePath}' (not in the source-file extension set); pass --source tool --tool edit to evaluate tool-scoped rules`
|
|
357
|
+
: undefined;
|
|
358
|
+
|
|
345
359
|
const context: TtsrMatchContext = {
|
|
346
360
|
source,
|
|
347
361
|
toolName: tool,
|
|
@@ -373,6 +387,7 @@ async function runTest(args: TtsrTestArgs, json: boolean, cwd: string): Promise<
|
|
|
373
387
|
evaluated: rules.length,
|
|
374
388
|
triggered,
|
|
375
389
|
notTriggered,
|
|
390
|
+
inferenceNote,
|
|
376
391
|
};
|
|
377
392
|
|
|
378
393
|
if (json) {
|
|
@@ -390,6 +405,9 @@ function renderTestReport(report: TestReport, verbose: boolean, isolated: boolea
|
|
|
390
405
|
`${chalk.bold("TTSR test")} — source=${chalk.cyan(ctxLabel)}${pathLabel} snippet=${chalk.dim(`${report.snippetBytes}b`)}\n`,
|
|
391
406
|
);
|
|
392
407
|
process.stdout.write(`${chalk.dim(` "${report.snippetPreview}"`)}\n\n`);
|
|
408
|
+
if (report.inferenceNote) {
|
|
409
|
+
process.stdout.write(`${chalk.yellow(`note: ${report.inferenceNote}`)}\n\n`);
|
|
410
|
+
}
|
|
393
411
|
|
|
394
412
|
if (report.triggered.length === 0) {
|
|
395
413
|
process.stdout.write(`${chalk.red("No rules triggered.")} (evaluated ${report.evaluated})\n`);
|
package/src/cli.ts
CHANGED
|
@@ -32,6 +32,7 @@ import { extractProfileFlags } from "./cli/profile-bootstrap";
|
|
|
32
32
|
import { startJsEvalProcess } from "./eval/js/process-entry";
|
|
33
33
|
import type { WorkerInbound as JsWorkerInbound, WorkerOutbound as JsWorkerOutbound } from "./eval/js/worker-protocol";
|
|
34
34
|
import { DAEMON_BROKER_WORKER_ARG } from "./launch/protocol";
|
|
35
|
+
import { TERMINAL_OUTPUT_WORKER_ARG } from "./launch/terminal-output-worker-protocol";
|
|
35
36
|
import { COMPUTER_WORKER_ARG } from "./tools/computer/protocol";
|
|
36
37
|
import { smokeTestComputerWorker } from "./tools/computer/supervisor";
|
|
37
38
|
import { startComputerWorker } from "./tools/computer/worker-entry";
|
|
@@ -88,6 +89,7 @@ async function runSmokeTest(): Promise<void> {
|
|
|
88
89
|
const { smokeTestJsEvalWorker } = await import("./eval/js/context-manager");
|
|
89
90
|
// Other smoke dependencies stay lazy so normal CLI startup does not load their worker clients.
|
|
90
91
|
const { smokeTestDaemonBroker } = await import("./launch/client");
|
|
92
|
+
const { smokeTestTerminalOutputWorker } = await import("./launch/terminal-output-worker-client");
|
|
91
93
|
await smokeTestSyncWorker();
|
|
92
94
|
|
|
93
95
|
const statsServer = await startServer(0);
|
|
@@ -109,6 +111,7 @@ async function runSmokeTest(): Promise<void> {
|
|
|
109
111
|
await smokeTestTtsWorker();
|
|
110
112
|
await smokeTestMnemopiEmbedWorker();
|
|
111
113
|
await smokeTestDaemonBroker();
|
|
114
|
+
await smokeTestTerminalOutputWorker();
|
|
112
115
|
process.stdout.write("smoke-test: ok\n");
|
|
113
116
|
}
|
|
114
117
|
|
|
@@ -195,6 +198,12 @@ async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
|
|
|
195
198
|
await runIpcSubprocessWorker(startMnemopiEmbedWorker);
|
|
196
199
|
return true;
|
|
197
200
|
}
|
|
201
|
+
if (arg === TERMINAL_OUTPUT_WORKER_ARG) {
|
|
202
|
+
if (parentPort) installWorkerInbox(parentPort);
|
|
203
|
+
// This selector is the isolation boundary; a static import would evaluate xterm in normal CLI startup.
|
|
204
|
+
await import("./launch/terminal-output-worker");
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
198
207
|
if (arg === DAEMON_BROKER_WORKER_ARG) {
|
|
199
208
|
// Worker selectors must dispatch before the normal command graph loads.
|
|
200
209
|
const { startDaemonBrokerFromEnvironment } = await import("./launch/broker");
|
package/src/collab/guest.ts
CHANGED
|
@@ -85,14 +85,15 @@ interface PendingSnapshot {
|
|
|
85
85
|
/** Minimal context surface the idle-state reconciler mutates. */
|
|
86
86
|
export interface GuestIdleReconcilerCtx {
|
|
87
87
|
statusLine: { markActivityEnd: () => void };
|
|
88
|
+
statusContainer: Pick<InteractiveModeContext["statusContainer"], "disposeChildren">;
|
|
88
89
|
loadingAnimation: { stop: () => void } | undefined;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
92
93
|
* Close the guest UI state held open by an earlier `agent_start` whose
|
|
93
94
|
* matching `agent_end` never reached us — most often because a reconnect
|
|
94
|
-
* dropped the event mid-stream.
|
|
95
|
-
* `state
|
|
95
|
+
* dropped the event mid-stream. Reached via {@link reconcileGuestSnapshotHostState}
|
|
96
|
+
* (the live `state`-frame and welcome/resync reconciler) when the host reports `isStreaming === false`:
|
|
96
97
|
* folds the in-flight active-time window into the per-session meter (so
|
|
97
98
|
* `time_spent` stops ticking) and stops the `Working…` loader if one is
|
|
98
99
|
* still animating. No-op when the host is still streaming.
|
|
@@ -106,17 +107,49 @@ export function reconcileGuestIdleHostState(ctx: GuestIdleReconcilerCtx, isStrea
|
|
|
106
107
|
if (ctx.loadingAnimation) {
|
|
107
108
|
ctx.loadingAnimation.stop();
|
|
108
109
|
ctx.loadingAnimation = undefined;
|
|
110
|
+
ctx.statusContainer.disposeChildren();
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
/** Reconcile a welcome/resync snapshot's host activity state into the guest meter. */
|
|
113
115
|
export interface GuestSnapshotActivityReconcilerCtx extends GuestIdleReconcilerCtx {
|
|
114
116
|
statusLine: GuestIdleReconcilerCtx["statusLine"] & { markActivityStart: () => void };
|
|
117
|
+
/**
|
|
118
|
+
* Start (or re-attach) the live "Working…" loader. Mirrors
|
|
119
|
+
* `InteractiveModeContext.ensureLoadingAnimation`, which is what
|
|
120
|
+
* `EventController` calls on `agent_start`. Required so a guest that
|
|
121
|
+
* missed an earlier `agent_start` (a reconnect dropped it mid-stream)
|
|
122
|
+
* starts its spinner when the host later reports it is streaming.
|
|
123
|
+
*/
|
|
124
|
+
ensureLoadingAnimation: InteractiveModeContext["ensureLoadingAnimation"];
|
|
125
|
+
autoCompactionLoader: InteractiveModeContext["autoCompactionLoader"];
|
|
126
|
+
retryLoader: InteractiveModeContext["retryLoader"];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Status-area state which cannot outlive removal of its child components. */
|
|
130
|
+
export interface GuestTransientStatusCtx {
|
|
131
|
+
statusContainer: Pick<InteractiveModeContext["statusContainer"], "clear">;
|
|
132
|
+
autoCompactionLoader: InteractiveModeContext["autoCompactionLoader"];
|
|
133
|
+
retryLoader: InteractiveModeContext["retryLoader"];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Stop and forget status-area loaders before detaching their components. */
|
|
137
|
+
export function clearGuestTransientStatus(ctx: GuestTransientStatusCtx): void {
|
|
138
|
+
if (ctx.autoCompactionLoader) {
|
|
139
|
+
ctx.autoCompactionLoader.stop();
|
|
140
|
+
ctx.autoCompactionLoader = undefined;
|
|
141
|
+
}
|
|
142
|
+
if (ctx.retryLoader) {
|
|
143
|
+
ctx.retryLoader.stop();
|
|
144
|
+
ctx.retryLoader = undefined;
|
|
145
|
+
}
|
|
146
|
+
ctx.statusContainer.clear();
|
|
115
147
|
}
|
|
116
148
|
|
|
117
149
|
export function reconcileGuestSnapshotHostState(ctx: GuestSnapshotActivityReconcilerCtx, isStreaming: boolean): void {
|
|
118
150
|
if (isStreaming) {
|
|
119
151
|
ctx.statusLine.markActivityStart();
|
|
152
|
+
if (!ctx.autoCompactionLoader && !ctx.retryLoader) ctx.ensureLoadingAnimation();
|
|
120
153
|
return;
|
|
121
154
|
}
|
|
122
155
|
reconcileGuestIdleHostState(ctx, false);
|
|
@@ -482,7 +515,7 @@ export class CollabGuestLink {
|
|
|
482
515
|
this.#applyHostState(frame.state);
|
|
483
516
|
setSessionTerminalTitle(frame.state.sessionName, frame.state.cwd);
|
|
484
517
|
this.#updateStatusSegment();
|
|
485
|
-
|
|
518
|
+
reconcileGuestSnapshotHostState(this.#ctx, frame.state.isStreaming);
|
|
486
519
|
this.#ctx.statusLine.invalidate();
|
|
487
520
|
this.#ctx.ui.requestRender();
|
|
488
521
|
break;
|
|
@@ -681,7 +714,7 @@ export class CollabGuestLink {
|
|
|
681
714
|
|
|
682
715
|
#clearTransientUi(): void {
|
|
683
716
|
this.#clearUiRequests();
|
|
684
|
-
this.#ctx
|
|
717
|
+
clearGuestTransientStatus(this.#ctx);
|
|
685
718
|
this.#ctx.pendingMessagesContainer.clear();
|
|
686
719
|
this.#ctx.compactionQueuedMessages = [];
|
|
687
720
|
this.#ctx.streamingComponent = undefined;
|
package/src/collab/host.ts
CHANGED
package/src/commands/launch.ts
CHANGED
|
@@ -99,6 +99,12 @@ export default class Index extends Command {
|
|
|
99
99
|
char: "r",
|
|
100
100
|
description: "Resume a session (by ID prefix, path, or picker if omitted)",
|
|
101
101
|
}),
|
|
102
|
+
"from-claude": Flags.boolean({
|
|
103
|
+
description: "Import a Claude Code session into OMP",
|
|
104
|
+
}),
|
|
105
|
+
"from-codex": Flags.boolean({
|
|
106
|
+
description: "Import a Codex session into OMP",
|
|
107
|
+
}),
|
|
102
108
|
"session-dir": Flags.string({
|
|
103
109
|
description: "Directory for session storage and lookup",
|
|
104
110
|
}),
|
|
@@ -62,6 +62,33 @@ async function withTimeoutSignal<T>(timeoutMs: number, fn: (signal: AbortSignal)
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/** Generous discovery budget for a non-loopback (remote / LAN) inference host. */
|
|
66
|
+
const REMOTE_DISCOVERY_TIMEOUT_MS = 10_000;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Pick a discovery-probe timeout for a local-engine base URL.
|
|
70
|
+
*
|
|
71
|
+
* The implicit `127.0.0.1` default probe keeps a tight `loopbackMs` cap so a
|
|
72
|
+
* busy or foreign service on the default port never stalls startup. But that
|
|
73
|
+
* cap is far too short for a host reached over the network: a user who points
|
|
74
|
+
* `LLAMA_CPP_BASE_URL` / `OLLAMA_BASE_URL` / `OLLAMA_HOST` at a remote or LAN
|
|
75
|
+
* machine has real round-trip latency, and a 250ms cap made that server look
|
|
76
|
+
* empty (issue #7087). Anything that is not strictly loopback therefore gets
|
|
77
|
+
* {@link REMOTE_DISCOVERY_TIMEOUT_MS}.
|
|
78
|
+
*/
|
|
79
|
+
export function discoveryProbeTimeoutMs(baseUrl: string, loopbackMs: number): number {
|
|
80
|
+
let hostname: string;
|
|
81
|
+
try {
|
|
82
|
+
hostname = new URL(baseUrl).hostname;
|
|
83
|
+
} catch {
|
|
84
|
+
return loopbackMs;
|
|
85
|
+
}
|
|
86
|
+
hostname = hostname.replace(/^\[/, "").replace(/\]$/, "");
|
|
87
|
+
const isLoopback =
|
|
88
|
+
hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::1" || /^127\./.test(hostname);
|
|
89
|
+
return isLoopback ? loopbackMs : REMOTE_DISCOVERY_TIMEOUT_MS;
|
|
90
|
+
}
|
|
91
|
+
|
|
65
92
|
const DEFAULT_OLLAMA_BASE_URL = "http://127.0.0.1:11434";
|
|
66
93
|
const OLLAMA_HOST_DEFAULT_PORT = "11434";
|
|
67
94
|
|
|
@@ -391,7 +418,7 @@ async function discoverOllamaModelMetadata(
|
|
|
391
418
|
): Promise<OllamaDiscoveredModelMetadata | null> {
|
|
392
419
|
const showUrl = `${endpoint}/api/show`;
|
|
393
420
|
try {
|
|
394
|
-
const payload = await withTimeoutSignal(150, async signal => {
|
|
421
|
+
const payload = await withTimeoutSignal(discoveryProbeTimeoutMs(endpoint, 150), async signal => {
|
|
395
422
|
const response = await ctx.fetch(showUrl, {
|
|
396
423
|
method: "POST",
|
|
397
424
|
headers: { ...(headers ?? {}), "Content-Type": "application/json" },
|
|
@@ -444,7 +471,7 @@ export async function discoverOllamaModels(
|
|
|
444
471
|
const endpoint = normalizeOllamaBaseUrl(providerConfig.baseUrl);
|
|
445
472
|
const tagsUrl = `${endpoint}/api/tags`;
|
|
446
473
|
const headers = { ...(providerConfig.headers ?? {}) };
|
|
447
|
-
const payload = await withTimeoutSignal(250, async signal => {
|
|
474
|
+
const payload = await withTimeoutSignal(discoveryProbeTimeoutMs(endpoint, 250), async signal => {
|
|
448
475
|
const response = await ctx.fetch(tagsUrl, {
|
|
449
476
|
headers,
|
|
450
477
|
signal,
|
|
@@ -491,7 +518,7 @@ async function discoverLlamaCppServerMetadata(
|
|
|
491
518
|
): Promise<LlamaCppDiscoveredServerMetadata | null> {
|
|
492
519
|
const propsUrl = `${toLlamaCppNativeBaseUrl(baseUrl)}/props`;
|
|
493
520
|
try {
|
|
494
|
-
const payload = await withTimeoutSignal(150, async signal => {
|
|
521
|
+
const payload = await withTimeoutSignal(discoveryProbeTimeoutMs(baseUrl, 150), async signal => {
|
|
495
522
|
const response = await ctx.fetch(propsUrl, {
|
|
496
523
|
headers,
|
|
497
524
|
signal,
|
|
@@ -567,7 +594,7 @@ export async function discoverLlamaCppModels(
|
|
|
567
594
|
let headers = baseHeaders;
|
|
568
595
|
const attempt = async (h: Record<string, string>) => {
|
|
569
596
|
const [payload, metadata] = await Promise.all([
|
|
570
|
-
withTimeoutSignal(250, async signal => {
|
|
597
|
+
withTimeoutSignal(discoveryProbeTimeoutMs(baseUrl, 250), async signal => {
|
|
571
598
|
const response = await ctx.fetch(modelsUrl, {
|
|
572
599
|
headers: h,
|
|
573
600
|
signal,
|
|
@@ -641,7 +668,7 @@ export async function discoverLlamaCppModelRuntimeMetadata(
|
|
|
641
668
|
const baseHeaders: Record<string, string> = { ...(model.headers ?? {}) };
|
|
642
669
|
const attempt = async (headers: Record<string, string>) => {
|
|
643
670
|
const [entries, serverMetadata] = await Promise.all([
|
|
644
|
-
withTimeoutSignal(250, async signal => {
|
|
671
|
+
withTimeoutSignal(discoveryProbeTimeoutMs(nativeBaseUrl, 250), async signal => {
|
|
645
672
|
const response = await ctx.fetch(modelsUrl, {
|
|
646
673
|
headers,
|
|
647
674
|
signal,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
openaiCodexModelManagerOptions,
|
|
28
28
|
PROVIDER_DESCRIPTORS,
|
|
29
29
|
resolveModelCacheProviderId,
|
|
30
|
+
resolveOllamaModelCacheProviderId,
|
|
30
31
|
} from "@oh-my-pi/pi-catalog/provider-models";
|
|
31
32
|
import {
|
|
32
33
|
collapseBuiltModelVariants,
|
|
@@ -539,6 +540,12 @@ interface ModelPatch {
|
|
|
539
540
|
*/
|
|
540
541
|
type ModelTransportPolicy = "merge" | "replace";
|
|
541
542
|
|
|
543
|
+
/**
|
|
544
|
+
* Credential-aware model projection supplied by an extension provider. Receives
|
|
545
|
+
* the fully composed catalog and returns the list the host should serve.
|
|
546
|
+
*/
|
|
547
|
+
type ModifyModelsHook = (models: Model<Api>[], credentials: OAuthCredentials) => Model<Api>[];
|
|
548
|
+
|
|
542
549
|
function applyModelPatch(base: Model<Api>, patch: ModelPatch, transport: ModelTransportPolicy): Model<Api> {
|
|
543
550
|
const result = { ...base };
|
|
544
551
|
if (patch.name !== undefined) result.name = patch.name;
|
|
@@ -779,6 +786,7 @@ export type ResolvedRequestAuth =
|
|
|
779
786
|
*/
|
|
780
787
|
export class ModelRegistry {
|
|
781
788
|
#models: Model<Api>[] = [];
|
|
789
|
+
#unprojectedModels: Model<Api>[] = [];
|
|
782
790
|
#hasFullSnapshot = false;
|
|
783
791
|
#cachedStandardModels: Model<Api>[] = [];
|
|
784
792
|
#cachedDiscoverableModels: Model<Api>[] = [];
|
|
@@ -805,6 +813,13 @@ export class ModelRegistry {
|
|
|
805
813
|
#runtimeModelOverlays: CustomModelOverlay[] = [];
|
|
806
814
|
#runtimeProviderApiKeys: Map<string, string> = new Map();
|
|
807
815
|
#runtimeProviderOverrides: Map<string, ProviderOverride> = new Map();
|
|
816
|
+
// Credential-aware model projections registered via
|
|
817
|
+
// `registerProvider({ oauth: { modifyModels } })`. Persisted for the same
|
|
818
|
+
// reason as #runtimeModelOverlays: the overlays hold the *pre-projection*
|
|
819
|
+
// definitions, so without re-applying the projection every static reload
|
|
820
|
+
// would silently revert the provider to its unprojected catalog.
|
|
821
|
+
#runtimeModelModifiers: Map<string, ModifyModelsHook> = new Map();
|
|
822
|
+
#lastModelModifierWarnings: Map<string, string> = new Map();
|
|
808
823
|
#runtimeProvidersBySource: Map<string, Set<string>> = new Map();
|
|
809
824
|
#runtimeProviderSourceByName: Map<string, string> = new Map();
|
|
810
825
|
// Runtime model managers registered by extensions via fetchDynamicModels.
|
|
@@ -1002,6 +1017,15 @@ export class ModelRegistry {
|
|
|
1002
1017
|
if (patch.contextWindow === undefined && patch.maxTokens === undefined && patch.input === undefined) {
|
|
1003
1018
|
return current;
|
|
1004
1019
|
}
|
|
1020
|
+
const unprojected = resolveProviderModelReference(current.provider, current.id, this.#unprojectedModels);
|
|
1021
|
+
if (unprojected) {
|
|
1022
|
+
const patchedBase = applyModelPatch(unprojected, patch, "merge");
|
|
1023
|
+
this.#unprojectedModels = this.#unprojectedModels.map(candidate =>
|
|
1024
|
+
candidate.provider === unprojected.provider && candidate.id === unprojected.id ? patchedBase : candidate,
|
|
1025
|
+
);
|
|
1026
|
+
this.#models = this.#applyRuntimeModelModifiers(this.#unprojectedModels);
|
|
1027
|
+
return resolveProviderModelReference(current.provider, current.id, this.#models) ?? patchedBase;
|
|
1028
|
+
}
|
|
1005
1029
|
const patched = applyModelPatch(current, patch, "merge");
|
|
1006
1030
|
this.#models = this.#models.map(candidate =>
|
|
1007
1031
|
candidate.provider === current.provider && candidate.id === current.id ? patched : candidate,
|
|
@@ -1102,6 +1126,7 @@ export class ModelRegistry {
|
|
|
1102
1126
|
|
|
1103
1127
|
#resetStaticComposition(): void {
|
|
1104
1128
|
this.#models = [];
|
|
1129
|
+
this.#unprojectedModels = [];
|
|
1105
1130
|
this.#hasFullSnapshot = false;
|
|
1106
1131
|
this.#internedStaticModels.clear();
|
|
1107
1132
|
this.#providerLookupSnapshots.clear();
|
|
@@ -1126,7 +1151,46 @@ export class ModelRegistry {
|
|
|
1126
1151
|
});
|
|
1127
1152
|
}
|
|
1128
1153
|
|
|
1129
|
-
|
|
1154
|
+
/**
|
|
1155
|
+
* Re-apply the credential-aware projections registered by extension providers.
|
|
1156
|
+
*
|
|
1157
|
+
* Runtime overlays hold the pre-projection definitions, so the registry keeps
|
|
1158
|
+
* those definitions separate from `#models` and reruns the ordered hooks after
|
|
1159
|
+
* every catalog rebuild. Otherwise an offline refresh silently restores the
|
|
1160
|
+
* provider's placeholder catalog.
|
|
1161
|
+
*
|
|
1162
|
+
* A throwing hook falls back to the catalog produced by earlier hooks instead
|
|
1163
|
+
* of failing the whole composition; one bad extension must not empty the
|
|
1164
|
+
* registry. The failure is logged (deduped per provider) so it is not silent.
|
|
1165
|
+
* Each hook receives a deep clone because the public contract permits
|
|
1166
|
+
* mutation of both the array and its model records before returning.
|
|
1167
|
+
*/
|
|
1168
|
+
#applyRuntimeModelModifiers(models: Model<Api>[]): Model<Api>[] {
|
|
1169
|
+
if (this.#runtimeModelModifiers.size === 0) return models;
|
|
1170
|
+
let projected = models;
|
|
1171
|
+
for (const [providerName, modifyModels] of this.#runtimeModelModifiers) {
|
|
1172
|
+
const credential = this.authStorage.getOAuthCredential(providerName);
|
|
1173
|
+
if (!credential) continue;
|
|
1174
|
+
try {
|
|
1175
|
+
projected = modifyModels(structuredClone(projected), credential);
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
this.#warnModelModifierFailure(providerName, error instanceof Error ? error.message : String(error));
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
return projected;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Dedup key is separate from `#lastDiscoveryWarnings` so a repeated modifier
|
|
1185
|
+
* failure cannot mask a subsequent discovery failure for the same provider.
|
|
1186
|
+
*/
|
|
1187
|
+
#warnModelModifierFailure(provider: string, error: string): void {
|
|
1188
|
+
if (this.#lastModelModifierWarnings.get(provider) === error) return;
|
|
1189
|
+
this.#lastModelModifierWarnings.set(provider, error);
|
|
1190
|
+
logger.warn("extension model projection failed; serving unprojected catalog", { provider, error });
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
#composeUnprojectedStaticModels(providerFilter?: ReadonlySet<string>): Model<Api>[] {
|
|
1130
1194
|
const select = <T extends { provider: string }>(models: readonly T[]): T[] =>
|
|
1131
1195
|
providerFilter ? models.filter(model => providerFilter.has(model.provider)) : [...models];
|
|
1132
1196
|
let builtInModels = this.#applyHardcodedModelPolicies(
|
|
@@ -1141,17 +1205,24 @@ export class ModelRegistry {
|
|
|
1141
1205
|
);
|
|
1142
1206
|
const withConfigModels = this.#mergeCustomModels(resolvedDefaults, select(this.#customModelOverlays));
|
|
1143
1207
|
const combined = this.#mergeCustomModels(withConfigModels, select(this.#runtimeModelOverlays));
|
|
1144
|
-
// Custom/config providers bypass the model-manager merge point —
|
|
1145
|
-
// collapse effort-tier variants here so X/X-thinking twins fold.
|
|
1146
1208
|
const withModelOverrides = this.#applyModelOverrides(collapseBuiltModelVariants(combined), this.#modelOverrides);
|
|
1147
|
-
return this.#
|
|
1148
|
-
|
|
1149
|
-
|
|
1209
|
+
return this.#applyLlamaCppQwenThinkingToModels(this.#applyRuntimeProviderOverrides(withModelOverrides));
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
#composeStaticModels(providerFilter?: ReadonlySet<string>): Model<Api>[] {
|
|
1213
|
+
// A modifier is a whole-catalog transform. Build and project the full catalog
|
|
1214
|
+
// before narrowing a lazy lookup, matching getAll() followed by filtering.
|
|
1215
|
+
const projectFullCatalog = providerFilter !== undefined && this.#runtimeModelModifiers.size > 0;
|
|
1216
|
+
const unprojected = this.#composeUnprojectedStaticModels(projectFullCatalog ? undefined : providerFilter);
|
|
1217
|
+
const projected = this.#applyRuntimeModelModifiers(unprojected);
|
|
1218
|
+
const selected = projectFullCatalog ? projected.filter(model => providerFilter.has(model.provider)) : projected;
|
|
1219
|
+
return this.#internStaticModels(selected);
|
|
1150
1220
|
}
|
|
1151
1221
|
|
|
1152
1222
|
#ensureFullSnapshot(): Model<Api>[] {
|
|
1153
1223
|
if (!this.#hasFullSnapshot) {
|
|
1154
|
-
this.#
|
|
1224
|
+
this.#unprojectedModels = this.#composeUnprojectedStaticModels();
|
|
1225
|
+
this.#models = this.#internStaticModels(this.#applyRuntimeModelModifiers(this.#unprojectedModels));
|
|
1155
1226
|
this.#hasFullSnapshot = true;
|
|
1156
1227
|
this.#providerLookupSnapshots.clear();
|
|
1157
1228
|
}
|
|
@@ -1605,7 +1676,7 @@ export class ModelRegistry {
|
|
|
1605
1676
|
discovered.map(model =>
|
|
1606
1677
|
mergeDiscoveredModel(
|
|
1607
1678
|
model,
|
|
1608
|
-
|
|
1679
|
+
resolveProviderModelReference(model.provider, model.id, this.#unprojectedModels),
|
|
1609
1680
|
this.#providerOverrides.get(model.provider),
|
|
1610
1681
|
),
|
|
1611
1682
|
),
|
|
@@ -1615,16 +1686,23 @@ export class ModelRegistry {
|
|
|
1615
1686
|
authoritativeProviders.add(provider);
|
|
1616
1687
|
}
|
|
1617
1688
|
const baseModels =
|
|
1618
|
-
authoritativeProviders.size > 0
|
|
1689
|
+
authoritativeProviders.size > 0
|
|
1690
|
+
? dropProviderModels(this.#unprojectedModels, authoritativeProviders)
|
|
1691
|
+
: this.#unprojectedModels;
|
|
1619
1692
|
const resolved = this.#mergeResolvedModels(baseModels, discoveredModels);
|
|
1620
1693
|
const withConfigModels = this.#mergeCustomModels(resolved, this.#customModelOverlays);
|
|
1621
|
-
// Merge runtime extension models so they survive online discovery completion
|
|
1622
1694
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
1623
1695
|
const withModelOverrides = this.#applyModelOverrides(collapseBuiltModelVariants(combined), this.#modelOverrides);
|
|
1624
|
-
this.#
|
|
1696
|
+
this.#unprojectedModels = this.#applyLlamaCppQwenThinkingToModels(
|
|
1697
|
+
this.#applyRuntimeProviderOverrides(withModelOverrides),
|
|
1698
|
+
);
|
|
1699
|
+
this.#models = this.#applyRuntimeModelModifiers(this.#unprojectedModels);
|
|
1625
1700
|
}
|
|
1626
1701
|
|
|
1627
1702
|
#configuredDiscoveryCacheProviderId(providerConfig: DiscoveryProviderConfig): string {
|
|
1703
|
+
if (providerConfig.discovery.type === "ollama") {
|
|
1704
|
+
return resolveOllamaModelCacheProviderId(providerConfig.provider, providerConfig.baseUrl);
|
|
1705
|
+
}
|
|
1628
1706
|
if (providerConfig.discovery.type === "openai-models-list") {
|
|
1629
1707
|
return `${providerConfig.provider}:openai-models-list-context-v2`;
|
|
1630
1708
|
}
|
|
@@ -2393,6 +2471,8 @@ export class ModelRegistry {
|
|
|
2393
2471
|
this.#runtimeProviderOverrides.delete(providerName);
|
|
2394
2472
|
this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(overlay => overlay.provider !== providerName);
|
|
2395
2473
|
this.#runtimeModelManagers.delete(providerName);
|
|
2474
|
+
this.#runtimeModelModifiers.delete(providerName);
|
|
2475
|
+
this.#lastModelModifierWarnings.delete(providerName);
|
|
2396
2476
|
this.authStorage.removeConfigApiKey(providerName);
|
|
2397
2477
|
}
|
|
2398
2478
|
|
|
@@ -2529,13 +2609,15 @@ export class ModelRegistry {
|
|
|
2529
2609
|
this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(m => m.provider !== providerName);
|
|
2530
2610
|
this.#runtimeModelOverlays.push(...newOverlays);
|
|
2531
2611
|
|
|
2532
|
-
//
|
|
2533
|
-
|
|
2612
|
+
// Update the unprojected snapshot, then rerun every whole-catalog
|
|
2613
|
+
// projection exactly once. Incremental projection is not safe because one
|
|
2614
|
+
// provider's hook may inspect or suppress another provider's models.
|
|
2615
|
+
const nextModels = this.#unprojectedModels.filter(model => model.provider !== providerName);
|
|
2534
2616
|
for (const overlay of newOverlays) {
|
|
2535
2617
|
nextModels.push(finalizeCustomModel(overlay, { useDefaults: true }));
|
|
2536
2618
|
}
|
|
2537
2619
|
const runtimeTransportOverride = this.#runtimeProviderOverrides.get(providerName);
|
|
2538
|
-
|
|
2620
|
+
this.#unprojectedModels = runtimeTransportOverride
|
|
2539
2621
|
? nextModels.map(model => {
|
|
2540
2622
|
if (model.provider !== providerName) return model;
|
|
2541
2623
|
return this.#applyProviderTransportOverrideToModel(model, runtimeTransportOverride);
|
|
@@ -2543,14 +2625,12 @@ export class ModelRegistry {
|
|
|
2543
2625
|
: nextModels;
|
|
2544
2626
|
|
|
2545
2627
|
if (config.oauth?.modifyModels) {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
return;
|
|
2550
|
-
}
|
|
2628
|
+
this.#runtimeModelModifiers.set(providerName, config.oauth.modifyModels);
|
|
2629
|
+
} else {
|
|
2630
|
+
this.#runtimeModelModifiers.delete(providerName);
|
|
2551
2631
|
}
|
|
2552
|
-
|
|
2553
|
-
this.#
|
|
2632
|
+
this.#models = this.#applyRuntimeModelModifiers(this.#unprojectedModels);
|
|
2633
|
+
this.#providerLookupSnapshots.clear();
|
|
2554
2634
|
return;
|
|
2555
2635
|
}
|
|
2556
2636
|
|
|
@@ -2619,12 +2699,14 @@ export class ModelRegistry {
|
|
|
2619
2699
|
transportOverride,
|
|
2620
2700
|
);
|
|
2621
2701
|
this.#runtimeProviderOverrides.set(providerName, nextRuntimeOverride);
|
|
2622
|
-
this.#
|
|
2623
|
-
this.#
|
|
2624
|
-
if (
|
|
2625
|
-
return this.#applyProviderTransportOverrideToModel(
|
|
2702
|
+
this.#unprojectedModels = this.#applyLlamaCppQwenThinkingToModels(
|
|
2703
|
+
this.#unprojectedModels.map(model => {
|
|
2704
|
+
if (model.provider !== providerName) return model;
|
|
2705
|
+
return this.#applyProviderTransportOverrideToModel(model, transportOverride);
|
|
2626
2706
|
}),
|
|
2627
2707
|
);
|
|
2708
|
+
this.#models = this.#applyRuntimeModelModifiers(this.#unprojectedModels);
|
|
2709
|
+
this.#providerLookupSnapshots.clear();
|
|
2628
2710
|
}
|
|
2629
2711
|
}
|
|
2630
2712
|
|