github-router 0.3.21 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -2344,7 +2344,7 @@ function initProxyFromEnv() {
2344
2344
  //#endregion
2345
2345
  //#region package.json
2346
2346
  var name = "github-router";
2347
- var version = "0.3.21";
2347
+ var version = "0.3.22";
2348
2348
 
2349
2349
  //#endregion
2350
2350
  //#region src/lib/approval.ts
@@ -2455,6 +2455,50 @@ function detectCapabilityMismatch(info, model) {
2455
2455
  const err = info.errorBody.toLowerCase();
2456
2456
  return err.includes("token") || err.includes("context") || err.includes("too long") || err.includes("max_tokens") || err.includes("prompt is too long");
2457
2457
  }
2458
+ /**
2459
+ * Opt-in instrumentation for the discovery loop (Phase 0.5 of the
2460
+ * long-horizon plan). When `GH_ROUTER_LOG_FIELDS=1` is set in the
2461
+ * environment, emits a single structured `[fields]` log line per request
2462
+ * recording the top-level body keys, per-tool field keys, and
2463
+ * anthropic-beta header values seen.
2464
+ *
2465
+ * Default-off (zero overhead). The companion
2466
+ * `scripts/discover-new-fields.sh` greps these lines, aggregates unique
2467
+ * field names per request shape, and diffs against the known-fields
2468
+ * list in `docs/copilot-compat-matrix.md` — surfacing anything new
2469
+ * that should get a probe row added.
2470
+ *
2471
+ * Format (single line, deterministic-ish key order):
2472
+ * [fields] path=<P> body_keys=<csv> tool_field_keys=<csv> beta_values=<csv>
2473
+ *
2474
+ * Where:
2475
+ * - `body_keys` is the alphabetical union of top-level keys in the
2476
+ * request body
2477
+ * - `tool_field_keys` is the alphabetical union of all keys appearing
2478
+ * across every entry of `body.tools[]` (or empty)
2479
+ * - `beta_values` is the comma-split anthropic-beta header value as
2480
+ * received (NOT filtered) — captures what the client sends, not
2481
+ * what we forward
2482
+ */
2483
+ function logRequestFields(opts) {
2484
+ if (process.env.GH_ROUTER_LOG_FIELDS !== "1") return;
2485
+ const bodyKeys = collectTopLevelKeys(opts.body);
2486
+ const toolFieldKeys = collectToolFieldKeys(opts.body);
2487
+ const betaValues = (opts.betaHeader ?? "").split(",").map((v) => v.trim()).filter(Boolean);
2488
+ consola.info(`[fields] path=${opts.path} body_keys=${bodyKeys.join(",")} tool_field_keys=${toolFieldKeys.join(",")} beta_values=${betaValues.join(",")}`);
2489
+ }
2490
+ function collectTopLevelKeys(body) {
2491
+ if (!body || typeof body !== "object" || Array.isArray(body)) return [];
2492
+ return Object.keys(body).sort();
2493
+ }
2494
+ function collectToolFieldKeys(body) {
2495
+ if (!body || typeof body !== "object") return [];
2496
+ const tools = body.tools;
2497
+ if (!Array.isArray(tools)) return [];
2498
+ const seen = /* @__PURE__ */ new Set();
2499
+ for (const tool of tools) if (tool && typeof tool === "object" && !Array.isArray(tool)) for (const k of Object.keys(tool)) seen.add(k);
2500
+ return [...seen].sort();
2501
+ }
2458
2502
 
2459
2503
  //#endregion
2460
2504
  //#region src/lib/stream-relay.ts
@@ -4747,7 +4791,7 @@ function resolveModelInBody$1(rawBody) {
4747
4791
  }
4748
4792
  }
4749
4793
  if (rawBody.includes("\"scope\"") && sanitizeCacheControl$1(parsed)) modified = true;
4750
- if ((rawBody.includes("\"budget\"") || rawBody.includes("\"output_config\"") || rawBody.includes("\"betas\"")) && stripAnthropicOnlyFields$1(parsed)) modified = true;
4794
+ if ((rawBody.includes("\"budget\"") || rawBody.includes("\"output_config\"") || rawBody.includes("\"betas\"") || rawBody.includes("\"eager_input_streaming\"")) && stripAnthropicOnlyFields$1(parsed)) modified = true;
4751
4795
  const resolvedModel = typeof parsed.model === "string" ? parsed.model : originalModel;
