github-router 0.3.274 → 0.3.276

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 (72) hide show
  1. package/dist/{attribution-settings-Cqh_A9yx.js → attribution-settings-VOPvSn6v.js} +65 -49
  2. package/dist/attribution-settings-VOPvSn6v.js.map +1 -0
  3. package/dist/{auth-Q1tfmfCT.js → auth-CYoRwhC9.js} +3 -3
  4. package/dist/{auth-Q1tfmfCT.js.map → auth-CYoRwhC9.js.map} +1 -1
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/{check-usage-CHLz4tU_.js → check-usage-DDUyg8ZB.js} +4 -4
  7. package/dist/{check-usage-CHLz4tU_.js.map → check-usage-DDUyg8ZB.js.map} +1 -1
  8. package/dist/{claude-CqFkkQGo.js → claude-OTHh7iKz.js} +26 -16
  9. package/dist/claude-OTHh7iKz.js.map +1 -0
  10. package/dist/{codex-gGkDJHZJ.js → codex-tCxK30Su.js} +5 -5
  11. package/dist/{codex-gGkDJHZJ.js.map → codex-tCxK30Su.js.map} +1 -1
  12. package/dist/{debug-CuuxCk_J.js → debug-CJgsxoVw.js} +2 -2
  13. package/dist/{debug-CuuxCk_J.js.map → debug-CJgsxoVw.js.map} +1 -1
  14. package/dist/engine-BKJKMAzh.js +2 -0
  15. package/dist/{gate-discovery-e114xla0.js → gate-discovery-nf7PGgnS.js} +5 -5
  16. package/dist/{gate-discovery-e114xla0.js.map → gate-discovery-nf7PGgnS.js.map} +1 -1
  17. package/dist/{get-copilot-usage-BRN18uMJ.js → get-copilot-usage-D1OAH0EU.js} +2 -2
  18. package/dist/{get-copilot-usage-BRN18uMJ.js.map → get-copilot-usage-D1OAH0EU.js.map} +1 -1
  19. package/dist/{internal-artifact-open-CDqPfegE.js → internal-artifact-open-BCz9lnq0.js} +2 -2
  20. package/dist/{internal-artifact-open-CDqPfegE.js.map → internal-artifact-open-BCz9lnq0.js.map} +1 -1
  21. package/dist/{internal-plan-review-CH9qTHoZ.js → internal-plan-review-jRMpHu68.js} +3 -3
  22. package/dist/{internal-plan-review-CH9qTHoZ.js.map → internal-plan-review-jRMpHu68.js.map} +1 -1
  23. package/dist/{internal-prompt-submit-Bve1s4z_.js → internal-prompt-submit-CqWRFCuv.js} +4 -4
  24. package/dist/{internal-prompt-submit-Bve1s4z_.js.map → internal-prompt-submit-CqWRFCuv.js.map} +1 -1
  25. package/dist/{internal-session-bind-Cc8bMXHK.js → internal-session-bind-BiHWDxuC.js} +2 -2
  26. package/dist/{internal-session-bind-Cc8bMXHK.js.map → internal-session-bind-BiHWDxuC.js.map} +1 -1
  27. package/dist/{internal-stop-hook-BLPxEImO.js → internal-stop-hook-Ds9LwmZA.js} +5 -5
  28. package/dist/{internal-stop-hook-BLPxEImO.js.map → internal-stop-hook-Ds9LwmZA.js.map} +1 -1
  29. package/dist/{internal-stop-review-CWE7_QVv.js → internal-stop-review-BOFj7R3q.js} +2 -2
  30. package/dist/{internal-stop-review-CWE7_QVv.js.map → internal-stop-review-BOFj7R3q.js.map} +1 -1
  31. package/dist/{lifecycle-AgNOtFRv.js → lifecycle-AGXnd-ZY.js} +2 -2
  32. package/dist/{lifecycle-AgNOtFRv.js.map → lifecycle-AGXnd-ZY.js.map} +1 -1
  33. package/dist/lifecycle-DCrKbaIU.js +2 -0
  34. package/dist/lifecycle-KKSTKwd6.js +2 -0
  35. package/dist/{lifecycle-0XrazT05.js → lifecycle-LA4rAuAL.js} +2 -2
  36. package/dist/{lifecycle-0XrazT05.js.map → lifecycle-LA4rAuAL.js.map} +1 -1
  37. package/dist/main.js +14 -14
  38. package/dist/{models-d0gkuYGy.js → models-4Q45Q2Dw.js} +3 -3
  39. package/dist/{models-d0gkuYGy.js.map → models-4Q45Q2Dw.js.map} +1 -1
  40. package/dist/{orchestration-B_Q3ncXI.js → orchestration-CpkG8ScN.js} +2 -2
  41. package/dist/{orchestration-B_Q3ncXI.js.map → orchestration-CpkG8ScN.js.map} +1 -1
  42. package/dist/paths-DN3Nio42.js +2 -0
  43. package/dist/{paths-Bg1_DWhi.js → paths-j2B7b0DZ.js} +4 -4
  44. package/dist/{paths-Bg1_DWhi.js.map → paths-j2B7b0DZ.js.map} +1 -1
  45. package/dist/{peer-mcp-personas-pzk1zcDa.js → peer-mcp-personas-DRL_xT4h.js} +660 -120
  46. package/dist/{peer-mcp-personas-pzk1zcDa.js.map → peer-mcp-personas-DRL_xT4h.js.map} +1 -1
  47. package/dist/{plan-review-hook-BKOcW5s4.js → plan-review-hook-Dk5zTNke.js} +2 -2
  48. package/dist/{plan-review-hook-BKOcW5s4.js.map → plan-review-hook-Dk5zTNke.js.map} +1 -1
  49. package/dist/{prompt-submit-hook-F7Zih_au.js → prompt-submit-hook-lvTWwaTV.js} +2 -2
  50. package/dist/{prompt-submit-hook-F7Zih_au.js.map → prompt-submit-hook-lvTWwaTV.js.map} +1 -1
  51. package/dist/{provision-BIUvGrMS.js → provision-HzZ547dW.js} +4 -4
  52. package/dist/{provision-BIUvGrMS.js.map → provision-HzZ547dW.js.map} +1 -1
  53. package/dist/{serve-BYbEyMoF.js → serve-CM3OmF4I.js} +21 -12
  54. package/dist/serve-CM3OmF4I.js.map +1 -0
  55. package/dist/{server-setup-CNKsKERG.js → server-setup-C9r7jYnz.js} +6 -6
  56. package/dist/{server-setup-CNKsKERG.js.map → server-setup-C9r7jYnz.js.map} +1 -1
  57. package/dist/{start-Cy_fEtEw.js → start-KAUCsDao.js} +3 -3
  58. package/dist/{start-Cy_fEtEw.js.map → start-KAUCsDao.js.map} +1 -1
  59. package/dist/{stop-gate-hook-BqpumJtl.js → stop-gate-hook-CaUrldLh.js} +2 -2
  60. package/dist/{stop-gate-hook-BqpumJtl.js.map → stop-gate-hook-CaUrldLh.js.map} +1 -1
  61. package/dist/{stop-gate-policy-C0XOBZhB.js → stop-gate-policy-lAabj_pP.js} +2 -2
  62. package/dist/{stop-gate-policy-C0XOBZhB.js.map → stop-gate-policy-lAabj_pP.js.map} +1 -1
  63. package/dist/{token-Bz6HoH2H.js → token-CxPaBEUS.js} +2 -2
  64. package/dist/{token-Bz6HoH2H.js.map → token-CxPaBEUS.js.map} +1 -1
  65. package/package.json +1 -1
  66. package/dist/attribution-settings-Cqh_A9yx.js.map +0 -1
  67. package/dist/claude-CqFkkQGo.js.map +0 -1
  68. package/dist/engine-BM2mIXsc.js +0 -2
  69. package/dist/lifecycle-2M0IThRr.js +0 -2
  70. package/dist/lifecycle-C8kFsAj-.js +0 -2
  71. package/dist/paths-BbHtQAOs.js +0 -2
  72. package/dist/serve-BYbEyMoF.js.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import { t as getPackageVersion } from "./version-_Q1WpsQp.js";
2
- import { t as PATHS } from "./paths-Bg1_DWhi.js";
3
- import { A as state, C as GITHUB_GRAPHQL_URL, D as githubAgentGraphQLHeaders, E as copilotVersion, O as githubAgentHeaders, S as GITHUB_API_BASE_URL, T as copilotHeaders, b as sanitizeTransportText, d as resolveModel, f as sleep$2, g as HTTPError, h as isAllowedCopilotHost, i as tryRefreshAndRetry, v as classifyTransportError, w as copilotBaseUrl, x as withTransientRetry, y as fetchWithTransientRetry } from "./token-Bz6HoH2H.js";
2
+ import { t as PATHS } from "./paths-j2B7b0DZ.js";
3
+ import { A as state, C as GITHUB_GRAPHQL_URL, D as githubAgentGraphQLHeaders, E as copilotVersion, O as githubAgentHeaders, S as GITHUB_API_BASE_URL, T as copilotHeaders, b as sanitizeTransportText, d as resolveModel, f as sleep$2, g as HTTPError, h as isAllowedCopilotHost, i as tryRefreshAndRetry, v as classifyTransportError, w as copilotBaseUrl, x as withTransientRetry, y as fetchWithTransientRetry } from "./token-CxPaBEUS.js";
4
4
  import { a as resolveExecutable, c as runManagedExeCapture, i as parseIntEnv, l as spawnTaskkillBestEffort, r as parseBoolEnv } from "./exec-y8C_MU8A.js";
