Skip to content

Commit d39347d

Browse files
committed
sync(bfmono): fix(gambit): clamp deck-level maxTurns bounds in test run selection (+19 more) (bfmono@1de6b335c)
This PR is an automated gambitmono sync of bfmono Gambit packages. - Source: `packages/gambit/` - Core: `packages/gambit/packages/gambit-core/` - bfmono rev: 1de6b335c Changes: - 1de6b335c fix(gambit): clamp deck-level maxTurns bounds in test run selection - 01d7abbb9 fix(gambit): default verify tab bootstrap flag to enabled - f3d186c7b fix(gambit): include extension schemas in exports and default serve to restored workspace - a83b7cbe7 fix(gambit): move unbounded build timeout to deck opt-in - acb2de627 fix(gambit): avoid strict json_schema 400s in openrouter responses - 8aba573b6 fix(gambit-simulator-ui): treat errored calibrate runs as failed - ca2028cf8 fix(gambit): prevent circular trace crashes in workspace test run API - 7e41517e5 fix(gambit): make build assistant run timeout unbounded - 24341143d feat(gambit): add deterministic verify fixture seeding - ff2c2d33d feat(gambit): add verify tab consistency UI - 91f0c93bb feat(gambit): add feature-flagged verify routing - 1392f8b65 feat(gambit): support deck-level maxTurns override - f5920ef86 chore(gambit): cut 0.8.5-rc.10 - 073c84d0e chore(gambit): cut 0.8.5-rc.10 - 2c669ba51 fix(gambit-core): ship builtin snippets/decks in npm runtime layouts - 498708844 docs(gambit): retire synthetic respond/end guidance and align deck authoring surfaces - 35ad1d5b5 feat(gambit-core): add OpenResponses convergence runtime, extensions, and async action semantics - b2d78cfb3 chore(gambit): cut 0.8.5-rc.9 - a0029f933 revert(gambit-ci): restore shell-based nix develop in ci-gambit-core - 1b2930064 fix(gambit-ci): create nix profile parent dir in ci-gambit-core Do not edit this repo directly; make changes in bfmono and re-run the sync.
1 parent 9bf5613 commit d39347d

2 files changed

Lines changed: 144 additions & 11 deletions

File tree