4752
4796
  return {
4753
4797
  body: modified ? JSON.stringify(parsed) : rawBody,
@@ -4809,6 +4853,20 @@ function stripAnthropicOnlyFields$1(body) {
4809
4853
  delete body.betas;
4810
4854
  stripped = true;
4811
4855
  }
4856
+ if (Array.isArray(body.tools)) {
4857
+ let warnedFGTS = false;
4858
+ for (const tool of body.tools) if (typeof tool === "object" && tool !== null) {
4859
+ const t = tool;
4860
+ if (t.eager_input_streaming !== void 0) {
4861
+ delete t.eager_input_streaming;
4862
+ stripped = true;
4863
+ if (!warnedFGTS) {
4864
+ consola.warn("[count_tokens] Stripping per-tool `eager_input_streaming` (Copilot 400s on `tools.*.custom.eager_input_streaming`)");
4865
+ warnedFGTS = true;
4866
+ }
4867
+ }
4868
+ }
4869
+ }
4812
4870
  return stripped;
4813
4871
  }
4814
4872
 
@@ -4919,6 +4977,17 @@ async function handleCompletion(c) {
4919
4977
  const rawBody = await c.req.text();
4920
4978
  const debugEnabled = consola.level >= 4;
4921
4979
  if (debugEnabled) consola.debug("Anthropic request body:", rawBody.slice(0, 2e3));
4980
+ if (process.env.GH_ROUTER_LOG_FIELDS === "1") {
4981
+ let parsedForLog = void 0;
4982
+ try {
4983
+ parsedForLog = JSON.parse(rawBody);
4984
+ } catch {}
4985
+ logRequestFields({
4986
+ path: c.req.path,
4987
+ body: parsedForLog,
4988
+ betaHeader: c.req.header("anthropic-beta")
4989
+ });
4990
+ }
4922
4991
  if (state.manualApprove) await awaitApproval();
4923
4992
  const betaHeaders = extractBetaHeaders(c);
4924
4993
  const advisorEnabled = isAdvisorRequested(c.req.header("anthropic-beta"));
@@ -5061,7 +5130,7 @@ function resolveModelInBody(rawBody) {
5061
5130
  const selectedModel = resolvedModel ? state.models?.data.find((m) => m.id === resolvedModel) : void 0;
5062
5131
  if (translateThinking(parsed, selectedModel)) modified = true;
5063
5132
  if (rawBody.includes("\"scope\"") && sanitizeCacheControl(parsed)) modified = true;
5064
- if ((rawBody.includes("\"budget\"") || rawBody.includes("\"output_config\"") || rawBody.includes("\"betas\"")) && stripAnthropicOnlyFields(parsed)) modified = true;
5133
+ if ((rawBody.includes("\"budget\"") || rawBody.includes("\"output_config\"") || rawBody.includes("\"betas\"") || rawBody.includes("\"eager_input_streaming\"")) && stripAnthropicOnlyFields(parsed)) modified = true;
5065
5134
  return {
5066
5135
  body: modified ? JSON.stringify(parsed) : rawBody,
5067
5136
  originalModel,
@@ -5230,6 +5299,20 @@ function stripAnthropicOnlyFields(body) {
5230
5299
  delete body.betas;
5231
5300
  stripped = true;
5232
5301
  }
5302
+ if (Array.isArray(body.tools)) {
5303
+ let warnedFGTS = false;
5304
+ for (const tool of body.tools) if (typeof tool === "object" && tool !== null) {
5305
+ const t = tool;
5306
+ if (t.eager_input_streaming !== void 0) {
5307
+ delete t.eager_input_streaming;
5308
+ stripped = true;
5309
+ if (!warnedFGTS) {
5310
+ consola.warn("Stripping per-tool `eager_input_streaming` field (Copilot 400s on `tools.*.custom.eager_input_streaming`; FGTS chunk-size optimization disabled, but streaming correctness is unaffected — `input_json_delta` events still flow normally)");
5311
+ warnedFGTS = true;
5312
+ }
5313
+ }
5314
+ }
5315
+ }
5233
5316
  return stripped;
5234
5317
  }
5235
5318
  /**