5
- import { i as registerExitHandlers$1, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-0XrazT05.js";
5
+ import { i as registerExitHandlers$1, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-LA4rAuAL.js";
6
6
  import { t as MCP_WORKSPACE_HEADER } from "./mcp-workspace-header-CGJbNeHb.js";
7
7
  import { n as ArtifactError, r as applyInsecureTls, t as ArtifactClient } from "./client-CQMfroGV.js";
8
- import { n as isPidAlive$1, r as registerColbertExitHandlers, s as trackChild, t as getColbertInstanceUuid } from "./lifecycle-AgNOtFRv.js";
9
- import { h as runGateChecks, m as sealedGateIds, p as resolveSealedGate } from "./stop-gate-hook-BqpumJtl.js";
10
- import { t as liveExec } from "./orchestration-B_Q3ncXI.js";
8
+ import { n as isPidAlive$1, r as registerColbertExitHandlers, s as trackChild, t as getColbertInstanceUuid } from "./lifecycle-AGXnd-ZY.js";
9
+ import { h as runGateChecks, m as sealedGateIds, p as resolveSealedGate } from "./stop-gate-hook-CaUrldLh.js";
10
+ import { t as liveExec } from "./orchestration-CpkG8ScN.js";
11
11
  import { createRequire } from "node:module";
12
12
  import consola from "consola";
13
13
  import { chmodSync, closeSync, constants, cpSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
@@ -18470,7 +18470,7 @@ function logAudit$1(record) {
18470
18470
  try {
18471
18471
  const fs = await import("node:fs/promises");
18472
18472
  const path = await import("node:path");
18473
- const { PATHS } = await import("./paths-BbHtQAOs.js");
18473
+ const { PATHS } = await import("./paths-DN3Nio42.js");
18474
18474
  const dir = path.join(PATHS.APP_DIR, "browser-mcp");
18475
18475
  await fs.mkdir(dir, { recursive: true });
18476
18476
  const line = JSON.stringify({
@@ -19052,14 +19052,42 @@ function currentInFlight() {
19052
19052
  //#endregion
19053
19053
  //#region src/lib/vision-preflight.ts
19054
19054
  /**
19055
- * Outbound vision preflight.
19055
+ * Outbound vision handling.
19056
19056
  *
19057
- * Converts what would otherwise be an opaque upstream `400` into a local,
19058
- * actionable rejection — the same trade `predictedWindowOverflow` in
19059
- * `src/routes/mcp/handler.ts` already makes for prompt tokens, and the reason
19060
- * this file exists at all: `capabilities.limits.vision` advertises three fields
19061
- * (`max_prompt_images`, `max_prompt_image_size`, `supported_media_types`) and
19062
- * until now the proxy read exactly one of them, in the `models` pretty-printer.
19057
+ * WHO DECIDES THE IMAGE LIMIT
19058
+ *
19059
+ * Copilot does, and this module no longer guesses. The catalog advertises a
19060
+ * `max_prompt_images` limit, and that field was measured against the live API
19061
+ * on 2026-08-10 across all 23 vision-capable models:
19062
+ *
19063
+ * - gemini-3.x catalog 10 → upstream enforces exactly 10
19064
+ * - gpt-5.6-sol catalog 1 → upstream enforces 50
19065
+ * - gpt-5.5 catalog 1 → accepted 120, no ceiling found
19066
+ * - claude-opus-5 catalog 1 → accepted 200, no ceiling found
19067
+ * - claude-sonnet/haiku catalog 5 → accepted 32+
19068
+ *
19069
+ * So the field is accurate for one family and understates the truth by 50x to
19070
+ * 200x everywhere else. Note the real ceiling is not even uniform WITHIN a
19071
+ * family: gpt-5.6-sol stops at 50 while gpt-5.5 took 120, which is the sharpest
19072
+ * argument against substituting one hardcoded number for another. A local cardinality reject built on it rejected at 2
19073
+ * what upstream serves at 50, and — because the count was taken over the whole
19074
+ * assembled payload including replayed history — the caller could not act on
19075
+ * the error, so every retry reproduced it and the session was finished.
19076
+ *
19077
+ * Upstream also enforces rules this module does not model at all (minimum pixel
19078
+ * dimensions on grok-4.5, per-model media-type support on gpt-4o), which is the
19079
+ * general argument: local pre-validation cannot be complete, and where it
19080
+ * guesses it is wrong.
19081
+ *
19082
+ * WHAT WE DO INSTEAD
19083
+ *
19084
+ * Send it, and recover from upstream's answer. Copilot's rejections name the
19085
+ * real number ("maximum allowed for model gemini-3.6-flash is 10, got 16",
19086
+ * "Exceeded maximum number of images (50) allowed in the request"), so
19087
+ * `parseUpstreamImageCeiling` reads it, `planOutboundImages` prunes to it, the
19088
+ * transport retries once, and `rememberImageCeiling` keeps it for the process
19089
+ * lifetime so the round trip is paid once per model per boot. The learned value
19090
+ * is an observation rather than a guess, and it self-heals if Copilot changes.
19063
19091
  *
19064
19092
  * WHY ONE CHOKEPOINT, NOT PER-ADAPTER CHECKS
19065
19093
  *
@@ -19067,38 +19095,82 @@ function currentInFlight() {
19067
19095
  * block, a block nested inside a `tool_result`, the synthetic follow-up user
19068
19096
  * message the shim emits because a tool-output item cannot carry images, images
19069
19097
  * already present in replayed conversation history, and peer-critic
19070
- * attachments. Validating in each adapter means each adapter has to
19098
+ * attachments. Handling this in each adapter means each adapter has to
19071
19099
  * rediscover every one of those shapes, and the ones it forgets fail silently.
19072
- * So validation runs ONCE, on the fully assembled payload, immediately before
19073
- * transport serialization `createResponses` and `createChatCompletions` and
19074
- * is therefore total by construction.
19100
+ * So it runs ONCE, on the fully assembled payload, immediately before transport
19101
+ * serialization, and is therefore total by construction.
19075
19102
  *
19076
- * POLICY ON MISSING METADATA
19103
+ * NOTHING HERE FAILS A REQUEST
19077
19104
  *
19078
- * Deliberately NOT fail-open. "We don't know the limit" is not "there is no
19079
- * limit"; treating it as the latter reproduces the exact opaque-400 this
19080
- * module exists to remove. The three cases are distinguished:
19105
+ * A defect that is fatal for a whole request is fatal forever once the image is
19106
+ * in replayed history, because the caller cannot edit history. So every defect
19107
+ * this module recognises drops that one image and replaces it IN PLACE with a
19108
+ * short text note the model can read and relay. In place, never deletion: a
19109
+ * turn whose only content was a screenshot would otherwise become an empty
19110
+ * content array, which upstream does reject.
19081
19111
  *
19082
- * - model absent from the catalog entirely ALLOW. We have no basis for a
19083
- * judgement, upstream remains authoritative, and blocking would break
19084
- * custom catalogs and offline tests. This mirrors `modelSupportsEndpoint`.
19085
- * - model present, `supports.vision` not true → REJECT, naming the model.
19086
- * - model present, vision supported, limits absent → CONSERVATIVE FLOOR
19087
- * (`FLOOR_MAX_IMAGES` / `FLOOR_MAX_IMAGE_BYTES`), and the message says so,
19088
- * so the caller can tell a real limit from an assumed one.
19089
- *
19090
- * Error strings are returned to callers and end up in front of a model, so they
19091
- * name the model, the numbers, and the fix — never a stack, a local path, or a
19092
- * raw catalog object.
19112
+ * Note text carries no ordinals and no running totals. Claude Code replays the
19113
+ * whole transcript every turn, so a note reading "image 1 of 2" would become
19114
+ * "image 1 of 3" on the next image and invalidate the prompt-cache prefix from
19115
+ * the earliest omission onward.
19093
19116
  */
19094
19117
  /**
19095
- * Floor applied when a model advertises vision but publishes no limits. Every
19096
- * vision-capable model in the live catalog reports at least 1 image and a
19097
- * 3 MiB ceiling, so this is the observed minimum rather than a guess.
19118
+ * Applied when a model advertises vision but publishes no size limit. Every
19119
+ * vision-capable model in the live catalog reports 3 MiB, so this is the
19120
+ * observed value rather than a guess. There is deliberately no image-COUNT
19121
+ * floor: that is the number upstream turned out to own.
19098
19122
  */
19099
- const FLOOR_MAX_IMAGES = 1;
19100
19123
  const FLOOR_MAX_IMAGE_BYTES = 3145728;
19101
19124
  /**
19125
+ * Ceilings observed from upstream rejections, keyed by model id. Process
19126
+ * lifetime only, and deliberately not persisted: a stale number on disk would
19127
+ * be exactly the catalog problem again, one indirection further away.
19128
+ */
19129
+ const learnedCeilings = /* @__PURE__ */ new Map();
19130
+ function learnedImageCeiling(modelId) {
19131
+ return learnedCeilings.get(modelId);
19132
+ }
19133
+ function rememberImageCeiling(modelId, ceiling) {
19134
+ if (!Number.isInteger(ceiling) || ceiling < 1) return;
19135
+ learnedCeilings.set(modelId, ceiling);
19136
+ }
19137
+ /** True when anything has been learned at all — a cheap pre-parse gate. */
19138
+ function hasLearnedImageCeilings() {
19139
+ return learnedCeilings.size > 0;
19140
+ }
19141
+ /**
19142
+ * Both rejection shapes Copilot was observed to emit carry the real ceiling:
19143
+ *
19144
+ * too many images: maximum allowed for model gemini-3.6-flash is 10, got 16
19145
+ * Exceeded maximum number of images (50) allowed in the request.
19146
+ *
19147
+ * Anything else returns `undefined`, and the caller forwards upstream's error
19148
+ * untouched. A parser that guesses would reintroduce the failure this module
19149
+ * was rewritten to remove.
19150
+ */
19151
+ const CEILING_PATTERNS = [/maximum allowed for model \S+ is (\d+)/i, /exceeded maximum number of images \((\d+)\)/i];
19152
+ function parseUpstreamImageCeiling(body) {
19153
+ if (!/image/i.test(body)) return void 0;
19154
+ for (const pattern of CEILING_PATTERNS) {
19155
+ const match = pattern.exec(body);
19156
+ if (!match) continue;
19157
+ const ceiling = Number(match[1]);
19158
+ if (Number.isInteger(ceiling) && ceiling >= 1 && ceiling <= 1e4) return ceiling;
19159
+ }
19160
+ }
19161
+ /**
19162
+ * Read an error body without consuming it, so the existing error path can still
19163
+ * report the same text downstream. Never throws: a body we cannot read simply
19164
+ * yields no ceiling and upstream's error is forwarded as it always was.
19165
+ */
19166
+ async function peekErrorBody(response) {
19167
+ try {
19168
+ return await response.clone().text();
19169
+ } catch {
19170
+ return "";
19171
+ }
19172
+ }
19173
+ /**
19102
19174
  * Split a `data:<mime>;base64,<payload>` URI. Returns `null` for any other URL
19103
19175
  * shape (including a plain remote `https://` reference, which the caller
19104
19176
  * handles separately because its bytes are not ours to inspect).
@@ -19111,45 +19183,162 @@ function parseDataUrl(url) {
19111
19183
  base64: match[2]
19112
19184
  };
19113
19185
  }
19114
- function reject(message) {
19186
+ /**
19187
+ * Judge one image on its own merits. Returns `undefined` when it is fine.
19188
+ *
19189
+ * Remote-URL images are never judged here: the bytes live on someone else's
19190
+ * server, so any local claim about them would be fiction.
19191
+ */
19192
+ function imageDefect(image, modelId, maxBytes, assumed, allowedTypes) {
19193
+ if (image.url !== void 0 && image.base64 === void 0) return void 0;
19194
+ if (image.declaredMimeType === void 0 || image.declaredMimeType.length === 0) return {
19195
+ reason: "no media type declared",
19196
+ note: "[image removed: no media type was declared, so it could not be sent]"
19197
+ };
19198
+ if (image.base64 === void 0) return void 0;
19199
+ const bytes = decodeBase64Strict(image.base64);
19200
+ if (!bytes) return {
19201
+ reason: "not valid base64",
19202
+ note: "[image removed: the image data was not valid base64]"
19203
+ };
19204
+ if (bytes.length > maxBytes) return {
19205
+ reason: `${bytes.length} bytes over model ${modelId}'s ${maxBytes}-byte limit${assumed}`,
19206
+ note: `[image removed: it is over this model's ${maxBytes}-byte limit; re-capture at a smaller scale or lower quality]`
19207
+ };
19208
+ const actual = detectImageMimeType(bytes);
19209
+ if (!actual) return {
19210
+ reason: `declared ${image.declaredMimeType} but the bytes are not a supported image`,
19211
+ note: `[image removed: it is declared ${image.declaredMimeType} but its bytes are not a supported image (jpeg, png, webp, gif, heic, heif)]`
19212
+ };
19213
+ if (actual !== image.declaredMimeType) return {
19214
+ reason: `declared ${image.declaredMimeType} but the bytes are ${actual}`,
19215
+ note: `[image removed: it is declared ${image.declaredMimeType} but its bytes are ${actual}; send the correct media type]`
19216
+ };
19217
+ if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(actual)) return {
19218
+ reason: `${actual} is not in model ${modelId}'s accepted media types`,
19219
+ note: `[image removed: ${actual} is not accepted by this model; it accepts ${allowedTypes.join(", ")}]`
19220
+ };
19221
+ }
19222
+ function keepAll(count) {
19115
19223
  return {
19116
- ok: false,
19117
- message
19224
+ verdicts: Array.from({ length: count }, () => ({ keep: true })),
19225
+ kept: count,
19226
+ dropped: 0
19118
19227
  };
19119
19228
  }
19120
19229
  /**
19121
- * Validate every image on an outbound request against the resolved model's
19122
- * advertised vision capability.
19230
+ * Decide, per image, what goes on the wire.
19231
+ *
19232
+ * `maxImages` is the CARDINALITY budget, and it comes only from a ceiling
19233
+ * upstream actually stated — never from the catalog, whose `max_prompt_images`
19234
+ * was measured wrong for 20 of 23 models. Omit it and no image is dropped for
19235
+ * being one too many.
19123
19236
  *
19124
- * Remote-URL images are counted toward the cardinality limit but not size- or
19125
- * type-checked: the bytes live on someone else's server, so any local claim
19126
- * about them would be fiction. Upstream stays authoritative for those.
19237
+ * Validity is judged BEFORE the budget so a malformed image cannot consume a
19238
+ * slot and evict a good one, and the budget pass never rewrites a verdict the
19239
+ * validity pass already set a malformed image must not be reported as a
19240
+ * cardinality eviction.
19127
19241
  */
19128
- function checkOutboundImages(modelId, images) {
19129
- if (images.length === 0) return { ok: true };
19242
+ function planOutboundImages(modelId, images, options) {
19243
+ if (images.length === 0) return {
19244
+ verdicts: [],
19245
+ kept: 0,
19246
+ dropped: 0
19247
+ };
19130
19248
  const model = state.models?.data.find((m) => m.id === modelId);
19131
- if (!model) return { ok: true };
19132
- if ((model.capabilities?.supports)?.vision !== true) return reject(`Model ${modelId} does not support image input. Send text only, or select a vision-capable model.`);
19133
- const limits = model.capabilities?.limits?.vision;
19134
- const maxImages = limits?.max_prompt_images ?? FLOOR_MAX_IMAGES;
19249
+ const maxImages = options?.maxImages;
19250
+ if (!model) {
19251
+ if (maxImages === void 0) return keepAll(images.length);
19252
+ }
19253
+ const supports = model?.capabilities?.supports;
19254
+ if (model && supports?.vision !== true) {
19255
+ const note = `[image removed: model ${modelId} does not accept image input; select a vision-capable model to send images]`;
19256
+ return {
19257
+ verdicts: images.map(() => ({
19258
+ keep: false,
19259
+ reason: `model ${modelId} does not support image input`,
19260
+ note
19261
+ })),
19262
+ kept: 0,
19263
+ dropped: images.length
19264
+ };
19265
+ }
19266
+ const limits = model?.capabilities?.limits?.vision;
19135
19267
  const maxBytes = limits?.max_prompt_image_size ?? FLOOR_MAX_IMAGE_BYTES;
19136
- const assumed = limits?.max_prompt_images === void 0 ? " (assumed; the model publishes no image limits)" : "";
19137
- if (images.length > maxImages) return reject(`Model ${modelId} accepts at most ${maxImages} image(s) per request${assumed}, but this request carries ${images.length}. Send fewer images, or use a model with a higher image limit.`);
19268
+ const assumed = limits?.max_prompt_image_size === void 0 ? " (assumed; the model publishes no image size limit)" : "";
19138
19269
  const allowedTypes = limits?.supported_media_types;
19139
- for (const [index, image] of images.entries()) {
19140
- if (image.url !== void 0 && image.base64 === void 0) continue;
19141
- const position = `image ${index + 1} of ${images.length}`;
19142
- if (image.declaredMimeType === void 0 || image.declaredMimeType.length === 0) return reject(`${position} has no media type. Anthropic's \`source.media_type\` (or a \`data:<mime>;base64,\` prefix) is required — it used to be defaulted, which silently asserted a type the model may not accept.`);
19143
- if (image.base64 === void 0) continue;
19144
- const bytes = decodeBase64Strict(image.base64);
19145
- if (!bytes) return reject(`${position} is not valid base64 and cannot be sent.`);
19146
- if (bytes.length > maxBytes) return reject(`${position} is ${bytes.length} bytes, over model ${modelId}'s ${maxBytes}-byte limit${assumed}. Re-capture at a smaller scale or lower quality.`);
19147
- const actual = detectImageMimeType(bytes);
19148
- if (!actual) return reject(`${position} is declared ${image.declaredMimeType} but its bytes are not a supported image (jpeg, png, webp, gif, heic, heif).`);
19149
- if (actual !== image.declaredMimeType) return reject(`${position} is declared ${image.declaredMimeType} but its bytes are ${actual}. Send the correct media type.`);
19150
- if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(actual)) return reject(`${position} is ${actual}, which model ${modelId} does not accept. Supported: ${allowedTypes.join(", ")}.`);
19270
+ const verdicts = images.map((image) => {
19271
+ const defect = model ? imageDefect(image, modelId, maxBytes, assumed, allowedTypes) : imageDefect(image, modelId, Number.POSITIVE_INFINITY, "", void 0);
19272
+ return defect ? {
19273
+ keep: false,
19274
+ reason: defect.reason,
19275
+ note: defect.note
19276
+ } : { keep: true };
19277
+ });
19278
+ if (maxImages !== void 0) {
19279
+ const overflowNote = `[image removed: this model accepts at most ${maxImages} image${maxImages === 1 ? "" : "s"} per request, so only the most recent were sent]`;
19280
+ let admitted = 0;
19281
+ for (let index = verdicts.length - 1; index >= 0; index--) {
19282
+ const verdict = verdicts[index];
19283
+ if (!verdict.keep) continue;
19284
+ if (admitted < maxImages) {
19285
+ admitted++;
19286
+ continue;
19287
+ }
19288
+ verdict.keep = false;
19289
+ verdict.reason = `over model ${modelId}'s ${maxImages}-image ceiling`;
19290
+ verdict.note = overflowNote;
19291
+ }
19151
19292
  }
19152
- return { ok: true };
19293
+ const kept = verdicts.filter((v) => v.keep).length;
19294
+ return {
19295
+ verdicts,
19296
+ kept,
19297
+ dropped: verdicts.length - kept
19298
+ };
19299
+ }
19300
+ /**
19301
+ * Emit one warn line per drop. Rewriting a request body on the hot path with no
19302
+ * record is not acceptable: the note is addressed to the model, and the model
19303
+ * may or may not relay it, so the operator needs their own channel.
19304
+ */
19305
+ function logImagePlan(modelId, plan) {
19306
+ if (plan.dropped === 0) return;
19307
+ const reasons = [...new Set(plan.verdicts.filter((v) => !v.keep).map((v) => v.reason))];
19308
+ consola.warn(`Dropped ${plan.dropped} of ${plan.verdicts.length} image(s) bound for ${modelId} (${plan.kept} kept): ${reasons.join("; ")}`);
19309
+ }
19310
+ function responsesImageUrl(part) {
19311
+ if (part === null || typeof part !== "object") return void 0;
19312
+ const p = part;
19313
+ if (p.type !== "input_image" || typeof p.image_url !== "string") return void 0;
19314
+ return p.image_url;
19315
+ }
19316
+ function chatImageUrl(part) {
19317
+ if (part === null || typeof part !== "object") return void 0;
19318
+ const p = part;
19319
+ if (p.type !== "image_url") return void 0;
19320
+ const url = p.image_url?.url;
19321
+ return typeof url === "string" ? url : void 0;
19322
+ }
19323
+ /** Anthropic-native `{type:"image", source:{type:"base64", media_type, data}}`. */
19324
+ function anthropicImage(part) {
19325
+ if (part === null || typeof part !== "object") return void 0;
19326
+ const p = part;
19327
+ if (p.type !== "image") return void 0;
19328
+ const source = p.source;
19329
+ if (!source) return void 0;
19330
+ if (typeof source.url === "string" && typeof source.data !== "string") return { url: source.url };
19331
+ return {
19332
+ base64: typeof source.data === "string" ? source.data : void 0,
19333
+ declaredMimeType: typeof source.media_type === "string" ? source.media_type : void 0
19334
+ };
19335
+ }
19336
+ function fromUrl(url) {
19337
+ const parsed = parseDataUrl(url);
19338
+ return parsed ? {
19339
+ base64: parsed.base64,
19340
+ declaredMimeType: parsed.mimeType
19341
+ } : { url };
19153
19342
  }
19154
19343
  /** Collect the images on an assembled Copilot `/responses` payload. */
19155
19344
  function imagesInResponsesPayload(input) {
@@ -19160,10 +19349,8 @@ function imagesInResponsesPayload(input) {
19160
19349
  const content = item.content;
19161
19350
  if (!Array.isArray(content)) continue;
19162
19351
  for (const part of content) {
19163
- if (part === null || typeof part !== "object") continue;
19164
- const p = part;
19165
- if (p.type !== "input_image" || typeof p.image_url !== "string") continue;
19166
- images.push(fromUrl(p.image_url));
19352
+ const url = responsesImageUrl(part);
19353
+ if (url !== void 0) images.push(fromUrl(url));
19167
19354
  }
19168
19355
  }
19169
19356
  return images;
@@ -19177,40 +19364,167 @@ function imagesInChatPayload(messages) {
19177
19364
  const content = message.content;
19178
19365
  if (!Array.isArray(content)) continue;
19179
19366
  for (const part of content) {
19180
- if (part === null || typeof part !== "object") continue;
19181
- const p = part;
19182
- if (p.type !== "image_url") continue;
19183
- const url = p.image_url?.url;
19184
- if (typeof url !== "string") continue;
19185
- images.push(fromUrl(url));
19367
+ const url = chatImageUrl(part);
19368
+ if (url !== void 0) images.push(fromUrl(url));
19186
19369
  }
19187
19370
  }
19188
19371
  return images;
19189
19372
  }
19190
- function fromUrl(url) {
19191
- const parsed = parseDataUrl(url);
19192
- return parsed ? {
19193
- base64: parsed.base64,
19194
- declaredMimeType: parsed.mimeType
19195
- } : { url };
19196
- }
19197
19373
  /**
19198
- * Run the preflight and throw a client-visible 400 when it fails.
19374
+ * Visit every Anthropic image part in order, descending into the nested
19375
+ * `content` a `tool_result` carries.
19199
19376
  *
19200
- * Shaped as an `HTTPError` carrying a synthetic `Response` so it flows through
19201
- * the existing `forwardError` path and reaches the client as a normal
19202
- * Anthropic-format `invalid_request_error` — the same envelope an upstream
19203
- * rejection would produce, except it names the actual problem and no upstream
19204
- * request was made. Tests assert that second property: a preflight failure must
19205
- * cost zero upstream calls.
19377
+ * The extractor and the pruner BOTH drive off this one walker, so their
19378
+ * traversal orders cannot drift apart which is the failure that would drop
19379
+ * the wrong image.
19206
19380
  */
19207
- function assertOutboundImagesOk(modelId, images) {
19208
- const verdict = checkOutboundImages(modelId, images);
19209
- if (verdict.ok) return;
19210
- throw new HTTPError(verdict.message, new Response(JSON.stringify({ error: { message: verdict.message } }), {
19211
- status: 400,
19212
- headers: { "content-type": "application/json" }
19213
- }));
19381
+ function walkAnthropicParts(part, visit) {
19382
+ const image = anthropicImage(part);
19383
+ if (image !== void 0) {
19384
+ visit(image);
19385
+ return;
19386
+ }
19387
+ if (part === null || typeof part !== "object") return;
19388
+ const nested = part.content;
19389
+ if (!Array.isArray(nested)) return;
19390
+ for (const inner of nested) walkAnthropicParts(inner, visit);
19391
+ }
19392
+ /** Collect the images on an Anthropic-native `/v1/messages` body. */
19393
+ function imagesInAnthropicMessages(messages) {
19394
+ const images = [];
19395
+ if (!Array.isArray(messages)) return images;
19396
+ for (const message of messages) {
19397
+ if (message === null || typeof message !== "object") continue;
19398
+ const content = message.content;
19399
+ if (!Array.isArray(content)) continue;
19400
+ for (const part of content) walkAnthropicParts(part, (image) => images.push(image));
19401
+ }
19402
+ return images;
19403
+ }
19404
+ /**
19405
+ * Thrown when the pruner's walk does not visit exactly as many images as the
19406
+ * planner judged. Fail loudly: defaulting an unjudged image to "keep" would
19407
+ * turn a traversal-drift bug into a silently leaked image.
19408
+ */
19409
+ var ImageIndexDriftError = class extends Error {};
19410
+ function assertConsumed(imageIndex, verdicts) {
19411
+ if (imageIndex !== verdicts.length) throw new ImageIndexDriftError(`image traversal drift: pruner visited ${imageIndex} image(s), planner judged ${verdicts.length}`);
19412
+ }
19413
+ /**
19414
+ * Replace each dropped image with a text part IN PLACE, preserving position.
19415
+ * Untouched items and parts keep their original object identity, so nothing is
19416
+ * needlessly copied and the caller's payload is never mutated.
19417
+ */
19418
+ function pruneImagesFromResponsesInput(input, verdicts) {
19419
+ if (!Array.isArray(input)) return input;
19420
+ let imageIndex = 0;
19421
+ const out = input.map((item) => {
19422
+ if (item === null || typeof item !== "object") return item;
19423
+ const content = item.content;
19424
+ if (!Array.isArray(content)) return item;
19425
+ let replaced = false;
19426
+ const parts = content.map((part) => {
19427
+ if (responsesImageUrl(part) === void 0) return part;
19428
+ const verdict = verdicts[imageIndex++];
19429
+ if (!verdict || verdict.keep) return part;
19430
+ replaced = true;
19431
+ return {
19432
+ type: "input_text",
19433
+ text: verdict.note
19434
+ };
19435
+ });
19436
+ return replaced ? {
19437
+ ...item,
19438
+ content: parts
19439
+ } : item;
19440
+ });
19441
+ assertConsumed(imageIndex, verdicts);
19442
+ return out;
19443
+ }
19444
+ function pruneImagesFromChatMessages(messages, verdicts) {
19445
+ if (!Array.isArray(messages)) return messages;
19446
+ let imageIndex = 0;
19447
+ const out = messages.map((message) => {
19448
+ if (message === null || typeof message !== "object") return message;
19449
+ const content = message.content;
19450
+ if (!Array.isArray(content)) return message;
19451
+ let replaced = false;
19452
+ const parts = content.map((part) => {
19453
+ if (chatImageUrl(part) === void 0) return part;
19454
+ const verdict = verdicts[imageIndex++];
19455
+ if (!verdict || verdict.keep) return part;
19456
+ replaced = true;
19457
+ return {
19458
+ type: "text",
19459
+ text: verdict.note
19460
+ };
19461
+ });
19462
+ return replaced ? {
19463
+ ...message,
19464
+ content: parts
19465
+ } : message;
19466
+ });
19467
+ assertConsumed(imageIndex, verdicts);
19468
+ return out;
19469
+ }
19470
+ function pruneImagesFromAnthropicMessages(messages, verdicts) {
19471
+ if (!Array.isArray(messages)) return messages;
19472
+ let imageIndex = 0;
19473
+ const replacePart = (part) => {
19474
+ if (anthropicImage(part) !== void 0) {
19475
+ const verdict = verdicts[imageIndex++];
19476
+ if (!verdict || verdict.keep) return {
19477
+ part,
19478
+ replaced: false
19479
+ };
19480
+ return {
19481
+ part: {
19482
+ type: "text",
19483
+ text: verdict.note
19484
+ },
19485
+ replaced: true
19486
+ };
19487
+ }
19488
+ if (part !== null && typeof part === "object") {
19489
+ const nested = part.content;
19490
+ if (Array.isArray(nested)) {
19491
+ let nestedReplaced = false;
19492
+ const inner = nested.map((innerPart) => {
19493
+ const result = replacePart(innerPart);
19494
+ if (result.replaced) nestedReplaced = true;
19495
+ return result.part;
19496
+ });
19497
+ if (nestedReplaced) return {
19498
+ part: {
19499
+ ...part,
19500
+ content: inner
19501
+ },
19502
+ replaced: true
19503
+ };
19504
+ }
19505
+ }
19506
+ return {
19507
+ part,
19508
+ replaced: false
19509
+ };
19510
+ };
19511
+ const out = messages.map((message) => {
19512
+ if (message === null || typeof message !== "object") return message;
19513
+ const content = message.content;
19514
+ if (!Array.isArray(content)) return message;
19515
+ let replaced = false;
19516
+ const parts = content.map((part) => {
19517
+ const result = replacePart(part);
19518
+ if (result.replaced) replaced = true;
19519
+ return result.part;
19520
+ });
19521
+ return replaced ? {
19522
+ ...message,
19523
+ content: parts
19524
+ } : message;
19525
+ });
19526
+ assertConsumed(imageIndex, verdicts);
19527
+ return out;
19214
19528
  }
19215
19529
  //#endregion
19216
19530
  //#region src/lib/diagnose-response.ts
@@ -19336,8 +19650,35 @@ async function readResponseBodyCapped(response, routePath, capBytes = MAX_RESPON
19336
19650
  */
19337
19651
  const createChatCompletions = async (payload, modelHeaders, callerSignal, retryTransient = false) => {
19338
19652
  if (!state.copilotToken) throw new Error("Copilot token not found");
19339
- const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
19340
- if (enableVision) assertOutboundImagesOk(payload.model, imagesInChatPayload(payload.messages));
19653
+ const carriesImages = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
19654
+ let enableVision = carriesImages;
19655
+ /**
19656
+ * Apply a cardinality budget plus the per-image checks on the fully assembled
19657
+ * payload — the single outbound chokepoint, so every path that can introduce
19658
+ * an image (top-level block, nested tool_result, the shim's synthetic
19659
+ * follow-up user message, replayed history, peer attachments) is covered
19660
+ * without each of them re-deriving the rules.
19661
+ *
19662
+ * `enableVision` is recomputed: if nothing survives, sending
19663
+ * `copilot-vision-request: true` would trade one 400 for another.
19664
+ */
19665
+ const applyImagePlan = (maxImages) => {
19666
+ try {
19667
+ const plan = planOutboundImages(payload.model, imagesInChatPayload(payload.messages), maxImages === void 0 ? void 0 : { maxImages });
19668
+ if (plan.dropped === 0) return false;
19669
+ logImagePlan(payload.model, plan);
19670
+ payload = {
19671
+ ...payload,
19672
+ messages: pruneImagesFromChatMessages(payload.messages, plan.verdicts)
19673
+ };
19674
+ enableVision = plan.kept > 0;
19675
+ return true;
19676
+ } catch (error) {
19677
+ consola.warn(`Image pruning skipped for ${payload.model}: ${String(error)}`);
19678
+ return false;
19679
+ }
19680
+ };
19681
+ if (carriesImages) applyImagePlan(learnedImageCeiling(payload.model));
19341
19682
  const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
19342
19683
  const url = `${copilotBaseUrl(state)}/chat/completions`;
19343
19684
  const doFetch = () => {
@@ -19358,10 +19699,21 @@ const createChatCompletions = async (payload, modelHeaders, callerSignal, retryT
19358
19699
  return fetch(url, fetchInit);
19359
19700
  };
19360
19701
  const withRefresh = () => tryRefreshAndRetry(doFetch, "/chat/completions");
19361
- const response = retryTransient ? await fetchWithTransientRetry(withRefresh, {
19702
+ const send = () => retryTransient ? fetchWithTransientRetry(withRefresh, {
19362
19703
  signal: callerSignal,
19363
19704
  label: "/chat/completions"
19364
- }) : await withRefresh();
19705
+ }) : withRefresh();
19706
+ let response = await send();
19707
+ if (!response.ok && carriesImages) {
19708
+ const ceiling = parseUpstreamImageCeiling(await peekErrorBody(response));
19709
+ if (ceiling !== void 0) {
19710
+ rememberImageCeiling(payload.model, ceiling);
19711
+ if (applyImagePlan(ceiling)) {
19712
+ response.body?.cancel();
19713
+ response = await send();
19714
+ }
19715
+ }
19716
+ }
19365
19717
  if (!response.ok) {
19366
19718
  let errorBody;
19367
19719
  try {
@@ -19400,8 +19752,32 @@ const createChatCompletions = async (payload, modelHeaders, callerSignal, retryT
19400
19752
  */
19401
19753
  const createResponses = async (payload, modelHeaders, callerSignal, retryTransient = false) => {
19402
19754
  if (!state.copilotToken) throw new Error("Copilot token not found");
19403
- const enableVision = detectVision(payload.input);
19404
- if (enableVision) assertOutboundImagesOk(payload.model, imagesInResponsesPayload(payload.input));
19755
+ const carriesImages = detectVision(payload.input);
19756
+ let enableVision = carriesImages;
19757
+ /**
19758
+ * Apply a cardinality budget plus the per-image checks, rewriting `payload`
19759
+ * in place of the caller's object. Returns whether anything changed.
19760
+ *
19761
+ * `enableVision` is recomputed here: if nothing survives, sending
19762
+ * `copilot-vision-request: true` would trade one 400 for another.
19763
+ */
19764
+ const applyImagePlan = (maxImages) => {
19765
+ try {
19766
+ const plan = planOutboundImages(payload.model, imagesInResponsesPayload(payload.input), maxImages === void 0 ? void 0 : { maxImages });
19767
+ if (plan.dropped === 0) return false;
19768
+ logImagePlan(payload.model, plan);
19769
+ payload = {
19770
+ ...payload,
19771
+ input: pruneImagesFromResponsesInput(payload.input, plan.verdicts)
19772
+ };
19773
+ enableVision = plan.kept > 0;
19774
+ return true;
19775
+ } catch (error) {
19776
+ consola.warn(`Image pruning skipped for ${payload.model}: ${String(error)}`);
19777
+ return false;
19778
+ }
19779
+ };
19780
+ if (carriesImages) applyImagePlan(learnedImageCeiling(payload.model));
19405
19781
  const isAgentCall = detectAgentCall(payload.input);
19406
19782
  const url = `${copilotBaseUrl(state)}/responses`;
19407
19783
  const doFetch = () => {
@@ -19422,10 +19798,21 @@ const createResponses = async (payload, modelHeaders, callerSignal, retryTransie
19422
19798
  return fetch(url, fetchInit);
19423
19799
  };
19424
19800
  const withRefresh = () => tryRefreshAndRetry(doFetch, "/responses");
19425
- const response = retryTransient ? await fetchWithTransientRetry(withRefresh, {
19801
+ const send = () => retryTransient ? fetchWithTransientRetry(withRefresh, {
19426
19802
  signal: callerSignal,
19427
19803
  label: "/responses"
19428
- }) : await withRefresh();
19804
+ }) : withRefresh();
19805
+ let response = await send();
19806
+ if (!response.ok && carriesImages) {
19807
+ const ceiling = parseUpstreamImageCeiling(await peekErrorBody(response));
19808
+ if (ceiling !== void 0) {
19809
+ rememberImageCeiling(payload.model, ceiling);
19810
+ if (applyImagePlan(ceiling)) {
19811
+ response.body?.cancel();
19812
+ response = await send();
19813
+ }
19814
+ }
19815
+ }
19429
19816
  if (!response.ok) {
19430
19817
  let bodyText;
19431
19818
  try {
@@ -25498,7 +25885,7 @@ function capToolResultText(content, capBytes) {
25498
25885
  * loaded into memory, let alone base64-expanded by a third.
25499
25886
  *
25500
25887
  * The per-model check still runs later at the transport boundary
25501
- * (`assertOutboundImagesOk`); this is the cheap guard, not the authority.
25888
+ * (`planOutboundImages`); this is the cheap guard, not the authority.
25502
25889
  */
25503
25890
  const MAX_ATTACHMENT_BYTES = 3145728;
25504
25891
  const MAX_TOTAL_ATTACHMENT_BYTES = 12582912;
@@ -25522,7 +25909,7 @@ const READ_FLAGS = typeof constants.O_NOFOLLOW === "number" ? constants.O_RDONLY
25522
25909
  async function loadPeerImages(paths, workspace) {
25523
25910
  if (paths.length > 10) return {
25524
25911
  ok: false,
25525
- error: `imagePaths: ${paths.length} paths exceeds the 10-image ceiling (the most any Copilot model accepts). Send fewer.`
25912
+ error: `imagePaths: ${paths.length} paths exceeds the 10-image per-call budget. Send fewer, or split across calls.`
25526
25913
  };
25527
25914
  let workspaceAbs;
25528
25915
  try {
@@ -25857,6 +26244,67 @@ function buildHeaders(extraHeaders) {
25857
26244
  };
25858
26245
  }
25859
26246
  /**
26247
+ * Prune the images in a parsed Anthropic request body down to `ceiling` and
26248
+ * re-serialize. Returns `undefined` when nothing needed dropping.
26249
+ *
26250
+ * Never throws. A latent traversal bug in the pruner must not convert an error
26251
+ * we could have forwarded into a proxy crash — the entire point of this change
26252
+ * is that an image problem stops being fatal.
26253
+ *
26254
+ * This is the one place the proxy re-serializes a `/v1/messages` body it would
26255
+ * otherwise forward byte-for-byte, so it inherits the usual JSON round-trip
26256
+ * caveat for integers beyond 2^53. It runs only when a ceiling is in hand:
26257
+ * either one upstream just stated, or one learned earlier in this process.
26258
+ */
26259
+ function applyCeiling(parsed, ceiling) {
26260
+ const model = typeof parsed.model === "string" ? parsed.model : "";
26261
+ try {
26262
+ const plan = planOutboundImages(model, imagesInAnthropicMessages(parsed.messages), { maxImages: ceiling });
26263
+ if (plan.dropped === 0) return void 0;
26264
+ logImagePlan(model, plan);
26265
+ return JSON.stringify({
26266
+ ...parsed,
26267
+ messages: pruneImagesFromAnthropicMessages(parsed.messages, plan.verdicts)
26268
+ });
26269
+ } catch (error) {
26270
+ consola.warn(`Image pruning skipped for ${model || "unknown model"}: ${String(error)}`);
26271
+ return;
26272
+ }
26273
+ }
26274
+ function parseBody(body) {
26275
+ try {
26276
+ return JSON.parse(body);
26277
+ } catch {
26278
+ return;
26279
+ }
26280
+ }
26281
+ /** Prune to a ceiling this process already learned for the body's own model. */
26282
+ function pruneToLearnedCeiling(body) {
26283
+ const parsed = parseBody(body);
26284
+ if (!parsed) return void 0;
26285
+ const model = typeof parsed.model === "string" ? parsed.model : "";
26286
+ if (model.length === 0) return void 0;
26287
+ const ceiling = learnedImageCeiling(model);
26288
+ return ceiling === void 0 ? void 0 : applyCeiling(parsed, ceiling);
26289
+ }
26290
+ /** Prune to a ceiling upstream just stated, and remember it for next time. */
26291
+ function pruneToStatedCeiling(body, ceiling) {
26292
+ const parsed = parseBody(body);
26293
+ if (!parsed) return void 0;
26294
+ const model = typeof parsed.model === "string" ? parsed.model : "";
26295
+ if (model.length > 0) rememberImageCeiling(model, ceiling);
26296
+ return applyCeiling(parsed, ceiling);
26297
+ }
26298
+ /**
26299
+ * Cheap pre-parse gate for the proactive path only. Whitespace-tolerant: a
26300
+ * client that pretty-prints its JSON still matches. Deliberately NOT used to
26301
+ * gate the recovery path — gating recovery on a string probe means a probe miss
26302
+ * silently restores the fatal 400 this whole mechanism removes.
26303
+ */
26304
+ function mayCarryImages(body) {
26305
+ return /"type"\s*:\s*"image"/.test(body);
26306
+ }
26307
+ /**
25860
26308
  * Forward an Anthropic Messages API request to Copilot's native /v1/messages endpoint.
25861
26309
  * Returns the raw Response so callers can handle streaming vs non-streaming.
25862
26310
  *
@@ -25879,6 +26327,7 @@ async function createMessages(body, extraHeaders, callerSignal, retryTransient =
25879
26327
  if (!state.copilotToken) throw new Error("Copilot token not found");
25880
26328
  const url = `${copilotBaseUrl(state)}/v1/messages?beta=true`;
25881
26329
  consola.debug(`Forwarding to ${url}`);
26330
+ if (hasLearnedImageCeilings() && mayCarryImages(body)) body = pruneToLearnedCeiling(body) ?? body;
25882
26331
  const doFetch = () => {
25883
26332
  const fetchInit = {
25884
26333
  method: "POST",
@@ -25893,10 +26342,22 @@ async function createMessages(body, extraHeaders, callerSignal, retryTransient =
25893
26342
  return fetch(url, fetchInit);
25894
26343
  };
25895
26344
  const withRefresh = () => tryRefreshAndRetry(doFetch, "/v1/messages");
25896
- const response = retryTransient ? await fetchWithTransientRetry(withRefresh, {
26345
+ const send = () => retryTransient ? fetchWithTransientRetry(withRefresh, {
25897
26346
  signal: callerSignal,
25898
26347
  label: "/v1/messages"
25899
- }) : await withRefresh();
26348
+ }) : withRefresh();
26349
+ let response = await send();
26350
+ if (!response.ok) {
26351
+ const ceiling = parseUpstreamImageCeiling(await peekErrorBody(response));
26352
+ if (ceiling !== void 0) {
26353
+ const pruned = pruneToStatedCeiling(body, ceiling);
26354
+ if (pruned !== void 0) {
26355
+ response.body?.cancel();
26356
+ body = pruned;
26357
+ response = await send();
26358
+ }
26359
+ }
26360
+ }
25900
26361
  if (!response.ok) {
25901
26362
  let errorBody;
25902
26363
  try {
@@ -26040,13 +26501,23 @@ function geminiAvailable(source = state) {
26040
26501
  /**
26041
26502
  * First id in `chain` that is present in the live catalog. With
26042
26503
  * `requireToolCalls`, skips an entry whose catalog record does not advertise
26043
- * `tool_calls` (strict `!== true`, so absent metadata fails closed). Returns
26044
- * undefined when the catalog is unavailable or nothing in the chain matches, so
26045
- * every caller degrades gracefully rather than throwing on a thin catalog.
26504
+ * `tool_calls` (strict `!== true`, so absent metadata fails closed). With
26505
+ * `minContextTokens`, skips an entry whose advertised context window is below
26506
+ * that floor (absent metadata likewise fails closed). Returns undefined when
26507
+ * the catalog is unavailable or nothing in the chain matches, so every caller
26508
+ * degrades gracefully rather than throwing on a thin catalog.
26046
26509
  *
26047
26510
  * Extracted from `resolveOpenAiFrontier` so the per-agent resolvers below share
26048
26511
  * one walk instead of hand-copying it. Ids are matched EXACTLY against
26049
26512
  * `catalog.id` — no slug translation, matching the pre-existing behavior.
26513
+ *
26514
+ * `minContextTokens` is OPT-IN because the two constraints are genuinely
26515
+ * per-agent: the `generic*` chains promise 1M end to end, while `scoutModel`
26516
+ * deliberately keeps a 400K last resort for its wider availability. Enforcing
26517
+ * the floor here rather than by comment is what stops a chain silently
26518
+ * degrading when an id's advertised window shrinks upstream — `withOneMSuffix`
26519
+ * would then just omit the `[1m]` bracket, and the agent would be budgeted at
26520
+ * Claude Code's 200K default with no signal that anything changed.
26050
26521
  */
26051
26522
  function firstPresentInCatalog(chain, opts) {
26052
26523
  const models = state.models?.data;
@@ -26055,6 +26526,7 @@ function firstPresentInCatalog(chain, opts) {
26055
26526
  const found = models.find((m) => m.id === id);
26056
26527
  if (!found) continue;
26057
26528
  if (opts?.requireToolCalls && found.capabilities?.supports?.tool_calls !== true) continue;
26529
+ if (opts?.minContextTokens != null && (found.capabilities?.limits?.max_context_window_tokens ?? 0) < opts.minContextTokens) continue;
26058
26530
  return id;
26059
26531
  }
26060
26532
  }
@@ -26134,9 +26606,66 @@ function scribeModel() {
26134
26606
  * caller drop the agent instead, so the lead falls back to the CLI's `Explore`
26135
26607
  * (same behavior as before `scout` existed) rather than to an expensive
26136
26608
  * impostor wearing the cheap agent's name.
26609
+ *
26610
+ * `gpt-5.6-luna` sits between the two originals because the old chain fell
26611
+ * straight from a 1M model to 400K `gpt-5.4-mini`, which loses the `[1m]`
26612
+ * bracket and drops Claude Code's accounting to its 200K default. Luna is
26613
+ * cheaper than mini, keeps 1M, and is cross-vendor from the primary, so it
26614
+ * covers a Gemini-side outage that a same-vendor entry would not.
26615
+ *
26616
+ * Deliberately NO `minContextTokens` floor, unlike the `generic*` resolvers:
26617
+ * `gpt-5.4-mini` is retained as the last resort precisely BECAUSE it is the
26618
+ * widest-availability id here (its `restricted_to` includes `individual_trial`
26619
+ * and `edu`, which neither flash nor luna does). On a thin non-enterprise
26620
+ * catalog a 400K scout beats no scout.
26137
26621
  */
26138
26622
  function scoutModel() {
26139
- return firstPresentInCatalog([EXPLORE_DEFAULT_MODEL, DEFAULT_MODEL], { requireToolCalls: true });
26623
+ return firstPresentInCatalog([
26624
+ EXPLORE_DEFAULT_MODEL,
26625
+ "gpt-5.6-luna",
26626
+ DEFAULT_MODEL
26627
+ ], { requireToolCalls: true });
26628
+ }
26629
+ /** Model for `generic` — the mid-tier catch-all. Absent → the agent is dropped.
26630
+ *
26631
+ * `gpt-5.6-sol` is deliberately NOT in this chain: the OpenAI frontier coder is
26632
+ * already `implementer`'s job, and a catch-all that quietly costs frontier
26633
+ * rates is the opposite of what this agent is for. Both entries are 1M+ and
26634
+ * mid-to-high capability, which is the most the description may claim. */
26635
+ function genericModel() {
26636
+ return firstPresentInCatalog(["gpt-5.6-terra", REVIEW_DEFAULT_MODEL], {
26637
+ requireToolCalls: true,
26638
+ minContextTokens: ONE_M_TOKENS
26639
+ });
26640
+ }
26641
+ /** Model for `generic-fast` — the Gemini flash tier. Absent → dropped.
26642
+ *
26643
+ * Both entries are the same vendor, context, price point and `minimal..high`
26644
+ * effort ladder, so the agent's identity survives the fallback intact. That is
26645
+ * why the fallback is `gemini-3.5-flash` and not `gpt-5.6-luna`, which would
26646
+ * otherwise be the natural cross-vendor choice: luna is `genericCheapModel`'s
26647
+ * only entry, and using it in both places would collapse two roster entries
26648
+ * onto one model in the degraded case. */
26649
+ function genericFastModel() {
26650
+ return firstPresentInCatalog([EXPLORE_DEFAULT_MODEL, "gemini-3.5-flash"], {
26651
+ requireToolCalls: true,
26652
+ minContextTokens: ONE_M_TOKENS
26653
+ });
26654
+ }
26655
+ /** Model for `generic-cheap` — the cheapest catch-all. Absent → dropped.
26656
+ *
26657
+ * Single-entry by design (see `genericFastModel` for why luna is not shared).
26658
+ * `gpt-5.6-luna` is the cheapest id in the catalog and undercuts even the 400K
26659
+ * `gpt-5.4-mini`, while carrying 1.05M context and the full `none..max` effort
26660
+ * ladder — so unlike the flash tier it propagates a CLI effort pick above
26661
+ * `high` rather than clamping it. No `-mini`/`-lite`/`-haiku` model in the
26662
+ * catalog serves 1M, which is why the cheap catch-all is a `gpt-5.6-*` slug
26663
+ * rather than a mini one. */
26664
+ function genericCheapModel() {
26665
+ return firstPresentInCatalog(["gpt-5.6-luna"], {
26666
+ requireToolCalls: true,
26667
+ minContextTokens: ONE_M_TOKENS
26668
+ });
26140
26669
  }
26141
26670
  /**
26142
26671
  * Gate for the worker tools (`explore`, `review`, `implement`).
@@ -26461,7 +26990,7 @@ function toolEntries(scope) {
26461
26990
  imagePaths: {
26462
26991
  type: "array",
26463
26992
  items: { type: "string" },
26464
- description: "Optional paths to image files (screenshots, diagrams, charts) INSIDE the workspace for the persona to LOOK AT. The proxy reads and encodes them, so no image data passes through your context. Content is identified by bytes, not extension; jpeg/png/webp/gif/heic/heif, 3 MiB each. Note paths are confined to the proxy's working directory. Note the per-model ceiling: the gemini-backed personas accept 10 images, the gpt- and opus-backed ones accept 1."
26993
+ description: "Optional paths to image files (screenshots, diagrams, charts) INSIDE the workspace for the persona to LOOK AT. The proxy reads and encodes them, so no image data passes through your context. Content is identified by bytes, not extension; jpeg/png/webp/gif/heic/heif, 3 MiB each and 12 MiB in total, up to 10 per call. Note paths are confined to the proxy's working directory."
26465
26994
  },
26466
26995
  effort: {
26467
26996
  type: "string",
@@ -33382,8 +33911,14 @@ function buildPeerAwarenessSnippet(opts) {
33382
33911
  criticList.push("`opus_critic` (Opus 5)");
33383
33912
  const codexCliClause = opts.codexCli ? " `mcp__codex-cli__codex` dispatches to `codex-implementer` (gpt-5.3-codex with workspace-write) for end-to-end coding tasks." : "";
33384
33913
  const para2Parts = [`\`mcp__${searchKey}__code\` is the one-stop code search (no extra model call). Its DEFAULT mode (or \`mode:"semantic"\`) ranks by MEANING via ColBERT over a per-workspace index, the first thing to reach for on intent/concept questions ("where is retry/backoff handled", "how does auth work"); when that index isn't ready it transparently falls back to lexical (the response \`source\` says which engine ran). Forced modes cover the rest: \`lexical\` (BM25F-ranked + tree-sitter, best for exact symbols), \`exact\`, \`regex\`, \`complete\` (exhaustive set), \`ast_pattern\`+\`ast_lang\` for multi-line AST shapes, \`scan\` for a whole-workspace symbol outline, \`multiline\` for cross-line regex. Multiple queries can run in a single turn. The index covers code-shaped files; for unstructured files (logs, \`.csv\`, \`.env*\`, config-only wiring), \`grep\`/\`glob\` still apply.`];
33385
- if (opts.workerToolsAvailable) para2Parts.push(`\`worker-*\` are background Agent subagents (subagent_type) that run the matching worker in its own context and deliver the result as a completion notification, so a long run never blocks the turn: \`worker-explore\` (read-only research), \`worker-review\` (reads the code to verify a change or claim), \`worker-plan\` (ordered implementation plan), \`worker-implement\` (edit/write/bash; ALWAYS runs in an isolated git worktree and returns the diff via a saved patch file; for in-place edits use the \`implementer\` subagent), \`worker-test\` (independent test author; also always worktree-isolated). The raw \`mcp__${workersKey}__*\` tools they call are guarded (a direct main-thread call is redirected to the matching agent); Workers themselves have \`code_search\`.`);
33386
- para2Parts.push(`Native subagents (Task), each in its own context so heavy work never fills yours: \`implementer\` (you know what to build), \`reviewer\` (something exists and you want it assessed, including reproducing and root-causing a failure), \`brainstorm\` (you do not yet know which approach to take)${opts.scoutAvailable === false ? "" : ", `scout` (find or understand something in the repo, cheap)"}, \`scribe\` (docs and ADRs that trail the code).`);
33914
+ if (opts.workerToolsAvailable) para2Parts.push(`\`worker-*\` are background Agent subagents (subagent_type) that run the matching worker in its own context and deliver the result as a completion notification, so a long run never blocks the turn: \`worker-explore\` (read-only research), \`worker-review\` (reads the code to verify a change or claim), \`worker-plan\` (ordered implementation plan), \`worker-implement\` (edit/write/bash; ALWAYS runs in an isolated git worktree and returns the diff via a saved patch file; for in-place edits use the \`implementer\` subagent), \`worker-test\` (independent test author; also always worktree-isolated)${opts.browseAvailable ? ", `worker-browse` (autonomous browser agent driving a real browser)" : ""}. The raw \`mcp__${workersKey}__*\` tools they call are guarded (a direct main-thread call is redirected to the matching agent); Workers themselves have \`code_search\`.`);
33915
+ const catchAllNames = [
33916
+ opts.genericAvailable === false ? void 0 : "`generic`",
33917
+ opts.genericFastAvailable === false ? void 0 : "`generic-fast`",
33918
+ opts.genericCheapAvailable === false ? void 0 : "`generic-cheap`"
33919
+ ].filter((n) => n != null);
33920
+ const catchAllClause = catchAllNames.length > 0 ? ` Catch-alls on non-lead models, for work no specialist fits, cheapest last: ${catchAllNames.join(", ")}.` : "";
33921
+ para2Parts.push(`Native subagents (Task), each in its own context so heavy work never fills yours: \`implementer\` (you know what to build), \`reviewer\` (something exists and you want it assessed, including reproducing and root-causing a failure), \`brainstorm\` (you do not yet know which approach to take)${opts.scoutAvailable === false ? "" : ", `scout` (find or understand something in the repo, cheap)"}, \`scribe\` (docs and ADRs that trail the code).${catchAllClause}`);
33387
33922
  if (opts.workerToolsAvailable) para2Parts.push(`For a bounded, well-scoped implementation, prefer the \`implementer\` subagent over \`worker-implement\`; reach for \`worker-implement\` only when you specifically need git-worktree isolation, parallel variants, or a throwaway experiment.`);
33388
33923
  if (opts.workerToolsAvailable) para2Parts.push(`\`mcp__${orchestrateKey}__decompose\` composes an open-ended ask into a typed, VERIFIED workflow IR (a strong driver decorrelated by a cross-lab critic, so the decompose step isn't a single point of failure), and \`mcp__${orchestrateKey}__run_workflow\` executes that IR through a frozen kernel delivering max(orchestrated, baseline) over a sealed executable gate, so it never ships worse than a plain single-model run. \`mcp__${orchestrateKey}__verify_workflow\` checks an IR's floor invariants before you run it, and \`mcp__${orchestrateKey}__attest_step\` audits that a finished run's producers were each checked by a different lab. They suit non-trivial, role-separated asks; a trivial ask does not need them.`);
33389
33924
  else para2Parts.push(`\`mcp__${orchestrateKey}__verify_workflow\` statically checks a workflow IR's floor invariants and \`mcp__${orchestrateKey}__attest_step\` audits a run's cross-lab lineage (the \`decompose\`/\`run_workflow\` composer + kernel need the worker backend, unavailable here).`);
@@ -33416,13 +33951,18 @@ function buildPeerAwarenessSnippet(opts) {
33416
33951
  */
33417
33952
  function buildPeerAwarenessSummary(opts) {
33418
33953
  const key = (g) => opts.groupKeys?.[g] ?? GROUP_META[g].preferredKey;
33954
+ const summaryCatchAlls = [
33955
+ opts.genericAvailable === false ? void 0 : "`generic`",
33956
+ opts.genericFastAvailable === false ? void 0 : "`generic-fast`",
33957
+ opts.genericCheapAvailable === false ? void 0 : "`generic-cheap`"
33958
+ ].filter((n) => n != null);
33419
33959
  const lines = [
33420
33960
  "## Injected capabilities (summary)",
33421
33961
  "",
33422
- `Native subagents (Task), each in its own context: \`implementer\` (you know what to build), \`reviewer\` (something exists and you want it assessed, including reproducing and root-causing a failure), \`brainstorm\` (you do not yet know which approach to take), \`scout\` (find or understand something in the repo, cheap), \`scribe\` (docs and ADRs that trail the code). They read the repo and can run things; the peer critics below cannot, so reach for \`reviewer\` when an assessment needs execution or repo context and for a critic when you already hold the artifact.`,
33962
+ `Native subagents (Task), each in its own context: \`implementer\` (you know what to build), \`reviewer\` (something exists and you want it assessed, including reproducing and root-causing a failure), \`brainstorm\` (you do not yet know which approach to take)${opts.scoutAvailable === false ? "" : ", `scout` (find or understand something in the repo, cheap)"}, \`scribe\` (docs and ADRs that trail the code).${summaryCatchAlls.length > 0 ? ` Catch-alls on non-lead models for work no specialist fits, cheapest last: ${summaryCatchAlls.join(", ")}.` : ""} They read the repo and can run things; the peer critics below cannot, so reach for \`reviewer\` when an assessment needs execution or repo context and for a critic when you already hold the artifact.`,
33423
33963
  `A layer of MCP tools, background workers, and skills is injected into this session. Cross-lab peer critics under \`mcp__${key("peers")}__*\` (plus the \`peer-review-coordinator\` subagent) review plans and diffs adversarially, and Claude Code's built-in \`advisor\` catches approach drift. \`mcp__${key("search")}__code\` is meaning-first code search and \`mcp__${key("search")}__web\` returns citable web sources.`
33424
33964
  ];
33425
- if (opts.workerToolsAvailable) lines.push(`Background \`worker-*\` agents (explore, review, plan, implement, test) run delegated work in their own context without blocking your turn, and \`mcp__${key("orchestrate")}__*\` composes, verifies, and runs floor-raising workflows.`);
33965
+ if (opts.workerToolsAvailable) lines.push(`Background \`worker-*\` agents (explore, review, plan, implement, test${opts.browseAvailable ? ", browse" : ""}) run delegated work in their own context without blocking your turn, and \`mcp__${key("orchestrate")}__*\` composes, verifies, and runs floor-raising workflows.`);
33426
33966
  if (opts.standInAvailable) lines.push(`\`mcp__${key("decide")}__stand_in\` returns a three-lab consensus for a decision when the user is unavailable.`);
33427
33967
  if (opts.browseAvailable) lines.push(`\`mcp__${key("browser")}__*\` drives a real Chrome or Edge browser.`);
33428
33968
  if (opts.fleetAvailable) lines.push(`\`mcp__${key("fleet")}__*\` drives remote ai-or-die coding sessions.`);
@@ -34596,6 +35136,6 @@ function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
34596
35136
  return [...new Set(names)];
34597
35137
  }
34598
35138
  //#endregion
34599
- export { browserToolsEnabled as $, searchWeb as A, collapsePathKeys as At, buildAnthropicErrorEvent as B, upstreamAllowH2 as Bt, buildToolbeltAwareness as C, provisionAndIndexColbert as Ct, TOOLBELT_TOOLS$1 as D, CONDENSED_OPERATING_SEQUENCE as Dt, vscodeRipgrepPath as E, warmTreeSitterPool as Et, isAdvisorRequested as F, DEFAULT_PORT as Ft, relayAnthropicStream as G, isControllerClosedError as H, withOneMSuffix as Ht, formatThinkingRepairDecline as I, UPSTREAM_FETCH_TIMEOUT_MS as It, agentToolsEnabled as J, handleMcpDelete as K, rememberThinkingHistoryRepair as L, UPSTREAM_INACTIVITY_TIMEOUT_MS as Lt, ADVISOR_TOOL_INSTRUCTIONS as M, DEFAULT_CLAUDE_MODEL_FALLBACKS as Mt, buildAdvisorStream as N, DEFAULT_CODEX_MODEL as Nt, assetFor as O, DEFINITION_OF_GREATNESS as Ot, injectAdvisorTool as P, DEFAULT_CODEX_MODEL_FALLBACKS as Pt, browserCompoundToolsEnabled as Q, repairKnownThinkingHistory as R, generateRandomPort as Rt, availableToolCommands as S, colbertDegradedWarning as St, toolbeltSkipSet as T, extractZipMember as Tt, logStreamError as U, withInstallLock as Ut, buildOpenAIErrorEvent as V, upstreamMaxConnections as Vt, readIteratorWithTimeout as W, brainstormModel as X, artifactToolsEnabled as Y, browseAgentEnabled as Z, appendPlanReminder as _, MAX_RESPONSE_BODY_BYTES as _t, buildPeerAwarenessSnippet as a, scribeModel as at, runWorkerAgent as b, provisionBrowserAssets as bt, personasFor as c, shimDefaultsToXhigh as ct, EXPLORE_DEFAULT_MODEL as d, getTokenCount as dt, fleetToolsEnabled as et, EXPLORE_DEFAULT_THINKING as f, assembleResponsesPayload as ft, TEST_DEFAULT_MODEL as g, createChatCompletions as gt, REVIEW_DEFAULT_MODEL as h, createResponses as ht, buildAgentPrompt as i, scoutModel as it, ADVISOR_INTERNAL_TOOL_NAME as j, toolbeltPathOverride as jt, satisfiesMinVersion as k, shouldUseInsecureTls as kt, BROWSE_DEFAULT_MODEL as l, countTokens as lt, PLAN_DEFAULT_MODEL as m, pickEndpoint as mt, MCP_GROUPS as n, nativeSubagentModel as nt, buildPeerAwarenessSummary as o, standInToolEnabled as ot, IMPLEMENT_DEFAULT_MODEL as p, resolveMcpToolTimeoutMs as pt, handleMcpPost as q, assertMcpToolSurfaceConsistent as r, reviewerModel as rt, enumerateInjectedMcpToolNames as s, workerToolsEnabled as st, GROUP_META as t, geminiAvailable as tt, DEFAULT_MODEL as u, createMessages as ut, resolveModeDefaults as v, readResponseBodyCapped as vt, toolbeltEnabled as w, extractTarGzMember as wt, buildEnv as x, hasSupportedBrowserInstalled as xt, resolveWorkerRunOpts as y, parseJsonOrDiagnose as yt, repairRejectedThinkingHistory as z, pickClaudeDefault as zt };
35139
+ export { browserToolsEnabled as $, searchWeb as A, CONDENSED_OPERATING_SEQUENCE as At, buildAnthropicErrorEvent as B, UPSTREAM_INACTIVITY_TIMEOUT_MS as Bt, buildToolbeltAwareness as C, provisionBrowserAssets as Ct, TOOLBELT_TOOLS$1 as D, extractTarGzMember as Dt, vscodeRipgrepPath as E, provisionAndIndexColbert as Et, isAdvisorRequested as F, DEFAULT_CLAUDE_MODEL_FALLBACKS as Ft, relayAnthropicStream as G, withOneMSuffix as Gt, isControllerClosedError as H, pickClaudeDefault as Ht, formatThinkingRepairDecline as I, DEFAULT_CODEX_MODEL as It, agentToolsEnabled as J, handleMcpDelete as K, withInstallLock as Kt, rememberThinkingHistoryRepair as L, DEFAULT_CODEX_MODEL_FALLBACKS as Lt, ADVISOR_TOOL_INSTRUCTIONS as M, shouldUseInsecureTls as Mt, buildAdvisorStream as N, collapsePathKeys as Nt, assetFor as O, extractZipMember as Ot, injectAdvisorTool as P, toolbeltPathOverride as Pt, browserCompoundToolsEnabled as Q, repairKnownThinkingHistory as R, DEFAULT_PORT as Rt, availableToolCommands as S, parseJsonOrDiagnose as St, toolbeltSkipSet as T, colbertDegradedWarning as Tt, logStreamError as U, upstreamAllowH2 as Ut, buildOpenAIErrorEvent as V, generateRandomPort as Vt, readIteratorWithTimeout as W, upstreamMaxConnections as Wt, brainstormModel as X, artifactToolsEnabled as Y, browseAgentEnabled as Z, appendPlanReminder as _, pickEndpoint as _t, buildPeerAwarenessSnippet as a, nativeSubagentModel as at, runWorkerAgent as b, MAX_RESPONSE_BODY_BYTES as bt, personasFor as c, scribeModel as ct, EXPLORE_DEFAULT_MODEL as d, shimDefaultsToXhigh as dt, fleetToolsEnabled as et, EXPLORE_DEFAULT_THINKING as f, countTokens as ft, TEST_DEFAULT_MODEL as g, resolveMcpToolTimeoutMs as gt, REVIEW_DEFAULT_MODEL as h, assembleResponsesPayload as ht, buildAgentPrompt as i, genericModel as it, ADVISOR_INTERNAL_TOOL_NAME as j, DEFINITION_OF_GREATNESS as jt, satisfiesMinVersion as k, warmTreeSitterPool as kt, BROWSE_DEFAULT_MODEL as l, standInToolEnabled as lt, PLAN_DEFAULT_MODEL as m, getTokenCount as mt, MCP_GROUPS as n, genericCheapModel as nt, buildPeerAwarenessSummary as o, reviewerModel as ot, IMPLEMENT_DEFAULT_MODEL as p, createMessages as pt, handleMcpPost as q, assertMcpToolSurfaceConsistent as r, genericFastModel as rt, enumerateInjectedMcpToolNames as s, scoutModel as st, GROUP_META as t, geminiAvailable as tt, DEFAULT_MODEL as u, workerToolsEnabled as ut, resolveModeDefaults as v, createResponses as vt, toolbeltEnabled as w, hasSupportedBrowserInstalled as wt, buildEnv as x, readResponseBodyCapped as xt, resolveWorkerRunOpts as y, createChatCompletions as yt, repairRejectedThinkingHistory as z, UPSTREAM_FETCH_TIMEOUT_MS as zt };
34600
35140
 
34601
- //# sourceMappingURL=peer-mcp-personas-pzk1zcDa.js.map
35141
+ //# sourceMappingURL=peer-mcp-personas-DRL_xT4h.js.map