src/server.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,7 +2235,8 @@ export function startWebSocketSimulator(opts: {
22352235
const parseDeckMaxTurns = (value: unknown): number | undefined => {
22362236
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
22372237
const rounded = Math.round(value);
2238-
if (rounded < 1 || rounded > 200) return undefined;
2238+
if (rounded < 1) return 1;
2239+
if (rounded > 200) return 200;
22392240
return rounded;
22402241
};
22412242

@@ -6512,11 +6513,11 @@ function simulatorReactHtml(
65126513
})();
65136514
const verifyTabEnabled = (() => {
65146515
const raw = Deno.env.get("GAMBIT_SIMULATOR_VERIFY_TAB");
6515-
if (raw === undefined) return false;
6516+
if (raw === undefined) return true;
65166517
const normalized = raw.trim().toLowerCase();
6517-
return normalized === "1" || normalized === "true" ||
6518-
normalized === "yes" ||
6519-
normalized === "on";
6518+
return !(normalized === "0" || normalized === "false" ||
6519+
normalized === "no" ||
6520+
normalized === "off");
65206521
})();
65216522
const chatAccordionEnabled = (() => {
65226523
const raw = Deno.env.get("GAMBIT_SIMULATOR_CHAT_ACCORDION");
@@ -6566,11 +6567,7 @@ function simulatorReactHtml(
65666567
window.__GAMBIT_DECK_LABEL__ = ${JSON.stringify(safeDeckLabel)};
65676568
window.__GAMBIT_VERSION__ = ${JSON.stringify(gambitVersion)};
65686569
window.__GAMBIT_BUILD_TAB_ENABLED__ = ${JSON.stringify(buildTabEnabled)};
6569-
window.__GAMBIT_VERIFY_TAB_ENABLED__ = ${
6570-
JSON.stringify(
6571-
verifyTabEnabled,
6572-
)
6573-
};
6570+
window.__GAMBIT_VERIFY_TAB_ENABLED__ = ${JSON.stringify(verifyTabEnabled)};
65746571
window.__GAMBIT_CHAT_ACCORDION_ENABLED__ = ${
65756572
JSON.stringify(
65766573
chatAccordionEnabled,

src/server_streams.test.ts

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,142 @@ Deno.test("test deck selection remains isolated across concurrent simulator inst
396396
}
397397
});
398398

399+
Deno.test("deck-level maxTurns is clamped and used by /api/test/run", async () => {
400+
const dir = await Deno.makeTempDir();
401+
const modHref = modImportPath();
402+
const rootDeckPath = path.join(dir, "max-turns-root.deck.ts");
403+
const lowDeckPath = path.join(dir, "max-turns-low.deck.ts");
404+
const highDeckPath = path.join(dir, "max-turns-high.deck.ts");
405+
const escapedLowPath = lowDeckPath.replaceAll("\\", "\\\\");
406+
const escapedHighPath = highDeckPath.replaceAll("\\", "\\\\");
407+
408+
await Deno.writeTextFile(
409+
lowDeckPath,
410+
`
411+
import { defineDeck } from "${modHref}";
412+
import { z } from "zod";
413+
export default defineDeck({
414+
inputSchema: z.string().optional(),
415+
outputSchema: z.string().optional(),
416+
modelParams: { model: "dummy-model" },
417+
});
418+
`,
419+
);
420+
421+
await Deno.writeTextFile(
422+
highDeckPath,
423+
`
424+
import { defineDeck } from "${modHref}";
425+
import { z } from "zod";
426+
export default defineDeck({
427+
inputSchema: z.string().optional(),
428+
outputSchema: z.string().optional(),
429+
modelParams: { model: "dummy-model" },
430+
});
431+
`,
432+
);
433+
434+
await Deno.writeTextFile(
435+
rootDeckPath,
436+
`
437+
import { defineDeck } from "${modHref}";
438+
import { z } from "zod";
439+
export default defineDeck({
440+
inputSchema: z.string().optional(),
441+
outputSchema: z.string().optional(),
442+
modelParams: { model: "dummy-model" },
443+
testDecks: [{
444+
id: "low-max-turns",
445+
label: "Low maxTurns",
446+
path: "${escapedLowPath}",
447+
maxTurns: 0,
448+
}, {
449+
id: "high-max-turns",
450+
label: "High maxTurns",
451+
path: "${escapedHighPath}",
452+
maxTurns: 500,
453+
}],
454+
});
455+
`,
456+
);
457+
458+
const provider: ModelProvider = {
459+
chat() {
460+
return Promise.resolve({
461+
message: { role: "assistant", content: "ok" },
462+
finishReason: "stop",
463+
});
464+
},
465+
};
466+
467+
const server = startWebSocketSimulator({
468+
deckPath: rootDeckPath,
469+
modelProvider: provider,
470+
port: 0,
471+
});
472+
const port = (server.addr as Deno.NetAddr).port;
473+
474+
try {
475+
const workspaceRes = await fetch(
476+
`http://127.0.0.1:${port}/api/workspace/new`,
477+
{ method: "POST" },
478+
);
479+
assertEquals(workspaceRes.ok, true);
480+
const workspaceBody = await workspaceRes.json() as { workspaceId?: string };
481+
const workspaceId = workspaceBody.workspaceId ?? "";
482+
assert(workspaceId.length > 0);
483+
484+
const listRes = await fetch(
485+
`http://127.0.0.1:${port}/api/test?workspaceId=${
486+
encodeURIComponent(workspaceId)
487+
}`,
488+
);
489+
assertEquals(listRes.ok, true);
490+
const listBody = await listRes.json() as {
491+
testDecks?: Array<{ id?: string; maxTurns?: number }>;
492+
};
493+
const lowDeck = listBody.testDecks?.find((deck) =>
494+
deck.id === "low-max-turns"
495+
);
496+
const highDeck = listBody.testDecks?.find((deck) =>
497+
deck.id === "high-max-turns"
498+
);
499+
assertEquals(lowDeck?.maxTurns, 1);
500+
assertEquals(highDeck?.maxTurns, 200);
501+
502+
const lowRunRes = await fetch(`http://127.0.0.1:${port}/api/test/run`, {
503+
method: "POST",
504+
headers: { "content-type": "application/json" },
505+
body: JSON.stringify({
506+
workspaceId,
507+
botDeckPath: lowDeckPath,
508+
}),
509+
});
510+
assertEquals(lowRunRes.ok, true);
511+
const lowRunBody = await lowRunRes.json() as {
512+
run?: { maxTurns?: number };
513+
};
514+
assertEquals(lowRunBody.run?.maxTurns, 1);
515+
516+
const highRunRes = await fetch(`http://127.0.0.1:${port}/api/test/run`, {
517+
method: "POST",
518+
headers: { "content-type": "application/json" },
519+
body: JSON.stringify({
520+
workspaceId,
521+
botDeckPath: highDeckPath,
522+
}),
523+
});
524+
assertEquals(highRunRes.ok, true);
525+
const highRunBody = await highRunRes.json() as {
526+
run?: { maxTurns?: number };
527+
};
528+
assertEquals(highRunBody.run?.maxTurns, 200);
529+
} finally {
530+
await server.shutdown();
531+
await server.finished;
532+
}
533+
});
534+
399535
Deno.test("scenario run completes with workerSandbox enabled", async () => {
400536
const dir = await Deno.makeTempDir();
401537
const modHref = modImportPath();
@@ -536,7 +672,7 @@ Deno.test("build bot endpoint streams status and runs", async () => {
536672
const html = await homepage.text();
537673
assert(html.includes("__GAMBIT_BUILD_TAB_ENABLED__"));
538674
assert(html.includes("__GAMBIT_VERIFY_TAB_ENABLED__"));
539-
assert(/__GAMBIT_VERIFY_TAB_ENABLED__\s*=\s*false/.test(html));
675+
assert(/__GAMBIT_VERIFY_TAB_ENABLED__\s*=\s*true/.test(html));
540676

541677
const runId = "test-build-run";
542678
const res = await fetch(`http://127.0.0.1:${port}/api/build/message`, {

0 commit comments

Comments
 (0)