gsd-pi 2.71.0-dev.7a61d89 → 2.71.0-dev.d4d916a
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/dist/resources/extensions/gsd/auto/phases.js +1 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
- package/dist/resources/extensions/gsd/auto-start.js +30 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +2 -5
- package/dist/resources/extensions/gsd/commands/handlers/core.js +11 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +26 -12
- package/dist/resources/extensions/gsd/notification-store.js +5 -4
- package/dist/resources/extensions/gsd/session-model-override.js +25 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +7 -1
- package/dist/resources/extensions/ollama/index.js +13 -5
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/phases.ts +2 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
- package/src/resources/extensions/gsd/auto-start.ts +37 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +2 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +12 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +27 -11
- package/src/resources/extensions/gsd/notification-store.ts +5 -4
- package/src/resources/extensions/gsd/session-model-override.ts +36 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +8 -1
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
- package/src/resources/extensions/ollama/index.ts +13 -3
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- /package/dist/web/standalone/.next/static/{ug91LJa0m7OdzrTVaz_48 → IRnpNeY-_eO7SxKBIkTbL}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ug91LJa0m7OdzrTVaz_48 → IRnpNeY-_eO7SxKBIkTbL}/_ssgManifest.js +0 -0
package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { describe, test, afterEach } from "node:test";
|
|
14
14
|
import assert from "node:assert/strict";
|
|
15
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync, readFileSync } from "node:fs";
|
|
15
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync, readFileSync, symlinkSync, unlinkSync } from "node:fs";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
18
|
import { execSync } from "node:child_process";
|
|
@@ -44,6 +44,27 @@ function createTempRepo(): string {
|
|
|
44
44
|
return dir;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function createTempRepoWithExternalGsd(): { repo: string; externalState: string } {
|
|
48
|
+
const realTmp = realpathSync(tmpdir());
|
|
49
|
+
const repo = realpathSync(mkdtempSync(join(realTmp, "wt-ms-merge-ext-test-")));
|
|
50
|
+
const externalState = realpathSync(mkdtempSync(join(realTmp, "wt-ms-merge-ext-state-")));
|
|
51
|
+
|
|
52
|
+
run("git init", repo);
|
|
53
|
+
run("git config user.email test@test.com", repo);
|
|
54
|
+
run("git config user.name Test", repo);
|
|
55
|
+
|
|
56
|
+
mkdirSync(join(externalState, "worktrees"), { recursive: true });
|
|
57
|
+
symlinkSync(externalState, join(repo, ".gsd"));
|
|
58
|
+
|
|
59
|
+
writeFileSync(join(repo, "README.md"), "# test\n");
|
|
60
|
+
writeFileSync(join(externalState, "STATE.md"), "# State\n");
|
|
61
|
+
run("git add .", repo);
|
|
62
|
+
run("git commit -m init", repo);
|
|
63
|
+
run("git branch -M main", repo);
|
|
64
|
+
|
|
65
|
+
return { repo, externalState };
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
/** Minimal roadmap content for mergeMilestoneToMain. */
|
|
48
69
|
function makeRoadmap(milestoneId: string, title: string, slices: Array<{ id: string; title: string }>): string {
|
|
49
70
|
const sliceLines = slices.map(s => `- [x] **${s.id}: ${s.title}**`).join("\n");
|
|
@@ -87,6 +108,12 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
|
|
|
87
108
|
return d;
|
|
88
109
|
}
|
|
89
110
|
|
|
111
|
+
function freshRepoWithExternalGsd(): { repo: string; externalState: string } {
|
|
112
|
+
const { repo, externalState } = createTempRepoWithExternalGsd();
|
|
113
|
+
tempDirs.push(repo, externalState);
|
|
114
|
+
return { repo, externalState };
|
|
115
|
+
}
|
|
116
|
+
|
|
90
117
|
afterEach(() => {
|
|
91
118
|
process.chdir(savedCwd);
|
|
92
119
|
for (const d of tempDirs) {
|
|
@@ -638,6 +665,44 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
|
|
|
638
665
|
"#1906: codeFilesChanged must be false when only .gsd/ files were merged");
|
|
639
666
|
});
|
|
640
667
|
|
|
668
|
+
test("#2156: mergeMilestoneToMain removes external-state worktrees using the milestone branch name", () => {
|
|
669
|
+
const { repo, externalState } = freshRepoWithExternalGsd();
|
|
670
|
+
const wtPath = createAutoWorktree(repo, "M215");
|
|
671
|
+
|
|
672
|
+
addSliceToMilestone(repo, wtPath, "M215", "S01", "External cleanup", [
|
|
673
|
+
{ file: "external-cleanup.ts", content: "export const externalCleanup = true;\n", message: "add external cleanup" },
|
|
674
|
+
]);
|
|
675
|
+
|
|
676
|
+
const realWtPath = realpathSync(wtPath);
|
|
677
|
+
assert.ok(
|
|
678
|
+
realWtPath.startsWith(externalState),
|
|
679
|
+
`worktree should be registered under external .gsd state, got ${realWtPath}`,
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
// Recreate the exact divergence from #1852: local .gsd/ is replaced with a
|
|
683
|
+
// stale real directory, so worktreePath() no longer matches git's record.
|
|
684
|
+
unlinkSync(join(repo, ".gsd"));
|
|
685
|
+
mkdirSync(join(repo, ".gsd", "worktrees", "M215"), { recursive: true });
|
|
686
|
+
writeFileSync(join(repo, ".gsd", "STATE.md"), "# Local stale state\n");
|
|
687
|
+
writeFileSync(join(repo, ".gsd", "worktrees", "M215", "stale.txt"), "stale local artifact\n");
|
|
688
|
+
|
|
689
|
+
const roadmap = makeRoadmap("M215", "External cleanup", [
|
|
690
|
+
{ id: "S01", title: "External cleanup" },
|
|
691
|
+
]);
|
|
692
|
+
|
|
693
|
+
mergeMilestoneToMain(repo, "M215", roadmap);
|
|
694
|
+
|
|
695
|
+
assert.ok(
|
|
696
|
+
!run("git worktree list", repo).includes("M215"),
|
|
697
|
+
"merged milestone worktree should be removed from git worktree list",
|
|
698
|
+
);
|
|
699
|
+
assert.ok(!existsSync(realWtPath), "real external worktree directory should be removed");
|
|
700
|
+
assert.ok(
|
|
701
|
+
!run("git branch", repo).includes("milestone/M215"),
|
|
702
|
+
"milestone branch should be deleted after merge cleanup",
|
|
703
|
+
);
|
|
704
|
+
});
|
|
705
|
+
|
|
641
706
|
test("#2912: MERGE_HEAD cleaned up after squash-merge conflict", () => {
|
|
642
707
|
const repo = freshRepo();
|
|
643
708
|
const wtPath = createAutoWorktree(repo, "M291");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for model config isolation between concurrent instances (#650, #1065)
|
|
3
|
-
* and
|
|
3
|
+
* and session-scoped model precedence behavior.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
@@ -157,75 +157,60 @@ describe("session model recovery on error (#1065)", () => {
|
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
-
// ───
|
|
160
|
+
// ─── Manual session model override precedence ───────────────────────────────
|
|
161
161
|
|
|
162
|
-
describe("
|
|
163
|
-
it("
|
|
164
|
-
|
|
165
|
-
const preferredModel = { provider: "
|
|
166
|
-
const ctxModel = { provider: "claude-code", id: "claude-
|
|
162
|
+
describe("manual session model override precedence", () => {
|
|
163
|
+
it("manual session override takes priority over preferences and ctx.model", () => {
|
|
164
|
+
const manualSessionOverride = { provider: "openai-codex", id: "gpt-5.4" };
|
|
165
|
+
const preferredModel = { provider: "anthropic", id: "claude-sonnet-4-6" };
|
|
166
|
+
const ctxModel = { provider: "claude-code", id: "claude-opus-4-6" };
|
|
167
167
|
|
|
168
|
-
const startModelSnapshot =
|
|
168
|
+
const startModelSnapshot = manualSessionOverride
|
|
169
|
+
?? preferredModel
|
|
169
170
|
?? { provider: ctxModel.provider, id: ctxModel.id };
|
|
170
171
|
|
|
171
|
-
assert.equal(startModelSnapshot.provider, "openai-codex"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
assert.equal(startModelSnapshot.provider, "openai-codex");
|
|
173
|
+
assert.equal(startModelSnapshot.id, "gpt-5.4");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("falls back to preferences when no manual override is active", () => {
|
|
177
|
+
const manualSessionOverride: { provider: string; id: string } | undefined = undefined;
|
|
178
|
+
const preferredModel = { provider: "anthropic", id: "claude-sonnet-4-6" };
|
|
179
|
+
const ctxModel = { provider: "claude-code", id: "claude-opus-4-6" };
|
|
180
|
+
|
|
181
|
+
const startModelSnapshot = manualSessionOverride
|
|
182
|
+
?? preferredModel
|
|
183
|
+
?? { provider: ctxModel.provider, id: ctxModel.id };
|
|
184
|
+
|
|
185
|
+
assert.equal(startModelSnapshot.provider, "anthropic");
|
|
186
|
+
assert.equal(startModelSnapshot.id, "claude-sonnet-4-6");
|
|
175
187
|
});
|
|
176
188
|
|
|
177
|
-
it("falls back to ctx.model when no
|
|
189
|
+
it("falls back to ctx.model when no manual override or preferences are configured", () => {
|
|
190
|
+
const manualSessionOverride: { provider: string; id: string } | undefined = undefined;
|
|
178
191
|
const preferredModel: { provider: string; id: string } | undefined = undefined;
|
|
179
|
-
const ctxModel = { provider: "claude-code", id: "claude-
|
|
192
|
+
const ctxModel = { provider: "claude-code", id: "claude-opus-4-6" };
|
|
180
193
|
|
|
181
|
-
const startModelSnapshot =
|
|
194
|
+
const startModelSnapshot = manualSessionOverride
|
|
195
|
+
?? preferredModel
|
|
182
196
|
?? { provider: ctxModel.provider, id: ctxModel.id };
|
|
183
197
|
|
|
184
|
-
assert.equal(startModelSnapshot.provider, "claude-code"
|
|
185
|
-
|
|
186
|
-
assert.equal(startModelSnapshot.id, "claude-sonnet-4-6",
|
|
187
|
-
"should fall back to ctx.model id when no preferences");
|
|
198
|
+
assert.equal(startModelSnapshot.provider, "claude-code");
|
|
199
|
+
assert.equal(startModelSnapshot.id, "claude-opus-4-6");
|
|
188
200
|
});
|
|
189
201
|
|
|
190
|
-
it("handles null ctx.model with no preferences gracefully", () => {
|
|
202
|
+
it("handles null ctx.model with no override or preferences gracefully", () => {
|
|
203
|
+
const manualSessionOverride: { provider: string; id: string } | undefined = undefined;
|
|
191
204
|
const preferredModel: { provider: string; id: string } | undefined = undefined;
|
|
192
205
|
// Use a function to prevent TS from narrowing to `never` in the ternary
|
|
193
206
|
function getCtxModel(): { provider: string; id: string } | null { return null; }
|
|
194
207
|
const ctxModel = getCtxModel();
|
|
195
208
|
|
|
196
|
-
const startModelSnapshot =
|
|
209
|
+
const startModelSnapshot = manualSessionOverride
|
|
210
|
+
?? preferredModel
|
|
197
211
|
?? (ctxModel ? { provider: ctxModel.provider, id: ctxModel.id } : null);
|
|
198
212
|
|
|
199
213
|
assert.equal(startModelSnapshot, null,
|
|
200
|
-
"should be null when
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("bare model ID uses session provider when available", () => {
|
|
204
|
-
// Simulates: PREFERENCES.md has "gpt-5.4" (no provider), session is openai-codex
|
|
205
|
-
const preferredModel = { provider: "openai-codex", id: "gpt-5.4" }; // from resolveDefaultSessionModel("openai-codex")
|
|
206
|
-
const ctxModel = { provider: "openai-codex", id: "claude-sonnet-4-6" };
|
|
207
|
-
|
|
208
|
-
const startModelSnapshot = preferredModel
|
|
209
|
-
?? { provider: ctxModel.provider, id: ctxModel.id };
|
|
210
|
-
|
|
211
|
-
assert.equal(startModelSnapshot.provider, "openai-codex");
|
|
212
|
-
assert.equal(startModelSnapshot.id, "gpt-5.4",
|
|
213
|
-
"bare model ID from preferences should still override ctx.model");
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("stale settings.json does not leak when preferences are set", () => {
|
|
217
|
-
// Scenario: settings.json has claude-code, PREFERENCES.md has openai-codex
|
|
218
|
-
const settingsJsonDefault = { provider: "claude-code", id: "claude-sonnet-4-6" };
|
|
219
|
-
const preferencesModel = { provider: "openai-codex", id: "gpt-5.4" };
|
|
220
|
-
|
|
221
|
-
// auto-start.ts captures preferredModel first, which preempts settingsJsonDefault
|
|
222
|
-
const startModelSnapshot = preferencesModel ?? settingsJsonDefault;
|
|
223
|
-
|
|
224
|
-
assert.equal(startModelSnapshot.provider, "openai-codex",
|
|
225
|
-
"PREFERENCES.md must override stale settings.json provider");
|
|
226
|
-
assert.equal(startModelSnapshot.id, "gpt-5.4",
|
|
227
|
-
"PREFERENCES.md must override stale settings.json model");
|
|
228
|
-
assert.notEqual(startModelSnapshot.provider, settingsJsonDefault.provider,
|
|
229
|
-
"settings.json provider must NOT leak through");
|
|
214
|
+
"should be null when no model source is available");
|
|
230
215
|
});
|
|
231
216
|
});
|
|
@@ -69,14 +69,15 @@ test("dashboard shortcut resolves the project root instead of the current worktr
|
|
|
69
69
|
|
|
70
70
|
assert.ok(customCalls > 0, "shortcut opens the dashboard overlay when project root is resolved");
|
|
71
71
|
assert.equal(notices.length, 0, "shortcut does not fall back to the missing-.gsd warning");
|
|
72
|
-
assert.equal(shortcuts.length,
|
|
72
|
+
assert.equal(shortcuts.length, 5, "all GSD shortcuts are still registered");
|
|
73
73
|
const keys = shortcuts.map((shortcut) => shortcut.key);
|
|
74
74
|
assert.ok(keys.includes("ctrl+alt+g"), "primary dashboard shortcut is registered");
|
|
75
75
|
assert.ok(keys.includes("ctrl+shift+g"), "fallback dashboard shortcut is registered");
|
|
76
76
|
assert.ok(keys.includes("ctrl+alt+n"), "primary notifications shortcut is registered");
|
|
77
77
|
assert.ok(keys.includes("ctrl+shift+n"), "fallback notifications shortcut is registered");
|
|
78
78
|
assert.ok(keys.includes("ctrl+alt+p"), "primary parallel shortcut is registered");
|
|
79
|
-
|
|
79
|
+
// No Ctrl+Shift+P fallback — conflicts with cycleModelBackward (shift+ctrl+p)
|
|
80
|
+
assert.ok(!keys.includes("ctrl+shift+p"), "parallel fallback must not be registered (conflicts with cycleModelBackward)");
|
|
80
81
|
});
|
|
81
82
|
|
|
82
83
|
test("parallel shortcut passes resolved project root into overlay", async (t) => {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
clearSessionModelOverride,
|
|
8
|
+
getSessionModelOverride,
|
|
9
|
+
setSessionModelOverride,
|
|
10
|
+
} from "../session-model-override.js";
|
|
11
|
+
|
|
12
|
+
const phasesSource = readFileSync(join(import.meta.dirname, "..", "auto", "phases.ts"), "utf-8");
|
|
13
|
+
|
|
14
|
+
test("setSessionModelOverride stores provider/model for the session", () => {
|
|
15
|
+
const sessionId = `session-override-${Date.now()}`;
|
|
16
|
+
setSessionModelOverride(sessionId, { provider: "openai-codex", id: "gpt-5.4" });
|
|
17
|
+
|
|
18
|
+
const override = getSessionModelOverride(sessionId);
|
|
19
|
+
assert.equal(override?.provider, "openai-codex");
|
|
20
|
+
assert.equal(override?.id, "gpt-5.4");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("clearSessionModelOverride removes the session override", () => {
|
|
24
|
+
const sessionId = `session-clear-${Date.now()}`;
|
|
25
|
+
setSessionModelOverride(sessionId, { provider: "anthropic", id: "claude-sonnet-4-6" });
|
|
26
|
+
clearSessionModelOverride(sessionId);
|
|
27
|
+
assert.equal(getSessionModelOverride(sessionId), undefined);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("auto dispatch threads manual session model override into selectAndApplyModel", () => {
|
|
31
|
+
assert.ok(
|
|
32
|
+
phasesSource.includes("s.manualSessionModelOverride"),
|
|
33
|
+
"auto/phases.ts should pass s.manualSessionModelOverride into selectAndApplyModel",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
@@ -57,7 +57,15 @@ async function probeAndRegister(pi: ExtensionAPI): Promise<boolean> {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const models = await discoverModels();
|
|
60
|
-
if (models.length === 0)
|
|
60
|
+
if (models.length === 0) {
|
|
61
|
+
// No local models means there's nothing usable to register in GSD.
|
|
62
|
+
// Keep the footer/status clean instead of advertising Ollama availability.
|
|
63
|
+
if (providerRegistered) {
|
|
64
|
+
pi.unregisterProvider("ollama");
|
|
65
|
+
providerRegistered = false;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
61
69
|
|
|
62
70
|
const baseUrl = client.getOllamaHost();
|
|
63
71
|
|
|
@@ -115,9 +123,11 @@ export default function ollama(pi: ExtensionAPI) {
|
|
|
115
123
|
} else {
|
|
116
124
|
probeAndRegister(pi)
|
|
117
125
|
.then((found) => {
|
|
118
|
-
|
|
126
|
+
ctx.ui.setStatus("ollama", found ? "Ollama" : undefined);
|
|
119
127
|
})
|
|
120
|
-
.catch(() => {
|
|
128
|
+
.catch(() => {
|
|
129
|
+
ctx.ui.setStatus("ollama", undefined);
|
|
130
|
+
});
|
|
121
131
|
}
|
|
122
132
|
});
|
|
123
133
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test: don't show an Ollama footer status unless Ollama is
|
|
3
|
+
* actually usable (running with at least one discovered model).
|
|
4
|
+
*/
|
|
5
|
+
import { test } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join, dirname } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const src = readFileSync(join(__dirname, "index.ts"), "utf-8");
|
|
13
|
+
|
|
14
|
+
test("probeAndRegister returns false when no Ollama models are discovered", () => {
|
|
15
|
+
assert.match(
|
|
16
|
+
src,
|
|
17
|
+
/if \(models\.length === 0\)[\s\S]*return false;/,
|
|
18
|
+
"running-without-models should not be treated as available",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("interactive session clears ollama footer status when unavailable", () => {
|
|
23
|
+
assert.match(
|
|
24
|
+
src,
|
|
25
|
+
/ctx\.ui\.setStatus\("ollama", found \? "Ollama" : undefined\)/,
|
|
26
|
+
"status should be cleared when probeAndRegister reports unavailable",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
File without changes
|
|
File without changes
|