github-router 0.3.288 → 0.3.289

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.
Files changed (29) hide show
  1. package/dist/{attribution-settings-CofaqSzw.js → attribution-settings-Cmz2jt7P.js} +3 -3
  2. package/dist/{attribution-settings-CofaqSzw.js.map → attribution-settings-Cmz2jt7P.js.map} +1 -1
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/{claude-CoZKRNB8.js → claude-C-9xFI4b.js} +8 -7
  5. package/dist/claude-C-9xFI4b.js.map +1 -0
  6. package/dist/{codex-y2OyLIdv.js → codex-DRW0yb8x.js} +4 -4
  7. package/dist/{codex-y2OyLIdv.js.map → codex-DRW0yb8x.js.map} +1 -1
  8. package/dist/engine-iEqGdx6T.js +2 -0
  9. package/dist/{gate-discovery-LQZ-enJa.js → gate-discovery-Cz6kwIVG.js} +2 -2
  10. package/dist/{gate-discovery-LQZ-enJa.js.map → gate-discovery-Cz6kwIVG.js.map} +1 -1
  11. package/dist/{internal-stop-hook-DSbaDb_m.js → internal-stop-hook-Dvkppwo7.js} +2 -2
  12. package/dist/{internal-stop-hook-DSbaDb_m.js.map → internal-stop-hook-Dvkppwo7.js.map} +1 -1
  13. package/dist/main.js +5 -5
  14. package/dist/{peer-mcp-personas-B5Wp6wIn.js → peer-mcp-personas-Bd56EmiO.js} +385 -21
  15. package/dist/peer-mcp-personas-Bd56EmiO.js.map +1 -0
  16. package/dist/{provision-CUqPki1z.js → provision-B53wbHwa.js} +2 -2
  17. package/dist/{provision-CUqPki1z.js.map → provision-B53wbHwa.js.map} +1 -1
  18. package/dist/{serve-BASqoXb3.js → serve-aZCEYFe5.js} +5 -5
  19. package/dist/{serve-BASqoXb3.js.map → serve-aZCEYFe5.js.map} +1 -1
  20. package/dist/{server-setup-D5hilphf.js → server-setup-DlztZAGT.js} +246 -89
  21. package/dist/server-setup-DlztZAGT.js.map +1 -0
  22. package/dist/{start-Rfim4TeF.js → start-DwNiXv5N.js} +3 -3
  23. package/dist/{start-Rfim4TeF.js.map → start-DwNiXv5N.js.map} +1 -1
  24. package/dist/token-BGCjZwtj.js.map +1 -1
  25. package/package.json +2 -1
  26. package/dist/claude-CoZKRNB8.js.map +0 -1
  27. package/dist/engine-B5nVGH4b.js +0 -2
  28. package/dist/peer-mcp-personas-B5Wp6wIn.js.map +0 -1
  29. package/dist/server-setup-D5hilphf.js.map +0 -1
@@ -19497,6 +19497,354 @@ function currentInFlight() {
19497
19497
  return inFlight$2;
19498
19498
  }
19499
19499
  //#endregion
