@oh-my-pi/pi-coding-agent 15.1.2 → 15.1.4
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 +60 -0
- package/dist/types/async/job-manager.d.ts +3 -2
- package/dist/types/cli/auth-broker-cli.d.ts +25 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +18 -0
- package/dist/types/cli/grievances-cli.d.ts +12 -0
- package/dist/types/commands/auth-broker.d.ts +54 -0
- package/dist/types/commands/auth-gateway.d.ts +32 -0
- package/dist/types/commands/grievances.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -1
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -1
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -1
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +46 -0
- package/dist/types/discovery/agents.d.ts +12 -1
- package/dist/types/edit/renderer.d.ts +3 -0
- package/dist/types/eval/index.d.ts +0 -2
- package/dist/types/goals/tools/goal-tool.d.ts +10 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/internal-urls/index.d.ts +1 -1
- package/dist/types/internal-urls/{pi-protocol.d.ts → omp-protocol.d.ts} +3 -3
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/main.d.ts +11 -2
- package/dist/types/modes/acp/acp-agent.d.ts +2 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +13 -1
- package/dist/types/modes/acp/acp-mode.d.ts +3 -1
- package/dist/types/modes/emoji-autocomplete.d.ts +16 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
- package/dist/types/plan-mode/approved-plan.d.ts +10 -4
- package/dist/types/sdk.d.ts +10 -3
- package/dist/types/session/agent-session.d.ts +7 -3
- package/dist/types/session/auth-broker-config.d.ts +13 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/client-bridge.d.ts +3 -0
- package/dist/types/tools/eval.d.ts +41 -7
- package/dist/types/tools/irc.d.ts +8 -2
- package/dist/types/tools/report-tool-issue.d.ts +118 -1
- package/dist/types/tools/resolve.d.ts +8 -2
- package/examples/custom-tools/README.md +3 -12
- package/examples/extensions/README.md +2 -15
- package/examples/extensions/api-demo.ts +1 -7
- package/package.json +7 -7
- package/src/async/job-manager.ts +111 -13
- package/src/autoresearch/tools/init-experiment.ts +11 -33
- package/src/autoresearch/tools/log-experiment.ts +10 -24
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/autoresearch/tools/update-notes.ts +2 -9
- package/src/cli/auth-broker-cli.ts +746 -0
- package/src/cli/auth-gateway-cli.ts +342 -0
- package/src/cli/grievances-cli.ts +109 -16
- package/src/cli/update-cli.ts +1 -5
- package/src/cli.ts +4 -2
- package/src/commands/auth-broker.ts +96 -0
- package/src/commands/auth-gateway.ts +61 -0
- package/src/commands/grievances.ts +13 -8
- package/src/commands/launch.ts +1 -1
- package/src/commit/agentic/agent.ts +2 -0
- package/src/commit/agentic/tools/analyze-file.ts +2 -2
- package/src/commit/agentic/tools/git-file-diff.ts +2 -2
- package/src/commit/agentic/tools/git-hunk.ts +3 -3
- package/src/commit/agentic/tools/git-overview.ts +2 -2
- package/src/commit/agentic/tools/propose-changelog.ts +1 -3
- package/src/commit/agentic/tools/recent-commits.ts +1 -1
- package/src/commit/agentic/tools/schemas.ts +1 -9
- package/src/config/model-equivalence.ts +279 -174
- package/src/config/model-registry.ts +37 -6
- package/src/config/model-resolver.ts +13 -8
- package/src/config/models-config-schema.ts +8 -0
- package/src/config/settings-schema.ts +52 -0
- package/src/cursor.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/profiler.ts +4 -0
- package/src/debug/raw-sse-buffer.ts +100 -59
- package/src/debug/raw-sse.ts +1 -1
- package/src/discovery/agents.ts +15 -4
- package/src/edit/modes/apply-patch.ts +1 -5
- package/src/edit/modes/patch.ts +5 -5
- package/src/edit/modes/replace.ts +5 -5
- package/src/edit/renderer.ts +2 -1
- package/src/edit/streaming.ts +1 -1
- package/src/eval/index.ts +0 -2
- package/src/eval/js/shared/runtime.ts +107 -2
- package/src/eval/py/kernel.ts +1 -1
- package/src/exa/researcher.ts +4 -4
- package/src/exa/search.ts +10 -22
- package/src/exa/websets.ts +33 -33
- package/src/extensibility/typebox.ts +44 -17
- package/src/goals/tools/goal-tool.ts +3 -3
- package/src/index.ts +0 -3
- package/src/internal-urls/docs-index.generated.ts +21 -18
- package/src/internal-urls/index.ts +1 -1
- package/src/internal-urls/{pi-protocol.ts → omp-protocol.ts} +10 -10
- package/src/internal-urls/router.ts +3 -3
- package/src/internal-urls/types.ts +1 -1
- package/src/lsp/types.ts +8 -11
- package/src/main.ts +216 -146
- package/src/mcp/tool-bridge.ts +3 -3
- package/src/modes/acp/acp-agent.ts +203 -57
- package/src/modes/acp/acp-client-bridge.ts +2 -1
- package/src/modes/acp/acp-event-mapper.ts +208 -32
- package/src/modes/acp/acp-mode.ts +11 -3
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/diff.ts +1 -2
- package/src/modes/components/eval-execution.ts +1 -1
- package/src/modes/components/oauth-selector.ts +38 -2
- package/src/modes/components/tool-execution.ts +1 -2
- package/src/modes/components/tree-selector.ts +26 -7
- package/src/modes/controllers/command-controller.ts +95 -34
- package/src/modes/controllers/input-controller.ts +4 -3
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/interactive-mode.ts +92 -19
- package/src/modes/print-mode.ts +3 -3
- package/src/modes/prompt-action-autocomplete.ts +14 -0
- package/src/plan-mode/approved-plan.ts +30 -9
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/tools/ask.md +4 -3
- package/src/prompts/tools/eval.md +25 -26
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/resolve.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/web-search.md +1 -1
- package/src/sdk.ts +81 -8
- package/src/session/agent-session.ts +362 -131
- package/src/session/agent-storage.ts +7 -2
- package/src/session/auth-broker-config.ts +102 -0
- package/src/session/auth-storage.ts +7 -1
- package/src/session/client-bridge.ts +3 -0
- package/src/session/streaming-output.ts +1 -1
- package/src/task/types.ts +10 -35
- package/src/tools/bash-interactive.ts +4 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser.ts +12 -20
- package/src/tools/eval.ts +77 -100
- package/src/tools/gh.ts +21 -45
- package/src/tools/hindsight-recall.ts +1 -1
- package/src/tools/hindsight-reflect.ts +2 -2
- package/src/tools/hindsight-retain.ts +3 -7
- package/src/tools/index.ts +8 -1
- package/src/tools/inspect-image.ts +4 -1
- package/src/tools/irc.ts +4 -12
- package/src/tools/job.ts +3 -11
- package/src/tools/report-tool-issue.ts +462 -17
- package/src/tools/resolve.ts +2 -7
- package/src/tools/todo-write.ts +8 -15
- package/src/utils/title-generator.ts +3 -0
- package/src/web/search/index.ts +6 -6
- package/dist/types/eval/parse.d.ts +0 -28
- package/dist/types/eval/sniff.d.ts +0 -11
- package/src/eval/eval.lark +0 -36
- package/src/eval/parse.ts +0 -407
- package/src/eval/sniff.ts +0 -28
|
@@ -41,35 +41,8 @@ interface ResolvedCanonicalModel {
|
|
|
41
41
|
source: CanonicalModelSource;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
"customtools",
|
|
47
|
-
"high",
|
|
48
|
-
"low",
|
|
49
|
-
"medium",
|
|
50
|
-
"minimal",
|
|
51
|
-
"xhigh",
|
|
52
|
-
"free",
|
|
53
|
-
"cloud",
|
|
54
|
-
"exacto",
|
|
55
|
-
"nitro",
|
|
56
|
-
"original",
|
|
57
|
-
"optimized",
|
|
58
|
-
"nvfp4",
|
|
59
|
-
"fp8",
|
|
60
|
-
"fp4",
|
|
61
|
-
"bf16",
|
|
62
|
-
"int8",
|
|
63
|
-
"int4",
|
|
64
|
-
] as const;
|
|
65
|
-
const TRAILING_MARKER_SUFFIXES: readonly string[] = (() => {
|
|
66
|
-
const suffixes: string[] = [];
|
|
67
|
-
for (const marker of TRAILING_CANONICAL_MARKERS) {
|
|
68
|
-
const lower = marker.toLowerCase();
|
|
69
|
-
suffixes.push(`-${lower}`, `:${lower}`);
|
|
70
|
-
}
|
|
71
|
-
return suffixes;
|
|
72
|
-
})();
|
|
44
|
+
const TRAILING_MARKER_PATTERN =
|
|
45
|
+
/[-:](?:thinking|customtools|high|low|medium|minimal|xhigh|free|cloud|exacto|nitro|original|optimized|nvfp4|fp8|fp4|bf16|int8|int4)$/i;
|
|
73
46
|
const WRAPPER_PREFIXES = ["duo-chat-"] as const;
|
|
74
47
|
|
|
75
48
|
let referenceDataCache: CanonicalReferenceData | undefined;
|
|
@@ -77,7 +50,10 @@ const EMPTY_COMPILED_EQUIVALENCE: CompiledEquivalenceConfig = {
|
|
|
77
50
|
overrides: new Map<string, string>(),
|
|
78
51
|
exclude: new Set<string>(),
|
|
79
52
|
};
|
|
80
|
-
const
|
|
53
|
+
const kModelResolutionCache = Symbol("model-equivalence.resolutionCache");
|
|
54
|
+
interface CompiledEquivalenceConfigWithCache extends CompiledEquivalenceConfig {
|
|
55
|
+
[kModelResolutionCache]?: WeakMap<Model<Api>, ResolvedCanonicalModel>;
|
|
56
|
+
}
|
|
81
57
|
const FAMILY_EXTRACTION_PATTERNS = [
|
|
82
58
|
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+)(?::|$)/i,
|
|
83
59
|
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+(?:[-_/][a-z0-9.]+)*)(?::|$)/i,
|
|
@@ -172,13 +148,12 @@ function addCanonicalCandidate(candidates: Set<string>, candidate: string): void
|
|
|
172
148
|
}
|
|
173
149
|
|
|
174
150
|
function stripTrailingMarker(candidate: string): string | undefined {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return undefined;
|
|
151
|
+
const match = TRAILING_MARKER_PATTERN.exec(candidate);
|
|
152
|
+
return match ? candidate.slice(0, match.index) : undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function hasTrailingMarker(candidate: string): boolean {
|
|
156
|
+
return TRAILING_MARKER_PATTERN.test(candidate);
|
|
182
157
|
}
|
|
183
158
|
|
|
184
159
|
function lowercaseCandidate(candidate: string): string | undefined {
|
|
@@ -186,23 +161,41 @@ function lowercaseCandidate(candidate: string): string | undefined {
|
|
|
186
161
|
return lowercased !== candidate ? lowercased : undefined;
|
|
187
162
|
}
|
|
188
163
|
|
|
164
|
+
const STRIP_SYNTHETIC_PREFIX_PATTERN = /^hf:/i;
|
|
165
|
+
const STRIP_LATEST_SUFFIX_PATTERN = /-latest$/i;
|
|
166
|
+
const STRIP_LEGACY_GLM_TURBO_PATTERN = /^(glm-4(?:\.\d+)?v?)-turbo$/i;
|
|
167
|
+
const REORDER_ANTHROPIC_FAMILY_PATTERN = /^claude-(\d+(?:[.-]\d+)+)-(opus|sonnet|haiku)$/i;
|
|
168
|
+
const STRIP_PROVIDER_VERSION_SUFFIX_PATTERN = /-v\d+(?::\d+)?$/i;
|
|
169
|
+
const STRIP_DATE_SUFFIX_PATTERN = /-\d{8}$/i;
|
|
170
|
+
const INSERT_ATTACHED_FAMILY_VERSION_SEPARATOR_PATTERN =
|
|
171
|
+
/(^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder))(\d+(?:[.-]\d+)*)(?=$|[-_/.:a-z])/gi;
|
|
172
|
+
const SERIES_MINOR_DOT_TO_DASH_PATTERN = /(^|[/:._-])([a-z])(\d)\.(\d)(?=$|[-_/.:a-z])/gi;
|
|
173
|
+
const SERIES_MINOR_DASH_TO_DOT_PATTERN = /(^|[/:._-])([a-z])(\d)-(\d)(?=$|[-_/.:a-z])/gi;
|
|
174
|
+
const EXPAND_COMPACT_SERIES_MINOR_PATTERN = /(^|[/:._-])([a-z])(\d)(\d)(?=$|[-_/.:a-z])/gi;
|
|
175
|
+
const NAMESPACE_SUFFIX_BOUNDARY_PATTERN = /[/:.]/;
|
|
176
|
+
const NAMESPACE_SUFFIX_ALPHA_PATTERN = /[a-z]/i;
|
|
177
|
+
const NAMESPACE_SUFFIX_DIGIT_PATTERN = /\d/;
|
|
178
|
+
const SHORT_VERSION_DOT_TO_DASH_PATTERN = /(^|[-_/])(\d{1,2})\.(\d{1,2})(?=$|[-_a-z])/gi;
|
|
179
|
+
const SHORT_VERSION_DASH_TO_DOT_PATTERN = /(^|[-_/])(\d{1,2})-(\d{1,2})(?=$|[-_a-z])/gi;
|
|
180
|
+
const EXPAND_COMPACT_MINOR_PATTERN = /(^|[-_/])(\d)(\d)(?=$|[-_a-z])/g;
|
|
181
|
+
|
|
189
182
|
function stripSyntheticPrefix(candidate: string): string | undefined {
|
|
190
|
-
const stripped = candidate.replace(
|
|
183
|
+
const stripped = candidate.replace(STRIP_SYNTHETIC_PREFIX_PATTERN, "");
|
|
191
184
|
return stripped !== candidate ? stripped : undefined;
|
|
192
185
|
}
|
|
193
186
|
|
|
194
187
|
function stripLatestSuffix(candidate: string): string | undefined {
|
|
195
|
-
const stripped = candidate.replace(
|
|
188
|
+
const stripped = candidate.replace(STRIP_LATEST_SUFFIX_PATTERN, "");
|
|
196
189
|
return stripped !== candidate ? stripped : undefined;
|
|
197
190
|
}
|
|
198
191
|
|
|
199
192
|
function stripLegacyGlmTurboSuffix(candidate: string): string | undefined {
|
|
200
|
-
const stripped = candidate.replace(
|
|
193
|
+
const stripped = candidate.replace(STRIP_LEGACY_GLM_TURBO_PATTERN, "$1");
|
|
201
194
|
return stripped !== candidate ? stripped : undefined;
|
|
202
195
|
}
|
|
203
196
|
|
|
204
197
|
function reorderAnthropicFamily(candidate: string): string | undefined {
|
|
205
|
-
const match =
|
|
198
|
+
const match = REORDER_ANTHROPIC_FAMILY_PATTERN.exec(candidate);
|
|
206
199
|
if (!match) {
|
|
207
200
|
return undefined;
|
|
208
201
|
}
|
|
@@ -211,30 +204,27 @@ function reorderAnthropicFamily(candidate: string): string | undefined {
|
|
|
211
204
|
}
|
|
212
205
|
|
|
213
206
|
function stripProviderVersionSuffix(candidate: string): string | undefined {
|
|
214
|
-
const stripped = candidate.replace(
|
|
207
|
+
const stripped = candidate.replace(STRIP_PROVIDER_VERSION_SUFFIX_PATTERN, "");
|
|
215
208
|
return stripped !== candidate ? stripped : undefined;
|
|
216
209
|
}
|
|
217
210
|
|
|
218
211
|
function stripDateSuffix(candidate: string): string | undefined {
|
|
219
|
-
const stripped = candidate.replace(
|
|
212
|
+
const stripped = candidate.replace(STRIP_DATE_SUFFIX_PATTERN, "");
|
|
220
213
|
return stripped !== candidate ? stripped : undefined;
|
|
221
214
|
}
|
|
222
215
|
|
|
223
216
|
function insertAttachedFamilyVersionSeparator(candidate: string): string | undefined {
|
|
224
|
-
const inserted = candidate.replace(
|
|
225
|
-
/(^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder))(\d+(?:[.-]\d+)*)(?=$|[-_/.:a-z])/gi,
|
|
226
|
-
"$1$2-$3",
|
|
227
|
-
);
|
|
217
|
+
const inserted = candidate.replace(INSERT_ATTACHED_FAMILY_VERSION_SEPARATOR_PATTERN, "$1$2-$3");
|
|
228
218
|
return inserted !== candidate ? inserted : undefined;
|
|
229
219
|
}
|
|
230
220
|
|
|
231
221
|
function toggleSeriesMinorVersionSeparators(candidate: string): string[] {
|
|
232
222
|
const toggled = new Set<string>();
|
|
233
|
-
const dotToDash = candidate.replace(
|
|
223
|
+
const dotToDash = candidate.replace(SERIES_MINOR_DOT_TO_DASH_PATTERN, "$1$2$3-$4");
|
|
234
224
|
if (dotToDash !== candidate) {
|
|
235
225
|
toggled.add(dotToDash);
|
|
236
226
|
}
|
|
237
|
-
const dashToDot = candidate.replace(
|
|
227
|
+
const dashToDot = candidate.replace(SERIES_MINOR_DASH_TO_DOT_PATTERN, "$1$2$3.$4");
|
|
238
228
|
if (dashToDot !== candidate) {
|
|
239
229
|
toggled.add(dashToDot);
|
|
240
230
|
}
|
|
@@ -243,33 +233,51 @@ function toggleSeriesMinorVersionSeparators(candidate: string): string[] {
|
|
|
243
233
|
|
|
244
234
|
function expandCompactSeriesMinorVersions(candidate: string): string[] {
|
|
245
235
|
const expanded = new Set<string>();
|
|
246
|
-
const compactToDash = candidate.replace(
|
|
236
|
+
const compactToDash = candidate.replace(EXPAND_COMPACT_SERIES_MINOR_PATTERN, "$1$2$3-$4");
|
|
247
237
|
if (compactToDash !== candidate) {
|
|
248
238
|
expanded.add(compactToDash);
|
|
249
239
|
}
|
|
250
|
-
const compactToDot = candidate.replace(
|
|
240
|
+
const compactToDot = candidate.replace(EXPAND_COMPACT_SERIES_MINOR_PATTERN, "$1$2$3.$4");
|
|
251
241
|
if (compactToDot !== candidate) {
|
|
252
242
|
expanded.add(compactToDot);
|
|
253
243
|
}
|
|
254
244
|
return [...expanded];
|
|
255
245
|
}
|
|
256
246
|
|
|
247
|
+
// Bounded FIFO memo: pure function of `candidate`. Cached arrays are read-only at
|
|
248
|
+
// every callsite (they are iterated to push into a queue — never mutated), so we
|
|
249
|
+
// safely return the same instance. Cap keeps memory bounded under adversarial
|
|
250
|
+
// model-id churn.
|
|
251
|
+
const QUALIFIED_NAMESPACE_SUFFIX_CACHE = new Map<string, string[]>();
|
|
252
|
+
const QUALIFIED_NAMESPACE_SUFFIX_CACHE_CAP = 256;
|
|
257
253
|
function getQualifiedNamespaceSuffixes(candidate: string): string[] {
|
|
254
|
+
const cached = QUALIFIED_NAMESPACE_SUFFIX_CACHE.get(candidate);
|
|
255
|
+
if (cached !== undefined) {
|
|
256
|
+
return cached;
|
|
257
|
+
}
|
|
258
258
|
const results = new Set<string>();
|
|
259
259
|
for (let index = 1; index < candidate.length; index += 1) {
|
|
260
|
-
if (
|
|
260
|
+
if (!NAMESPACE_SUFFIX_BOUNDARY_PATTERN.test(candidate[index - 1]!)) {
|
|
261
261
|
continue;
|
|
262
262
|
}
|
|
263
263
|
const suffix = candidate.slice(index);
|
|
264
264
|
if (suffix.length < 4) {
|
|
265
265
|
continue;
|
|
266
266
|
}
|
|
267
|
-
if (
|
|
267
|
+
if (!NAMESPACE_SUFFIX_ALPHA_PATTERN.test(suffix) || !NAMESPACE_SUFFIX_DIGIT_PATTERN.test(suffix)) {
|
|
268
268
|
continue;
|
|
269
269
|
}
|
|
270
270
|
addCanonicalCandidate(results, suffix);
|
|
271
271
|
}
|
|
272
|
-
|
|
272
|
+
const output = [...results];
|
|
273
|
+
if (QUALIFIED_NAMESPACE_SUFFIX_CACHE.size >= QUALIFIED_NAMESPACE_SUFFIX_CACHE_CAP) {
|
|
274
|
+
const oldest = QUALIFIED_NAMESPACE_SUFFIX_CACHE.keys().next().value;
|
|
275
|
+
if (oldest !== undefined) {
|
|
276
|
+
QUALIFIED_NAMESPACE_SUFFIX_CACHE.delete(oldest);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
QUALIFIED_NAMESPACE_SUFFIX_CACHE.set(candidate, output);
|
|
280
|
+
return output;
|
|
273
281
|
}
|
|
274
282
|
|
|
275
283
|
function extractUpstreamFamilyCandidate(candidate: string): string | undefined {
|
|
@@ -282,6 +290,15 @@ function extractUpstreamFamilyCandidate(candidate: string): string | undefined {
|
|
|
282
290
|
return undefined;
|
|
283
291
|
}
|
|
284
292
|
|
|
293
|
+
const PENALTY_DATE_SUFFIX = /-\d{8}$/i;
|
|
294
|
+
const PENALTY_PROVIDER_VERSION_SUFFIX = /-v\d+(?::\d+)?$/i;
|
|
295
|
+
const PENALTY_HAS_UPPERCASE = /[A-Z]/;
|
|
296
|
+
const PENALTY_CLAUDE_LEADING_VERSION = /^claude-\d/i;
|
|
297
|
+
const PENALTY_CLAUDE_LEGACY_DATE = /^claude-(?:opus|sonnet|haiku)-\d{2}(?=$|[-_a-z])/i;
|
|
298
|
+
const PENALTY_LETTER_DIGIT_DIGIT = /(?:^|[/:._-])[a-z]\d-\d(?=$|[-_/.:a-z])/i;
|
|
299
|
+
const PENALTY_DIGIT_DIGIT = /(?:^|[-_/])\d-\d(?=$|[-_a-z])/;
|
|
300
|
+
const PENALTY_CLAUDE_FAMILY_DIGIT_DIGIT = /^claude-(?:opus|sonnet|haiku)-\d-\d/i;
|
|
301
|
+
|
|
285
302
|
function getCandidatePenalty(candidate: string): number {
|
|
286
303
|
let penalty = 0;
|
|
287
304
|
if (candidate.includes("/")) {
|
|
@@ -290,28 +307,28 @@ function getCandidatePenalty(candidate: string): number {
|
|
|
290
307
|
if (candidate.includes(":")) {
|
|
291
308
|
penalty += 40;
|
|
292
309
|
}
|
|
293
|
-
if (
|
|
310
|
+
if (PENALTY_DATE_SUFFIX.test(candidate)) {
|
|
294
311
|
penalty += 25;
|
|
295
312
|
}
|
|
296
|
-
if (
|
|
313
|
+
if (PENALTY_PROVIDER_VERSION_SUFFIX.test(candidate)) {
|
|
297
314
|
penalty += 25;
|
|
298
315
|
}
|
|
299
|
-
if (
|
|
316
|
+
if (hasTrailingMarker(candidate)) {
|
|
300
317
|
penalty += 20;
|
|
301
318
|
}
|
|
302
|
-
if (
|
|
319
|
+
if (PENALTY_HAS_UPPERCASE.test(candidate)) {
|
|
303
320
|
penalty += 10;
|
|
304
321
|
}
|
|
305
|
-
if (
|
|
322
|
+
if (PENALTY_CLAUDE_LEADING_VERSION.test(candidate)) {
|
|
306
323
|
penalty += 20;
|
|
307
324
|
}
|
|
308
|
-
if (
|
|
325
|
+
if (PENALTY_CLAUDE_LEGACY_DATE.test(candidate)) {
|
|
309
326
|
penalty += 10;
|
|
310
327
|
}
|
|
311
|
-
if (
|
|
328
|
+
if (PENALTY_LETTER_DIGIT_DIGIT.test(candidate)) {
|
|
312
329
|
penalty += 6;
|
|
313
330
|
}
|
|
314
|
-
if (
|
|
331
|
+
if (PENALTY_DIGIT_DIGIT.test(candidate) && !PENALTY_CLAUDE_FAMILY_DIGIT_DIGIT.test(candidate)) {
|
|
315
332
|
penalty += 4;
|
|
316
333
|
}
|
|
317
334
|
penalty += candidate.length * 0.01;
|
|
@@ -333,8 +350,40 @@ function selectBestOfficialCandidate(candidates: readonly string[]): string | un
|
|
|
333
350
|
if (candidates.length === 0) {
|
|
334
351
|
return undefined;
|
|
335
352
|
}
|
|
336
|
-
|
|
337
|
-
|
|
353
|
+
let bestCandidate: string | undefined;
|
|
354
|
+
let bestPenalty = 0;
|
|
355
|
+
let bestLength = 0;
|
|
356
|
+
for (const candidate of candidates) {
|
|
357
|
+
const penalty = getCandidatePenalty(candidate);
|
|
358
|
+
const length = candidate.length;
|
|
359
|
+
if (bestCandidate === undefined) {
|
|
360
|
+
bestCandidate = candidate;
|
|
361
|
+
bestPenalty = penalty;
|
|
362
|
+
bestLength = length;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (penalty < bestPenalty) {
|
|
366
|
+
bestCandidate = candidate;
|
|
367
|
+
bestPenalty = penalty;
|
|
368
|
+
bestLength = length;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (penalty > bestPenalty) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (length < bestLength) {
|
|
375
|
+
bestCandidate = candidate;
|
|
376
|
+
bestLength = length;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (length > bestLength) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (candidate.localeCompare(bestCandidate) < 0) {
|
|
383
|
+
bestCandidate = candidate;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return bestCandidate;
|
|
338
387
|
}
|
|
339
388
|
|
|
340
389
|
function getWrapperCanonicalCandidates(candidate: string): string[] {
|
|
@@ -405,48 +454,81 @@ function parseClaudeFamilyVersionSegments(candidate: string, prefix: string): nu
|
|
|
405
454
|
return versionSegments;
|
|
406
455
|
}
|
|
407
456
|
|
|
457
|
+
const CLAUDE_FAMILY_ALIAS_PATTERN = /^(?:anthropic\/)?(claude(?:-\d(?:[.-]\d+)?)?-(?:haiku|opus|sonnet))(?:-latest)?$/i;
|
|
458
|
+
const CLAUDE_DATE_SUFFIX_PATTERN = /-\d{8}(?:$|-)/i;
|
|
459
|
+
|
|
408
460
|
function getClaudeFamilyAliasOfficial(candidate: string, officialIds: Set<string>): string | undefined {
|
|
409
|
-
const match =
|
|
461
|
+
const match = CLAUDE_FAMILY_ALIAS_PATTERN.exec(candidate);
|
|
410
462
|
if (!match?.[1]) {
|
|
411
463
|
return undefined;
|
|
412
464
|
}
|
|
413
465
|
const familyPrefix = match[1].toLowerCase();
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
466
|
+
const familyPrefixWithDash = `${familyPrefix}-`;
|
|
467
|
+
|
|
468
|
+
let best: string | undefined;
|
|
469
|
+
let bestVersion: number[] = [];
|
|
470
|
+
let bestHasDate = false;
|
|
471
|
+
let bestHasMarker = false;
|
|
472
|
+
|
|
473
|
+
for (const officialId of officialIds) {
|
|
474
|
+
const normalized = officialId.toLowerCase();
|
|
475
|
+
if (normalized !== familyPrefix && !normalized.startsWith(familyPrefixWithDash)) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
const version = parseClaudeFamilyVersionSegments(officialId, familyPrefix);
|
|
479
|
+
const hasDate = CLAUDE_DATE_SUFFIX_PATTERN.test(officialId);
|
|
480
|
+
const hasMarker = hasTrailingMarker(officialId);
|
|
481
|
+
|
|
482
|
+
if (best === undefined) {
|
|
483
|
+
best = officialId;
|
|
484
|
+
bestVersion = version;
|
|
485
|
+
bestHasDate = hasDate;
|
|
486
|
+
bestHasMarker = hasMarker;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const versionDiff = compareVersionSegments(version, bestVersion);
|
|
426
491
|
if (versionDiff !== 0) {
|
|
427
|
-
|
|
492
|
+
if (versionDiff > 0) {
|
|
493
|
+
best = officialId;
|
|
494
|
+
bestVersion = version;
|
|
495
|
+
bestHasDate = hasDate;
|
|
496
|
+
bestHasMarker = hasMarker;
|
|
497
|
+
}
|
|
498
|
+
continue;
|
|
428
499
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
500
|
+
if (hasDate !== bestHasDate) {
|
|
501
|
+
if (!hasDate) {
|
|
502
|
+
best = officialId;
|
|
503
|
+
bestVersion = version;
|
|
504
|
+
bestHasDate = hasDate;
|
|
505
|
+
bestHasMarker = hasMarker;
|
|
506
|
+
}
|
|
507
|
+
continue;
|
|
433
508
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
509
|
+
if (hasMarker !== bestHasMarker) {
|
|
510
|
+
if (!hasMarker) {
|
|
511
|
+
best = officialId;
|
|
512
|
+
bestVersion = version;
|
|
513
|
+
bestHasMarker = hasMarker;
|
|
514
|
+
}
|
|
515
|
+
continue;
|
|
438
516
|
}
|
|
439
|
-
|
|
440
|
-
|
|
517
|
+
if (compareCandidatePreference(officialId, best) < 0) {
|
|
518
|
+
best = officialId;
|
|
519
|
+
bestVersion = version;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return best;
|
|
441
523
|
}
|
|
442
524
|
|
|
443
525
|
function toggleShortVersionSeparators(candidate: string): string[] {
|
|
444
526
|
const toggled = new Set<string>();
|
|
445
|
-
const dotToDash = candidate.replace(
|
|
527
|
+
const dotToDash = candidate.replace(SHORT_VERSION_DOT_TO_DASH_PATTERN, "$1$2-$3");
|
|
446
528
|
if (dotToDash !== candidate) {
|
|
447
529
|
toggled.add(dotToDash);
|
|
448
530
|
}
|
|
449
|
-
const dashToDot = candidate.replace(
|
|
531
|
+
const dashToDot = candidate.replace(SHORT_VERSION_DASH_TO_DOT_PATTERN, "$1$2.$3");
|
|
450
532
|
if (dashToDot !== candidate) {
|
|
451
533
|
toggled.add(dashToDot);
|
|
452
534
|
}
|
|
@@ -455,115 +537,138 @@ function toggleShortVersionSeparators(candidate: string): string[] {
|
|
|
455
537
|
|
|
456
538
|
function expandCompactMinorVersions(candidate: string): string[] {
|
|
457
539
|
const expanded = new Set<string>();
|
|
458
|
-
const compactToDash = candidate.replace(
|
|
540
|
+
const compactToDash = candidate.replace(EXPAND_COMPACT_MINOR_PATTERN, "$1$2-$3");
|
|
459
541
|
if (compactToDash !== candidate) {
|
|
460
542
|
expanded.add(compactToDash);
|
|
461
543
|
}
|
|
462
|
-
const compactToDot = candidate.replace(
|
|
544
|
+
const compactToDot = candidate.replace(EXPAND_COMPACT_MINOR_PATTERN, "$1$2.$3");
|
|
463
545
|
if (compactToDot !== candidate) {
|
|
464
546
|
expanded.add(compactToDot);
|
|
465
547
|
}
|
|
466
548
|
return [...expanded];
|
|
467
549
|
}
|
|
468
550
|
|
|
469
|
-
function
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
551
|
+
function expandCheapCanonicalCandidates(normalized: string, queue: string[]): void {
|
|
552
|
+
const lowercased = lowercaseCandidate(normalized);
|
|
553
|
+
if (lowercased) {
|
|
554
|
+
queue.push(lowercased);
|
|
555
|
+
}
|
|
473
556
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
const normalized = candidate.trim();
|
|
480
|
-
if (!normalized || visited.has(normalized)) {
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
visited.add(normalized);
|
|
484
|
-
addCanonicalCandidate(candidates, normalized);
|
|
557
|
+
const pathSegments = normalized.split("/");
|
|
558
|
+
for (let index = 1; index < pathSegments.length; index += 1) {
|
|
559
|
+
queue.push(pathSegments.slice(index).join("/"));
|
|
560
|
+
}
|
|
485
561
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
562
|
+
for (const suffix of getQualifiedNamespaceSuffixes(normalized)) {
|
|
563
|
+
queue.push(suffix);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
490
566
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
567
|
+
function expandHeavyCanonicalCandidates(normalized: string, queue: string[]): void {
|
|
568
|
+
for (const toggled of toggleShortVersionSeparators(normalized)) {
|
|
569
|
+
queue.push(toggled);
|
|
570
|
+
}
|
|
495
571
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
572
|
+
const attachedFamilyVersion = insertAttachedFamilyVersionSeparator(normalized);
|
|
573
|
+
if (attachedFamilyVersion) {
|
|
574
|
+
queue.push(attachedFamilyVersion);
|
|
575
|
+
}
|
|
499
576
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
577
|
+
for (const toggledSeriesVersion of toggleSeriesMinorVersionSeparators(normalized)) {
|
|
578
|
+
queue.push(toggledSeriesVersion);
|
|
579
|
+
}
|
|
503
580
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
581
|
+
for (const expandedVersion of expandCompactMinorVersions(normalized)) {
|
|
582
|
+
queue.push(expandedVersion);
|
|
583
|
+
}
|
|
508
584
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
585
|
+
for (const expandedSeriesVersion of expandCompactSeriesMinorVersions(normalized)) {
|
|
586
|
+
queue.push(expandedSeriesVersion);
|
|
587
|
+
}
|
|
512
588
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
589
|
+
for (const wrapperCandidate of getWrapperCanonicalCandidates(normalized)) {
|
|
590
|
+
queue.push(wrapperCandidate);
|
|
591
|
+
}
|
|
516
592
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
593
|
+
const strippedSyntheticPrefix = stripSyntheticPrefix(normalized);
|
|
594
|
+
if (strippedSyntheticPrefix) {
|
|
595
|
+
queue.push(strippedSyntheticPrefix);
|
|
596
|
+
}
|
|
520
597
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
598
|
+
const strippedLatest = stripLatestSuffix(normalized);
|
|
599
|
+
if (strippedLatest) {
|
|
600
|
+
queue.push(strippedLatest);
|
|
601
|
+
}
|
|
524
602
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
603
|
+
const strippedLegacyGlmTurbo = stripLegacyGlmTurboSuffix(normalized);
|
|
604
|
+
if (strippedLegacyGlmTurbo) {
|
|
605
|
+
queue.push(strippedLegacyGlmTurbo);
|
|
606
|
+
}
|
|
529
607
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
608
|
+
const extractedFamily = extractUpstreamFamilyCandidate(normalized);
|
|
609
|
+
if (extractedFamily) {
|
|
610
|
+
queue.push(extractedFamily);
|
|
611
|
+
}
|
|
534
612
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
613
|
+
const strippedProviderVersion = stripProviderVersionSuffix(normalized);
|
|
614
|
+
if (strippedProviderVersion) {
|
|
615
|
+
queue.push(strippedProviderVersion);
|
|
616
|
+
}
|
|
539
617
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
618
|
+
const strippedDate = stripDateSuffix(normalized);
|
|
619
|
+
if (strippedDate) {
|
|
620
|
+
queue.push(strippedDate);
|
|
621
|
+
}
|
|
544
622
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
623
|
+
const strippedMarker = stripTrailingMarker(normalized);
|
|
624
|
+
if (strippedMarker) {
|
|
625
|
+
queue.push(strippedMarker);
|
|
626
|
+
}
|
|
549
627
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
628
|
+
const reorderedAnthropic = reorderAnthropicFamily(normalized);
|
|
629
|
+
if (reorderedAnthropic) {
|
|
630
|
+
queue.push(reorderedAnthropic);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
554
633
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
634
|
+
// Bounded FIFO memo: result depends only on `modelId` (the `_officialIds` param
|
|
635
|
+
// is unused — kept for signature stability). The returned array is consumed via
|
|
636
|
+
// `.filter` at every callsite, so sharing the cached instance is safe.
|
|
637
|
+
const HEURISTIC_CANDIDATES_CACHE = new Map<string, string[]>();
|
|
638
|
+
const HEURISTIC_CANDIDATES_CACHE_CAP = 256;
|
|
639
|
+
function getHeuristicCanonicalCandidates(modelId: string, _officialIds?: ReadonlySet<string>): string[] {
|
|
640
|
+
const cached = HEURISTIC_CANDIDATES_CACHE.get(modelId);
|
|
641
|
+
if (cached !== undefined) {
|
|
642
|
+
return cached;
|
|
643
|
+
}
|
|
644
|
+
const candidates = new Set<string>();
|
|
645
|
+
const queue: string[] = [modelId];
|
|
646
|
+
const visited = new Set<string>();
|
|
559
647
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
648
|
+
for (let qi = 0; qi < queue.length; qi += 1) {
|
|
649
|
+
const candidate = queue[qi];
|
|
650
|
+
if (!candidate) {
|
|
651
|
+
continue;
|
|
563
652
|
}
|
|
653
|
+
const normalized = candidate.trim();
|
|
654
|
+
if (!normalized || visited.has(normalized)) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
visited.add(normalized);
|
|
658
|
+
addCanonicalCandidate(candidates, normalized);
|
|
659
|
+
expandCheapCanonicalCandidates(normalized, queue);
|
|
660
|
+
expandHeavyCanonicalCandidates(normalized, queue);
|
|
564
661
|
}
|
|
565
662
|
|
|
566
|
-
|
|
663
|
+
const output = [...candidates];
|
|
664
|
+
if (HEURISTIC_CANDIDATES_CACHE.size >= HEURISTIC_CANDIDATES_CACHE_CAP) {
|
|
665
|
+
const oldest = HEURISTIC_CANDIDATES_CACHE.keys().next().value;
|
|
666
|
+
if (oldest !== undefined) {
|
|
667
|
+
HEURISTIC_CANDIDATES_CACHE.delete(oldest);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
HEURISTIC_CANDIDATES_CACHE.set(modelId, output);
|
|
671
|
+
return output;
|
|
567
672
|
}
|
|
568
673
|
|
|
569
674
|
function getPreferredFallbackCanonicalCandidate(modelId: string, candidates: readonly string[]): string | undefined {
|
|
@@ -612,7 +717,7 @@ function resolveCanonicalIdForModel(
|
|
|
612
717
|
return { id: claudeFamilyAlias, source: claudeFamilyAlias === model.id ? "bundled" : "heuristic" };
|
|
613
718
|
}
|
|
614
719
|
|
|
615
|
-
const heuristicCandidates = getHeuristicCanonicalCandidates(model.id);
|
|
720
|
+
const heuristicCandidates = getHeuristicCanonicalCandidates(model.id, referenceData.officialIds);
|
|
616
721
|
const officialMatches = heuristicCandidates.filter(candidate => referenceData.officialIds.has(candidate));
|
|
617
722
|
const preferredFallback = getPreferredFallbackCanonicalCandidate(model.id, heuristicCandidates);
|
|
618
723
|
const match = selectBestOfficialCandidate(officialMatches);
|
|
@@ -665,10 +770,11 @@ export function buildCanonicalModelIndex(
|
|
|
665
770
|
const byId = new Map<string, CanonicalModelRecord>();
|
|
666
771
|
const bySelector = new Map<string, string>();
|
|
667
772
|
|
|
668
|
-
|
|
773
|
+
const compiledWithCache = compiledEquivalence as CompiledEquivalenceConfigWithCache;
|
|
774
|
+
let modelCache = compiledWithCache[kModelResolutionCache];
|
|
669
775
|
if (!modelCache) {
|
|
670
776
|
modelCache = new WeakMap<Model<Api>, ResolvedCanonicalModel>();
|
|
671
|
-
|
|
777
|
+
compiledWithCache[kModelResolutionCache] = modelCache;
|
|
672
778
|
}
|
|
673
779
|
|
|
674
780
|
for (const model of models) {
|
|
@@ -688,10 +794,9 @@ export function buildCanonicalModelIndex(
|
|
|
688
794
|
const existing = byId.get(canonicalKey);
|
|
689
795
|
const nextRecord: CanonicalModelRecord = existing ?? {
|
|
690
796
|
id: canonical.id,
|
|
691
|
-
name: getCanonicalRecordName(
|
|
797
|
+
name: getCanonicalRecordName(undefined, canonical.id, variant, referenceData),
|
|
692
798
|
variants: [],
|
|
693
799
|
};
|
|
694
|
-
nextRecord.name = getCanonicalRecordName(existing, canonical.id, variant, referenceData);
|
|
695
800
|
nextRecord.variants.push(variant);
|
|
696
801
|
byId.set(canonicalKey, nextRecord);
|
|
697
802
|
bySelector.set(normalizeSelectorKey(selector), canonical.id);
|