@hsupu/copilot-api 0.8.2 → 0.8.3

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.
@@ -129,8 +129,12 @@ anthropic:
129
129
  # Inject Copilot tool_search helper when the model supports it.
130
130
  tool_search: true
131
131
 
132
- # Inject cache_control breakpoints on stable tools/system blocks for prompt caching.
133
- auto_cache_control: true
132
+ # Cache control mode for prompt caching.
133
+ # disabled: strip all cache_control fields (no caching)
134
+ # passthrough: forward client cache_control as-is
135
+ # sanitize: forward but normalize to { type: "ephemeral" } (strip non-standard fields like scope)
136
+ # proxied: proxy controls injection — auto-add breakpoints on tools/system (default)
137
+ cache_control: proxied
134
138
 
135
139
  # Additional tool names that should never be deferred when tool_search is enabled.
136
140
  # non_deferred_tools:
package/dist/main.mjs CHANGED
@@ -98,7 +98,7 @@ const CONFIG_MANAGED_DEFAULTS = {
98
98
  contextEditingKeepTools: 3,
99
99
  contextEditingKeepThinking: 1,
100
100
  toolSearchEnabled: true,
101
- autoCacheControl: true,
101
+ cacheControlMode: "proxied",
102
102
  nonDeferredTools: [],
103
103
  rewriteSystemReminders: false,
104
104
  systemPromptOverrides: [],
@@ -125,7 +125,7 @@ function resetConfigManagedState() {
125
125
  contextEditingKeepTools: CONFIG_MANAGED_DEFAULTS.contextEditingKeepTools,
126
126
  contextEditingKeepThinking: CONFIG_MANAGED_DEFAULTS.contextEditingKeepThinking,
127
127
  toolSearchEnabled: CONFIG_MANAGED_DEFAULTS.toolSearchEnabled,
128
- autoCacheControl: CONFIG_MANAGED_DEFAULTS.autoCacheControl,
128
+ cacheControlMode: CONFIG_MANAGED_DEFAULTS.cacheControlMode,
129
129
  nonDeferredTools: [...CONFIG_MANAGED_DEFAULTS.nonDeferredTools],
130
130
  rewriteSystemReminders: CONFIG_MANAGED_DEFAULTS.rewriteSystemReminders,
131
131
  systemPromptOverrides: [...CONFIG_MANAGED_DEFAULTS.systemPromptOverrides],
@@ -161,7 +161,7 @@ const mutableState = {
161
161
  contextEditingKeepTools: CONFIG_MANAGED_DEFAULTS.contextEditingKeepTools,
162
162
  contextEditingKeepThinking: CONFIG_MANAGED_DEFAULTS.contextEditingKeepThinking,
163
163
  toolSearchEnabled: CONFIG_MANAGED_DEFAULTS.toolSearchEnabled,
164
- autoCacheControl: CONFIG_MANAGED_DEFAULTS.autoCacheControl,
164
+ cacheControlMode: CONFIG_MANAGED_DEFAULTS.cacheControlMode,
165
165
  nonDeferredTools: [...CONFIG_MANAGED_DEFAULTS.nonDeferredTools],
166
166
  stripServerTools: CONFIG_MANAGED_DEFAULTS.stripServerTools,
167
167
  immutableThinkingMessages: CONFIG_MANAGED_DEFAULTS.immutableThinkingMessages,
@@ -1766,7 +1766,12 @@ async function applyConfigToState() {
1766
1766
  if (a.context_editing_keep_tools !== void 0) setAnthropicBehavior({ contextEditingKeepTools: a.context_editing_keep_tools });
1767
1767
  if (a.context_editing_keep_thinking !== void 0) setAnthropicBehavior({ contextEditingKeepThinking: a.context_editing_keep_thinking });
1768
1768
  if (a.tool_search !== void 0) setAnthropicBehavior({ toolSearchEnabled: a.tool_search });
1769
- if (a.auto_cache_control !== void 0) setAnthropicBehavior({ autoCacheControl: a.auto_cache_control });
1769
+ if (a.cache_control !== void 0) setAnthropicBehavior({ cacheControlMode: a.cache_control });
1770
+ else if (a.auto_cache_control !== void 0) {
1771
+ const mapped = a.auto_cache_control ? "proxied" : "disabled";
1772
+ consola.warn(`[Config] anthropic.auto_cache_control is deprecated, use cache_control: "${mapped}" instead`);
1773
+ setAnthropicBehavior({ cacheControlMode: mapped });
1774
+ }
1770
1775
  if (Array.isArray(a.non_deferred_tools)) setAnthropicBehavior({ nonDeferredTools: a.non_deferred_tools });
1771
1776
  if (a.rewrite_system_reminders !== void 0) {
1772
1777
  if (typeof a.rewrite_system_reminders === "boolean") setAnthropicBehavior({ rewriteSystemReminders: a.rewrite_system_reminders });
@@ -4709,20 +4714,29 @@ function finalize(tracker) {
4709
4714
  function handleShutdownSignal(signal, opts) {
4710
4715
  const shutdownFn = opts?.gracefulShutdownFn ?? ((shutdownSignal) => gracefulShutdown(shutdownSignal));
4711
4716
  const exitFn = opts?.exitFn ?? ((code) => process.exit(code));
4712
- if (_isShuttingDown) {
4713
- if (shutdownPhase === "phase2") {
4717
+ if (_isShuttingDown) switch (shutdownPhase) {
4718
+ case "phase1":
4719
+ consola.warn("Signal received during Phase 1 setup, waiting for shutdown to proceed");
4720
+ return shutdownPromise ?? void 0;
4721
+ case "phase2":
4714
4722
  consola.warn("Second signal received, escalating shutdown to abort active requests");
4715
4723
  shutdownDrainAbortController?.abort();
4716
4724
  return shutdownPromise ?? void 0;
4717
- }
4718
- if (shutdownPhase === "phase3") {
4725
+ case "phase3":
4719
4726
  consola.warn("Additional signal received, escalating shutdown to force-close remaining requests");
4720
4727
  shutdownDrainAbortController?.abort();
4721
4728
  return shutdownPromise ?? void 0;
4722
- }
4723
- consola.warn("Additional signal received during forced shutdown, exiting immediately");
4724
- exitFn(1);
4725
- return shutdownPromise ?? void 0;
4729
+ case "phase4":
4730
+ consola.warn("Additional signal received during forced shutdown, exiting immediately");
4731
+ exitFn(1);
4732
+ return shutdownPromise ?? void 0;
4733
+ case "finalized":
4734
+ consola.info("Signal received after shutdown finalized, ignoring");
4735
+ return shutdownPromise ?? void 0;
4736
+ default:
4737
+ consola.warn("Signal received in unexpected shutdown phase, exiting immediately");
4738
+ exitFn(1);
4739
+ return shutdownPromise ?? void 0;
4726
4740
  }
4727
4741
  shutdownPromise = shutdownFn(signal).catch((error) => {
4728
4742
  consola.error("Fatal error during shutdown:", error);
@@ -5500,7 +5514,7 @@ const setupClaudeCode = defineCommand({
5500
5514
  });
5501
5515
  //#endregion
5502
5516
  //#region package.json
5503
- var version = "0.8.2";
5517
+ var version = "0.8.3";
5504
5518
  //#endregion
5505
5519
  //#region src/lib/system-prompt/override.ts
5506
5520
  /**
@@ -9124,7 +9138,7 @@ configRoutes.get("/", (c) => {
9124
9138
  contextEditingKeepTools: state.contextEditingKeepTools,
9125
9139
  contextEditingKeepThinking: state.contextEditingKeepThinking,
9126
9140
  toolSearchEnabled: state.toolSearchEnabled,
9127
- autoCacheControl: state.autoCacheControl,
9141
+ cacheControlMode: state.cacheControlMode,
9128
9142
  nonDeferredTools: state.nonDeferredTools,
9129
9143
  rewriteSystemReminders: serializeRewriteSystemReminders(state.rewriteSystemReminders),
9130
9144
  stripReadToolResultTags: state.stripReadToolResultTags,
@@ -9227,6 +9241,7 @@ const ANTHROPIC_KEYS = new Set([
9227
9241
  "context_editing_keep_tools",
9228
9242
  "context_editing_keep_thinking",
9229
9243
  "tool_search",
9244
+ "cache_control",
9230
9245
  "auto_cache_control",
9231
9246
  "non_deferred_tools",
9232
9247
  "rewrite_system_reminders"
@@ -9306,7 +9321,20 @@ function validateAnthropic(value, details) {
9306
9321
  if (hasOwn(value, "context_editing_keep_tools")) validateNonNegativeInteger(value.context_editing_keep_tools, "anthropic.context_editing_keep_tools", details);
9307
9322
  if (hasOwn(value, "context_editing_keep_thinking")) validateNonNegativeInteger(value.context_editing_keep_thinking, "anthropic.context_editing_keep_thinking", details);
9308
9323
  if (hasOwn(value, "tool_search")) validateBoolean(value.tool_search, "anthropic.tool_search", details);
9309
- if (hasOwn(value, "auto_cache_control")) validateBoolean(value.auto_cache_control, "anthropic.auto_cache_control", details);
9324
+ if (hasOwn(value, "cache_control")) {
9325
+ const valid = [
9326
+ "disabled",
9327
+ "passthrough",
9328
+ "sanitize",
9329
+ "proxied"
9330
+ ];
9331
+ if (!valid.includes(value.cache_control)) details.push({
9332
+ field: "anthropic.cache_control",
9333
+ message: `Must be one of: ${valid.join(", ")}`,
9334
+ value: value.cache_control
9335
+ });
9336
+ }
9337
+ if (hasOwn(value, "auto_cache_control")) validateBoolean(value.auto_cache_control, "anthropic.auto_cache_control (deprecated)", details);
9310
9338
  if (hasOwn(value, "non_deferred_tools")) validateStringArray(value.non_deferred_tools, "anthropic.non_deferred_tools", details);
9311
9339
  if (hasOwn(value, "rewrite_system_reminders")) {
9312
9340
  const rewrite = value.rewrite_system_reminders;
@@ -11255,7 +11283,7 @@ const EPHEMERAL_CACHE_CONTROL = { type: "ephemeral" };
11255
11283
  function prepareAnthropicRequest(payload, opts) {
11256
11284
  const wire = buildWirePayload(payload);
11257
11285
  adjustThinkingBudget(wire, opts?.resolvedModel);
11258
- addToolsAndSystemCacheControl(wire);
11286
+ applyCacheControlMode(wire);
11259
11287
  const model = wire.model;
11260
11288
  const messages = wire.messages;
11261
11289
  const thinking = wire.thinking;
@@ -11316,8 +11344,29 @@ function adjustThinkingBudget(wire, resolvedModel) {
11316
11344
  consola.debug(`[DirectAnthropic] Capped thinking.budget_tokens: ${budgetTokens} → ${adjusted} (max_tokens=${maxTokens})`);
11317
11345
  }
11318
11346
  }
11347
+ /**
11348
+ * Dispatch cache_control handling based on the configured mode.
11349
+ * - disabled: strip all cache_control from the wire payload
11350
+ * - passthrough: leave everything as-is
11351
+ * - sanitize: normalize all cache_control to { type: "ephemeral" }
11352
+ * - proxied: strip client cache_control then auto-inject breakpoints
11353
+ */
11354
+ function applyCacheControlMode(wire) {
11355
+ switch (state.cacheControlMode) {
11356
+ case "disabled":
11357
+ walkCacheControl(wire, () => void 0);
11358
+ break;
11359
+ case "passthrough": break;
11360
+ case "sanitize":
11361
+ walkCacheControl(wire, () => EPHEMERAL_CACHE_CONTROL);
11362
+ break;
11363
+ case "proxied":
11364
+ walkCacheControl(wire, () => void 0);
11365
+ addToolsAndSystemCacheControl(wire);
11366
+ break;
11367
+ }
11368
+ }
11319
11369
  function addToolsAndSystemCacheControl(wire) {
11320
- if (!state.autoCacheControl) return;
11321
11370
  let remaining = CACHE_CONTROL_BREAKPOINT_LIMIT - countExistingCacheBreakpoints(wire);
11322
11371
  if (remaining <= 0) return;
11323
11372
  const toolResult = addToolCacheControl(wire.tools, remaining);
@@ -11387,6 +11436,30 @@ function findLastIndex(items, predicate) {
11387
11436
  for (let index = items.length - 1; index >= 0; index--) if (predicate(items[index])) return index;
11388
11437
  return -1;
11389
11438
  }
11439
+ /**
11440
+ * Walk all cache_control occurrences in the wire payload (system, messages, tools)
11441
+ * and apply a handler. The handler receives the existing cache_control value and returns:
11442
+ * - undefined: delete the cache_control field
11443
+ * - an object: replace the cache_control field with this value
11444
+ */
11445
+ function walkCacheControl(wire, handler) {
11446
+ for (const key of [
11447
+ "system",
11448
+ "messages",
11449
+ "tools"
11450
+ ]) if (Array.isArray(wire[key])) walkCacheControlArray(wire[key], handler);
11451
+ }
11452
+ function walkCacheControlArray(items, handler) {
11453
+ for (const item of items) {
11454
+ if (!item || typeof item !== "object") continue;
11455
+ if ("cache_control" in item && item.cache_control) {
11456
+ const replacement = handler(item.cache_control);
11457
+ if (replacement === void 0) delete item.cache_control;
11458
+ else item.cache_control = replacement;
11459
+ }
11460
+ if (Array.isArray(item.content)) walkCacheControlArray(item.content, handler);
11461
+ }
11462
+ }
11390
11463
  //#endregion
11391
11464
  //#region src/lib/anthropic/client.ts
11392
11465
  /**