@phi-code-admin/phi-code 0.85.0 → 0.87.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/README.md +92 -123
- package/config/routing.example.json +129 -0
- package/config/routing.schema.json +58 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -6
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +9 -8
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js +4 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/docs/compaction.md +11 -11
- package/docs/custom-provider.md +4 -4
- package/docs/development.md +2 -2
- package/docs/extensions.md +47 -47
- package/docs/fork-policy.md +81 -0
- package/docs/index.md +7 -7
- package/docs/json.md +3 -3
- package/docs/keybindings.md +5 -5
- package/docs/models.md +6 -6
- package/docs/packages.md +37 -37
- package/docs/prompt-templates.md +4 -4
- package/docs/providers.md +9 -9
- package/docs/quickstart.md +25 -25
- package/docs/rpc.md +3 -3
- package/docs/sdk.md +34 -34
- package/docs/session-format.md +4 -4
- package/docs/sessions.md +11 -11
- package/docs/settings.md +9 -9
- package/docs/shell-aliases.md +2 -2
- package/docs/skills.md +9 -9
- package/docs/terminal-setup.md +7 -7
- package/docs/termux.md +6 -6
- package/docs/themes.md +9 -9
- package/docs/tmux.md +3 -3
- package/docs/tui.md +8 -8
- package/docs/usage.md +39 -39
- package/docs/windows.md +2 -2
- package/extensions/phi/agents.ts +12 -117
- package/extensions/phi/benchmark.ts +129 -49
- package/extensions/phi/browser.ts +10 -37
- package/extensions/phi/btw/btw.ts +1 -7
- package/extensions/phi/chrome/index.ts +1283 -741
- package/extensions/phi/commit.ts +9 -14
- package/extensions/phi/goal/index.ts +10 -33
- package/extensions/phi/init.ts +37 -47
- package/extensions/phi/keys.ts +2 -7
- package/extensions/phi/mcp/callback-server.ts +162 -165
- package/extensions/phi/mcp/config.ts +122 -136
- package/extensions/phi/mcp/errors.ts +18 -23
- package/extensions/phi/mcp/index.ts +322 -355
- package/extensions/phi/mcp/oauth-provider.ts +289 -289
- package/extensions/phi/mcp/server-manager.ts +390 -413
- package/extensions/phi/mcp/tool-bridge.ts +381 -415
- package/extensions/phi/memory.ts +2 -2
- package/extensions/phi/models.ts +27 -26
- package/extensions/phi/orchestrator.ts +343 -266
- package/extensions/phi/productivity.ts +4 -2
- package/extensions/phi/providers/agent-def.ts +128 -0
- package/extensions/phi/providers/alibaba.ts +56 -7
- package/extensions/phi/providers/context-window.ts +4 -1
- package/extensions/phi/providers/live-models.ts +5 -20
- package/extensions/phi/providers/orchestrator-helpers.ts +19 -5
- package/extensions/phi/providers/phase-machine.ts +220 -0
- package/extensions/phi/setup.ts +196 -169
- package/extensions/phi/skill-loader.ts +18 -21
- package/extensions/phi/smart-router.ts +6 -6
- package/extensions/phi/web-search.ts +90 -50
- package/package.json +2 -1
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
* - /benchmark clear — Clear all results
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import
|
|
21
|
-
import { writeFile, mkdir, readFile, access } from "node:fs/promises";
|
|
22
|
-
import { join } from "node:path";
|
|
20
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
23
21
|
import { homedir } from "node:os";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import type { ExtensionAPI, ExtensionContext } from "phi-code";
|
|
24
24
|
|
|
25
25
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
26
26
|
|
|
@@ -88,12 +88,12 @@ Respond with ONLY the function code, no explanations.`,
|
|
|
88
88
|
{ test: /(<=\s*0|===?\s*0|<\s*1)/.test(code), detail: "Handles edge case n=0" },
|
|
89
89
|
{ test: /(===?\s*1|<=\s*1)/.test(code), detail: "Handles edge case n=1" },
|
|
90
90
|
];
|
|
91
|
-
const passed = checks.filter(c => c.test).length;
|
|
91
|
+
const passed = checks.filter((c) => c.test).length;
|
|
92
92
|
const total = checks.length;
|
|
93
93
|
return {
|
|
94
94
|
passed: passed >= 5,
|
|
95
95
|
score: Math.round((passed / total) * 100),
|
|
96
|
-
details: checks.map(c => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
96
|
+
details: checks.map((c) => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
97
97
|
};
|
|
98
98
|
},
|
|
99
99
|
},
|
|
@@ -125,16 +125,29 @@ Explain the bug and provide the fixed code.`,
|
|
|
125
125
|
validate: (response: string) => {
|
|
126
126
|
const lower = response.toLowerCase();
|
|
127
127
|
const checks = [
|
|
128
|
-
{
|
|
129
|
-
|
|
128
|
+
{
|
|
129
|
+
test: /reference|shallow|copy|spread|\[\.\.\./.test(lower),
|
|
130
|
+
detail: "Identifies reference/copy issue",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
test: /\[\.\.\.arr1\]|\[\.\.\.arr1,|Array\.from|\.slice\(\)|structuredClone|concat/.test(response),
|
|
134
|
+
detail: "Uses spread/copy/concat fix",
|
|
135
|
+
},
|
|
130
136
|
{ test: /mutate|modify|original|side.?effect/.test(lower), detail: "Explains the mutation problem" },
|
|
131
|
-
{
|
|
137
|
+
{
|
|
138
|
+
test:
|
|
139
|
+
/const result\s*=\s*\[/.test(response) ||
|
|
140
|
+
/\.slice\(/.test(response) ||
|
|
141
|
+
/\.concat\(/.test(response) ||
|
|
142
|
+
/Array\.from/.test(response),
|
|
143
|
+
detail: "Creates new array in fix",
|
|
144
|
+
},
|
|
132
145
|
];
|
|
133
|
-
const passed = checks.filter(c => c.test).length;
|
|
146
|
+
const passed = checks.filter((c) => c.test).length;
|
|
134
147
|
return {
|
|
135
148
|
passed: passed >= 3,
|
|
136
149
|
score: Math.round((passed / checks.length) * 100),
|
|
137
|
-
details: checks.map(c => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
150
|
+
details: checks.map((c) => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
138
151
|
};
|
|
139
152
|
},
|
|
140
153
|
},
|
|
@@ -170,11 +183,11 @@ Provide a structured plan with specific files to create/modify, dependencies to
|
|
|
170
183
|
{ test: /env|secret|config/.test(lower), detail: "Addresses secret management" },
|
|
171
184
|
{ test: /step|phase|\d\.|create|modify|add/.test(lower), detail: "Provides structured steps" },
|
|
172
185
|
];
|
|
173
|
-
const passed = checks.filter(c => c.test).length;
|
|
186
|
+
const passed = checks.filter((c) => c.test).length;
|
|
174
187
|
return {
|
|
175
188
|
passed: passed >= 6,
|
|
176
189
|
score: Math.round((passed / checks.length) * 100),
|
|
177
|
-
details: checks.map(c => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
190
|
+
details: checks.map((c) => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
178
191
|
};
|
|
179
192
|
},
|
|
180
193
|
},
|
|
@@ -219,9 +232,15 @@ Output ONLY the JSON.`,
|
|
|
219
232
|
checks.push({ test: obj?.name?.first === "Alice", detail: 'name.first = "Alice"' });
|
|
220
233
|
checks.push({ test: obj?.name?.last === "Smith", detail: 'name.last = "Smith"' });
|
|
221
234
|
checks.push({ test: obj?.email === "alice@example.com", detail: "Correct email" });
|
|
222
|
-
checks.push({
|
|
235
|
+
checks.push({
|
|
236
|
+
test: typeof obj?.profile?.age === "number" && obj.profile.age === 28,
|
|
237
|
+
detail: "Age is number 28",
|
|
238
|
+
});
|
|
223
239
|
checks.push({ test: obj?.preferences?.theme === "dark", detail: 'theme = "dark"' });
|
|
224
|
-
checks.push({
|
|
240
|
+
checks.push({
|
|
241
|
+
test: obj?.preferences?.notifications?.email === true,
|
|
242
|
+
detail: "email notifications = true",
|
|
243
|
+
});
|
|
225
244
|
} catch {
|
|
226
245
|
checks.push({ test: false, detail: "Valid JSON (parse failed)" });
|
|
227
246
|
checks.push({ test: false, detail: "name.first" });
|
|
@@ -232,11 +251,11 @@ Output ONLY the JSON.`,
|
|
|
232
251
|
checks.push({ test: false, detail: "notifications" });
|
|
233
252
|
}
|
|
234
253
|
|
|
235
|
-
const passed = checks.filter(c => c.test).length;
|
|
254
|
+
const passed = checks.filter((c) => c.test).length;
|
|
236
255
|
return {
|
|
237
256
|
passed: passed >= 5,
|
|
238
257
|
score: Math.round((passed / checks.length) * 100),
|
|
239
|
-
details: checks.map(c => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
258
|
+
details: checks.map((c) => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
240
259
|
};
|
|
241
260
|
},
|
|
242
261
|
},
|
|
@@ -248,7 +267,11 @@ Output ONLY the JSON.`,
|
|
|
248
267
|
weight: 1,
|
|
249
268
|
prompt: `Reply with exactly this text and nothing else: "Hello, World!"`,
|
|
250
269
|
validate: (response: string) => {
|
|
251
|
-
const trimmed = response
|
|
270
|
+
const trimmed = response
|
|
271
|
+
.trim()
|
|
272
|
+
.replace(/^["']|["']$/g, "")
|
|
273
|
+
.replace(/```\w*\n?/g, "")
|
|
274
|
+
.trim();
|
|
252
275
|
const exact = trimmed === "Hello, World!";
|
|
253
276
|
const close = trimmed.toLowerCase().includes("hello, world");
|
|
254
277
|
return {
|
|
@@ -287,15 +310,23 @@ Be specific and technical.`,
|
|
|
287
310
|
{ test: /memory.?leak|leak/.test(lower), detail: "Identifies memory leak" },
|
|
288
311
|
{ test: /stream|buffer|file|upload|temp|cleanup/.test(lower), detail: "Links to file upload handling" },
|
|
289
312
|
{ test: /close|destroy|cleanup|dispose|gc|garbage/.test(lower), detail: "Suggests resource cleanup" },
|
|
290
|
-
{
|
|
291
|
-
|
|
313
|
+
{
|
|
314
|
+
test: /event.?listener|handler|remove|off/.test(lower) || /stream|pipe/.test(lower),
|
|
315
|
+
detail: "Checks for handler/stream leaks",
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
test:
|
|
319
|
+
/heapdump|heap.?snapshot|inspect|profile|--max-old-space/.test(lower) ||
|
|
320
|
+
/process\.memoryUsage/.test(lower),
|
|
321
|
+
detail: "Suggests debugging tools",
|
|
322
|
+
},
|
|
292
323
|
{ test: /monitor|alert|metric|prometheus|grafana|threshold/.test(lower), detail: "Suggests monitoring" },
|
|
293
324
|
];
|
|
294
|
-
const passed = checks.filter(c => c.test).length;
|
|
325
|
+
const passed = checks.filter((c) => c.test).length;
|
|
295
326
|
return {
|
|
296
327
|
passed: passed >= 4,
|
|
297
328
|
score: Math.round((passed / checks.length) * 100),
|
|
298
|
-
details: checks.map(c => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
329
|
+
details: checks.map((c) => `${c.test ? "✅" : "❌"} ${c.detail}`).join("\n"),
|
|
299
330
|
};
|
|
300
331
|
},
|
|
301
332
|
},
|
|
@@ -322,7 +353,16 @@ function getProviderConfigs(): ProviderConfig[] {
|
|
|
322
353
|
name: "alibaba-codingplan",
|
|
323
354
|
envVar: "ALIBABA_CODING_PLAN_KEY",
|
|
324
355
|
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1",
|
|
325
|
-
models: [
|
|
356
|
+
models: [
|
|
357
|
+
"qwen3.5-plus",
|
|
358
|
+
"qwen3-max-2026-01-23",
|
|
359
|
+
"qwen3-coder-plus",
|
|
360
|
+
"qwen3-coder-next",
|
|
361
|
+
"kimi-k2.5",
|
|
362
|
+
"glm-5",
|
|
363
|
+
"glm-4.7",
|
|
364
|
+
"MiniMax-M2.5",
|
|
365
|
+
],
|
|
326
366
|
},
|
|
327
367
|
{
|
|
328
368
|
name: "openai",
|
|
@@ -386,14 +426,16 @@ async function getAvailableModels(): Promise<Array<{ id: string; provider: strin
|
|
|
386
426
|
for (const m of providerConfig.models) {
|
|
387
427
|
const modelId = typeof m === "string" ? m : m.id;
|
|
388
428
|
// Skip if already added from env vars
|
|
389
|
-
if (!models.some(existing => existing.id === modelId && existing.baseUrl === baseUrl)) {
|
|
429
|
+
if (!models.some((existing) => existing.id === modelId && existing.baseUrl === baseUrl)) {
|
|
390
430
|
models.push({ id: modelId, provider: id, baseUrl, apiKey });
|
|
391
431
|
}
|
|
392
432
|
}
|
|
393
433
|
}
|
|
394
434
|
}
|
|
395
435
|
}
|
|
396
|
-
} catch {
|
|
436
|
+
} catch {
|
|
437
|
+
/* ignore parse errors */
|
|
438
|
+
}
|
|
397
439
|
}
|
|
398
440
|
|
|
399
441
|
// 3. Try to detect LM Studio (port 1234) and Ollama (port 11434) directly
|
|
@@ -402,7 +444,7 @@ async function getAvailableModels(): Promise<Array<{ id: string; provider: strin
|
|
|
402
444
|
{ name: "ollama", port: 11434, baseUrl: "http://localhost:11434/v1" },
|
|
403
445
|
]) {
|
|
404
446
|
// Skip if already discovered via models.json
|
|
405
|
-
if (models.some(m => m.baseUrl === local.baseUrl)) continue;
|
|
447
|
+
if (models.some((m) => m.baseUrl === local.baseUrl)) continue;
|
|
406
448
|
|
|
407
449
|
try {
|
|
408
450
|
const controller = new AbortController();
|
|
@@ -411,16 +453,18 @@ async function getAvailableModels(): Promise<Array<{ id: string; provider: strin
|
|
|
411
453
|
clearTimeout(timeout);
|
|
412
454
|
|
|
413
455
|
if (resp.ok) {
|
|
414
|
-
const data = await resp.json() as any;
|
|
456
|
+
const data = (await resp.json()) as any;
|
|
415
457
|
const modelList = data?.data || [];
|
|
416
458
|
for (const m of modelList) {
|
|
417
459
|
const modelId = m.id || m.name;
|
|
418
|
-
if (modelId && !models.some(existing => existing.id === modelId)) {
|
|
460
|
+
if (modelId && !models.some((existing) => existing.id === modelId)) {
|
|
419
461
|
models.push({ id: modelId, provider: local.name, baseUrl: local.baseUrl, apiKey: "local" });
|
|
420
462
|
}
|
|
421
463
|
}
|
|
422
464
|
}
|
|
423
|
-
} catch {
|
|
465
|
+
} catch {
|
|
466
|
+
/* not running */
|
|
467
|
+
}
|
|
424
468
|
}
|
|
425
469
|
|
|
426
470
|
return models;
|
|
@@ -514,7 +558,7 @@ export default function benchmarkExtension(pi: ExtensionAPI) {
|
|
|
514
558
|
for (let testIdx = 0; testIdx < tests.length; testIdx++) {
|
|
515
559
|
const test = tests[testIdx];
|
|
516
560
|
// Rate limiting: 1.5s between API calls to avoid throttling
|
|
517
|
-
if (testIdx > 0) await new Promise(r => setTimeout(r, 1500));
|
|
561
|
+
if (testIdx > 0) await new Promise((r) => setTimeout(r, 1500));
|
|
518
562
|
ctx.ui.notify(` ⏳ ${test.category}: ${test.name}...`, "info");
|
|
519
563
|
|
|
520
564
|
try {
|
|
@@ -586,15 +630,24 @@ export default function benchmarkExtension(pi: ExtensionAPI) {
|
|
|
586
630
|
report += "**Leaderboard:**\n";
|
|
587
631
|
sorted.forEach((r, i) => {
|
|
588
632
|
const medal = i === 0 ? "🥇" : i === 1 ? "🥈" : i === 2 ? "🥉" : `${i + 1}.`;
|
|
589
|
-
const tier =
|
|
633
|
+
const tier =
|
|
634
|
+
r.totalScore >= 80
|
|
635
|
+
? "S"
|
|
636
|
+
: r.totalScore >= 65
|
|
637
|
+
? "A"
|
|
638
|
+
: r.totalScore >= 50
|
|
639
|
+
? "B"
|
|
640
|
+
: r.totalScore >= 35
|
|
641
|
+
? "C"
|
|
642
|
+
: "D";
|
|
590
643
|
report += `${medal} **${r.modelId}** — ${r.totalScore}/100 [${tier}] (avg ${r.avgTimeMs}ms)\n`;
|
|
591
644
|
});
|
|
592
645
|
|
|
593
646
|
// Category breakdown
|
|
594
647
|
report += "\n**Category Breakdown:**\n```\n";
|
|
595
|
-
const header = "Model".padEnd(25) + categories.map(c => c.substring(0, 8).padEnd(10)).join("")
|
|
648
|
+
const header = `${"Model".padEnd(25) + categories.map((c) => c.substring(0, 8).padEnd(10)).join("")}TOTAL\n`;
|
|
596
649
|
report += header;
|
|
597
|
-
report += "-".repeat(header.length)
|
|
650
|
+
report += `${"-".repeat(header.length)}\n`;
|
|
598
651
|
|
|
599
652
|
for (const r of sorted) {
|
|
600
653
|
let line = r.modelId.substring(0, 24).padEnd(25);
|
|
@@ -603,7 +656,7 @@ export default function benchmarkExtension(pi: ExtensionAPI) {
|
|
|
603
656
|
line += String(score).padEnd(10);
|
|
604
657
|
}
|
|
605
658
|
line += String(r.totalScore);
|
|
606
|
-
report += line
|
|
659
|
+
report += `${line}\n`;
|
|
607
660
|
}
|
|
608
661
|
report += "```\n";
|
|
609
662
|
|
|
@@ -627,7 +680,8 @@ export default function benchmarkExtension(pi: ExtensionAPI) {
|
|
|
627
680
|
// ─── Command ─────────────────────────────────────────────────────────
|
|
628
681
|
|
|
629
682
|
pi.registerCommand("benchmark", {
|
|
630
|
-
description:
|
|
683
|
+
description:
|
|
684
|
+
"Run AI model benchmarks (6 categories: code-gen, debug, planning, tool-calling, speed, orchestration)",
|
|
631
685
|
handler: async (args, ctx) => {
|
|
632
686
|
const arg = args.trim().toLowerCase();
|
|
633
687
|
|
|
@@ -658,7 +712,8 @@ export default function benchmarkExtension(pi: ExtensionAPI) {
|
|
|
658
712
|
|
|
659
713
|
// Help
|
|
660
714
|
if (arg === "help" || arg === "?") {
|
|
661
|
-
ctx.ui.notify(
|
|
715
|
+
ctx.ui.notify(
|
|
716
|
+
`**Phi Code Benchmark** — 6 categories, real API calls
|
|
662
717
|
|
|
663
718
|
Commands:
|
|
664
719
|
/benchmark Run on current model
|
|
@@ -676,7 +731,9 @@ Categories tested (weighted):
|
|
|
676
731
|
⏱️ speed (×1) — Response latency
|
|
677
732
|
🧩 orchestration (×2) — Multi-step analysis
|
|
678
733
|
|
|
679
|
-
Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`,
|
|
734
|
+
Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`,
|
|
735
|
+
"info",
|
|
736
|
+
);
|
|
680
737
|
return;
|
|
681
738
|
}
|
|
682
739
|
|
|
@@ -684,8 +741,13 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
684
741
|
const available = await getAvailableModels();
|
|
685
742
|
if (available.length === 0) {
|
|
686
743
|
const providers = getProviderConfigs();
|
|
687
|
-
const hint = providers
|
|
688
|
-
|
|
744
|
+
const hint = providers
|
|
745
|
+
.map((p) => ` ${p.envVar}: ${process.env[p.envVar] ? "set but no models configured" : "not set"}`)
|
|
746
|
+
.join("\n");
|
|
747
|
+
ctx.ui.notify(
|
|
748
|
+
`❌ No benchmarkable models found.\n\nProvider status:\n${hint}\n\nSet at least one API key with known models.`,
|
|
749
|
+
"warning",
|
|
750
|
+
);
|
|
689
751
|
return;
|
|
690
752
|
}
|
|
691
753
|
|
|
@@ -700,7 +762,7 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
700
762
|
const result = await benchmarkModel(model.id, model.provider, model.baseUrl, model.apiKey, ctx);
|
|
701
763
|
|
|
702
764
|
// Replace existing result for this model
|
|
703
|
-
store.results = store.results.filter(r => r.modelId !== model.id);
|
|
765
|
+
store.results = store.results.filter((r) => r.modelId !== model.id);
|
|
704
766
|
store.results.push(result);
|
|
705
767
|
await saveStore(store);
|
|
706
768
|
}
|
|
@@ -713,21 +775,27 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
713
775
|
if (arg) {
|
|
714
776
|
// Benchmark specific model: prefer exact id match, then require an
|
|
715
777
|
// unambiguous substring match to avoid benchmarking the wrong model.
|
|
716
|
-
const exact = available.find(m => m.id.toLowerCase() === arg);
|
|
717
|
-
const matches = available.filter(m => m.id.toLowerCase().includes(arg));
|
|
778
|
+
const exact = available.find((m) => m.id.toLowerCase() === arg);
|
|
779
|
+
const matches = available.filter((m) => m.id.toLowerCase().includes(arg));
|
|
718
780
|
const model = exact ?? (matches.length === 1 ? matches[0] : undefined);
|
|
719
781
|
if (!model) {
|
|
720
782
|
if (!exact && matches.length > 1) {
|
|
721
|
-
ctx.ui.notify(
|
|
783
|
+
ctx.ui.notify(
|
|
784
|
+
`Model "${arg}" is ambiguous. Did you mean:\n${matches.map((m) => ` - ${m.id} (${m.provider})`).join("\n")}`,
|
|
785
|
+
"warning",
|
|
786
|
+
);
|
|
722
787
|
return;
|
|
723
788
|
}
|
|
724
|
-
ctx.ui.notify(
|
|
789
|
+
ctx.ui.notify(
|
|
790
|
+
`Model "${arg}" not found or no API key. Available:\n${available.map((m) => ` - ${m.id} (${m.provider})`).join("\n")}`,
|
|
791
|
+
"warning",
|
|
792
|
+
);
|
|
725
793
|
return;
|
|
726
794
|
}
|
|
727
795
|
|
|
728
796
|
ctx.ui.notify(`🧪 Benchmarking **${model.id}** (6 categories)...\n`, "info");
|
|
729
797
|
const result = await benchmarkModel(model.id, model.provider, model.baseUrl, model.apiKey, ctx);
|
|
730
|
-
store.results = store.results.filter(r => r.modelId !== model.id);
|
|
798
|
+
store.results = store.results.filter((r) => r.modelId !== model.id);
|
|
731
799
|
store.results.push(result);
|
|
732
800
|
await saveStore(store);
|
|
733
801
|
|
|
@@ -739,11 +807,17 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
739
807
|
// Try to find current model in available list
|
|
740
808
|
const currentModel = ctx.model;
|
|
741
809
|
if (currentModel) {
|
|
742
|
-
const modelConfig = available.find(m => m.id === currentModel.id);
|
|
810
|
+
const modelConfig = available.find((m) => m.id === currentModel.id);
|
|
743
811
|
if (modelConfig) {
|
|
744
812
|
ctx.ui.notify(`🧪 Benchmarking current model **${currentModel.id}** (6 categories)...\n`, "info");
|
|
745
|
-
const result = await benchmarkModel(
|
|
746
|
-
|
|
813
|
+
const result = await benchmarkModel(
|
|
814
|
+
modelConfig.id,
|
|
815
|
+
modelConfig.provider,
|
|
816
|
+
modelConfig.baseUrl,
|
|
817
|
+
modelConfig.apiKey,
|
|
818
|
+
ctx,
|
|
819
|
+
);
|
|
820
|
+
store.results = store.results.filter((r) => r.modelId !== modelConfig.id);
|
|
747
821
|
store.results.push(result);
|
|
748
822
|
await saveStore(store);
|
|
749
823
|
ctx.ui.notify(`\n✅ **${currentModel.id}** — Total: ${result.totalScore}/100`, "info");
|
|
@@ -752,7 +826,10 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
752
826
|
}
|
|
753
827
|
|
|
754
828
|
// Fallback: show available models
|
|
755
|
-
ctx.ui.notify(
|
|
829
|
+
ctx.ui.notify(
|
|
830
|
+
`Available models for benchmark:\n${available.map((m) => ` - ${m.id} (${m.provider})`).join("\n")}\n\nUsage: /benchmark <model-id> or /benchmark all`,
|
|
831
|
+
"info",
|
|
832
|
+
);
|
|
756
833
|
},
|
|
757
834
|
});
|
|
758
835
|
|
|
@@ -761,7 +838,10 @@ Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)`, "info");
|
|
|
761
838
|
try {
|
|
762
839
|
const store = await loadStore();
|
|
763
840
|
if (store.results.length > 0) {
|
|
764
|
-
ctx.ui.notify(
|
|
841
|
+
ctx.ui.notify(
|
|
842
|
+
`🧪 ${store.results.length} benchmark results available. /benchmark results to view.`,
|
|
843
|
+
"info",
|
|
844
|
+
);
|
|
765
845
|
}
|
|
766
846
|
} catch {
|
|
767
847
|
// ignore
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* user keeps the legacy `web_search` / `fetch_url` only).
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { createRequire } from "node:module";
|
|
26
25
|
import { existsSync } from "node:fs";
|
|
26
|
+
import { createRequire } from "node:module";
|
|
27
27
|
import { dirname, join } from "node:path";
|
|
28
28
|
import { pathToFileURL } from "node:url";
|
|
29
29
|
import { Type } from "@sinclair/typebox";
|
|
@@ -129,11 +129,7 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
129
129
|
url: Type.String({ description: "Full URL (https://...)" }),
|
|
130
130
|
tabId: Type.Optional(Type.String()),
|
|
131
131
|
waitUntil: Type.Optional(
|
|
132
|
-
Type.Union([
|
|
133
|
-
Type.Literal("load"),
|
|
134
|
-
Type.Literal("domcontentloaded"),
|
|
135
|
-
Type.Literal("networkidle"),
|
|
136
|
-
]),
|
|
132
|
+
Type.Union([Type.Literal("load"), Type.Literal("domcontentloaded"), Type.Literal("networkidle")]),
|
|
137
133
|
),
|
|
138
134
|
timeoutMs: Type.Optional(Type.Number()),
|
|
139
135
|
}),
|
|
@@ -161,13 +157,7 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
161
157
|
parameters: Type.Object({
|
|
162
158
|
tabId: Type.Optional(Type.String()),
|
|
163
159
|
url: Type.Optional(Type.String()),
|
|
164
|
-
mode: Type.Optional(
|
|
165
|
-
Type.Union([
|
|
166
|
-
Type.Literal("readability"),
|
|
167
|
-
Type.Literal("html"),
|
|
168
|
-
Type.Literal("text"),
|
|
169
|
-
]),
|
|
170
|
-
),
|
|
160
|
+
mode: Type.Optional(Type.Union([Type.Literal("readability"), Type.Literal("html"), Type.Literal("text")])),
|
|
171
161
|
}),
|
|
172
162
|
execute: async (_toolCallId, params) => {
|
|
173
163
|
const api = await getBrowserApi();
|
|
@@ -181,9 +171,9 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
181
171
|
name: "browser_screenshot",
|
|
182
172
|
description:
|
|
183
173
|
"Capture a PNG screenshot of an open tab. Use this whenever the user asks " +
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
174
|
+
'to *see* a page, when a visual proof is requested (e.g. "show me what ' +
|
|
175
|
+
'this looks like", "is the layout broken", "did the bot detection page ' +
|
|
176
|
+
'trigger?"), or to confirm a UI state after `browser_click` / ' +
|
|
187
177
|
"`browser_type`. Requires a `tabId` from a prior `browser_navigate`. " +
|
|
188
178
|
"Returns the image as base64 under `bytesBase64` with `mimeType: image/png`.",
|
|
189
179
|
parameters: Type.Object({
|
|
@@ -208,16 +198,10 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
208
198
|
"the rendered search engine UI (e.g. featured snippets, knowledge cards, " +
|
|
209
199
|
"AI Overview boxes). Slower than `web_search` and requires the Camoufox " +
|
|
210
200
|
"browser to boot. Defaults to DuckDuckGo (least restrictive); pass " +
|
|
211
|
-
|
|
201
|
+
'`engine: "google"` only when you need Google-specific results.',
|
|
212
202
|
parameters: Type.Object({
|
|
213
203
|
query: Type.String(),
|
|
214
|
-
engine: Type.Optional(
|
|
215
|
-
Type.Union([
|
|
216
|
-
Type.Literal("google"),
|
|
217
|
-
Type.Literal("duckduckgo"),
|
|
218
|
-
Type.Literal("bing"),
|
|
219
|
-
]),
|
|
220
|
-
),
|
|
204
|
+
engine: Type.Optional(Type.Union([Type.Literal("google"), Type.Literal("duckduckgo"), Type.Literal("bing")])),
|
|
221
205
|
}),
|
|
222
206
|
execute: async (_toolCallId, params) => {
|
|
223
207
|
const api = await getBrowserApi();
|
|
@@ -243,13 +227,7 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
243
227
|
tabId: Type.String(),
|
|
244
228
|
ref: Type.Optional(Type.String()),
|
|
245
229
|
selector: Type.Optional(Type.String()),
|
|
246
|
-
button: Type.Optional(
|
|
247
|
-
Type.Union([
|
|
248
|
-
Type.Literal("left"),
|
|
249
|
-
Type.Literal("right"),
|
|
250
|
-
Type.Literal("middle"),
|
|
251
|
-
]),
|
|
252
|
-
),
|
|
230
|
+
button: Type.Optional(Type.Union([Type.Literal("left"), Type.Literal("right"), Type.Literal("middle")])),
|
|
253
231
|
}),
|
|
254
232
|
execute: async (_toolCallId, params) => {
|
|
255
233
|
const api = await getBrowserApi();
|
|
@@ -296,12 +274,7 @@ export default function browserExtension(pi: ExtensionAPI) {
|
|
|
296
274
|
"content.",
|
|
297
275
|
parameters: Type.Object({
|
|
298
276
|
tabId: Type.String(),
|
|
299
|
-
direction: Type.Union([
|
|
300
|
-
Type.Literal("up"),
|
|
301
|
-
Type.Literal("down"),
|
|
302
|
-
Type.Literal("left"),
|
|
303
|
-
Type.Literal("right"),
|
|
304
|
-
]),
|
|
277
|
+
direction: Type.Union([Type.Literal("up"), Type.Literal("down"), Type.Literal("left"), Type.Literal("right")]),
|
|
305
278
|
ref: Type.Optional(Type.String()),
|
|
306
279
|
pixels: Type.Optional(Type.Number()),
|
|
307
280
|
}),
|
|
@@ -9,13 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
-
import {
|
|
13
|
-
type AssistantMessage,
|
|
14
|
-
completeSimple,
|
|
15
|
-
type Message,
|
|
16
|
-
type StopReason,
|
|
17
|
-
type UserMessage,
|
|
18
|
-
} from "phi-code-ai";
|
|
19
12
|
import {
|
|
20
13
|
convertToLlm,
|
|
21
14
|
type ExtensionAPI,
|
|
@@ -23,6 +16,7 @@ import {
|
|
|
23
16
|
type ExtensionContext,
|
|
24
17
|
type SessionEntry,
|
|
25
18
|
} from "phi-code";
|
|
19
|
+
import { type AssistantMessage, completeSimple, type Message, type StopReason, type UserMessage } from "phi-code-ai";
|
|
26
20
|
import { showBtwOverlay } from "./btw-ui.js";
|
|
27
21
|
|
|
28
22
|
// ---------------------------------------------------------------------------
|