@sean.holung/minicode 0.3.4 → 0.3.6
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/README.md +25 -47
- package/dist/scripts/run-benchmarks.js +73 -28
- package/dist/src/agent/config.js +51 -66
- package/dist/src/agent/editable-config.js +50 -58
- package/dist/src/agent/home-env.js +74 -0
- package/dist/src/benchmark/runner.js +142 -59
- package/dist/src/cli/config-slash-command.js +15 -13
- package/dist/src/indexer/project-index.js +49 -13
- package/dist/src/serve/agent-bridge.js +99 -31
- package/dist/src/serve/mcp-server.js +70 -21
- package/dist/src/serve/server.js +198 -8
- package/dist/src/session/session-preview.js +14 -0
- package/dist/src/shared/graph-search.js +80 -0
- package/dist/src/shared/graph-selection.js +40 -0
- package/dist/src/shared/graph-symbols.js +82 -0
- package/dist/src/shared/symbol-resolution.js +33 -0
- package/dist/src/tools/find-path.js +15 -6
- package/dist/src/tools/find-references.js +7 -2
- package/dist/src/tools/get-dependencies.js +8 -3
- package/dist/src/tools/read-symbol.js +9 -3
- package/dist/src/tools/registry.js +4 -1
- package/dist/src/tools/search-code-map.js +18 -3
- package/dist/src/web/app.js +646 -87
- package/dist/src/web/index.html +68 -6
- package/dist/src/web/style.css +208 -1
- package/dist/tests/benchmark-harness.test.js +100 -0
- package/dist/tests/config-api.test.js +5 -5
- package/dist/tests/config-integration.test.js +130 -56
- package/dist/tests/config-slash-command.test.js +12 -11
- package/dist/tests/config.test.js +12 -4
- package/dist/tests/editable-config.test.js +15 -12
- package/dist/tests/file-tools.test.js +34 -1
- package/dist/tests/find-path.test.js +43 -2
- package/dist/tests/find-references.test.js +49 -0
- package/dist/tests/get-dependencies.test.js +23 -0
- package/dist/tests/graph-onboarding.test.js +10 -1
- package/dist/tests/graph-search.test.js +66 -0
- package/dist/tests/graph-selection.test.js +58 -0
- package/dist/tests/graph-symbols.test.js +45 -0
- package/dist/tests/home-env.test.js +56 -0
- package/dist/tests/indexer.test.js +6 -0
- package/dist/tests/read-symbol.test.js +35 -0
- package/dist/tests/request-tracker.test.js +15 -0
- package/dist/tests/run-benchmarks.test.js +117 -33
- package/dist/tests/search-code-map.test.js +2 -0
- package/dist/tests/serve.integration.test.js +338 -9
- package/dist/tests/session-preview.test.js +56 -0
- package/dist/tests/session-ui.test.js +4 -0
- package/dist/tests/settings-ui.test.js +18 -0
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js +2 -1
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/types.d.ts +3 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/types.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/registry.d.ts +3 -0
- package/node_modules/@minicode/agent-sdk/dist/src/tools/registry.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/registry.js +4 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/registry.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.d.ts +11 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.js +4 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/search.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/search.js +16 -8
- package/node_modules/@minicode/agent-sdk/dist/src/tools/search.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js +19 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test, afterEach } from "node:test";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
|
-
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
4
|
+
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
5
7
|
import { createRequestHandler, shutdownServe } from "../src/serve/server.js";
|
|
6
8
|
import { AgentBridge } from "../src/serve/agent-bridge.js";
|
|
9
|
+
import { Session } from "@minicode/agent-sdk";
|
|
7
10
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
8
11
|
/**
|
|
9
12
|
* Lightweight AgentBridge subclass for testing.
|
|
@@ -12,7 +15,11 @@ import { createTestAgentConfig } from "./test-utils.js";
|
|
|
12
15
|
class MockBridge extends AgentBridge {
|
|
13
16
|
_busy = false;
|
|
14
17
|
_currentSessionId = "sess-1";
|
|
18
|
+
_baseConfig = createTestAgentConfig("/tmp/test-workspace");
|
|
19
|
+
_config = createTestAgentConfig("/tmp/test-workspace");
|
|
15
20
|
turnHistory = [];
|
|
21
|
+
openRouterKey;
|
|
22
|
+
openRouterSessionActive = false;
|
|
16
23
|
constructor() {
|
|
17
24
|
super(() => { }, false);
|
|
18
25
|
}
|
|
@@ -20,7 +27,7 @@ class MockBridge extends AgentBridge {
|
|
|
20
27
|
return this._busy;
|
|
21
28
|
}
|
|
22
29
|
getConfig() {
|
|
23
|
-
return
|
|
30
|
+
return this._config;
|
|
24
31
|
}
|
|
25
32
|
async runTurn(message) {
|
|
26
33
|
if (this._busy)
|
|
@@ -65,11 +72,47 @@ class MockBridge extends AgentBridge {
|
|
|
65
72
|
async loadSess(label) {
|
|
66
73
|
if (label === "nonexistent")
|
|
67
74
|
return null;
|
|
68
|
-
|
|
75
|
+
const session = new Session("sess-1");
|
|
76
|
+
session.addMessage({
|
|
77
|
+
role: "user",
|
|
78
|
+
content: "[Conversation Summary — earlier messages were compacted to save context]\nOlder summary",
|
|
79
|
+
});
|
|
80
|
+
for (let i = 1; i <= 11; i += 1) {
|
|
81
|
+
session.addMessage({
|
|
82
|
+
role: i % 2 === 0 ? "assistant" : "user",
|
|
83
|
+
content: `message-${i}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return { session, label };
|
|
69
87
|
}
|
|
70
88
|
getCurrentSessionId() {
|
|
71
89
|
return this._currentSessionId;
|
|
72
90
|
}
|
|
91
|
+
connectOpenRouter(apiKey) {
|
|
92
|
+
this.openRouterKey = apiKey;
|
|
93
|
+
this.openRouterSessionActive = true;
|
|
94
|
+
this._config.modelProvider = "openai-compatible";
|
|
95
|
+
this._config.openAiBaseUrl = "https://openrouter.ai/api/v1";
|
|
96
|
+
this._config.openAiApiKey = apiKey;
|
|
97
|
+
}
|
|
98
|
+
disconnectOpenRouter() {
|
|
99
|
+
if (!this.openRouterSessionActive) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
this.openRouterSessionActive = false;
|
|
103
|
+
this.openRouterKey = undefined;
|
|
104
|
+
this._config.modelProvider = this._baseConfig.modelProvider;
|
|
105
|
+
this._config.model = this._baseConfig.model;
|
|
106
|
+
this._config.openAiBaseUrl = this._baseConfig.openAiBaseUrl;
|
|
107
|
+
delete this._config.openAiApiKey;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
isOpenRouterSessionConnected() {
|
|
111
|
+
return this.openRouterSessionActive;
|
|
112
|
+
}
|
|
113
|
+
switchModel(modelId) {
|
|
114
|
+
this._config.model = modelId;
|
|
115
|
+
}
|
|
73
116
|
setBusy(busy) {
|
|
74
117
|
this._busy = busy;
|
|
75
118
|
}
|
|
@@ -84,11 +127,19 @@ class MockBridge extends AgentBridge {
|
|
|
84
127
|
];
|
|
85
128
|
}
|
|
86
129
|
getSymbol(name) {
|
|
87
|
-
const
|
|
88
|
-
const match = syms.find((s) => s.qualifiedName === name || s.name === name);
|
|
130
|
+
const [match] = this.getSymbolMatches(name);
|
|
89
131
|
if (!match)
|
|
90
132
|
return undefined;
|
|
91
|
-
return
|
|
133
|
+
return match;
|
|
134
|
+
}
|
|
135
|
+
getSymbolMatches(name) {
|
|
136
|
+
return this.getSymbols()
|
|
137
|
+
.filter((s) => s.qualifiedName === name || s.name === name)
|
|
138
|
+
.map((match) => ({
|
|
139
|
+
...match,
|
|
140
|
+
dependencies: [],
|
|
141
|
+
kind: match.kind,
|
|
142
|
+
}));
|
|
92
143
|
}
|
|
93
144
|
getDependencies(symbolName) {
|
|
94
145
|
if (symbolName === "foo") {
|
|
@@ -247,10 +298,63 @@ class MockBridge extends AgentBridge {
|
|
|
247
298
|
return `Interpreting ${findingId}...`;
|
|
248
299
|
}
|
|
249
300
|
}
|
|
301
|
+
class AmbiguousMockBridge extends MockBridge {
|
|
302
|
+
getSymbols() {
|
|
303
|
+
return [
|
|
304
|
+
...super.getSymbols(),
|
|
305
|
+
{
|
|
306
|
+
name: "Review (type)",
|
|
307
|
+
qualifiedName: "Review#type",
|
|
308
|
+
kind: "type",
|
|
309
|
+
filePath: "src/review.ts",
|
|
310
|
+
startLine: 1,
|
|
311
|
+
endLine: 1,
|
|
312
|
+
signature: "type Review = { id: string }",
|
|
313
|
+
exported: true,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "Review (class)",
|
|
317
|
+
qualifiedName: "Review#class",
|
|
318
|
+
kind: "class",
|
|
319
|
+
filePath: "src/review.ts",
|
|
320
|
+
startLine: 3,
|
|
321
|
+
endLine: 7,
|
|
322
|
+
signature: "class Review",
|
|
323
|
+
exported: true,
|
|
324
|
+
},
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
getSymbolMatches(name) {
|
|
328
|
+
if (name === "Review") {
|
|
329
|
+
return this.getSymbols()
|
|
330
|
+
.filter((symbol) => symbol.qualifiedName.startsWith("Review#"))
|
|
331
|
+
.map((match) => ({
|
|
332
|
+
...match,
|
|
333
|
+
dependencies: [],
|
|
334
|
+
kind: match.kind,
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
return super.getSymbolMatches(name);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
class EmptySessionsBridge extends MockBridge {
|
|
341
|
+
async listSess() {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
async saveSess(label) {
|
|
345
|
+
return {
|
|
346
|
+
id: "sess-1",
|
|
347
|
+
label: label ?? "auto-label",
|
|
348
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
349
|
+
savedAt: "2026-01-01T00:00:00.000Z",
|
|
350
|
+
messageCount: 1,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
250
354
|
// ── Test harness ──
|
|
251
355
|
let activeServer;
|
|
252
|
-
function startTestServer(bridge) {
|
|
253
|
-
const handler = createRequestHandler(bridge);
|
|
356
|
+
function startTestServer(bridge, options = {}) {
|
|
357
|
+
const handler = createRequestHandler(bridge, undefined, options);
|
|
254
358
|
const server = createServer(handler);
|
|
255
359
|
activeServer = server;
|
|
256
360
|
return new Promise((resolve) => {
|
|
@@ -306,7 +410,7 @@ test("GET /api/config returns formatted config plus structured settings", async
|
|
|
306
410
|
assert.ok(body.config.includes("test-model"));
|
|
307
411
|
assert.equal(body.restartRequired, true);
|
|
308
412
|
assert.equal(body.secretsUiSupported, false);
|
|
309
|
-
assert.ok(body.settings.configPath.endsWith("/.minicode
|
|
413
|
+
assert.ok(body.settings.configPath.endsWith("/.minicode/.env"));
|
|
310
414
|
assert.ok(body.settings.entries.some((entry) => entry.key === "maxSteps"));
|
|
311
415
|
});
|
|
312
416
|
test("GET /api/sessions returns session list", async () => {
|
|
@@ -332,6 +436,24 @@ test("POST /api/sessions/save saves a session", async () => {
|
|
|
332
436
|
const body = (await res.json());
|
|
333
437
|
assert.equal(body.label, "my-save");
|
|
334
438
|
});
|
|
439
|
+
test("session APIs support the first save when no sessions exist yet", async () => {
|
|
440
|
+
const bridge = new EmptySessionsBridge();
|
|
441
|
+
const base = await startTestServer(bridge);
|
|
442
|
+
const listRes = await fetch(`${base}/api/sessions`);
|
|
443
|
+
assert.equal(listRes.status, 200);
|
|
444
|
+
const listBody = (await listRes.json());
|
|
445
|
+
assert.equal(listBody.sessions.length, 0);
|
|
446
|
+
assert.equal(listBody.currentSessionId, "sess-1");
|
|
447
|
+
const saveRes = await fetch(`${base}/api/sessions/save`, {
|
|
448
|
+
method: "POST",
|
|
449
|
+
headers: { "Content-Type": "application/json" },
|
|
450
|
+
body: JSON.stringify({}),
|
|
451
|
+
});
|
|
452
|
+
assert.equal(saveRes.status, 200);
|
|
453
|
+
const saveBody = (await saveRes.json());
|
|
454
|
+
assert.equal(saveBody.id, "sess-1");
|
|
455
|
+
assert.equal(saveBody.label, "auto-label");
|
|
456
|
+
});
|
|
335
457
|
test("POST /api/sessions/load returns 404 for unknown session", async () => {
|
|
336
458
|
const bridge = new MockBridge();
|
|
337
459
|
const base = await startTestServer(bridge);
|
|
@@ -353,6 +475,10 @@ test("POST /api/sessions/load returns success for known session", async () => {
|
|
|
353
475
|
assert.equal(res.status, 200);
|
|
354
476
|
const body = (await res.json());
|
|
355
477
|
assert.equal(body.label, "test-session");
|
|
478
|
+
assert.equal(body.messages.length, 10);
|
|
479
|
+
assert.equal(body.messages[0]?.content, "message-2");
|
|
480
|
+
assert.equal(body.messages[9]?.content, "message-11");
|
|
481
|
+
assert.ok(body.messages.every((message) => !message.content.startsWith("[Conversation Summary")));
|
|
356
482
|
});
|
|
357
483
|
test("POST /api/chat returns agent response", async () => {
|
|
358
484
|
const bridge = new MockBridge();
|
|
@@ -389,6 +515,179 @@ test("POST /api/chat returns 429 when agent is busy", async () => {
|
|
|
389
515
|
});
|
|
390
516
|
assert.equal(res.status, 429);
|
|
391
517
|
});
|
|
518
|
+
test("POST /api/openrouter/connect exchanges code and stores a session-only key", async () => {
|
|
519
|
+
const bridge = new MockBridge();
|
|
520
|
+
const base = await startTestServer(bridge);
|
|
521
|
+
const originalFetch = globalThis.fetch;
|
|
522
|
+
globalThis.fetch = async (input, init) => {
|
|
523
|
+
if (String(input) !== "https://openrouter.ai/api/v1/auth/keys") {
|
|
524
|
+
return originalFetch(input, init);
|
|
525
|
+
}
|
|
526
|
+
assert.equal(init?.method, "POST");
|
|
527
|
+
assert.equal((init?.headers)["Content-Type"], "application/json");
|
|
528
|
+
const body = JSON.parse(String(init?.body));
|
|
529
|
+
assert.equal(body.code, "oauth-code");
|
|
530
|
+
assert.equal(body.code_verifier, "pkce-verifier");
|
|
531
|
+
assert.equal(body.code_challenge_method, "S256");
|
|
532
|
+
return new Response(JSON.stringify({ key: "sk-or-v1-session-key" }), { status: 200, headers: { "content-type": "application/json" } });
|
|
533
|
+
};
|
|
534
|
+
try {
|
|
535
|
+
const res = await originalFetch(`${base}/api/openrouter/connect`, {
|
|
536
|
+
method: "POST",
|
|
537
|
+
headers: { "Content-Type": "application/json" },
|
|
538
|
+
body: JSON.stringify({ code: "oauth-code", codeVerifier: "pkce-verifier" }),
|
|
539
|
+
});
|
|
540
|
+
assert.equal(res.status, 200);
|
|
541
|
+
const body = await res.json();
|
|
542
|
+
assert.equal(body.ok, true);
|
|
543
|
+
assert.equal(body.sessionOnly, true);
|
|
544
|
+
assert.equal(body.persistedToEnv, false);
|
|
545
|
+
assert.equal(body.persistedEnvPath, null);
|
|
546
|
+
assert.equal(body.persistWarning, null);
|
|
547
|
+
assert.equal(body.baseUrl, "https://openrouter.ai/api/v1");
|
|
548
|
+
assert.equal(body.needsSetup, false);
|
|
549
|
+
assert.deepEqual(body.missing, []);
|
|
550
|
+
assert.equal(body.message, "OpenRouter connected for this serve session.");
|
|
551
|
+
assert.equal(bridge.openRouterKey, "sk-or-v1-session-key");
|
|
552
|
+
assert.equal(bridge.getConfig().modelProvider, "openai-compatible");
|
|
553
|
+
assert.equal(bridge.getConfig().openAiBaseUrl, "https://openrouter.ai/api/v1");
|
|
554
|
+
assert.equal(bridge.getConfig().openAiApiKey, "sk-or-v1-session-key");
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
globalThis.fetch = originalFetch;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
test("POST /api/openrouter/connect can persist OpenRouter setup to ~/.minicode/.env", async () => {
|
|
561
|
+
const bridge = new MockBridge();
|
|
562
|
+
const minicodeHome = mkdtempSync(path.join(os.tmpdir(), "minicode-openrouter-home-"));
|
|
563
|
+
const base = await startTestServer(bridge, { minicodeHome });
|
|
564
|
+
const originalFetch = globalThis.fetch;
|
|
565
|
+
globalThis.fetch = async (input, init) => {
|
|
566
|
+
if (String(input) !== "https://openrouter.ai/api/v1/auth/keys") {
|
|
567
|
+
return originalFetch(input, init);
|
|
568
|
+
}
|
|
569
|
+
return new Response(JSON.stringify({ key: "sk-or-v1-session-key" }), { status: 200, headers: { "content-type": "application/json" } });
|
|
570
|
+
};
|
|
571
|
+
try {
|
|
572
|
+
const res = await originalFetch(`${base}/api/openrouter/connect`, {
|
|
573
|
+
method: "POST",
|
|
574
|
+
headers: { "Content-Type": "application/json" },
|
|
575
|
+
body: JSON.stringify({ code: "oauth-code", codeVerifier: "pkce-verifier", persistToEnv: true }),
|
|
576
|
+
});
|
|
577
|
+
assert.equal(res.status, 200);
|
|
578
|
+
const body = await res.json();
|
|
579
|
+
assert.equal(body.persistedToEnv, true);
|
|
580
|
+
assert.equal(body.persistWarning, null);
|
|
581
|
+
assert.equal(body.persistedEnvPath, path.join(minicodeHome, ".env"));
|
|
582
|
+
assert.match(body.message, /saved to ~\/\.minicode\/\.env/);
|
|
583
|
+
const envContents = readFileSync(path.join(minicodeHome, ".env"), "utf8");
|
|
584
|
+
assert.match(envContents, /^MODEL_PROVIDER=openai-compatible$/m);
|
|
585
|
+
assert.match(envContents, /^OPENAI_BASE_URL=https:\/\/openrouter\.ai\/api\/v1$/m);
|
|
586
|
+
assert.match(envContents, /^OPENROUTER_API_KEY=sk-or-v1-session-key$/m);
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
globalThis.fetch = originalFetch;
|
|
590
|
+
rmSync(minicodeHome, { recursive: true, force: true });
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
test("POST /api/model persists the selected model to ~/.minicode/.env", async () => {
|
|
594
|
+
const bridge = new MockBridge();
|
|
595
|
+
const minicodeHome = mkdtempSync(path.join(os.tmpdir(), "minicode-model-home-"));
|
|
596
|
+
const envPath = path.join(minicodeHome, ".env");
|
|
597
|
+
writeFileSync(envPath, [
|
|
598
|
+
"MODEL_PROVIDER=openai-compatible",
|
|
599
|
+
"OPENAI_BASE_URL=https://openrouter.ai/api/v1",
|
|
600
|
+
"OPENROUTER_API_KEY=sk-or-v1-session-key",
|
|
601
|
+
"",
|
|
602
|
+
].join("\n"), "utf8");
|
|
603
|
+
const base = await startTestServer(bridge, { minicodeHome });
|
|
604
|
+
try {
|
|
605
|
+
const res = await fetch(`${base}/api/model`, {
|
|
606
|
+
method: "POST",
|
|
607
|
+
headers: { "Content-Type": "application/json" },
|
|
608
|
+
body: JSON.stringify({ model: "openrouter/test-model" }),
|
|
609
|
+
});
|
|
610
|
+
assert.equal(res.status, 200);
|
|
611
|
+
const body = await res.json();
|
|
612
|
+
assert.equal(body.model, "openrouter/test-model");
|
|
613
|
+
assert.equal(body.persistedToEnv, true);
|
|
614
|
+
assert.equal(body.persistedEnvPath, envPath);
|
|
615
|
+
const envContents = readFileSync(envPath, "utf8");
|
|
616
|
+
assert.match(envContents, /^MODEL=openrouter\/test-model$/m);
|
|
617
|
+
assert.match(envContents, /^OPENROUTER_API_KEY=sk-or-v1-session-key$/m);
|
|
618
|
+
assert.equal(bridge.getConfig().model, "openrouter/test-model");
|
|
619
|
+
}
|
|
620
|
+
finally {
|
|
621
|
+
rmSync(minicodeHome, { recursive: true, force: true });
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
test("POST /api/openrouter/disconnect removes the session-only OpenRouter connection", async () => {
|
|
625
|
+
const bridge = new MockBridge();
|
|
626
|
+
bridge.connectOpenRouter("sk-or-v1-session-key");
|
|
627
|
+
const base = await startTestServer(bridge);
|
|
628
|
+
const res = await fetch(`${base}/api/openrouter/disconnect`, {
|
|
629
|
+
method: "POST",
|
|
630
|
+
headers: { "Content-Type": "application/json" },
|
|
631
|
+
});
|
|
632
|
+
assert.equal(res.status, 200);
|
|
633
|
+
const body = await res.json();
|
|
634
|
+
assert.equal(body.ok, true);
|
|
635
|
+
assert.equal(body.disconnected, true);
|
|
636
|
+
assert.equal(body.sessionOnly, true);
|
|
637
|
+
assert.equal(body.provider, "anthropic");
|
|
638
|
+
assert.equal(body.baseUrl, "http://localhost:1234/v1");
|
|
639
|
+
assert.equal(body.message, "Removed the session-only OpenRouter connection and restored your original provider settings.");
|
|
640
|
+
assert.equal(bridge.openRouterSessionActive, false);
|
|
641
|
+
assert.equal(bridge.getConfig().modelProvider, "anthropic");
|
|
642
|
+
assert.equal(bridge.getConfig().openAiBaseUrl, "http://localhost:1234/v1");
|
|
643
|
+
assert.equal(bridge.getConfig().openAiApiKey, undefined);
|
|
644
|
+
});
|
|
645
|
+
test("GET /api/status exposes OpenRouter session state and base URL", async () => {
|
|
646
|
+
const bridge = new MockBridge();
|
|
647
|
+
bridge.connectOpenRouter("sk-or-v1-session-key");
|
|
648
|
+
const base = await startTestServer(bridge);
|
|
649
|
+
const res = await fetch(`${base}/api/status`);
|
|
650
|
+
assert.equal(res.status, 200);
|
|
651
|
+
const body = await res.json();
|
|
652
|
+
assert.equal(body.provider, "openai-compatible");
|
|
653
|
+
assert.equal(body.baseUrl, "https://openrouter.ai/api/v1");
|
|
654
|
+
assert.equal(body.sessionOpenRouterConnected, true);
|
|
655
|
+
});
|
|
656
|
+
test("POST /api/openrouter/connect returns 400 when code is missing", async () => {
|
|
657
|
+
const bridge = new MockBridge();
|
|
658
|
+
const base = await startTestServer(bridge);
|
|
659
|
+
const res = await fetch(`${base}/api/openrouter/connect`, {
|
|
660
|
+
method: "POST",
|
|
661
|
+
headers: { "Content-Type": "application/json" },
|
|
662
|
+
body: JSON.stringify({ codeVerifier: "pkce-verifier" }),
|
|
663
|
+
});
|
|
664
|
+
assert.equal(res.status, 400);
|
|
665
|
+
});
|
|
666
|
+
test("POST /api/openrouter/connect surfaces exchange failures", async () => {
|
|
667
|
+
const bridge = new MockBridge();
|
|
668
|
+
const base = await startTestServer(bridge);
|
|
669
|
+
const originalFetch = globalThis.fetch;
|
|
670
|
+
globalThis.fetch = async (input, init) => {
|
|
671
|
+
if (String(input) !== "https://openrouter.ai/api/v1/auth/keys") {
|
|
672
|
+
return originalFetch(input, init);
|
|
673
|
+
}
|
|
674
|
+
return new Response("Invalid code", { status: 403 });
|
|
675
|
+
};
|
|
676
|
+
try {
|
|
677
|
+
const res = await originalFetch(`${base}/api/openrouter/connect`, {
|
|
678
|
+
method: "POST",
|
|
679
|
+
headers: { "Content-Type": "application/json" },
|
|
680
|
+
body: JSON.stringify({ code: "bad-code", codeVerifier: "pkce-verifier" }),
|
|
681
|
+
});
|
|
682
|
+
assert.equal(res.status, 403);
|
|
683
|
+
const body = await res.json();
|
|
684
|
+
assert.ok(body.error.includes("OpenRouter OAuth exchange failed"));
|
|
685
|
+
assert.ok(body.error.includes("Invalid code"));
|
|
686
|
+
}
|
|
687
|
+
finally {
|
|
688
|
+
globalThis.fetch = originalFetch;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
392
691
|
// ── OpenAI-compatible API tests ──
|
|
393
692
|
test("GET /v1/models returns minicode-agent model", async () => {
|
|
394
693
|
const bridge = new MockBridge();
|
|
@@ -497,6 +796,7 @@ test("GET / serves index.html", async () => {
|
|
|
497
796
|
const res = await fetch(`${base}/`);
|
|
498
797
|
assert.equal(res.status, 200);
|
|
499
798
|
assert.equal(res.headers.get("content-type"), "text/html");
|
|
799
|
+
assert.equal(res.headers.get("cache-control"), "no-store");
|
|
500
800
|
const html = await res.text();
|
|
501
801
|
assert.ok(html.includes("minicode"));
|
|
502
802
|
});
|
|
@@ -506,6 +806,7 @@ test("GET /style.css serves CSS file", async () => {
|
|
|
506
806
|
const res = await fetch(`${base}/style.css`);
|
|
507
807
|
assert.equal(res.status, 200);
|
|
508
808
|
assert.equal(res.headers.get("content-type"), "text/css");
|
|
809
|
+
assert.equal(res.headers.get("cache-control"), "no-store");
|
|
509
810
|
});
|
|
510
811
|
test("GET /app.js serves JS file", async () => {
|
|
511
812
|
const bridge = new MockBridge();
|
|
@@ -513,6 +814,7 @@ test("GET /app.js serves JS file", async () => {
|
|
|
513
814
|
const res = await fetch(`${base}/app.js`);
|
|
514
815
|
assert.equal(res.status, 200);
|
|
515
816
|
assert.equal(res.headers.get("content-type"), "application/javascript");
|
|
817
|
+
assert.equal(res.headers.get("cache-control"), "no-store");
|
|
516
818
|
});
|
|
517
819
|
test("GET /nonexistent returns 404", async () => {
|
|
518
820
|
const bridge = new MockBridge();
|
|
@@ -570,6 +872,15 @@ test("GET /api/symbols/:name/dependencies returns 404 for unknown symbol", async
|
|
|
570
872
|
const res = await fetch(`${base}/api/symbols/nonexistent/dependencies`);
|
|
571
873
|
assert.equal(res.status, 404);
|
|
572
874
|
});
|
|
875
|
+
test("GET /api/symbols/:name/dependencies returns 409 for ambiguous symbol", async () => {
|
|
876
|
+
const bridge = new AmbiguousMockBridge();
|
|
877
|
+
const base = await startTestServer(bridge);
|
|
878
|
+
const res = await fetch(`${base}/api/symbols/Review/dependencies`);
|
|
879
|
+
assert.equal(res.status, 409);
|
|
880
|
+
const body = (await res.json());
|
|
881
|
+
assert.equal(body.error, 'Symbol "Review" is ambiguous');
|
|
882
|
+
assert.deepEqual(body.candidates.map((candidate) => candidate.qualifiedName).sort(), ["Review#class", "Review#type"]);
|
|
883
|
+
});
|
|
573
884
|
test("GET /api/symbols/:name/references returns references", async () => {
|
|
574
885
|
const bridge = new MockBridge();
|
|
575
886
|
const base = await startTestServer(bridge);
|
|
@@ -586,6 +897,15 @@ test("GET /api/symbols/:name/references returns 404 for unknown symbol", async (
|
|
|
586
897
|
const res = await fetch(`${base}/api/symbols/nonexistent/references`);
|
|
587
898
|
assert.equal(res.status, 404);
|
|
588
899
|
});
|
|
900
|
+
test("GET /api/symbols/:name/references returns 409 for ambiguous symbol", async () => {
|
|
901
|
+
const bridge = new AmbiguousMockBridge();
|
|
902
|
+
const base = await startTestServer(bridge);
|
|
903
|
+
const res = await fetch(`${base}/api/symbols/Review/references`);
|
|
904
|
+
assert.equal(res.status, 409);
|
|
905
|
+
const body = (await res.json());
|
|
906
|
+
assert.equal(body.error, 'Symbol "Review" is ambiguous');
|
|
907
|
+
assert.deepEqual(body.candidates.map((candidate) => candidate.qualifiedName).sort(), ["Review#class", "Review#type"]);
|
|
908
|
+
});
|
|
589
909
|
test("GET /api/code-map returns code map", async () => {
|
|
590
910
|
const bridge = new MockBridge();
|
|
591
911
|
const base = await startTestServer(bridge);
|
|
@@ -741,6 +1061,15 @@ test("GET /api/symbols/:name/source returns 404 for unknown symbol", async () =>
|
|
|
741
1061
|
const body = (await res.json());
|
|
742
1062
|
assert.ok(body.error.includes("nonexistent"));
|
|
743
1063
|
});
|
|
1064
|
+
test("GET /api/symbols/:name/source returns 409 for ambiguous symbol", async () => {
|
|
1065
|
+
const bridge = new AmbiguousMockBridge();
|
|
1066
|
+
const base = await startTestServer(bridge);
|
|
1067
|
+
const res = await fetch(`${base}/api/symbols/Review/source`);
|
|
1068
|
+
assert.equal(res.status, 409);
|
|
1069
|
+
const body = (await res.json());
|
|
1070
|
+
assert.equal(body.error, 'Symbol "Review" is ambiguous');
|
|
1071
|
+
assert.deepEqual(body.candidates.map((candidate) => candidate.qualifiedName).sort(), ["Review#class", "Review#type"]);
|
|
1072
|
+
});
|
|
744
1073
|
test("GET /api/symbols/:name/source returns 500 when file is missing", async () => {
|
|
745
1074
|
const bridge = new MockBridge();
|
|
746
1075
|
const base = await startTestServer(bridge);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { buildSessionPreview, isCompactionSummaryMessage, } from "../src/session/session-preview.js";
|
|
4
|
+
test("isCompactionSummaryMessage detects compacted summary stubs", () => {
|
|
5
|
+
assert.equal(isCompactionSummaryMessage({
|
|
6
|
+
role: "user",
|
|
7
|
+
content: "[Conversation Summary — earlier messages were compacted to save context]\nSummary text",
|
|
8
|
+
}), true);
|
|
9
|
+
assert.equal(isCompactionSummaryMessage({
|
|
10
|
+
role: "assistant",
|
|
11
|
+
content: "[Conversation Summary — earlier messages were compacted to save context]\nSummary text",
|
|
12
|
+
}), false);
|
|
13
|
+
});
|
|
14
|
+
test("buildSessionPreview filters compaction summaries and keeps the last ten messages", () => {
|
|
15
|
+
const preview = buildSessionPreview([
|
|
16
|
+
{
|
|
17
|
+
role: "user",
|
|
18
|
+
content: "[Conversation Summary — earlier messages were compacted using LLM summarization]\nSummary text",
|
|
19
|
+
},
|
|
20
|
+
{ role: "user", content: "message-1" },
|
|
21
|
+
{ role: "assistant", content: "message-2" },
|
|
22
|
+
{ role: "user", content: "message-3" },
|
|
23
|
+
{ role: "assistant", content: "message-4" },
|
|
24
|
+
{ role: "user", content: "message-5" },
|
|
25
|
+
{ role: "assistant", content: "message-6" },
|
|
26
|
+
{ role: "user", content: "message-7" },
|
|
27
|
+
{ role: "assistant", content: "message-8" },
|
|
28
|
+
{ role: "user", content: "message-9" },
|
|
29
|
+
{ role: "assistant", content: "message-10" },
|
|
30
|
+
{ role: "user", content: "message-11" },
|
|
31
|
+
]);
|
|
32
|
+
assert.equal(preview.length, 10);
|
|
33
|
+
assert.equal(preview[0]?.content, "message-2");
|
|
34
|
+
assert.equal(preview[9]?.content, "message-11");
|
|
35
|
+
assert.ok(preview.every((message) => !message.content.startsWith("[Conversation Summary")));
|
|
36
|
+
});
|
|
37
|
+
test("buildSessionPreview preserves tool messages in order", () => {
|
|
38
|
+
const preview = buildSessionPreview([
|
|
39
|
+
{
|
|
40
|
+
role: "assistant",
|
|
41
|
+
content: "Let me check that",
|
|
42
|
+
toolCalls: [{ id: "tool-1", name: "search", input: { query: "foo" } }],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
role: "tool",
|
|
46
|
+
toolCallId: "tool-1",
|
|
47
|
+
toolName: "search",
|
|
48
|
+
content: "search output",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
role: "assistant",
|
|
52
|
+
content: "Found it",
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
assert.deepEqual(preview.map((message) => message.role), ["assistant", "tool", "assistant"]);
|
|
56
|
+
});
|
|
@@ -18,4 +18,8 @@ test("built JS contains active saved session update logic", () => {
|
|
|
18
18
|
assert.ok(js.includes("activeSavedSession"), "JS should track the active saved session");
|
|
19
19
|
assert.ok(js.includes("currentSessionId"), "JS should read the current session id from the sessions API");
|
|
20
20
|
assert.ok(js.includes("Session updated:"), "JS should emit the update confirmation message");
|
|
21
|
+
assert.ok(js.includes("sessionRefreshTracker"), "JS should guard session list refreshes against stale responses");
|
|
22
|
+
assert.ok(js.includes('saveBtn.setAttribute("disabled", "true")'), "JS should disable saving while the first save is in flight");
|
|
23
|
+
assert.ok(js.includes("renderLoadedSessionMessages"), "JS should render session previews after load");
|
|
24
|
+
assert.ok(js.includes("body.messages"), "JS should read preview messages from the load session response");
|
|
21
25
|
});
|
|
@@ -7,6 +7,12 @@ test("built HTML contains settings entry point and modal shell", () => {
|
|
|
7
7
|
const html = readFileSync(join(distWeb, "index.html"), "utf8");
|
|
8
8
|
assert.ok(html.includes('id="settings-btn"'), "HTML should contain the settings button");
|
|
9
9
|
assert.ok(html.includes('id="settings-modal"'), "HTML should contain the settings modal");
|
|
10
|
+
assert.ok(html.includes('id="connect-openrouter-btn"'), "HTML should contain the OpenRouter connect button");
|
|
11
|
+
assert.ok(html.includes('id="config-overlay-intro"'), "HTML should contain the setup overlay intro copy");
|
|
12
|
+
assert.ok(html.includes("Try minicode for free with OpenRouter"), "HTML should promote the free OpenRouter quick start");
|
|
13
|
+
assert.ok(html.includes('id="openrouter-connect-modal"'), "HTML should contain the OpenRouter consent modal");
|
|
14
|
+
assert.ok(html.includes('id="openrouter-persist-checkbox"'), "HTML should contain the OpenRouter persistence checkbox");
|
|
15
|
+
assert.ok(html.includes('id="disconnect-openrouter-btn"'), "HTML should contain the OpenRouter disconnect button");
|
|
10
16
|
assert.ok(!html.includes('id="settings-scope"'), "HTML should no longer contain the settings scope selector");
|
|
11
17
|
assert.ok(html.includes('id="settings-save"'), "HTML should contain the settings save action");
|
|
12
18
|
});
|
|
@@ -16,11 +22,23 @@ test("built CSS contains modal and settings layout styles", () => {
|
|
|
16
22
|
assert.ok(css.includes(".settings-list"), "CSS should contain settings list styles");
|
|
17
23
|
assert.ok(css.includes(".settings-item-meta"), "CSS should contain settings metadata grid styles");
|
|
18
24
|
assert.ok(css.includes(".settings-help-warning"), "CSS should contain warning styling for env overrides");
|
|
25
|
+
assert.ok(css.includes("align-items: flex-start;"), "CSS should top-align scrollable setup overlay content");
|
|
26
|
+
assert.ok(css.includes(".config-overlay-spotlight"), "CSS should style the OpenRouter quick-start spotlight");
|
|
27
|
+
assert.ok(css.includes(".openrouter-connect-body"), "CSS should style the OpenRouter consent modal body");
|
|
28
|
+
assert.ok(css.includes(".config-connect-status.success"), "CSS should style OpenRouter connect success state");
|
|
29
|
+
assert.ok(css.includes(".settings-session-banner"), "CSS should style the OpenRouter session banner");
|
|
19
30
|
assert.ok(css.includes("body.modal-open"), "CSS should lock scroll while the settings modal is open");
|
|
20
31
|
});
|
|
21
32
|
test("built JS contains config loading and saving logic for settings", () => {
|
|
22
33
|
const js = readFileSync(join(distWeb, "app.js"), "utf8");
|
|
23
34
|
assert.ok(js.includes("/api/config"), "JS should fetch the config API");
|
|
35
|
+
assert.ok(js.includes("/api/openrouter/connect"), "JS should call the OpenRouter connect API");
|
|
36
|
+
assert.ok(js.includes("/api/openrouter/disconnect"), "JS should call the OpenRouter disconnect API");
|
|
37
|
+
assert.ok(js.includes("persistToHomeEnv"), "JS should support persisting the selected model after OpenRouter setup");
|
|
38
|
+
assert.ok(js.includes("code_challenge_method"), "JS should generate an OpenRouter PKCE auth request");
|
|
39
|
+
assert.ok(js.includes("sessionStorage"), "JS should persist the PKCE verifier for the OAuth callback");
|
|
40
|
+
assert.ok(js.includes("minicode:openrouter:persist-to-env"), "JS should persist the optional OpenRouter env-write choice across OAuth");
|
|
41
|
+
assert.ok(js.includes("sessionOpenRouterConnected"), "JS should track session-only OpenRouter state");
|
|
24
42
|
assert.ok(js.includes("Save settings"), "JS should contain the settings save action text");
|
|
25
43
|
assert.ok(js.includes("settingsPayload"), "JS should track settings payload state");
|
|
26
44
|
assert.ok(js.includes("persistedValue"), "JS should wire persisted settings behavior");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/agent/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AAgGrE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AACrE,MAAM,MAAM,sBAAsB,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAClF,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAC1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AACF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AACF,MAAM,MAAM,QAAQ,GAChB,gBAAgB,GAChB,sBAAsB,GACtB,YAAY,GACZ,qBAAqB,GACrB,mBAAmB,GACnB,qBAAqB,CAAC;AA+B1B,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0E;IACrG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IACpE,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAyC;IAE/E;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IAEnD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAEhE,kEAAkE;IAClE,OAAO,CAAC,kBAAkB,CAAqB;gBAEnC,MAAM,EAAE;QAClB,MAAM,EAAE,WAAW,CAAC;QACpB,WAAW,EAAE,WAAW,CAAC;QACzB,YAAY,EAAE,YAAY,CAAC;QAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,aAAa,GAAG,SAAS,CAAC;QACvE,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;QACvC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;QACvC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;QACtC,qBAAqB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;KAClD;IAaD,OAAO,CAAC,UAAU;IASlB,UAAU,IAAI,OAAO;IAIrB,kBAAkB,IAAI,WAAW,CAAC,iBAAiB,CAAC;IAIpD,gBAAgB,IAAI;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE;IAOvE,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,iBAAiB,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/agent/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -157,7 +157,8 @@ export class CodingAgent {
|
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
setReasoningEffort(effort) {
|
|
160
|
-
const
|
|
160
|
+
const rest = { ...this.config };
|
|
161
|
+
delete rest.reasoningEffort;
|
|
161
162
|
this.config = effort ? { ...rest, reasoningEffort: effort } : { ...rest };
|
|
162
163
|
}
|
|
163
164
|
/**
|