@crouton-kit/crouter 0.3.68 → 0.3.70
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/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +31 -31
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +28 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +11 -11
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -0
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +8 -4
- package/dist/clients/attach/attach-cmd.js +667 -667
- package/dist/clients/attach/auth-pickers.d.ts +12 -0
- package/dist/clients/attach/auth-pickers.js +11 -0
- package/dist/clients/attach/pickers.js +2 -0
- package/dist/clients/attach/slash-commands.js +1 -0
- package/dist/commands/node.js +3 -3
- package/dist/commands/profile/new.js +30 -5
- package/dist/commands/sys/setup-core.js +2 -0
- package/dist/commands/sys/setup.js +132 -16
- package/dist/core/__tests__/full/broker-pane-resolution.test.js +9 -6
- package/dist/core/__tests__/full/cascade-close.test.js +5 -2
- package/dist/core/__tests__/full/dead-pane-regression.test.js +6 -3
- package/dist/core/__tests__/full/detach-focus.test.js +10 -5
- package/dist/core/__tests__/full/human-new-window-regression.test.js +6 -3
- package/dist/core/__tests__/full/review-render-pane-regression.test.js +2 -2
- package/dist/core/__tests__/helpers/harness.d.ts +1 -0
- package/dist/core/__tests__/helpers/harness.js +28 -4
- package/dist/core/__tests__/live-mutation-verbs.test.js +5 -4
- package/dist/core/__tests__/review-model-floor.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-event-stream.test.js +11 -9
- package/dist/core/canvas/__tests__/render-remote.test.js +7 -4
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +30 -13
- package/dist/core/canvas/pid.d.ts +14 -10
- package/dist/core/canvas/pid.js +14 -10
- package/dist/core/runtime/broker.js +2 -0
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/revive.js +28 -18
- package/dist/core/view/__tests__/transport-remote.test.js +9 -6
- package/dist/daemon/crtrd-cli.d.ts +1 -1
- package/dist/daemon/crtrd-cli.js +1 -0
- package/dist/suppress-experimental-warnings.d.ts +1 -0
- package/dist/suppress-experimental-warnings.js +15 -0
- package/dist/types.js +10 -10
- package/dist/web-client/assets/index-BUdm9s9s.css +2 -0
- package/dist/web-client/assets/{index-CnF5r8ky.js → index-DiFuLcp6.js} +18 -17
- package/dist/web-client/index.html +3 -3
- package/dist/web-client/sw.js +1 -1
- package/package.json +10 -7
- package/scripts/postinstall.mjs +1 -1
- package/dist/web-client/assets/index-BnmSLNLa.css +0 -2
package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts
CHANGED
|
@@ -26,10 +26,10 @@ function writeLadderConfig() {
|
|
|
26
26
|
light: "anthropic/custom-light-x",
|
|
27
27
|
},
|
|
28
28
|
openai: {
|
|
29
|
-
ultra: "openai-codex/gpt-5.
|
|
30
|
-
strong: "openai-codex/gpt-5.
|
|
31
|
-
medium: "openai-codex/gpt-5.
|
|
32
|
-
light: "openai-codex/gpt-5.
|
|
29
|
+
ultra: "openai-codex/gpt-5.6-sol:max",
|
|
30
|
+
strong: "openai-codex/gpt-5.6-sol:high",
|
|
31
|
+
medium: "openai-codex/gpt-5.6-terra:medium",
|
|
32
|
+
light: "openai-codex/gpt-5.6-luna:low",
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
35
|
},
|
|
@@ -726,8 +726,8 @@ test("resolves Anthropic to strength-matched OpenAI Codex fallback", () => {
|
|
|
726
726
|
|
|
727
727
|
assert.deepEqual(helpers.resolveFallbackTarget("anthropic", "anthropic/claude-opus-4-8:high"), {
|
|
728
728
|
providerId: "openai-codex",
|
|
729
|
-
modelId: "gpt-5.
|
|
730
|
-
label: "openai-codex/gpt-5.
|
|
729
|
+
modelId: "gpt-5.6-sol",
|
|
730
|
+
label: "openai-codex/gpt-5.6-sol:high",
|
|
731
731
|
strength: "strong",
|
|
732
732
|
thinkingLevel: "high",
|
|
733
733
|
});
|
|
@@ -736,7 +736,7 @@ test("resolves Anthropic to strength-matched OpenAI Codex fallback", () => {
|
|
|
736
736
|
test("resolves OpenAI Codex to strength-matched Anthropic fallback", () => {
|
|
737
737
|
writeLadderConfig();
|
|
738
738
|
|
|
739
|
-
assert.deepEqual(helpers.resolveFallbackTarget("openai-codex", "openai-codex/gpt-5.
|
|
739
|
+
assert.deepEqual(helpers.resolveFallbackTarget("openai-codex", "openai-codex/gpt-5.6-terra:medium"), {
|
|
740
740
|
providerId: "anthropic",
|
|
741
741
|
modelId: "claude-sonnet-4-6",
|
|
742
742
|
label: "anthropic/claude-sonnet-4-6:high",
|
|
@@ -745,15 +745,15 @@ test("resolves OpenAI Codex to strength-matched Anthropic fallback", () => {
|
|
|
745
745
|
});
|
|
746
746
|
});
|
|
747
747
|
|
|
748
|
-
// Ultra and strong share the bare model id gpt-5.
|
|
748
|
+
// Ultra and strong share the bare model id gpt-5.6-sol; the live thinking level selects
|
|
749
749
|
// the matching ladder rung.
|
|
750
750
|
test("disambiguates colliding ladder rungs by the live thinking level", () => {
|
|
751
751
|
resetRotationState();
|
|
752
752
|
writeLadderConfig();
|
|
753
753
|
const config = helpers.readRotationConfig();
|
|
754
754
|
|
|
755
|
-
//
|
|
756
|
-
assert.deepEqual(helpers.resolveFallbackTarget("openai-codex", "gpt-5.
|
|
755
|
+
// max -> ultra rung -> Anthropic ultra (claude-fable-5)
|
|
756
|
+
assert.deepEqual(helpers.resolveFallbackTarget("openai-codex", "gpt-5.6-sol", config, "max"), {
|
|
757
757
|
providerId: "anthropic",
|
|
758
758
|
modelId: "claude-fable-5",
|
|
759
759
|
label: "anthropic/claude-fable-5:high",
|
|
@@ -762,10 +762,10 @@ test("disambiguates colliding ladder rungs by the live thinking level", () => {
|
|
|
762
762
|
});
|
|
763
763
|
|
|
764
764
|
// high -> strong rung -> Anthropic strong (claude-opus-4-8)
|
|
765
|
-
assert.equal(helpers.resolveFallbackTarget("openai-codex", "gpt-5.
|
|
765
|
+
assert.equal(helpers.resolveFallbackTarget("openai-codex", "gpt-5.6-sol", config, "high").modelId, "claude-opus-4-8");
|
|
766
766
|
|
|
767
767
|
// No thinking level falls back to the default-strength tie-break (strong -> opus).
|
|
768
|
-
assert.equal(helpers.resolveFallbackTarget("openai-codex", "gpt-5.
|
|
768
|
+
assert.equal(helpers.resolveFallbackTarget("openai-codex", "gpt-5.6-sol", config).modelId, "claude-opus-4-8");
|
|
769
769
|
});
|
|
770
770
|
|
|
771
771
|
test("parses Retry-After seconds and HTTP dates", () => {
|
|
@@ -783,7 +783,7 @@ test("restores preferred model when its provider pool frees up", async () => {
|
|
|
783
783
|
resetRotationState();
|
|
784
784
|
writeLadderConfig();
|
|
785
785
|
helpers.writeSubscriptionPool("openai-codex", [credential("codex")]);
|
|
786
|
-
helpers.writeRotationConfig({ preferredModel: { providerId: "openai-codex", modelId: "gpt-5.
|
|
786
|
+
helpers.writeRotationConfig({ preferredModel: { providerId: "openai-codex", modelId: "gpt-5.6-sol" } });
|
|
787
787
|
|
|
788
788
|
const setModelCalls: any[] = [];
|
|
789
789
|
const ctx = {
|
|
@@ -807,7 +807,7 @@ test("restores preferred model when its provider pool frees up", async () => {
|
|
|
807
807
|
assert.equal(restored, true);
|
|
808
808
|
assert.deepEqual(setModelCalls[0], {
|
|
809
809
|
provider: "openai-codex",
|
|
810
|
-
id: "gpt-5.
|
|
810
|
+
id: "gpt-5.6-sol",
|
|
811
811
|
api: "openai-codex-responses",
|
|
812
812
|
});
|
|
813
813
|
assert.equal(helpers.readRotationConfig().preferredModel, undefined);
|
|
@@ -860,7 +860,7 @@ test("switches exhausted Anthropic sessions to OpenAI Codex fallback", async ()
|
|
|
860
860
|
model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
|
|
861
861
|
modelRegistry: {
|
|
862
862
|
find(provider: string, id: string) {
|
|
863
|
-
return provider === "openai-codex" && id === "gpt-5.
|
|
863
|
+
return provider === "openai-codex" && id === "gpt-5.6-terra" ? { provider, id, api: "openai-codex-responses" } : undefined;
|
|
864
864
|
},
|
|
865
865
|
},
|
|
866
866
|
} as any;
|
|
@@ -881,14 +881,14 @@ test("switches exhausted Anthropic sessions to OpenAI Codex fallback", async ()
|
|
|
881
881
|
|
|
882
882
|
assert.deepEqual(target, {
|
|
883
883
|
providerId: "openai-codex",
|
|
884
|
-
modelId: "gpt-5.
|
|
885
|
-
label: "openai-codex/gpt-5.
|
|
884
|
+
modelId: "gpt-5.6-terra",
|
|
885
|
+
label: "openai-codex/gpt-5.6-terra:medium",
|
|
886
886
|
strength: "medium",
|
|
887
887
|
thinkingLevel: "medium",
|
|
888
888
|
});
|
|
889
889
|
assert.deepEqual(switchedTo, {
|
|
890
890
|
provider: "openai-codex",
|
|
891
|
-
id: "gpt-5.
|
|
891
|
+
id: "gpt-5.6-terra",
|
|
892
892
|
api: "openai-codex-responses",
|
|
893
893
|
});
|
|
894
894
|
assert.equal(thinkingLevel, "medium");
|
|
@@ -903,7 +903,7 @@ test("switches exhausted OpenAI Codex sessions to Anthropic fallback", async ()
|
|
|
903
903
|
writeLadderConfig();
|
|
904
904
|
|
|
905
905
|
const ctx = {
|
|
906
|
-
model: { provider: "openai-codex", id: "gpt-5.
|
|
906
|
+
model: { provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-codex-responses" },
|
|
907
907
|
modelRegistry: {
|
|
908
908
|
find(provider: string, id: string) {
|
|
909
909
|
return provider === "anthropic" && id === "claude-opus-4-8" ? { provider, id, api: "anthropic-messages" } : undefined;
|
|
@@ -940,18 +940,18 @@ test("switches exhausted OpenAI Codex sessions to Anthropic fallback", async ()
|
|
|
940
940
|
assert.equal(thinkingLevel, "high");
|
|
941
941
|
assert.deepEqual(helpers.readRotationConfig().preferredModel, {
|
|
942
942
|
providerId: "openai-codex",
|
|
943
|
-
modelId: "gpt-5.
|
|
943
|
+
modelId: "gpt-5.6-sol",
|
|
944
944
|
});
|
|
945
945
|
});
|
|
946
946
|
|
|
947
947
|
function makeRotationCtx() {
|
|
948
948
|
const ctx = {
|
|
949
|
-
model: { provider: "openai-codex", id: "gpt-5.
|
|
949
|
+
model: { provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-codex-responses" },
|
|
950
950
|
ui: { setStatus() {} },
|
|
951
951
|
modelRegistry: {
|
|
952
952
|
find(provider: string, id: string) {
|
|
953
953
|
if (provider === "anthropic" && id === "claude-opus-4-8") return { provider, id, api: "anthropic-messages" };
|
|
954
|
-
if (provider === "openai-codex" && id === "gpt-5.
|
|
954
|
+
if (provider === "openai-codex" && id === "gpt-5.6-sol") return { provider, id, api: "openai-codex-responses" };
|
|
955
955
|
return undefined;
|
|
956
956
|
},
|
|
957
957
|
},
|
|
@@ -999,7 +999,7 @@ test("falls back transparently and serves the turn on Anthropic when Codex is ex
|
|
|
999
999
|
await providerRotationExtension(pi);
|
|
1000
1000
|
await events.session_start?.({}, ctx);
|
|
1001
1001
|
const stream = providers["openai-codex"].streamSimple(
|
|
1002
|
-
{ provider: "openai-codex", id: "gpt-5.
|
|
1002
|
+
{ provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-codex-responses" },
|
|
1003
1003
|
ctx,
|
|
1004
1004
|
);
|
|
1005
1005
|
const emitted = [] as any[];
|
|
@@ -1377,7 +1377,7 @@ test("degrades gracefully via the builtin ladder when a config-less user has no
|
|
|
1377
1377
|
modelRegistry: {
|
|
1378
1378
|
find(provider: string, id: string) {
|
|
1379
1379
|
if (provider === "anthropic" && id === "claude-opus-4-8") return { provider, id, api: "anthropic-messages" };
|
|
1380
|
-
if (provider === "openai-codex" && id === "gpt-5.
|
|
1380
|
+
if (provider === "openai-codex" && id === "gpt-5.6-sol") return { provider, id, api: "openai-codex-responses" };
|
|
1381
1381
|
return undefined;
|
|
1382
1382
|
},
|
|
1383
1383
|
},
|
|
@@ -1404,7 +1404,7 @@ test("degrades gracefully via the builtin ladder when a config-less user has no
|
|
|
1404
1404
|
await providerRotationExtension(pi);
|
|
1405
1405
|
await events.session_start?.({}, ctx);
|
|
1406
1406
|
// Anthropic pool is empty (reset above), so the credential loop never runs. The builtin
|
|
1407
|
-
// ladder resolves a cross-provider target (openai-codex/gpt-5.
|
|
1407
|
+
// ladder resolves a cross-provider target (openai-codex/gpt-5.6-sol), but that provider was
|
|
1408
1408
|
// never authenticated either (#123 gates fallback on the target actually being authed),
|
|
1409
1409
|
// so the session model stays unchanged and the turn returns an actionable message.
|
|
1410
1410
|
const stream = providers["anthropic"].streamSimple(ctx.model, ctx);
|
|
@@ -1419,16 +1419,16 @@ test("degrades gracefully via the builtin ladder when a config-less user has no
|
|
|
1419
1419
|
});
|
|
1420
1420
|
|
|
1421
1421
|
// readModelLadders() reads the builtin anthropic/openai ladder when config.json is absent.
|
|
1422
|
-
test("resolves the builtin anthropic ladder (medium =
|
|
1422
|
+
test("resolves the builtin anthropic ladder (medium = opus-4-8) with no ~/.crouter/config.json present", () => {
|
|
1423
1423
|
resetRotationState();
|
|
1424
1424
|
rmSync(join(home, ".crouter", "config.json"), { force: true });
|
|
1425
1425
|
|
|
1426
|
-
const target = helpers.resolveFallbackTarget("anthropic", "anthropic/claude-
|
|
1426
|
+
const target = helpers.resolveFallbackTarget("anthropic", "anthropic/claude-opus-4-8:high");
|
|
1427
1427
|
|
|
1428
1428
|
assert.deepEqual(target, {
|
|
1429
1429
|
providerId: "openai-codex",
|
|
1430
|
-
modelId: "gpt-5.
|
|
1431
|
-
label: "openai-codex/gpt-5.
|
|
1430
|
+
modelId: "gpt-5.6-terra",
|
|
1431
|
+
label: "openai-codex/gpt-5.6-terra:medium",
|
|
1432
1432
|
strength: "medium",
|
|
1433
1433
|
thinkingLevel: "medium",
|
|
1434
1434
|
});
|
|
@@ -1578,7 +1578,7 @@ test("treats a Codex usage-limit message on a non-429 status as a rate limit: co
|
|
|
1578
1578
|
helpers.writeSubscriptionPool("anthropic", [credential("personal")]);
|
|
1579
1579
|
|
|
1580
1580
|
const { ctx, setModelCalls, providers, events, pi } = makeRotationCtx();
|
|
1581
|
-
ctx.model = { provider: "openai-codex", id: "gpt-5.
|
|
1581
|
+
ctx.model = { provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-codex-responses" };
|
|
1582
1582
|
const streamedModels: any[] = [];
|
|
1583
1583
|
rotationModule.__setStreamForProviderForTest((model) => {
|
|
1584
1584
|
streamedModels.push(model);
|
|
@@ -1601,7 +1601,7 @@ test("treats a Codex usage-limit message on a non-429 status as a rate limit: co
|
|
|
1601
1601
|
await providerRotationExtension(pi);
|
|
1602
1602
|
await events.session_start?.({}, ctx);
|
|
1603
1603
|
const stream = providers["openai-codex"].streamSimple(
|
|
1604
|
-
{ provider: "openai-codex", id: "gpt-5.
|
|
1604
|
+
{ provider: "openai-codex", id: "gpt-5.6-sol", api: "openai-codex-responses" },
|
|
1605
1605
|
ctx,
|
|
1606
1606
|
);
|
|
1607
1607
|
const emitted = [] as any[];
|
|
@@ -736,31 +736,51 @@ function updateRotationStatus(ctx: ExtensionContext): void {
|
|
|
736
736
|
if (current) setStatus(formatStatusLine(current.providerId));
|
|
737
737
|
}
|
|
738
738
|
|
|
739
|
+
// Manual-code input is a human-in-the-loop OAuth round-trip (open link, log in on
|
|
740
|
+
// whatever device, copy the localhost redirect URL back) that routinely exceeds
|
|
741
|
+
// DEFAULT_DIALOG_TIMEOUT_MS -- give it a human-scale bound instead of the ordinary
|
|
742
|
+
// confirm/select default.
|
|
743
|
+
const OAUTH_MANUAL_INPUT_TIMEOUT_MS = 600_000;
|
|
744
|
+
|
|
739
745
|
async function loginProvider(providerId: ManagedProviderId, ctx: ExtensionContext) {
|
|
740
746
|
if (providerId === ANTHROPIC_PROVIDER_ID) {
|
|
747
|
+
let authorizeUrl: string | undefined;
|
|
741
748
|
return loginAnthropic({
|
|
742
749
|
onAuth: ({ url, instructions }) => {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
750
|
+
authorizeUrl = url;
|
|
751
|
+
// Best-effort for the local attach case where a browser is reachable; on a
|
|
752
|
+
// headless remote box this opens nothing and the URL below is the real path.
|
|
746
753
|
openBrowser(url);
|
|
754
|
+
if (instructions) ctx.ui.notify(instructions, "info");
|
|
747
755
|
},
|
|
748
756
|
onPrompt: async (prompt) => (await ctx.ui.input(prompt.message, prompt.placeholder ?? "")) ?? "",
|
|
749
757
|
onProgress: (message) => ctx.ui.notify(message, "info"),
|
|
750
|
-
onManualCodeInput: async () =>
|
|
758
|
+
onManualCodeInput: async () =>
|
|
759
|
+
(await ctx.ui.input(
|
|
760
|
+
`Open this URL to authenticate Claude:\n${authorizeUrl}\n\nPaste the redirect URL or code`,
|
|
761
|
+
"http://localhost:53692/callback?...",
|
|
762
|
+
{ timeout: OAUTH_MANUAL_INPUT_TIMEOUT_MS },
|
|
763
|
+
)) ?? "",
|
|
751
764
|
});
|
|
752
765
|
}
|
|
753
766
|
|
|
767
|
+
let authorizeUrl: string | undefined;
|
|
754
768
|
return loginOpenAICodex({
|
|
755
769
|
onAuth: ({ url, instructions }) => {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
770
|
+
authorizeUrl = url;
|
|
771
|
+
// Best-effort for the local attach case where a browser is reachable; on a
|
|
772
|
+
// headless remote box this opens nothing and the URL below is the real path.
|
|
759
773
|
openBrowser(url);
|
|
774
|
+
if (instructions) ctx.ui.notify(instructions, "info");
|
|
760
775
|
},
|
|
761
776
|
onPrompt: async (prompt) => (await ctx.ui.input(prompt.message, prompt.placeholder ?? "")) ?? "",
|
|
762
777
|
onProgress: (message) => ctx.ui.notify(message, "info"),
|
|
763
|
-
onManualCodeInput: async () =>
|
|
778
|
+
onManualCodeInput: async () =>
|
|
779
|
+
(await ctx.ui.input(
|
|
780
|
+
`Open this URL to authenticate OpenAI Codex:\n${authorizeUrl}\n\nPaste the redirect URL or code`,
|
|
781
|
+
"http://localhost:1455/auth/callback?...",
|
|
782
|
+
{ timeout: OAUTH_MANUAL_INPUT_TIMEOUT_MS },
|
|
783
|
+
)) ?? "",
|
|
764
784
|
});
|
|
765
785
|
}
|
|
766
786
|
|
|
@@ -291,7 +291,7 @@ export function parseRetryAfterHeader(value: string | undefined, now = Date.now(
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
function thinkingLevelOfRef(ref: string): ThinkingLevel | undefined {
|
|
294
|
-
const match = ref.trim().match(/:(thinking|off|minimal|low|medium|high|xhigh)$/i);
|
|
294
|
+
const match = ref.trim().match(/:(thinking|off|minimal|low|medium|high|xhigh|max)$/i);
|
|
295
295
|
if (!match) return undefined;
|
|
296
296
|
const level = match[1].toLowerCase();
|
|
297
297
|
return level === "off" || level === "thinking" ? undefined : (level as ThinkingLevel);
|
|
@@ -301,7 +301,7 @@ function normalizeModelRef(ref: string): string {
|
|
|
301
301
|
return ref
|
|
302
302
|
.trim()
|
|
303
303
|
.replace(/^[^/]+\//, "")
|
|
304
|
-
.replace(/:(?:thinking|off|minimal|low|medium|high|xhigh)$/i, "")
|
|
304
|
+
.replace(/:(?:thinking|off|minimal|low|medium|high|xhigh|max)$/i, "")
|
|
305
305
|
.toLowerCase();
|
|
306
306
|
}
|
|
307
307
|
|
|
@@ -313,7 +313,7 @@ function splitModelRef(ref: string): FallbackTarget {
|
|
|
313
313
|
throw new Error(`Unsupported fallback provider: ${providerId}`);
|
|
314
314
|
}
|
|
315
315
|
const rawModelId = slash === -1 ? trimmed : trimmed.slice(slash + 1).trim();
|
|
316
|
-
const thinkingMatch = rawModelId.match(/^(.*?)(?::(thinking|off|minimal|low|medium|high|xhigh))$/i);
|
|
316
|
+
const thinkingMatch = rawModelId.match(/^(.*?)(?::(thinking|off|minimal|low|medium|high|xhigh|max))$/i);
|
|
317
317
|
return {
|
|
318
318
|
providerId,
|
|
319
319
|
modelId: thinkingMatch ? thinkingMatch[1] : rawModelId,
|
|
@@ -333,15 +333,15 @@ function splitModelRef(ref: string): FallbackTarget {
|
|
|
333
333
|
const BUILTIN_MODEL_LADDERS: Record<LadderProviderId, ModelLadderConfig> = {
|
|
334
334
|
anthropic: {
|
|
335
335
|
ultra: "anthropic/claude-fable-5:high",
|
|
336
|
-
strong: "anthropic/claude-
|
|
337
|
-
medium: "anthropic/claude-
|
|
338
|
-
light: "anthropic/claude-
|
|
336
|
+
strong: "anthropic/claude-fable-5:medium",
|
|
337
|
+
medium: "anthropic/claude-opus-4-8:high",
|
|
338
|
+
light: "anthropic/claude-sonnet-5:high",
|
|
339
339
|
},
|
|
340
340
|
openai: {
|
|
341
|
-
ultra: "openai-codex/gpt-5.
|
|
342
|
-
strong: "openai-codex/gpt-5.
|
|
343
|
-
medium: "openai-codex/gpt-5.
|
|
344
|
-
light: "openai-codex/gpt-5.
|
|
341
|
+
ultra: "openai-codex/gpt-5.6-sol:max",
|
|
342
|
+
strong: "openai-codex/gpt-5.6-sol:high",
|
|
343
|
+
medium: "openai-codex/gpt-5.6-terra:medium",
|
|
344
|
+
light: "openai-codex/gpt-5.6-luna:low",
|
|
345
345
|
},
|
|
346
346
|
};
|
|
347
347
|
|
|
@@ -389,7 +389,7 @@ function findStrengthForModelId(
|
|
|
389
389
|
if (ref && normalizeModelRef(ref) === bareId) matches.push(strength);
|
|
390
390
|
}
|
|
391
391
|
if (matches.length === 0) return undefined;
|
|
392
|
-
// Rungs that share a bare model id (e.g. gpt-5.
|
|
392
|
+
// Rungs that share a bare model id (e.g. gpt-5.6-sol:max vs gpt-5.6-sol:high) are
|
|
393
393
|
// disambiguated by the live reasoning level so an ultra run doesn't collapse to strong.
|
|
394
394
|
if (matches.length > 1 && currentThinkingLevel) {
|
|
395
395
|
const exact = matches.find((strength) => thinkingLevelOfRef(source[strength]!) === currentThinkingLevel);
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import './suppress-experimental-warnings.js';
|
package/dist/cli.js
CHANGED
|
@@ -152,7 +152,7 @@ function seedPoisonedLocalState() {
|
|
|
152
152
|
last_activity: 'POISON ACTIVITY — local read leaked into remote attach chrome',
|
|
153
153
|
}));
|
|
154
154
|
}
|
|
155
|
-
test('buildCanvasPanelLines suppresses every local-read adornment for a RemoteCanvasSource, even with conflicting local state for the same node id', async () => {
|
|
155
|
+
test('buildCanvasPanelLines suppresses every local-read adornment for a RemoteCanvasSource, even with conflicting local state for the same node id', async (t) => {
|
|
156
156
|
seedPoisonedLocalState();
|
|
157
157
|
const rootMeta = meta('root');
|
|
158
158
|
const aMeta = meta('a'); // the REMOTE node's meta — unrelated to the poisoned local NodeMeta above
|
|
@@ -166,6 +166,10 @@ test('buildCanvasPanelLines suppresses every local-read adornment for a RemoteCa
|
|
|
166
166
|
}
|
|
167
167
|
return { status: 500, body: {} };
|
|
168
168
|
});
|
|
169
|
+
// Register the close BEFORE any assertion runs — a listening server left
|
|
170
|
+
// open by a thrown assertion keeps this test file's process alive forever
|
|
171
|
+
// (see the CI-hang postmortem on rebuild-coalescer.test.ts).
|
|
172
|
+
t.after(() => server.close());
|
|
169
173
|
const source = new RemoteCanvasSource(server.url, 'tok');
|
|
170
174
|
// 'root' has 'a' as a live report → row('a') renders into `reports`.
|
|
171
175
|
const { reports } = await buildCanvasPanelLines('root', {}, IDENTITY_PALETTE, source);
|
|
@@ -175,9 +179,8 @@ test('buildCanvasPanelLines suppresses every local-read adornment for a RemoteCa
|
|
|
175
179
|
assert.ok(!line.includes('POISON'), `poisoned local text must never appear in a remote row: ${line}`);
|
|
176
180
|
assert.ok(!line.includes('⚠'), `a local active fault must not surface as the hanging glyph on a remote row: ${line}`);
|
|
177
181
|
assert.ok(!line.includes(BG_ATTACHED), `a local focus/attach must not paint a remote row as attached: ${line}`);
|
|
178
|
-
await server.close();
|
|
179
182
|
});
|
|
180
|
-
test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSource AND makes Enter/m/x no-ops, even with conflicting local state for the same node id', async () => {
|
|
183
|
+
test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSource AND makes Enter/m/x no-ops, even with conflicting local state for the same node id', async (t) => {
|
|
181
184
|
seedPoisonedLocalState();
|
|
182
185
|
const rootMeta = meta('root');
|
|
183
186
|
const aMeta = meta('a');
|
|
@@ -191,6 +194,8 @@ test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSourc
|
|
|
191
194
|
}
|
|
192
195
|
return { status: 500, body: {} };
|
|
193
196
|
});
|
|
197
|
+
// Register the close BEFORE any assertion runs — see the note above.
|
|
198
|
+
t.after(() => server.close());
|
|
194
199
|
const source = new RemoteCanvasSource(server.url, 'tok');
|
|
195
200
|
const { tui } = fakeTui();
|
|
196
201
|
const overlay = new GraphOverlay(tui, 'root', () => ({}), IDENTITY_PALETTE, source);
|
|
@@ -219,7 +224,6 @@ test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSourc
|
|
|
219
224
|
// m must NOT close the overlay either (it would focusTarget the manager).
|
|
220
225
|
overlay.handleInput('m');
|
|
221
226
|
assert.equal(overlay.isOpen(), true, 'm must not act (close) on a remote graph row');
|
|
222
|
-
await server.close();
|
|
223
227
|
});
|
|
224
228
|
// ---------------------------------------------------------------------------
|
|
225
229
|
// Phase 3 review 2 — the ENCLOSING attach viewer (slash commands, pane tag,
|