19500
+ //#region src/lib/prompt-cache.ts
19501
+ /**
19502
+ * Conservative eligibility floor, in UTF-8 BYTES — never `.length`, which
19503
+ * counts UTF-16 code units and undercounts anything outside the BMP (an
19504
+ * emoji is 2 code units but 4 bytes). This is a proxy for "the prefix is
19505
+ * obviously large enough that marking it as a cache breakpoint is worth one
19506
+ * of the scarce marker slots," and it deliberately does NOT claim to be a
19507
+ * token count: byte-to-token density varies by tokenizer and content — CJK
19508
+ * text carries MORE tokens per byte than ASCII prose (undercounting risk is
19509
+ * the SAFE direction: we'd skip a marker that might have qualified), while a
19510
+ * long run of a repeated character or repeated whitespace carries FEWER
19511
+ * tokens per byte than either, since BPE merges long runs into very few
19512
+ * tokens (overcounting risk: a byte count clearing the floor doesn't
19513
+ * guarantee the real token count clears Anthropic's or Copilot's per-model
19514
+ * minimum). No fixed byte threshold can bound that adversarial case; this
19515
+ * value is chosen so ordinary Claude Code system prompts and tool schemas
19516
+ * (natural-language / JSON, not deliberately repetitive) reliably qualify,
19517
+ * while genuinely small prefixes never burn a marker for no benefit.
19518
+ */
19519
+ const MIN_CACHEABLE_PREFIX_BYTES = 4096;
19520
+ const CACHE_KEY_NAMESPACE = "ghr-cache-v1";
19521
+ const CACHE_DIAGNOSTIC_LIMIT = 128;
19522
+ const GPT56_EXPLICIT_CACHE_MODELS = /* @__PURE__ */ new Set([
19523
+ "gpt-5.6-sol",
19524
+ "gpt-5.6-terra",
19525
+ "gpt-5.6-luna"
19526
+ ]);
19527
+ const priorSignatures = /* @__PURE__ */ new Map();
19528
+ function nonNegativeInt(value) {
19529
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
19530
+ return Math.floor(value);
19531
+ }
19532
+ /**
19533
+ * Pick the first genuinely POSITIVE numeric candidate from an ordered,
19534
+ * priority-ranked list of usage-shape fields. `??`-chaining these fields is
19535
+ * wrong: it stops at the first field that is merely PRESENT, and a provider
19536
+ * surface that always populates a nested detail object with `0` as a
19537
+ * placeholder (while the real, positive count is reported only in a
19538
+ * lower-priority field, e.g. the top-level one) would have its explicit zero
19539
+ * silently shadow that populated count. Falling through zeros to find a real
19540
+ * positive value fixes that; when every candidate is zero, absent, or
19541
+ * non-numeric, this returns `0` — a genuine all-zero reading, never
19542
+ * `undefined` — so downstream `nonNegativeInt` always has a countable value.
19543
+ */
19544
+ function firstPositive(...candidates) {
19545
+ for (const c of candidates) if (typeof c === "number" && Number.isFinite(c) && c > 0) return c;
19546
+ return 0;
19547
+ }
19548
+ function usageDetails(usage) {
19549
+ const input = usage.input_tokens_details ?? {};
19550
+ const prompt = usage.prompt_tokens_details ?? {};
19551
+ return {
19552
+ cached_tokens: firstPositive(input.cached_tokens, prompt.cached_tokens, usage.cache_read_input_tokens),
19553
+ cache_write_tokens: firstPositive(input.cache_write_tokens, input.cache_creation_tokens, prompt.cache_write_tokens, prompt.cache_creation_tokens, usage.cache_write_tokens, usage.cache_creation_input_tokens),
19554
+ cache_ttl_seconds: firstPositive(input.cache_ttl_seconds, prompt.cache_ttl_seconds, usage.cache_ttl_seconds)
19555
+ };
19556
+ }
19557
+ /**
19558
+ * OpenAI totals INCLUDE cached and cache-write tokens. Normalize them into
19559
+ * mutually exclusive buckets so Anthropic and Pi consumers do not count the
19560
+ * same input twice.
19561
+ */
19562
+ function normalizeOpenAIUsage(usage) {
19563
+ if (!usage) return {
19564
+ totalInput: 0,
19565
+ uncachedInput: 0,
19566
+ output: 0,
19567
+ cacheRead: 0,
19568
+ cacheWrite: 0,
19569
+ totalTokens: 0
19570
+ };
19571
+ const totalInput = nonNegativeInt(usage.input_tokens ?? usage.prompt_tokens);
19572
+ const output = nonNegativeInt(usage.output_tokens ?? usage.completion_tokens);
19573
+ const details = usageDetails(usage);
19574
+ const cacheRead = Math.min(totalInput, nonNegativeInt(details.cached_tokens));
19575
+ const remaining = Math.max(0, totalInput - cacheRead);
19576
+ const cacheWrite = Math.min(remaining, nonNegativeInt(details.cache_write_tokens ?? details.cache_creation_tokens));
19577
+ const uncachedInput = Math.max(0, totalInput - cacheRead - cacheWrite);
19578
+ const reportedTotal = nonNegativeInt(usage.total_tokens);
19579
+ const cacheTtlSeconds = nonNegativeInt(details.cache_ttl_seconds);
19580
+ return {
19581
+ totalInput,
19582
+ uncachedInput,
19583
+ output,
19584
+ cacheRead,
19585
+ cacheWrite,
19586
+ totalTokens: Math.max(reportedTotal, totalInput + output),
19587
+ ...cacheTtlSeconds > 0 ? { cacheTtlSeconds } : {}
19588
+ };
19589
+ }
19590
+ function hash(value) {
19591
+ return createHash("sha256").update(value).digest("hex");
19592
+ }
19593
+ function signatureFor(value) {
19594
+ return hash(typeof value === "string" ? value : JSON.stringify(value ?? null));
19595
+ }
19596
+ function serializedBytes(value) {
19597
+ return Buffer.byteLength(typeof value === "string" ? value : JSON.stringify(value ?? null));
19598
+ }
19599
+ function logCacheSignature(args) {
19600
+ if (parseBoolEnv(process.env.GH_ROUTER_LOG_CACHE) !== true) return;
19601
+ const key = `${args.endpoint}:${args.model}:${args.workload}`;
19602
+ const current = {
19603
+ system: signatureFor(args.system),
19604
+ tools: signatureFor(args.tools),
19605
+ messages: signatureFor(args.messages)
19606
+ };
19607
+ const previous = priorSignatures.get(key);
19608
+ let changed = "cold";
19609
+ if (previous) changed = previous.system !== current.system ? "system" : previous.tools !== current.tools ? "tools" : previous.messages !== current.messages ? "messages" : "none";
19610
+ priorSignatures.set(key, current);
19611
+ if (priorSignatures.size > CACHE_DIAGNOSTIC_LIMIT) {
19612
+ const oldest = priorSignatures.keys().next().value;
19613
+ if (oldest !== void 0) priorSignatures.delete(oldest);
19614
+ }
19615
+ consola.info(`cache-signature endpoint=${args.endpoint} model=${args.model} workload=${args.workload} changed=${changed} system_bytes=${serializedBytes(args.system ?? "")} tools_bytes=${serializedBytes(args.tools ?? [])} messages_bytes=${serializedBytes(args.messages ?? [])}`);
19616
+ }
19617
+ function hasResponsesBreakpoint(input) {
19618
+ const visit = (value) => {
19619
+ if (!value || typeof value !== "object") return false;
19620
+ if (Array.isArray(value)) return value.some(visit);
19621
+ const record = value;
19622
+ return record.prompt_cache_breakpoint !== void 0 || Object.values(record).some(visit);
19623
+ };
19624
+ return visit(input);
19625
+ }
19626
+ function gpt56ExplicitCacheEnabled(model) {
19627
+ if (parseBoolEnv(process.env.GH_ROUTER_DISABLE_GPT56_EXPLICIT_CACHE) === true) return false;
19628
+ return GPT56_EXPLICIT_CACHE_MODELS.has(model);
19629
+ }
19630
+ function responsesCacheKey(payload, opts, stablePrefix) {
19631
+ const digest = hash(JSON.stringify({
19632
+ namespace: CACHE_KEY_NAMESPACE,
19633
+ model: payload.model,
19634
+ workload: opts.workload,
19635
+ scope: opts.scope ?? "",
19636
+ stablePrefix,
19637
+ tools: payload.tools ?? []
19638
+ }));
19639
+ return `${CACHE_KEY_NAMESPACE}-${digest.slice(0, 48)}`;
19640
+ }
19641
+ /**
19642
+ * Add GPT-5.6 explicit caching only to router-owned REUSABLE-PREFIX payloads.
19643
+ * Public passthrough routes never call this helper, and existing caller
19644
+ * fields always win. Live shape acceptance is pinned by compatibility probe
19645
+ * `gpt56_explicit_cache_breakpoint`.
19646
+ *
19647
+ * **`"conversation"` is deliberately EXCLUDED and left untouched (a no-op),
19648
+ * same as `"passthrough"`/`"one-shot"`.** A live-verified regression: on a
19649
+ * growing multi-turn conversation (Claude Code's translated main loop and
19650
+ * the worker-agent loop, both of which pass `workload: "conversation"`),
19651
+ * marking only the stable SYSTEM block with an explicit breakpoint measured
19652
+ * substantially worse than leaving caching provider-managed and implicit.
19653
+ * Explicit mode is a distinct
19654
+ * caching strategy from Copilot's provider-managed automatic caching, not an
19655
+ * addition to it — turning it on for a request marks only the bytes an
19656
+ * explicit breakpoint names, and the REST of that request's prefix (here,
19657
+ * the entire un-marked growing message history) stops receiving automatic
19658
+ * prefix-growth caching too. Measured on `gpt-5.6-sol` with explicit mode
19659
+ * force-enabled for conversation workloads: turn 1 (cold)
19660
+ * `input_tokens=27038, cache_write=2031, cache_read=0`; turn 2
19661
+ * `input_tokens=27054, cache_read=2031`; turn 3 `input_tokens=27071,
19662
+ * cache_read=2031` — the ~2k-token system block cached once and never grew,
19663
+ * while the other ~25k tokens of accumulating history were recomputed from
19664
+ * scratch on every single turn. `"reusable-prefix"` calls (peer/advisor/
19665
+ * worker-tool/browser-compressor prefixes reused verbatim across many
19666
+ * DISCRETE calls, never a single request whose own history keeps growing)
19667
+ * do not have this failure mode and keep the explicit treatment below.
19668
+ */
19669
+ function applyResponsesCachePolicy(payload, opts) {
19670
+ logCacheSignature({
19671
+ endpoint: "/responses",
19672
+ model: payload.model,
19673
+ workload: opts.workload,
19674
+ system: opts.stablePrefix ?? payload.instructions,
19675
+ tools: payload.tools,
19676
+ messages: payload.input
19677
+ });
19678
+ if (opts.workload !== "reusable-prefix" || !gpt56ExplicitCacheEnabled(payload.model) || payload.prompt_cache_key !== void 0 || payload.prompt_cache_options !== void 0 || hasResponsesBreakpoint(payload.input)) return payload;
19679
+ const stablePrefix = opts.stablePrefix ?? payload.instructions;
19680
+ const stableBytes = serializedBytes(stablePrefix ?? "") + serializedBytes(payload.tools ?? []);
19681
+ if (!stablePrefix || stableBytes < MIN_CACHEABLE_PREFIX_BYTES) return payload;
19682
+ const input = typeof payload.input === "string" ? [{
19683
+ role: "user",
19684
+ content: payload.input
19685
+ }] : [...payload.input];
19686
+ const stableContent = [{
19687
+ type: "input_text",
19688
+ text: stablePrefix,
19689
+ prompt_cache_breakpoint: { mode: "explicit" }
19690
+ }];
19691
+ let nextInput;
19692
+ let removeInstructions = false;
19693
+ if (payload.instructions === stablePrefix) {
19694
+ nextInput = [{
19695
+ role: "system",
19696
+ content: stableContent
19697
+ }, ...input];
19698
+ removeInstructions = true;
19699
+ } else {
19700
+ const stableSystemIndex = input.findIndex((item) => item.role === "system" && item.content === stablePrefix);
19701
+ if (stableSystemIndex < 0) return payload;
19702
+ nextInput = [...input];
19703
+ nextInput[stableSystemIndex] = {
19704
+ ...nextInput[stableSystemIndex],
19705
+ content: stableContent
19706
+ };
19707
+ }
19708
+ const next = {
19709
+ ...payload,
19710
+ input: nextInput,
19711
+ prompt_cache_key: responsesCacheKey(payload, opts, stablePrefix),
19712
+ prompt_cache_options: {
19713
+ mode: "explicit",
19714
+ ttl: "30m"
19715
+ }
19716
+ };
19717
+ if (removeInstructions) delete next.instructions;
19718
+ return next;
19719
+ }
19720
+ function itemHasCacheControl(value) {
19721
+ return !!value && typeof value === "object" && value.cache_control !== void 0;
19722
+ }
19723
+ function hasClaudeCacheControl(body) {
19724
+ if (itemHasCacheControl(body.system)) return true;
19725
+ if (Array.isArray(body.system) && body.system.some(itemHasCacheControl)) return true;
19726
+ if (Array.isArray(body.tools) && body.tools.some(itemHasCacheControl)) return true;
19727
+ if (!Array.isArray(body.messages)) return false;
19728
+ return body.messages.some((message) => {
19729
+ if (!message || typeof message !== "object") return false;
19730
+ const content = message.content;
19731
+ return itemHasCacheControl(content) || Array.isArray(content) && content.some(itemHasCacheControl);
19732
+ });
19733
+ }
19734
+ /**
19735
+ * UTF-8 byte length of the tools array alone — the eligibility floor for the
19736
+ * TOOL breakpoint. Marked on the last non-deferred tool, it caches only the
19737
+ * tools prefix (Claude's wire order is tools, then system, then messages), so
19738
+ * its own size — not the combined system+tools size — is what determines
19739
+ * whether that marker is worth spending. See `MIN_CACHEABLE_PREFIX_BYTES`.
19740
+ */
19741
+ function claudeToolsPrefixBytes(body) {
19742
+ return serializedBytes(body.tools ?? []);
19743
+ }
19744
+ /**
19745
+ * UTF-8 byte length of tools + system combined — the eligibility floor for
19746
+ * the SYSTEM breakpoint. Marked on the last system text block, it caches
19747
+ * everything up to and including system (tools THEN system in wire order),
19748
+ * so the combined size is the right measure — checked SEPARATELY from the
19749
+ * tools-only floor above so a large system prompt behind tiny tools doesn't
19750
+ * smuggle a useless tools-only marker in under the combined total, and a
19751
+ * large tools array behind an empty system doesn't get double-counted as
19752
+ * "small" just because system alone is tiny.
19753
+ */
19754
+ function claudeSystemPrefixBytes(body) {
19755
+ return claudeToolsPrefixBytes(body) + serializedBytes(body.system ?? "");
19756
+ }
19757
+ function markClaudeSystem(body) {
19758
+ if (typeof body.system === "string" && body.system.length > 0) {
19759
+ body.system = [{
19760
+ type: "text",
19761
+ text: body.system,
19762
+ cache_control: { type: "ephemeral" }
19763
+ }];
19764
+ return true;
19765
+ }
19766
+ if (!Array.isArray(body.system)) return false;
19767
+ for (let index = body.system.length - 1; index >= 0; index--) {
19768
+ const block = body.system[index];
19769
+ if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
19770
+ body.system[index] = {
19771
+ ...block,
19772
+ cache_control: { type: "ephemeral" }
19773
+ };
19774
+ return true;
19775
+ }
19776
+ }
19777
+ return false;
19778
+ }
19779
+ function markClaudeTool(body) {
19780
+ if (!Array.isArray(body.tools)) return false;
19781
+ for (let index = body.tools.length - 1; index >= 0; index--) {
19782
+ const tool = body.tools[index];
19783
+ if (tool && typeof tool === "object" && tool.defer_loading !== true) {
19784
+ body.tools[index] = {
19785
+ ...tool,
19786
+ cache_control: { type: "ephemeral" }
19787
+ };
19788
+ return true;
19789
+ }
19790
+ }
19791
+ return false;
19792
+ }
19793
+ /**
19794
+ * Apply the bounded Claude anchor policy to router-generated Messages bodies.
19795
+ * Caller-owned marker layouts are returned byte-for-byte unchanged.
19796
+ *
19797
+ * Marks at most TWO breakpoints — the last non-deferred tool and the stable
19798
+ * system boundary — each gated on its OWN eligibility check
19799
+ * (`claudeToolsPrefixBytes` / `claudeSystemPrefixBytes`) rather than one
19800
+ * combined check, so a large system prompt behind tiny tools doesn't also
19801
+ * mark a tools breakpoint too small to be worth a marker slot, and vice
19802
+ * versa. Anthropic's own hard ceiling is FOUR `cache_control` blocks per
19803
+ * request (probe `cache_control_marker_limit_5`); this policy only ever
19804
+ * spends up to two of them (`hasClaudeCacheControl` already refuses to run
19805
+ * at all once the caller has marked anything itself, so the two never
19806
+ * combine with a caller-owned marker to approach that ceiling).
19807
+ *
19808
+ * There used to be a third, message-level marking path gated on
19809
+ * `opts.workload === "conversation"`. It was removed as dead code: every
19810
+ * production call site of this function (`src/routes/mcp/handler.ts`,
19811
+ * `src/services/advisor/advisor.ts`) passes `workload: "reusable-prefix"`,
19812
+ * so the per-message branch never ran outside its own unit test, which gave
19813
+ * false confidence that production traffic exercised it. `CacheWorkload`
19814
+ * keeps `"conversation"` as a shared enum value — the Responses-side policy,
19815
+ * which has no per-message logic of its own, still uses it — so passing it
19816
+ * here remains type-valid; it now behaves identically to
19817
+ * `"reusable-prefix"`.
19818
+ */
19819
+ function applyClaudeCachePolicy(rawBody, opts) {
19820
+ if (opts.workload === "passthrough" || opts.workload === "one-shot" || parseBoolEnv(process.env.GH_ROUTER_DISABLE_CLAUDE_CACHE_POLICY) === true) return rawBody;
19821
+ let parsed;
19822
+ try {
19823
+ parsed = JSON.parse(rawBody);
19824
+ } catch {
19825
+ return rawBody;
19826
+ }
19827
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return rawBody;
19828
+ const body = parsed;
19829
+ if (typeof body.model !== "string" || !body.model.startsWith("claude-") || hasClaudeCacheControl(body)) return rawBody;
19830
+ const toolsEligible = claudeToolsPrefixBytes(body) >= MIN_CACHEABLE_PREFIX_BYTES;
19831
+ const systemEligible = claudeSystemPrefixBytes(body) >= MIN_CACHEABLE_PREFIX_BYTES;
19832
+ if (!toolsEligible && !systemEligible) return rawBody;
19833
+ let markers = 0;
19834
+ if (toolsEligible && markClaudeTool(body)) markers++;
19835
+ if (systemEligible && markClaudeSystem(body)) markers++;
19836
+ if (markers === 0) return rawBody;
19837
+ logCacheSignature({
19838
+ endpoint: "/messages",
19839
+ model: body.model,
19840
+ workload: opts.workload,
19841
+ system: body.system,
19842
+ tools: body.tools,
19843
+ messages: body.messages
19844
+ });
19845
+ return JSON.stringify(body);
19846
+ }
19847
+ //#endregion
19500
19848
  //#region src/lib/vision-preflight.ts
