@oh-my-pi/pi-coding-agent 14.0.4 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/package.json +11 -8
- package/src/async/index.ts +1 -0
- package/src/async/support.ts +5 -0
- package/src/cli/list-models.ts +96 -57
- package/src/commit/model-selection.ts +16 -13
- package/src/config/model-equivalence.ts +674 -0
- package/src/config/model-registry.ts +182 -13
- package/src/config/model-resolver.ts +203 -74
- package/src/config/settings-schema.ts +23 -0
- package/src/config/settings.ts +9 -2
- package/src/dap/session.ts +31 -39
- package/src/debug/log-formatting.ts +2 -2
- package/src/edit/modes/chunk.ts +8 -3
- package/src/export/html/template.css +82 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +612 -97
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/jobs-protocol.ts +2 -1
- package/src/lsp/client.ts +5 -3
- package/src/lsp/index.ts +4 -9
- package/src/lsp/utils.ts +26 -0
- package/src/main.ts +6 -1
- package/src/memories/index.ts +7 -6
- package/src/modes/components/diff.ts +1 -1
- package/src/modes/components/model-selector.ts +221 -64
- package/src/modes/controllers/command-controller.ts +18 -0
- package/src/modes/controllers/event-controller.ts +438 -426
- package/src/modes/controllers/selector-controller.ts +13 -5
- package/src/modes/theme/mermaid-cache.ts +5 -7
- package/src/priority.json +8 -0
- package/src/prompts/agents/designer.md +1 -2
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +15 -0
- package/src/prompts/tools/cancel-job.md +1 -1
- package/src/prompts/tools/chunk-edit.md +39 -40
- package/src/prompts/tools/read-chunk.md +13 -1
- package/src/prompts/tools/read.md +9 -0
- package/src/prompts/tools/write.md +1 -0
- package/src/sdk.ts +7 -4
- package/src/session/agent-session.ts +33 -6
- package/src/session/compaction/compaction.ts +1 -1
- package/src/task/executor.ts +5 -1
- package/src/tools/await-tool.ts +2 -1
- package/src/tools/bash.ts +221 -56
- package/src/tools/browser.ts +84 -21
- package/src/tools/cancel-job.ts +2 -1
- package/src/tools/fetch.ts +1 -1
- package/src/tools/find.ts +40 -94
- package/src/tools/gemini-image.ts +1 -0
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/read.ts +218 -1
- package/src/tools/render-utils.ts +1 -1
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/write.ts +187 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/git.ts +24 -1
- package/src/utils/image-resize.ts +73 -37
- package/src/utils/title-generator.ts +1 -1
- package/src/web/scrapers/types.ts +50 -32
- package/src/web/search/providers/codex.ts +21 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.1.0] - 2026-04-11
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added richer tool rendering details in session export HTML, including metadata badges, argument formatting, and todo task tree styling for exported tool and workflow messages
|
|
9
|
+
- Added a persistent `js` tool backed by `node:vm`, with cross-session `highway` KV/pubsub, tool calls from inside JS cells, and `$` / `$$` interactive JavaScript execution
|
|
10
|
+
- Added SQLite database read support to the `read` tool for `.sqlite`, `.sqlite3`, `.db`, and `.db3` files with table listing, schema + sample output, row lookup, paginated query filtering, and read-only `q=SELECT` mode
|
|
11
|
+
- Added SQLite mutation support to the `write` tool so `db.sqlite:table` inserts JSON5 rows and `db.sqlite:table:key` updates or deletes rows via row key
|
|
12
|
+
- Added rendering of usage report entries for accounts with no usage limits, including account label and optional plan type with a `-- no limits` indicator
|
|
13
|
+
- Updated account label resolution to fall back to email or accountId so unlabeled unlimited-plan accounts display a meaningful name
|
|
14
|
+
- Added canonical model equivalence and provider coalescing across `models.yml`, `enabledModels`, `--models`, `/model`, and `--list-models`
|
|
15
|
+
- Added `equivalence` overrides/exclusions to `models.yml` and `modelProviderOrder` to `config.yml` for global canonical-provider preference
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Enabled `await` and `cancel_job` to be available when `bash.autoBackground.enabled` is set, so auto-backgrounded bash jobs can be awaited or cancelled without enabling `async.enabled`
|
|
20
|
+
- Updated bash auto-background behavior so short commands returned inline output when they completed before the configured threshold, while longer runs moved to background jobs automatically
|
|
21
|
+
- Replaced the LLM-callable Python execution path with JavaScript execution in the shared VM context, including updated renderers, prompts, session messages, and extension events
|
|
22
|
+
- Updated interactive and CLI model listings/selectors to work with canonical model ids while resolving them to concrete provider variants for actual execution
|
|
23
|
+
- Updated role assignment persistence so selected model settings now store the selector used by users, including thinking-level suffixes, while runtime continues to run against the resolved concrete provider model
|
|
24
|
+
- Updated model scope resolution to expand exact canonical model ids into all matching provider variants when filtering supported model sets
|
|
25
|
+
- Changed the agent to avoid giving time estimates or task-duration predictions in user responses, focusing on required work instead
|
|
26
|
+
- Changed generated code guidance to avoid speculative abstractions and extra compatibility scaffolding, favoring direct implementations that match current needs
|
|
27
|
+
- Changed model role resolution so roles can store either canonical model ids or explicit `provider/model` selectors while sessions continue to record the concrete model actually used
|
|
28
|
+
- Updated bash execution to optionally auto-background long-running commands through the existing background-job pipeline, with dedicated settings for enabling the behavior and adjusting the delay
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Fixed session export rendering so JavaScript execution messages now use `jsExecution` labels and content instead of `pythonExecution`, matching current tool behavior
|
|
33
|
+
- Fixed JavaScript cell execution to auto-display returned values once and preserve persistent VM bindings across calls until reset
|
|
34
|
+
- Fixed `.db`/`.db3` reads to verify SQLite file headers and fall back to normal file reading when the extension matches but the content is not a SQLite database
|
|
35
|
+
- Fixed SQLite selector parsing and resolution to correctly route requests to database operations at the file-extension boundary instead of misrouting through plain file/archive handlers
|
|
36
|
+
- Fixed unsupported or unsafe selectors by rejecting missing tables, composite primary keys for row lookups, unknown query parameters, and row operations on non-existent tables
|
|
37
|
+
- Fixed model resolution for commit message generation, title generation, memory consolidation, and image inspection when role strings use canonical ids instead of raw provider/model values
|
|
38
|
+
- Fixed default-model updates so previously configured thinking levels were preserved when reassigning a role
|
|
39
|
+
- Fixed model scope and selection handling in CLI/session startup paths that previously failed to resolve aliases consistently across features
|
|
40
|
+
- Fixed short-lived git subprocesses to disable `core.fsmonitor` and `core.untrackedCache`, avoiding unnecessary repository watchers and cache work during agent git operations
|
|
41
|
+
|
|
42
|
+
### Security
|
|
43
|
+
|
|
44
|
+
- Blocked destructive SQL execution in read-mode SQLite access by using read-only connections and rejecting bound-parameter raw SQL
|
|
45
|
+
|
|
46
|
+
## [14.0.5] - 2026-04-11
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Added `designer` model role for UI/UX design tasks with Gemini 3.1 Pro as default model
|
|
50
|
+
- Added support for model role fallback lists — roles can now resolve to multiple model patterns with automatic fallback to next available model
|
|
51
|
+
- Added `extractReadableFromHtml` utility function to extract readable content from HTML with Readability article extraction and CSS selector fallback
|
|
52
|
+
- Added support for GFM (GitHub Flavored Markdown) features including tables, strikethrough, and task lists in HTML-to-markdown conversion
|
|
53
|
+
- Added `resolveDiagnosticTargets` utility function to handle glob pattern resolution with fallback to literal file paths for bracket-style paths
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Clarified fenced code block editing behavior in markdown — the tool now preserves literal indentation inside fenced blocks, with content written verbatim as supplied
|
|
58
|
+
- Updated guidance for inserting content after markdown section headings to use `after` on the heading chunk rather than `before`/`prepend` on the section itself
|
|
59
|
+
- Reduced default image resize limits to 1568px (from 2000px) and 500KB (from 4.5MB) to match Anthropic's internal downscaling threshold and reduce payload sizes in tool calls
|
|
60
|
+
- Adjusted screenshot compression to use 1024px max dimensions and 150KB budget for more aggressive optimization of browser screenshots in LLM requests
|
|
61
|
+
- Updated JPEG quality defaults from 80 to 75 and refined quality ladder steps (70, 60, 50, 40) for tighter byte budgets
|
|
62
|
+
- Improved image resize fast-path to skip re-encoding when images are already within dimensions and at ≤25% of byte budget, avoiding unnecessary processing of small icons and diagrams
|
|
63
|
+
- Clarified that chunk names are truncated and must be copied from `read` or `?` output rather than constructed from source identifiers
|
|
64
|
+
- Enhanced guidance for editing fenced code blocks in markdown to preserve exact whitespace using `raw` reads, as the tool normalizes tabs to spaces which can damage indentation-sensitive content
|
|
65
|
+
- Updated designer agent to use `pi/designer` role alias instead of explicit model list
|
|
66
|
+
- Refactored model role resolution to support multiple fallback patterns per role, improving model availability handling
|
|
67
|
+
- Replaced regex-based HTML-to-markdown conversion with Turndown library and GFM plugin for more accurate formatting of complex HTML structures
|
|
68
|
+
- Simplified no-changes response to omit redundant response text when chunk content already matches
|
|
69
|
+
- Clarified region suffix behavior on leaf and compound statement chunks — `~` and `^` now fall back to whole-chunk replacement with explicit guidance to supply complete structural content
|
|
70
|
+
- Updated CRC refresh guidance to direct users to use CRCs from edit responses or run `read(path="file", sel="?")`
|
|
71
|
+
- Added clarification that region suffixes fall back to whole-chunk replacement for prose and data formats (markdown, YAML, JSON, fenced code blocks, frontmatter)
|
|
72
|
+
- Documented `L20` shorthand syntax for single-line reads extending to end-of-file, with `L20-L20` for one-line windows
|
|
73
|
+
- Refactored diagnostic target resolution to use new `resolveDiagnosticTargets` function, consolidating glob pattern detection and file matching logic
|
|
74
|
+
- Updated chunk selector syntax from `@region` format to `~` (body) and `^` (head) suffixes for more concise region targeting
|
|
75
|
+
- Simplified chunk edit documentation to use new `~` and `^` region syntax instead of `@head`, `@body`, `@tail`, `@decl` keywords
|
|
76
|
+
- Replaced internal `raceAbort` function with imported `raceWithAbort` utility from pi-utils
|
|
77
|
+
- Refactored cleanup timer to use async iterator pattern with `timers.setInterval` instead of `setInterval`
|
|
78
|
+
- Made `#cleanupIdleSessions` synchronous and moved async cleanup loop logic to new `#runCleanupLoop` method
|
|
79
|
+
- Replaced regex-based `htmlToBasicMarkdown` with a Turndown + GFM plugin pipeline (tables, strikethrough, task lists, nested lists now convert correctly). Added direct `turndown` and `turndown-plugin-gfm` dependencies
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- Fixed chunk edit tool to report file-not-found error distinctly when attempting to use chunk selectors on non-existent files, with guidance to use write tool or verify the path
|
|
84
|
+
- Fixed stale child selector reuse to correctly match chunks by checksum when multiple sibling chunks with the same name exist under the same parent
|
|
85
|
+
- Fixed stale diagnostics being reused after unrelated file publishes by clearing cached diagnostics before refreshing file state
|
|
86
|
+
- Fixed Codex search to use streamed answer text when final answer is an image placeholder or empty
|
|
87
|
+
|
|
5
88
|
## [14.0.4] - 2026-04-10
|
|
6
89
|
### Added
|
|
7
90
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "14.0
|
|
4
|
+
"version": "14.1.0",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
48
48
|
"@mozilla/readability": "^0.6",
|
|
49
|
-
"@oh-my-pi/omp-stats": "14.0
|
|
50
|
-
"@oh-my-pi/pi-agent-core": "14.0
|
|
51
|
-
"@oh-my-pi/pi-ai": "14.0
|
|
52
|
-
"@oh-my-pi/pi-natives": "14.0
|
|
53
|
-
"@oh-my-pi/pi-tui": "14.0
|
|
54
|
-
"@oh-my-pi/pi-utils": "14.0
|
|
49
|
+
"@oh-my-pi/omp-stats": "14.1.0",
|
|
50
|
+
"@oh-my-pi/pi-agent-core": "14.1.0",
|
|
51
|
+
"@oh-my-pi/pi-ai": "14.1.0",
|
|
52
|
+
"@oh-my-pi/pi-natives": "14.1.0",
|
|
53
|
+
"@oh-my-pi/pi-tui": "14.1.0",
|
|
54
|
+
"@oh-my-pi/pi-utils": "14.1.0",
|
|
55
55
|
"@sinclair/typebox": "^0.34",
|
|
56
56
|
"@xterm/headless": "^6.0",
|
|
57
57
|
"ajv": "^8.18",
|
|
@@ -63,10 +63,13 @@
|
|
|
63
63
|
"lru-cache": "11.3.1",
|
|
64
64
|
"markit-ai": "0.5.0",
|
|
65
65
|
"puppeteer": "^24.37",
|
|
66
|
+
"turndown": "7.2.4",
|
|
67
|
+
"turndown-plugin-gfm": "1.0.2",
|
|
66
68
|
"zod": "4.3.6"
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
|
-
"@types/bun": "^1.3"
|
|
71
|
+
"@types/bun": "^1.3",
|
|
72
|
+
"@types/turndown": "5.0.6"
|
|
70
73
|
},
|
|
71
74
|
"engines": {
|
|
72
75
|
"bun": ">=1.3.7"
|
package/src/async/index.ts
CHANGED
package/src/cli/list-models.ts
CHANGED
|
@@ -6,6 +6,45 @@ import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
|
6
6
|
import type { ModelRegistry } from "../config/model-registry";
|
|
7
7
|
import { fuzzyFilter } from "../utils/fuzzy";
|
|
8
8
|
|
|
9
|
+
interface ProviderRow {
|
|
10
|
+
provider: string;
|
|
11
|
+
model: string;
|
|
12
|
+
context: string;
|
|
13
|
+
maxOut: string;
|
|
14
|
+
thinking: string;
|
|
15
|
+
images: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface CanonicalRow {
|
|
19
|
+
canonical: string;
|
|
20
|
+
selected: string;
|
|
21
|
+
variants: string;
|
|
22
|
+
context: string;
|
|
23
|
+
maxOut: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeLine(line = ""): void {
|
|
27
|
+
process.stdout.write(`${line}\n`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function renderTable<T extends Record<string, string>>(rows: T[], headers: T): void {
|
|
31
|
+
const widths = Object.fromEntries(
|
|
32
|
+
Object.keys(headers).map(key => [key, Math.max(headers[key]!.length, ...rows.map(row => row[key]!.length))]),
|
|
33
|
+
) as Record<keyof T, number>;
|
|
34
|
+
|
|
35
|
+
const headerLine = Object.keys(headers)
|
|
36
|
+
.map(key => headers[key as keyof T]!.padEnd(widths[key as keyof T]))
|
|
37
|
+
.join(" ");
|
|
38
|
+
writeLine(headerLine);
|
|
39
|
+
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
const line = Object.keys(headers)
|
|
42
|
+
.map(key => row[key as keyof T]!.padEnd(widths[key as keyof T]))
|
|
43
|
+
.join(" ");
|
|
44
|
+
writeLine(line);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
9
48
|
/**
|
|
10
49
|
* List available models, optionally filtered by search pattern
|
|
11
50
|
*/
|
|
@@ -13,77 +52,77 @@ export async function listModels(modelRegistry: ModelRegistry, searchPattern?: s
|
|
|
13
52
|
const models = modelRegistry.getAvailable();
|
|
14
53
|
|
|
15
54
|
if (models.length === 0) {
|
|
16
|
-
|
|
55
|
+
writeLine("No models available. Set API keys in environment variables.");
|
|
17
56
|
return;
|
|
18
57
|
}
|
|
19
58
|
|
|
20
|
-
// Apply fuzzy filter if search pattern provided
|
|
21
59
|
let filteredModels: Model<Api>[] = models;
|
|
22
60
|
if (searchPattern) {
|
|
23
|
-
filteredModels = fuzzyFilter(models, searchPattern,
|
|
61
|
+
filteredModels = fuzzyFilter(models, searchPattern, model => `${model.provider} ${model.id}`);
|
|
24
62
|
}
|
|
25
63
|
|
|
26
|
-
|
|
27
|
-
|
|
64
|
+
const filteredCanonical = modelRegistry
|
|
65
|
+
.getCanonicalModels({ availableOnly: true, candidates: filteredModels })
|
|
66
|
+
.map(record => {
|
|
67
|
+
const selected = modelRegistry.resolveCanonicalModel(record.id, {
|
|
68
|
+
availableOnly: true,
|
|
69
|
+
candidates: filteredModels,
|
|
70
|
+
});
|
|
71
|
+
if (!selected) return undefined;
|
|
72
|
+
return {
|
|
73
|
+
canonical: record.id,
|
|
74
|
+
selected: `${selected.provider}/${selected.id}`,
|
|
75
|
+
variants: String(record.variants.length),
|
|
76
|
+
context: formatNumber(selected.contextWindow),
|
|
77
|
+
maxOut: formatNumber(selected.maxTokens),
|
|
78
|
+
} satisfies CanonicalRow;
|
|
79
|
+
})
|
|
80
|
+
.filter((row): row is CanonicalRow => row !== undefined)
|
|
81
|
+
.sort((left, right) => left.canonical.localeCompare(right.canonical));
|
|
82
|
+
|
|
83
|
+
if (filteredModels.length === 0 && filteredCanonical.length === 0) {
|
|
84
|
+
writeLine(`No models matching "${searchPattern}"`);
|
|
28
85
|
return;
|
|
29
86
|
}
|
|
30
87
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const providerCmp = a.provider.localeCompare(b.provider);
|
|
88
|
+
filteredModels.sort((left, right) => {
|
|
89
|
+
const providerCmp = left.provider.localeCompare(right.provider);
|
|
34
90
|
if (providerCmp !== 0) return providerCmp;
|
|
35
|
-
return
|
|
91
|
+
return left.id.localeCompare(right.id);
|
|
36
92
|
});
|
|
37
93
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}));
|
|
94
|
+
const providerRows = filteredModels.map(model => ({
|
|
95
|
+
provider: model.provider,
|
|
96
|
+
model: model.id,
|
|
97
|
+
context: formatNumber(model.contextWindow),
|
|
98
|
+
maxOut: formatNumber(model.maxTokens),
|
|
99
|
+
thinking: model.thinking ? getSupportedEfforts(model).join(",") : model.reasoning ? "yes" : "-",
|
|
100
|
+
images: model.input.includes("image") ? "yes" : "no",
|
|
101
|
+
})) satisfies ProviderRow[];
|
|
47
102
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
maxOut: Math.max(headers.maxOut.length, ...rows.map(r => r.maxOut.length)),
|
|
62
|
-
thinking: Math.max(headers.thinking.length, ...rows.map(r => r.thinking.length)),
|
|
63
|
-
images: Math.max(headers.images.length, ...rows.map(r => r.images.length)),
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Print header
|
|
67
|
-
const headerLine = [
|
|
68
|
-
headers.provider.padEnd(widths.provider),
|
|
69
|
-
headers.model.padEnd(widths.model),
|
|
70
|
-
headers.context.padEnd(widths.context),
|
|
71
|
-
headers.maxOut.padEnd(widths.maxOut),
|
|
72
|
-
headers.thinking.padEnd(widths.thinking),
|
|
73
|
-
headers.images.padEnd(widths.images),
|
|
74
|
-
].join(" ");
|
|
75
|
-
console.log(headerLine);
|
|
103
|
+
if (filteredCanonical.length > 0) {
|
|
104
|
+
writeLine("Canonical models");
|
|
105
|
+
renderTable(filteredCanonical, {
|
|
106
|
+
canonical: "canonical",
|
|
107
|
+
selected: "selected",
|
|
108
|
+
variants: "variants",
|
|
109
|
+
context: "context",
|
|
110
|
+
maxOut: "max-out",
|
|
111
|
+
});
|
|
112
|
+
if (providerRows.length > 0) {
|
|
113
|
+
writeLine();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
76
116
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.log(line);
|
|
117
|
+
if (providerRows.length > 0) {
|
|
118
|
+
writeLine("Provider models");
|
|
119
|
+
renderTable(providerRows, {
|
|
120
|
+
provider: "provider",
|
|
121
|
+
model: "model",
|
|
122
|
+
context: "context",
|
|
123
|
+
maxOut: "max-out",
|
|
124
|
+
thinking: "thinking",
|
|
125
|
+
images: "images",
|
|
126
|
+
});
|
|
88
127
|
}
|
|
89
128
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { MODEL_ROLE_IDS } from "../config/model-registry";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type ModelLookupRegistry,
|
|
6
|
+
parseModelPattern,
|
|
7
|
+
resolveModelRoleValue,
|
|
8
|
+
resolveRoleSelection,
|
|
9
|
+
} from "../config/model-resolver";
|
|
5
10
|
import type { Settings } from "../config/settings";
|
|
6
11
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
7
12
|
|
|
@@ -11,19 +16,20 @@ export interface ResolvedCommitModel {
|
|
|
11
16
|
thinkingLevel?: ThinkingLevel;
|
|
12
17
|
}
|
|
13
18
|
|
|
19
|
+
type CommitModelRegistry = ModelLookupRegistry & {
|
|
20
|
+
getApiKey: (model: Model<Api>) => Promise<string | undefined>;
|
|
21
|
+
};
|
|
22
|
+
|
|
14
23
|
export async function resolvePrimaryModel(
|
|
15
24
|
override: string | undefined,
|
|
16
25
|
settings: Settings,
|
|
17
|
-
modelRegistry:
|
|
18
|
-
getAvailable: () => Model<Api>[];
|
|
19
|
-
getApiKey: (model: Model<Api>) => Promise<string | undefined>;
|
|
20
|
-
},
|
|
26
|
+
modelRegistry: CommitModelRegistry,
|
|
21
27
|
): Promise<ResolvedCommitModel> {
|
|
22
28
|
const available = modelRegistry.getAvailable();
|
|
23
29
|
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
|
24
30
|
const resolved = override
|
|
25
|
-
? resolveModelRoleValue(override, available, { settings, matchPreferences })
|
|
26
|
-
: resolveRoleSelection(["commit", "smol", ...MODEL_ROLE_IDS], settings, available);
|
|
31
|
+
? resolveModelRoleValue(override, available, { settings, matchPreferences, modelRegistry })
|
|
32
|
+
: resolveRoleSelection(["commit", "smol", ...MODEL_ROLE_IDS], settings, available, modelRegistry);
|
|
27
33
|
const model = resolved?.model;
|
|
28
34
|
if (!model) {
|
|
29
35
|
throw new Error("No model available for commit generation");
|
|
@@ -37,15 +43,12 @@ export async function resolvePrimaryModel(
|
|
|
37
43
|
|
|
38
44
|
export async function resolveSmolModel(
|
|
39
45
|
settings: Settings,
|
|
40
|
-
modelRegistry:
|
|
41
|
-
getAvailable: () => Model<Api>[];
|
|
42
|
-
getApiKey: (model: Model<Api>) => Promise<string | undefined>;
|
|
43
|
-
},
|
|
46
|
+
modelRegistry: CommitModelRegistry,
|
|
44
47
|
fallbackModel: Model<Api>,
|
|
45
48
|
fallbackApiKey: string,
|
|
46
49
|
): Promise<ResolvedCommitModel> {
|
|
47
50
|
const available = modelRegistry.getAvailable();
|
|
48
|
-
const resolvedSmol = resolveRoleSelection(["smol"], settings, available);
|
|
51
|
+
const resolvedSmol = resolveRoleSelection(["smol"], settings, available, modelRegistry);
|
|
49
52
|
if (resolvedSmol?.model) {
|
|
50
53
|
const apiKey = await modelRegistry.getApiKey(resolvedSmol.model);
|
|
51
54
|
if (apiKey) return { model: resolvedSmol.model, apiKey, thinkingLevel: resolvedSmol.thinkingLevel };
|
|
@@ -53,7 +56,7 @@ export async function resolveSmolModel(
|
|
|
53
56
|
|
|
54
57
|
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
|
55
58
|
for (const pattern of MODEL_PRIO.smol) {
|
|
56
|
-
const candidate = parseModelPattern(pattern, available, matchPreferences).model;
|
|
59
|
+
const candidate = parseModelPattern(pattern, available, matchPreferences, { modelRegistry }).model;
|
|
57
60
|
if (!candidate) continue;
|
|
58
61
|
const apiKey = await modelRegistry.getApiKey(candidate);
|
|
59
62
|
if (apiKey) return { model: candidate, apiKey };
|