@oh-my-pi/pi-coding-agent 14.0.5 → 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 +41 -0
- package/package.json +7 -7
- 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 +179 -11
- package/src/config/model-resolver.ts +171 -50
- package/src/config/settings-schema.ts +23 -0
- 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 +1 -1
- package/src/main.ts +6 -1
- package/src/memories/index.ts +7 -6
- package/src/modes/components/model-selector.ts +221 -64
- package/src/modes/controllers/command-controller.ts +18 -0
- package/src/modes/controllers/selector-controller.ts +13 -5
- 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/read-chunk.md +9 -0
- 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 +23 -6
- 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/cancel-job.ts +2 -1
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/read.ts +218 -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/title-generator.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,47 @@
|
|
|
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
|
+
|
|
5
46
|
## [14.0.5] - 2026-04-11
|
|
6
47
|
### Added
|
|
7
48
|
|
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",
|
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 };
|