From 3a730f83f6d2cae7a9a10ec15f6da88cabcededf Mon Sep 17 00:00:00 2001 From: Jignasu Pathak <63716051+Mister-JP@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:48:16 -0500 Subject: [PATCH 1/2] Freeze high-quality model configuration --- app/curiositypedia-experience.tsx | 9 +++++ app/globals.css | 25 +++++++------- lib/catalog.ts | 25 +++++++------- lib/live-research.ts | 3 +- lib/openai.ts | 15 +-------- lib/provider-usage.ts | 5 +++ lib/research-config.ts | 55 +++++++++++++++++++++++++++++++ lib/research/provider-stream.ts | 16 ++++++--- lib/usage-policy.ts | 4 +-- tests/live-redraw.test.mjs | 16 ++++----- tests/live-research.test.mjs | 43 ++++++++++++++++++------ tests/provider-usage.test.mjs | 34 +++++++++++++++++++ tests/routes.test.mjs | 14 ++++++++ tests/usage-summary.test.mjs | 4 +-- 14 files changed, 204 insertions(+), 64 deletions(-) create mode 100644 lib/research-config.ts diff --git a/app/curiositypedia-experience.tsx b/app/curiositypedia-experience.tsx index a5e121e..4448faa 100644 --- a/app/curiositypedia-experience.tsx +++ b/app/curiositypedia-experience.tsx @@ -729,6 +729,7 @@ export function CuriosityPediaExperience() { async function retryActiveQuestion() { if (!activeJourney || !activeTurn) return; + const previewMedia = activeTurn.media[0]; await create({ seed: activeTurn.question, performerId: nextPerformerId ?? activeJourney.performerId, @@ -736,6 +737,14 @@ export function CuriosityPediaExperience() { researchPreset: "standard", answerDensity: preferences.answerDensity, outputLocale: preferences.defaultOutputLocale, + preview: previewMedia ? { + question: activeTurn.question, + teaser: previewMedia.caption, + imageUrl: previewMedia.imageUrl, + imageAlt: previewMedia.alt, + sourceLabel: previewMedia.title ?? activeTurn.topicLabel, + sourceUrl: previewMedia.sourcePageUrl, + } : undefined, }); } diff --git a/app/globals.css b/app/globals.css index c4874e5..1172320 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1006,7 +1006,6 @@ html[data-theme="dark"] :is(input, textarea, select) { color-scheme: dark; } opacity: .75; position: absolute; top: 29%; - transform: rotate(-11deg); width: 94%; z-index: 1; } @@ -1074,9 +1073,12 @@ html[data-theme="dark"] :is(input, textarea, select) { color-scheme: dark; } inset calc(var(--celestial-size) * -.018) calc(var(--celestial-size) * -.018) calc(var(--celestial-size) * .03) rgba(122,74,0,.35), 0 0 calc(var(--celestial-size) * .06) rgba(255,212,59,.45); height: 16%; - left: 4%; + left: 0; + offset-anchor: center; + offset-path: ellipse(46% 21% at 50% 50%); + offset-rotate: 0deg; position: absolute; - top: 48%; + top: 0; width: 16%; z-index: 0; } @@ -1087,6 +1089,7 @@ html[data-theme="dark"] :is(input, textarea, select) { color-scheme: dark; } animation: none; background: linear-gradient(145deg, #ffffff, var(--sky)); left: 79%; + offset-path: none; top: 24%; transform: scale(.9); z-index: 4; @@ -1095,6 +1098,7 @@ html[data-theme="dark"] :is(input, textarea, select) { color-scheme: dark; } animation: none; background: var(--coral); left: 79%; + offset-path: none; top: 24%; transform: scale(.9); z-index: 4; @@ -1103,14 +1107,13 @@ html[data-theme="dark"] :is(input, textarea, select) { color-scheme: dark; } to { transform: translateX(-50%); } } @keyframes celestial-moon-orbit { - 0% { left: 4%; top: 48%; transform: translate(-50%, -50%) scale(.72); z-index: 0; } - 24.9% { z-index: 0; } - 25% { left: 50%; top: 14%; transform: translate(-50%, -50%) scale(.86); z-index: 0; } - 49.9% { z-index: 0; } - 50% { left: 96%; top: 48%; transform: translate(-50%, -50%) scale(1.08); z-index: 4; } - 75% { left: 50%; top: 82%; transform: translate(-50%, -50%) scale(.94); z-index: 4; } - 99.9% { z-index: 4; } - 100% { left: 4%; top: 48%; transform: translate(-50%, -50%) scale(.72); z-index: 0; } + 0% { offset-distance: 0%; transform: scale(1.08); z-index: 4; } + 25% { offset-distance: 25%; transform: scale(.94); z-index: 4; } + 49.9% { offset-distance: 49.9%; transform: scale(.72); z-index: 4; } + 50% { offset-distance: 50%; transform: scale(.72); z-index: 0; } + 75% { offset-distance: 75%; transform: scale(.86); z-index: 0; } + 99.9% { offset-distance: 99.9%; transform: scale(1.08); z-index: 0; } + 100% { offset-distance: 100%; transform: scale(1.08); z-index: 4; } } /* Empty / loading / footer */ diff --git a/lib/catalog.ts b/lib/catalog.ts index e3f9b1b..d834f2c 100644 --- a/lib/catalog.ts +++ b/lib/catalog.ts @@ -7,6 +7,7 @@ import type { PresetConfig, UserPreferences, } from "./contracts"; +import { OPENAI_PROMPT_LIMITS } from "./research-config"; export const PROMPT_VERSION = "wonder-research-turn@4.1.0"; @@ -188,33 +189,33 @@ export const PRESETS: PresetConfig[] = [ name: "Spark", description: "A quick sourced orientation for simple or stable curiosity.", sourceRange: "2–4 useful sources", - waitBand: "about 25 seconds", + waitBand: "up to 180 seconds", costBand: "lowest live cost", - maxToolCalls: 2, - maxOutputTokens: 4000, - deadlineMs: 25_000, + maxToolCalls: OPENAI_PROMPT_LIMITS.liveResearch.spark.maxToolCalls, + maxOutputTokens: OPENAI_PROMPT_LIMITS.liveResearch.spark.maxOutputTokens, + deadlineMs: OPENAI_PROMPT_LIMITS.liveResearch.spark.timeoutMs, }, { id: "standard", name: "Standard", description: "Enough homework for a thoughtful, balanced performance.", sourceRange: "3–8 useful sources", - waitBand: "about 60 seconds", + waitBand: "up to 240 seconds", costBand: "balanced live cost", - maxToolCalls: 5, - maxOutputTokens: 8000, - deadlineMs: 60_000, + maxToolCalls: OPENAI_PROMPT_LIMITS.liveResearch.standard.maxToolCalls, + maxOutputTokens: OPENAI_PROMPT_LIMITS.liveResearch.standard.maxOutputTokens, + deadlineMs: OPENAI_PROMPT_LIMITS.liveResearch.standard.timeoutMs, }, { id: "deep", name: "Deep", description: "Broader evidence for niche, current, or contested questions.", sourceRange: "6–15 useful sources", - waitBand: "up to 120 seconds", + waitBand: "up to 300 seconds", costBand: "highest live cost", - maxToolCalls: 10, - maxOutputTokens: 16000, - deadlineMs: 120_000, + maxToolCalls: OPENAI_PROMPT_LIMITS.liveResearch.deep.maxToolCalls, + maxOutputTokens: OPENAI_PROMPT_LIMITS.liveResearch.deep.maxOutputTokens, + deadlineMs: OPENAI_PROMPT_LIMITS.liveResearch.deep.timeoutMs, }, ]; diff --git a/lib/live-research.ts b/lib/live-research.ts index b36b106..2391ec2 100644 --- a/lib/live-research.ts +++ b/lib/live-research.ts @@ -23,6 +23,7 @@ import { responseIncompleteReason, structuredOutput, } from "./openai"; +import { OPENAI_IMAGE_SEARCH_MAX_RESULTS } from "./research-config"; import { recordOpenAIUsage } from "./provider-usage"; import { reserveProviderCost } from "./provider-cost-control"; import { localeName } from "./i18n"; @@ -218,7 +219,7 @@ export function liveResearchRequestBody( : { type: "web_search", search_content_types: ["image", "text"], - image_settings: { max_results: 10, caption: true }, + image_settings: { max_results: OPENAI_IMAGE_SEARCH_MAX_RESULTS, caption: true }, }], tool_choice: "auto", include: prepared.imagePreference === "avoid" diff --git a/lib/openai.ts b/lib/openai.ts index ecfdbb6..250d2d6 100644 --- a/lib/openai.ts +++ b/lib/openai.ts @@ -1,5 +1,6 @@ import { env } from "cloudflare:workers"; import { RepositoryError } from "./errors"; +export { OPENAI_PROMPT_LIMITS } from "./research-config"; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace -- Cloudflare augments this namespace. @@ -13,20 +14,6 @@ declare global { const RESPONSES_URL = "https://api.openai.com/v1/responses"; -export const OPENAI_PROMPT_LIMITS = { - liveResearch: { - spark: { maxToolCalls: 3, maxOutputTokens: 10_000, reasoning: "low", timeoutMs: 60_000 }, - standard: { maxToolCalls: 8, maxOutputTokens: 18_000, reasoning: "medium", timeoutMs: 150_000 }, - deep: { maxToolCalls: 12, maxOutputTokens: 26_000, reasoning: "high", timeoutMs: 180_000 }, - }, - starterGeneration: { maxOutputTokens: 6_000, reasoning: "high" }, - questionRedraw: { maxOutputTokens: 4_000, reasoning: "high" }, - visualCuration: { maxToolCalls: 12, maxOutputTokens: 14_000, reasoning: "medium", timeoutMs: 120_000 }, - imageNoteRepair: { maxOutputTokens: 3_000, reasoning: "medium", timeoutMs: 30_000 }, - citationRepair: { maxOutputTokens: 2_000, reasoning: "medium", timeoutMs: 30_000 }, - citationRecovery: { maxOutputTokens: 6_000, reasoning: "high", timeoutMs: 60_000 }, -} as const; - export function openAIConfigured(): boolean { return openAIEnabled() && Boolean(env.OPENAI_API_KEY?.trim()); } diff --git a/lib/provider-usage.ts b/lib/provider-usage.ts index 0406bef..2e46686 100644 --- a/lib/provider-usage.ts +++ b/lib/provider-usage.ts @@ -4,6 +4,7 @@ import type { ModelId } from "./contracts"; import { isRecord } from "./openai"; import { markProviderCostUncertain, + releaseProviderCost, settleProviderCost, } from "./provider-cost-control"; import type { ProviderCostOperation } from "./provider-cost-control"; @@ -97,6 +98,10 @@ export async function recordOpenAIUsage(input: RecordProviderUsageInput): Promis Math.round(usage.estimatedCostUsd * 1_000_000), input.providerRequestId ?? usage.providerResponseId, ); + } else if (input.outcome === "http_error") { + // A rejected HTTP request never started model work, so it must not + // retain a full model-priced hold or poison automatic retries. + await releaseProviderCost(input.costReservationId); } else { await markProviderCostUncertain(input.costReservationId, input.providerRequestId); } diff --git a/lib/research-config.ts b/lib/research-config.ts new file mode 100644 index 0000000..3d44786 --- /dev/null +++ b/lib/research-config.ts @@ -0,0 +1,55 @@ +/** + * The canonical generation profile for every selectable OpenAI model. + * + * Model routing may choose only `model`; prompts, reasoning, token budgets, + * tool budgets, and deadlines must come from this shared profile. + */ +export const GENERATION_CONFIG_VERSION = "high-quality@1"; + +export const OPENAI_PROMPT_LIMITS = Object.freeze({ + liveResearch: Object.freeze({ + spark: Object.freeze({ + maxToolCalls: 8, + maxOutputTokens: 20_000, + reasoning: "high", + timeoutMs: 180_000, + }), + standard: Object.freeze({ + maxToolCalls: 12, + maxOutputTokens: 30_000, + reasoning: "high", + timeoutMs: 240_000, + }), + deep: Object.freeze({ + maxToolCalls: 16, + maxOutputTokens: 40_000, + reasoning: "high", + timeoutMs: 300_000, + }), + }), + starterGeneration: Object.freeze({ maxOutputTokens: 10_000, reasoning: "high" }), + questionRedraw: Object.freeze({ maxOutputTokens: 8_000, reasoning: "high" }), + visualCuration: Object.freeze({ + maxToolCalls: 12, + maxOutputTokens: 20_000, + reasoning: "high", + timeoutMs: 180_000, + }), + imageNoteRepair: Object.freeze({ + maxOutputTokens: 6_000, + reasoning: "high", + timeoutMs: 60_000, + }), + citationRepair: Object.freeze({ + maxOutputTokens: 5_000, + reasoning: "high", + timeoutMs: 60_000, + }), + citationRecovery: Object.freeze({ + maxOutputTokens: 10_000, + reasoning: "high", + timeoutMs: 90_000, + }), +} as const); + +export const OPENAI_IMAGE_SEARCH_MAX_RESULTS = 10; diff --git a/lib/research/provider-stream.ts b/lib/research/provider-stream.ts index 5f7d3dc..cfc6ef3 100644 --- a/lib/research/provider-stream.ts +++ b/lib/research/provider-stream.ts @@ -113,6 +113,10 @@ export async function runProviderStream({ providerRequestId = response.headers.get("x-request-id"); if (!response.ok) { + const retryable = response.status === 408 + || response.status === 409 + || response.status === 429 + || response.status >= 500; usageRecorded = true; await recordOutcome({ ...usageContext, @@ -132,11 +136,15 @@ export async function runProviderStream({ response.status === 401 || response.status === 403 ? "PROVIDER_UNAVAILABLE" : "PROVIDER_ERROR", - response.status === 429 - ? "Live research is busy or has reached its provider limit. Please try again shortly." - : "Live research could not reach the provider. The journey was not committed; it is safe to retry.", + response.status === 401 || response.status === 403 + ? "This deployment cannot access the selected OpenAI model. Choose another model or verify the production OpenAI project access." + : response.status === 429 + ? "Live research is busy or has reached its provider limit. Please try again shortly." + : retryable + ? "Live research could not reach the provider. The journey was not committed; it is safe to retry." + : "The provider rejected this research request. Verify the selected model and shared generation configuration.", response.status === 429 ? 429 : 502, - true, + retryable, ); } diff --git a/lib/usage-policy.ts b/lib/usage-policy.ts index 0f1b2d9..18f3821 100644 --- a/lib/usage-policy.ts +++ b/lib/usage-policy.ts @@ -23,7 +23,7 @@ export const CURRENT_USER_POLICIES: Readonly { assert.equal(OPENAI_PROMPT_LIMITS.questionRedraw.reasoning, "high"); - assert.equal(OPENAI_PROMPT_LIMITS.questionRedraw.maxOutputTokens, 4_000); + assert.equal(OPENAI_PROMPT_LIMITS.questionRedraw.maxOutputTokens, 8_000); }); test("recognizes incomplete Responses API output across prompt paths", () => { @@ -62,12 +62,12 @@ test("recognizes incomplete Responses API output across prompt paths", () => { test("allocates scaled reasoning and output budgets to every prompt path", () => { assert.deepEqual(OPENAI_PROMPT_LIMITS.liveResearch, { - spark: { maxToolCalls: 3, maxOutputTokens: 10_000, reasoning: "low", timeoutMs: 60_000 }, - standard: { maxToolCalls: 8, maxOutputTokens: 18_000, reasoning: "medium", timeoutMs: 150_000 }, - deep: { maxToolCalls: 12, maxOutputTokens: 26_000, reasoning: "high", timeoutMs: 180_000 }, + spark: { maxToolCalls: 8, maxOutputTokens: 20_000, reasoning: "high", timeoutMs: 180_000 }, + standard: { maxToolCalls: 12, maxOutputTokens: 30_000, reasoning: "high", timeoutMs: 240_000 }, + deep: { maxToolCalls: 16, maxOutputTokens: 40_000, reasoning: "high", timeoutMs: 300_000 }, }); - assert.deepEqual(OPENAI_PROMPT_LIMITS.starterGeneration, { maxOutputTokens: 6_000, reasoning: "high" }); - assert.deepEqual(OPENAI_PROMPT_LIMITS.imageNoteRepair, { maxOutputTokens: 3_000, reasoning: "medium", timeoutMs: 30_000 }); - assert.deepEqual(OPENAI_PROMPT_LIMITS.citationRepair, { maxOutputTokens: 2_000, reasoning: "medium", timeoutMs: 30_000 }); - assert.deepEqual(OPENAI_PROMPT_LIMITS.citationRecovery, { maxOutputTokens: 6_000, reasoning: "high", timeoutMs: 60_000 }); + assert.deepEqual(OPENAI_PROMPT_LIMITS.starterGeneration, { maxOutputTokens: 10_000, reasoning: "high" }); + assert.deepEqual(OPENAI_PROMPT_LIMITS.imageNoteRepair, { maxOutputTokens: 6_000, reasoning: "high", timeoutMs: 60_000 }); + assert.deepEqual(OPENAI_PROMPT_LIMITS.citationRepair, { maxOutputTokens: 5_000, reasoning: "high", timeoutMs: 60_000 }); + assert.deepEqual(OPENAI_PROMPT_LIMITS.citationRecovery, { maxOutputTokens: 10_000, reasoning: "high", timeoutMs: 90_000 }); }); diff --git a/tests/live-research.test.mjs b/tests/live-research.test.mjs index 0347e3a..c8603a4 100644 --- a/tests/live-research.test.mjs +++ b/tests/live-research.test.mjs @@ -1,8 +1,8 @@ import assert from "node:assert/strict"; import test from "node:test"; import { env } from "cloudflare:workers"; -import { PERFORMERS } from "../lib/catalog.ts"; -import { runLiveResearch } from "../lib/live-research.ts"; +import { MODELS, PERFORMERS } from "../lib/catalog.ts"; +import { liveResearchRequestBody, runLiveResearch } from "../lib/live-research.ts"; import { TURN_SCHEMA, buildInstructions, @@ -41,6 +41,21 @@ function preparedResearch(overrides = {}) { }; } +test("freezes one standard request configuration across every live model", () => { + const liveModels = MODELS.filter((model) => model.mode === "live"); + const bodies = liveModels.map((model) => liveResearchRequestBody(preparedResearch({ + modelId: model.id, + imagePreference: "prefer", + }))); + const canonical = { ...bodies[0], model: "" }; + for (const body of bodies) { + assert.deepEqual({ ...body, model: "" }, canonical); + assert.deepEqual(body.reasoning, { effort: "high" }); + assert.equal(body.max_output_tokens, 30_000); + assert.equal(body.max_tool_calls, 12); + } +}); + test("lease ownership is checked before the first provider attempt", async () => { let providerCalls = 0; const originalFetch = globalThis.fetch; @@ -934,14 +949,22 @@ test("records exactly one usage outcome for HTTP, missing-body, and missing-term { response: () => new Response("rejected", { status: 429 }), code: "PROVIDER_ERROR", + retryable: true, + }, + { + response: () => new Response("rejected", { status: 403 }), + code: "PROVIDER_UNAVAILABLE", + retryable: false, }, { response: () => new Response(null, { status: 200 }), code: "PROVIDER_ERROR", + retryable: true, }, { response: () => new Response("data: [DONE]\n\n", { status: 200 }), code: "PROVIDER_ERROR", + retryable: true, }, ]; try { @@ -950,7 +973,7 @@ test("records exactly one usage outcome for HTTP, missing-body, and missing-term globalThis.fetch = async () => item.response(); await assert.rejects( () => runLiveResearch(preparedResearch({ requestId: `request-terminal-${index}` }), () => {}), - (error) => error?.code === item.code, + (error) => error?.code === item.code && error?.retryable === item.retryable, ); assert.equal(writes(), 1); } @@ -1139,8 +1162,8 @@ test("runs exactly one no-search repair after an initial citation mismatch", asy "web_search_call.results", ]); assert.equal(requests[0].tool_choice, "auto"); - assert.equal(requests[0].max_output_tokens, 18_000); - assert.deepEqual(requests[0].reasoning, { effort: "medium" }); + assert.equal(requests[0].max_output_tokens, 30_000); + assert.deepEqual(requests[0].reasoning, { effort: "high" }); assert.deepEqual(requests[0].text, { format: { type: "json_schema", @@ -1154,8 +1177,8 @@ test("runs exactly one no-search repair after an initial citation mismatch", asy assert.equal(requests[0].store, false); assert.equal(requests[0].stream, true); assert.equal(requests[1].tools, undefined); - assert.equal(requests[1].max_output_tokens, 2_000); - assert.deepEqual(requests[1].reasoning, { effort: "medium" }); + assert.equal(requests[1].max_output_tokens, 5_000); + assert.deepEqual(requests[1].reasoning, { effort: "high" }); assert.equal(draft.answerBlocks[0].sourceIds.length, 1); assert.equal(draft.usage.inputTokens, 120); assert.equal(draft.usage.outputTokens, 60); @@ -1258,10 +1281,10 @@ test("recovers an unsupported block with a targeted web search", async () => { assert.equal(requests.length, 3); assert.equal(requests[1].tools, undefined); - assert.equal(requests[1].max_output_tokens, 2_000); - assert.deepEqual(requests[1].reasoning, { effort: "medium" }); + assert.equal(requests[1].max_output_tokens, 5_000); + assert.deepEqual(requests[1].reasoning, { effort: "high" }); assert.deepEqual(requests[2].tools, [{ type: "web_search" }]); - assert.equal(requests[2].max_output_tokens, 6_000); + assert.equal(requests[2].max_output_tokens, 10_000); assert.deepEqual(requests[2].reasoning, { effort: "high" }); assert.equal(draft.answerBlocks[0].text, recoveredText); assert.ok(draft.sources.some((source) => source.url === "https://recovery.example.edu/finding")); diff --git a/tests/provider-usage.test.mjs b/tests/provider-usage.test.mjs index 328198d..b7e73f8 100644 --- a/tests/provider-usage.test.mjs +++ b/tests/provider-usage.test.mjs @@ -80,3 +80,37 @@ test("provider usage persists purpose, outcome, dimensions, and safe metadata", delete env.DB; } }); + +test("releases a reservation when the provider rejects the HTTP request before model work", async () => { + const writes = []; + env.DB = { + prepare(sql) { + let values = []; + return { + bind(...input) { values = input; return this; }, + async run() { + writes.push({ statement: sql, values }); + return { success: true, meta: { changes: 1 } }; + }, + }; + }, + }; + try { + await recordOpenAIUsage({ + identityId: "identity-rejected", + modelId: "gpt-5.6-luna", + operation: "live_research", + purpose: "primary_research", + outcome: "http_error", + costReservationId: "reservation-rejected", + providerRequestId: "req_rejected", + httpStatus: 403, + latencyMs: 25, + }); + assert.match(writes[0].statement, /status = 'released'/); + assert.equal(writes[0].values[1], "reservation-rejected"); + assert.match(writes[1].statement, /INSERT INTO provider_usage_events/); + } finally { + delete env.DB; + } +}); diff --git a/tests/routes.test.mjs b/tests/routes.test.mjs index 4c9a7ff..8c09ad3 100644 --- a/tests/routes.test.mjs +++ b/tests/routes.test.mjs @@ -74,3 +74,17 @@ test("the page-flip atlas leaves the final media page readable until the user co assert.match(source, /activePanel === panelCount - 1 \? "Continue to the knowledge questions"/); assert.match(source, /knowledgeDeclarationOpen \? setKnowledgeDeclarationOpen\(false\)/); }); + +test("retrying a question preserves its lead image on the research loading page", async () => { + const source = await readFile(new URL("../app/curiositypedia-experience.tsx", import.meta.url), "utf8"); + const retryStart = source.indexOf("async function retryActiveQuestion()"); + const retryEnd = source.indexOf("async function takeOverResearch()", retryStart); + const retrySource = source.slice(retryStart, retryEnd); + + assert.ok(retryStart >= 0); + assert.ok(retryEnd > retryStart); + assert.match(retrySource, /const previewMedia = activeTurn\.media\[0\]/); + assert.match(retrySource, /preview: previewMedia \? \{/); + assert.match(retrySource, /imageUrl: previewMedia\.imageUrl/); + assert.match(retrySource, /sourceUrl: previewMedia\.sourcePageUrl/); +}); diff --git a/tests/usage-summary.test.mjs b/tests/usage-summary.test.mjs index cf5a5b7..247eddb 100644 --- a/tests/usage-summary.test.mjs +++ b/tests/usage-summary.test.mjs @@ -38,7 +38,7 @@ test("models the current policy for 50 guest and ChatGPT users without a paid-us assert.deepEqual(policy.allowedModelIds, liveModelIds); assert.ok(liveModelIds.every((modelId) => isModelAllowed(user.mode, modelId))); assert.equal(policy.liveResearchLimit, guest ? 25 : 100); - assert.equal(policy.identitySpendLimitUsd, guest ? 1 : 5); + assert.equal(policy.identitySpendLimitUsd, guest ? 3 : 15); assert.equal(policy.journeyLimit, guest ? 50 : 25); assert.equal(liveResearchLimit(user.mode), policy.liveResearchLimit); assert.equal(identitySpendLimitUsd(user.mode), policy.identitySpendLimitUsd); @@ -93,7 +93,7 @@ test("maps rolling guest usage to exact slot-return times", () => { assert.equal(summary.spend.usedUsd, 0.25); assert.equal(summary.spend.heldUsd, 0.1); assert.equal(summary.spend.accountedUsd, 0.35); - assert.equal(summary.spend.remainingUsd, 0.65); + assert.equal(summary.spend.remainingUsd, 2.65); assert.equal(summary.spend.nextReleaseAt, now + 12 * HOUR); assert.deepEqual(summary.library, { used: 4, limit: 50, remaining: 46 }); }); From c50be11211879eadaa77b505b20d9157725e6e94 Mon Sep 17 00:00:00 2001 From: Jignasu Pathak <63716051+Mister-JP@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:55:10 -0500 Subject: [PATCH 2/2] Refresh architecture index --- docs/code-index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/code-index.md b/docs/code-index.md index ffdcf7c..b9ccd2e 100644 --- a/docs/code-index.md +++ b/docs/code-index.md @@ -74,7 +74,7 @@ Read [architecture.md](architecture.md) for system boundaries and change rules. | [`lib/api.ts`](../lib/api.ts) | [`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/background-research.ts`](../lib/background-research.ts) | [`db/index.ts`](../db/index.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/live-repository.ts`](../lib/live-repository.ts)
[`lib/live-research.ts`](../lib/live-research.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts)
[`lib/provider-usage.ts`](../lib/provider-usage.ts)
[`lib/research/provider-response.ts`](../lib/research/provider-response.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/bookmarks-repository.ts`](../lib/bookmarks-repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/request.ts`](../lib/request.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | -| [`lib/catalog.ts`](../lib/catalog.ts) | [`lib/contracts.ts`](../lib/contracts.ts) | +| [`lib/catalog.ts`](../lib/catalog.ts) | [`lib/contracts.ts`](../lib/contracts.ts)
[`lib/research-config.ts`](../lib/research-config.ts) | | [`lib/contracts.ts`](../lib/contracts.ts) | — | | [`lib/diagnostics.ts`](../lib/diagnostics.ts) | [`db/index.ts`](../db/index.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/errors.ts`](../lib/errors.ts) | [`lib/contracts.ts`](../lib/contracts.ts) | @@ -87,13 +87,14 @@ Read [architecture.md](architecture.md) for system boundaries and change rules. | [`lib/landing-recommendations-repository.ts`](../lib/landing-recommendations-repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts) | | [`lib/live-redraw.ts`](../lib/live-redraw.ts) | [`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/i18n.ts`](../lib/i18n.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts)
[`lib/provider-usage.ts`](../lib/provider-usage.ts) | | [`lib/live-repository.ts`](../lib/live-repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/i18n.ts`](../lib/i18n.ts)
[`lib/live-research.ts`](../lib/live-research.ts)
[`lib/repository.ts`](../lib/repository.ts)
[`lib/request.ts`](../lib/request.ts)
[`lib/turn-options.ts`](../lib/turn-options.ts)
[`lib/usage-policy.ts`](../lib/usage-policy.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | -| [`lib/live-research.ts`](../lib/live-research.ts) | [`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/fixtures.ts`](../lib/fixtures.ts)
[`lib/i18n.ts`](../lib/i18n.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts)
[`lib/provider-usage.ts`](../lib/provider-usage.ts)
[`lib/research/prompt-policy.ts`](../lib/research/prompt-policy.ts)
[`lib/research/provider-response.ts`](../lib/research/provider-response.ts)
[`lib/research/provider-stream.ts`](../lib/research/provider-stream.ts)
[`lib/research/turn-validation.ts`](../lib/research/turn-validation.ts) | -| [`lib/openai.ts`](../lib/openai.ts) | [`lib/errors.ts`](../lib/errors.ts) | +| [`lib/live-research.ts`](../lib/live-research.ts) | [`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/fixtures.ts`](../lib/fixtures.ts)
[`lib/i18n.ts`](../lib/i18n.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts)
[`lib/provider-usage.ts`](../lib/provider-usage.ts)
[`lib/research-config.ts`](../lib/research-config.ts)
[`lib/research/prompt-policy.ts`](../lib/research/prompt-policy.ts)
[`lib/research/provider-response.ts`](../lib/research/provider-response.ts)
[`lib/research/provider-stream.ts`](../lib/research/provider-stream.ts)
[`lib/research/turn-validation.ts`](../lib/research/turn-validation.ts) | +| [`lib/openai.ts`](../lib/openai.ts) | [`lib/errors.ts`](../lib/errors.ts)
[`lib/research-config.ts`](../lib/research-config.ts) | | [`lib/preferences-repository.ts`](../lib/preferences-repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/i18n.ts`](../lib/i18n.ts)
[`lib/request.ts`](../lib/request.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts) | [`db/index.ts`](../db/index.ts)
[`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/usage-policy.ts`](../lib/usage-policy.ts) | | [`lib/provider-usage.ts`](../lib/provider-usage.ts) | [`db/index.ts`](../db/index.ts)
[`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/openai.ts`](../lib/openai.ts)
[`lib/provider-cost-control.ts`](../lib/provider-cost-control.ts) | | [`lib/repository.ts`](../lib/repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/fixtures.ts`](../lib/fixtures.ts)
[`lib/journeys/read-model.ts`](../lib/journeys/read-model.ts)
[`lib/request.ts`](../lib/request.ts)
[`lib/turn-options.ts`](../lib/turn-options.ts)
[`lib/usage-policy.ts`](../lib/usage-policy.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/request.ts`](../lib/request.ts) | [`lib/contracts.ts`](../lib/contracts.ts)
[`lib/errors.ts`](../lib/errors.ts) | +| [`lib/research-config.ts`](../lib/research-config.ts) | — | | [`lib/research-status-repository.ts`](../lib/research-status-repository.ts) | [`db/index.ts`](../db/index.ts)
[`lib/errors.ts`](../lib/errors.ts)
[`lib/viewer.ts`](../lib/viewer.ts) | | [`lib/research/prompt-policy.ts`](../lib/research/prompt-policy.ts) | [`lib/catalog.ts`](../lib/catalog.ts)
[`lib/contracts.ts`](../lib/contracts.ts)
[`lib/i18n.ts`](../lib/i18n.ts) | | [`lib/research/provider-response.ts`](../lib/research/provider-response.ts) | — |