19501
19849
  /**
19502
19850
  * Outbound vision handling.
@@ -20421,7 +20769,7 @@ async function callViaChat(model, systemPrompt, userMessage, tool, signal) {
20421
20769
  * Image parts use `input_image` (vs chat's `image_url`) — see
20422
20770
  * `toResponsesContent`. */
20423
20771
  async function callViaResponses(model, systemPrompt, userMessage, tool, signal) {
20424
- const payload = {
20772
+ const payload = applyResponsesCachePolicy({
20425
20773
  model,
20426
20774
  stream: false,
20427
20775
  input: [{
@@ -20441,7 +20789,10 @@ async function callViaResponses(model, systemPrompt, userMessage, tool, signal)
20441
20789
  type: "function",
20442
20790
  name: tool.name
20443
20791
  }
20444
- };
20792
+ }, {
20793
+ workload: "reusable-prefix",
20794
+ stablePrefix: systemPrompt
20795
+ });
20445
20796
  const resp = await createResponses(payload, void 0, signal, true);
20446
20797
  const output = Array.isArray(resp.output) ? resp.output : [];
20447
20798
  for (const item of output) {
@@ -24491,6 +24842,10 @@ function neutralToolsToResponses(tools) {
24491
24842
  /** Assemble the full Responses payload from the neutral request shape. */
24492
24843
  function assembleResponsesPayload(opts) {
24493
24844
  const input = [];
24845
+ if (opts.dynamicInstructions) input.push({
24846
+ role: "system",
24847
+ content: opts.dynamicInstructions
24848
+ });
24494
24849
  for (const m of opts.messages) for (const item of neutralMessageToResponsesInput(m)) input.push(item);
24495
24850
  const payload = {
24496
24851
  model: opts.model,
@@ -24507,7 +24862,10 @@ function assembleResponsesPayload(opts) {
24507
24862
  if (typeof opts.maxOutputTokens === "number" && opts.maxOutputTokens > 0) payload.max_output_tokens = Math.max(opts.maxOutputTokens, RESPONSES_MIN_MAX_OUTPUT_TOKENS);
24508
24863
  if (opts.stopSequences && opts.stopSequences.length > 0) payload.stop = [...opts.stopSequences];
24509
24864
  if (opts.parallelToolCalls === false) payload.parallel_tool_calls = false;
24510
- return payload;
24865
+ return opts.cachePolicy ? applyResponsesCachePolicy(payload, {
24866
+ ...opts.cachePolicy,
24867
+ stablePrefix: opts.cachePolicy.stablePrefix ?? opts.instructions
24868
+ }) : payload;
24511
24869
  }
24512
24870
  //#endregion
24513
24871
  //#region src/lib/worker-agent/context-budget.ts
@@ -25042,7 +25400,10 @@ function mapResponsesUsage(u) {
25042
25400
  prompt_tokens: u.input_tokens ?? 0,
25043
25401
  completion_tokens: u.output_tokens ?? 0,
25044
25402
  total_tokens: u.total_tokens ?? 0,
25045
- prompt_tokens_details: u.input_tokens_details?.cached_tokens != null ? { cached_tokens: u.input_tokens_details.cached_tokens } : void 0
25403
+ prompt_tokens_details: u.input_tokens_details != null ? {
25404
+ cached_tokens: u.input_tokens_details.cached_tokens ?? 0,
25405
+ cache_write_tokens: u.input_tokens_details.cache_write_tokens ?? u.input_tokens_details.cache_creation_tokens ?? 0
25406
+ } : void 0
25046
25407
  };
25047
25408
  }
25048
25409
  /**
@@ -25291,6 +25652,7 @@ function buildResponsesPayload(context, resolved) {
25291
25652
  messages,
25292
25653
  tools: piToolsToNeutral(context.tools),
25293
25654
  reasoningEffort: resolved.thinking,
25655
+ cachePolicy: { workload: "conversation" },
25294
25656
  stream: true
25295
25657
  });
25296
25658
  }
@@ -25513,12 +25875,13 @@ function emptyUsage() {
25513
25875
  }
25514
25876
  function deriveUsage(u) {
25515
25877
  if (!u) return emptyUsage();
25878
+ const normalized = normalizeOpenAIUsage(u);
25516
25879
  return {
25517
- input: u.prompt_tokens ?? 0,
25518
- output: u.completion_tokens ?? 0,
25519
- cacheRead: u.prompt_tokens_details?.cached_tokens ?? 0,
25520
- cacheWrite: 0,
25521
- totalTokens: u.total_tokens ?? 0,
25880
+ input: normalized.uncachedInput,
25881
+ output: normalized.output,
25882
+ cacheRead: normalized.cacheRead,
25883
+ cacheWrite: normalized.cacheWrite,
25884
+ totalTokens: normalized.totalTokens,
25522
25885
  cost: {
25523
25886
  input: 0,
25524
25887
  output: 0,
@@ -27839,7 +28202,7 @@ function jsonPathPreflightCap(body, scope) {
27839
28202
  async function dispatchModelCall(args) {
27840
28203
  const resolvedModel = resolveModel(args.model);
27841
28204
  if (args.endpoint === "/v1/responses") {
27842
- const payload = {
28205
+ const payload = applyResponsesCachePolicy({
27843
28206
  model: resolvedModel,
27844
28207
  instructions: args.instructions,
27845
28208
  input: [{
@@ -27854,7 +28217,7 @@ async function dispatchModelCall(args) {
27854
28217
  }],
27855
28218
  stream: false,
27856
28219
  reasoning: { effort: args.effort }
27857
- };
28220
+ }, { workload: "reusable-prefix" });
27858
28221
  return extractResponsesText(await withTransientRetry(() => createResponses(payload, void 0, args.signal), {
27859
28222
  signal: args.signal,
27860
28223
  label: resolvedModel
@@ -27862,7 +28225,7 @@ async function dispatchModelCall(args) {
27862
28225
  }
27863
28226
  if (args.endpoint === "/v1/messages") {
27864
28227
  const maxTokens = args.effort === "low" ? 4096 : args.effort === "medium" ? 8192 : args.effort === "high" ? 16384 : 32768;
27865
- const body = JSON.stringify({
28228
+ const body = applyClaudeCachePolicy(JSON.stringify({
27866
28229
  model: resolvedModel,
27867
28230
  max_tokens: maxTokens,
27868
28231
  system: args.instructions,
@@ -27885,7 +28248,7 @@ async function dispatchModelCall(args) {
27885
28248
  role: "user",
27886
28249
  content: args.userText
27887
28250
  }]
27888
- });
28251
+ }), { workload: "reusable-prefix" });
27889
28252
  return extractMessagesText(await (await withTransientRetry(() => createMessages(body, void 0, args.signal), {
27890
28253
  signal: args.signal,
27891
28254
  label: resolvedModel
@@ -29387,7 +29750,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
29387
29750
  }
29388
29751
  const conversationText = renderConversationAsText(conversation, maxUnits, measure);
29389
29752
  if (advisorUsesResponses(resolvedAdvisorModel)) {
29390
- const payload = {
29753
+ const payload = applyResponsesCachePolicy({
29391
29754
  model: resolvedAdvisorModel,
29392
29755
  instructions: advisorSystem,
29393
29756
  input: [{
@@ -29399,7 +29762,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
29399
29762
  }],
29400
29763
  stream: false,
29401
29764
  reasoning: { effort: advisorEffort }
29402
- };
29765
+ }, { workload: "reusable-prefix" });
29403
29766
  const response = await withTransientRetry(() => createResponses(payload, void 0, signal), {
29404
29767
  signal,
29405
29768
  label: resolvedAdvisorModel
@@ -29424,7 +29787,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
29424
29787
  const advisorEntry = state.models?.data?.find((m) => m.id === resolvedAdvisorModel);
29425
29788
  const limits = advisorEntry?.capabilities?.limits;
29426
29789
  const maxTokens = limits?.max_non_streaming_output_tokens ?? limits?.max_output_tokens ?? ADVISOR_FALLBACK_MAX_OUTPUT_TOKENS;
29427
- const advisorBody = JSON.stringify({
29790
+ const advisorBody = applyClaudeCachePolicy(JSON.stringify({
29428
29791
  model: resolvedAdvisorModel,
29429
29792
  max_tokens: maxTokens,
29430
29793
  system: advisorSystem,
@@ -29437,7 +29800,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
29437
29800
  thinking: { type: "adaptive" },
29438
29801
  ...advertisedEffortLadder(resolvedAdvisorModel) ? { output_config: { effort: advisorEffort } } : {}
29439
29802
  } : {}
29440
- });
29803
+ }), { workload: "reusable-prefix" });
29441
29804
  const json = await (await withTransientRetry(() => createMessages(advisorBody, {}, signal), {
29442
29805
  signal,
29443
29806
  label: resolvedAdvisorModel
@@ -31715,7 +32078,7 @@ function advisorTool(getMessages) {
31715
32078
  const release = acquireInFlightSlot();
31716
32079
  if (!release) throw new Error(`advisor: MCP in-flight cap (${MAX_INFLIGHT_TOOLS_CALL}) saturated; retry shortly`);
31717
32080
  try {
31718
- const text = extractResponsesText(await createResponses({
32081
+ const payload = applyResponsesCachePolicy({
31719
32082
  model: resolvedModel,
31720
32083
  instructions: advisorSystem,
31721
32084
  input: [{
@@ -31727,7 +32090,8 @@ function advisorTool(getMessages) {
31727
32090
  }],
31728
32091
  stream: false,
31729
32092
  reasoning: { effort: ADVISOR_DEFAULT_EFFORT }
31730
- }, void 0, signal, true));
32093
+ }, { workload: "reusable-prefix" });
32094
+ const text = extractResponsesText(await createResponses(payload, void 0, signal, true));
31731
32095
  if (!text) throw new Error("advisor returned empty output");
31732
32096
  return textResult(text);
31733
32097
  } finally {
@@ -36111,6 +36475,6 @@ function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
36111
36475
  return [...new Set(names)];
36112
36476
  }
36113
36477
  //#endregion
36114
- export { handleMcpDelete as $, generateRandomPort as $t, satisfiesMinVersion as A, readResponseBodyCapped as At, rememberThinkingHistoryRepair as B, CONDENSED_OPERATING_SEQUENCE as Bt, availableToolCommands as C, getTokenCount as Ct, vscodeRipgrepPath as D, createResponses as Dt, toolbeltSkipSet as E, resolveMcpToolTimeoutMs as Et, injectAdvisorTool as F, provisionAndIndexColbert as Ft, isControllerClosedError as G, BUDGET_SMALL_FAST_CATALOG_ID as Gt, repairRejectedThinkingHistory as H, shouldUseInsecureTls as Ht, isAdvisorRequested as I, extractTarGzMember as It, relayAnthropicStream as J, DEFAULT_CODEX_MODEL as Jt, logStreamError as K, BUDGET_SMALL_FAST_SLUG as Kt, resolveAdvisorEffort as L, extractZipMember as Lt, ADVISOR_INTERNAL_TOOL_NAME as M, provisionBrowserAssets as Mt, ADVISOR_TOOL_INSTRUCTIONS as N, hasSupportedBrowserInstalled as Nt, TOOLBELT_TOOLS$1 as O, createChatCompletions as Ot, buildAdvisorStream as P, colbertDegradedWarning as Pt, clampEffort as Q, UPSTREAM_INACTIVITY_TIMEOUT_MS as Qt, resolveAdvisorModel as R, warmTreeSitterPool as Rt, buildEnv as S, createMessages as St, toolbeltEnabled as T, warnOnTokenPriceDrift as Tt, buildAnthropicErrorEvent as U, collapsePathKeys as Ut, repairKnownThinkingHistory as V, DEFINITION_OF_GREATNESS as Vt, buildOpenAIErrorEvent as W, toolbeltPathOverride as Wt, UNKNOWN_EFFORT_ANCHOR as X, DEFAULT_PORT as Xt, EFFORT_ORDER as Y, DEFAULT_CODEX_MODEL_FALLBACKS as Yt, bucketEffort as Z, UPSTREAM_FETCH_TIMEOUT_MS as Zt, appendPlanReminder as _, scribeModel as _t, buildPeerAwarenessSnippet as a, classifyMessagesRoute as an, browseAgentEnabled as at, resolveWorkerRunOpts as b, shimDefaultsToXhigh as bt, personasFor as c, withInstallLock as cn, fleetToolsEnabled as ct, EXPLORE_DEFAULT_MODEL as d, implementerFastModel as dt, isBudgetClaudeLead as en, handleMcpPost as et, EXPLORE_DEFAULT_THINKING as f, nativeSubagentModel as ft, TEST_DEFAULT_MODEL as g, scoutModel as gt, REVIEW_DEFAULT_MODEL as h, reviewerModel as ht, buildAgentPrompt as i, upstreamMaxConnections as in, brainstormModel as it, searchWeb as j, parseJsonOrDiagnose as jt, assetFor as k, MAX_RESPONSE_BODY_BYTES as kt, BROWSE_DEFAULT_MODEL as l, geminiAvailable as lt, PLAN_DEFAULT_MODEL as m, reviewerFastModel as mt, MCP_GROUPS as n, resolveLeadSlugArg as nn, agentToolsEnabled as nt, buildPeerAwarenessSummary as o, withOneMSuffix as on, browserCompoundToolsEnabled as ot, IMPLEMENT_DEFAULT_MODEL as p, resolveGeminiReviewModel as pt, readIteratorWithTimeout as q, DEFAULT_CLAUDE_MODEL_FALLBACKS as qt, assertMcpToolSurfaceConsistent as r, upstreamAllowH2 as rn, artifactToolsEnabled as rt, enumerateInjectedMcpToolNames as s, withOneMSuffixForLead as sn, browserToolsEnabled as st, GROUP_META as t, pickClaudeDefault as tn, REVIEW_FAST_DEFAULT_MODEL as tt, DEFAULT_MODEL_CHAIN as u, generalPurposeFastModel as ut, resolveDefaultModel as v, standInToolEnabled as vt, buildToolbeltAwareness as w, assembleResponsesPayload as wt, runWorkerAgent as x, countTokens as xt, resolveModeDefaults as y, workerToolsEnabled as yt, formatThinkingRepairDecline as z, provisionTreeSitterAssets as zt };
36478
+ export { handleMcpDelete as $, UPSTREAM_INACTIVITY_TIMEOUT_MS as $t, satisfiesMinVersion as A, readResponseBodyCapped as At, rememberThinkingHistoryRepair as B, provisionTreeSitterAssets as Bt, availableToolCommands as C, getTokenCount as Ct, vscodeRipgrepPath as D, createResponses as Dt, toolbeltSkipSet as E, resolveMcpToolTimeoutMs as Et, injectAdvisorTool as F, colbertDegradedWarning as Ft, isControllerClosedError as G, toolbeltPathOverride as Gt, repairRejectedThinkingHistory as H, DEFINITION_OF_GREATNESS as Ht, isAdvisorRequested as I, provisionAndIndexColbert as It, relayAnthropicStream as J, DEFAULT_CLAUDE_MODEL_FALLBACKS as Jt, logStreamError as K, BUDGET_SMALL_FAST_CATALOG_ID as Kt, resolveAdvisorEffort as L, extractTarGzMember as Lt, ADVISOR_INTERNAL_TOOL_NAME as M, normalizeOpenAIUsage as Mt, ADVISOR_TOOL_INSTRUCTIONS as N, provisionBrowserAssets as Nt, TOOLBELT_TOOLS$1 as O, createChatCompletions as Ot, buildAdvisorStream as P, hasSupportedBrowserInstalled as Pt, clampEffort as Q, UPSTREAM_FETCH_TIMEOUT_MS as Qt, resolveAdvisorModel as R, extractZipMember as Rt, buildEnv as S, createMessages as St, toolbeltEnabled as T, warnOnTokenPriceDrift as Tt, buildAnthropicErrorEvent as U, shouldUseInsecureTls as Ut, repairKnownThinkingHistory as V, CONDENSED_OPERATING_SEQUENCE as Vt, buildOpenAIErrorEvent as W, collapsePathKeys as Wt, UNKNOWN_EFFORT_ANCHOR as X, DEFAULT_CODEX_MODEL_FALLBACKS as Xt, EFFORT_ORDER as Y, DEFAULT_CODEX_MODEL as Yt, bucketEffort as Z, DEFAULT_PORT as Zt, appendPlanReminder as _, scribeModel as _t, buildPeerAwarenessSnippet as a, upstreamMaxConnections as an, browseAgentEnabled as at, resolveWorkerRunOpts as b, shimDefaultsToXhigh as bt, personasFor as c, withOneMSuffixForLead as cn, fleetToolsEnabled as ct, EXPLORE_DEFAULT_MODEL as d, implementerFastModel as dt, generateRandomPort as en, handleMcpPost as et, EXPLORE_DEFAULT_THINKING as f, nativeSubagentModel as ft, TEST_DEFAULT_MODEL as g, scoutModel as gt, REVIEW_DEFAULT_MODEL as h, reviewerModel as ht, buildAgentPrompt as i, upstreamAllowH2 as in, brainstormModel as it, searchWeb as j, parseJsonOrDiagnose as jt, assetFor as k, MAX_RESPONSE_BODY_BYTES as kt, BROWSE_DEFAULT_MODEL as l, withInstallLock as ln, geminiAvailable as lt, PLAN_DEFAULT_MODEL as m, reviewerFastModel as mt, MCP_GROUPS as n, pickClaudeDefault as nn, agentToolsEnabled as nt, buildPeerAwarenessSummary as o, classifyMessagesRoute as on, browserCompoundToolsEnabled as ot, IMPLEMENT_DEFAULT_MODEL as p, resolveGeminiReviewModel as pt, readIteratorWithTimeout as q, BUDGET_SMALL_FAST_SLUG as qt, assertMcpToolSurfaceConsistent as r, resolveLeadSlugArg as rn, artifactToolsEnabled as rt, enumerateInjectedMcpToolNames as s, withOneMSuffix as sn, browserToolsEnabled as st, GROUP_META as t, isBudgetClaudeLead as tn, REVIEW_FAST_DEFAULT_MODEL as tt, DEFAULT_MODEL_CHAIN as u, generalPurposeFastModel as ut, resolveDefaultModel as v, standInToolEnabled as vt, buildToolbeltAwareness as w, assembleResponsesPayload as wt, runWorkerAgent as x, countTokens as xt, resolveModeDefaults as y, workerToolsEnabled as yt, formatThinkingRepairDecline as z, warmTreeSitterPool as zt };
36115
36479
 
36116
- //# sourceMappingURL=peer-mcp-personas-B5Wp6wIn.js.map
36480
+ //# sourceMappingURL=peer-mcp-personas-Bd56EmiO.js.map