headroom-openclaw 0.40.0 → 0.41.0

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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Context compression plugin for [OpenClaw](https://github.com/openclaw/openclaw). Compresses tool outputs, code, logs, and structured data — 70-90% token savings with zero LLM calls.
4
4
 
5
+ Tool-call normalization preserves provider metadata, including thought signatures, when restoring assistant turns after compression.
6
+
5
7
  ## Install
6
8
 
7
9
  Recommended one-command setup:
@@ -210,6 +212,7 @@ Compression is lossless via CCR (Compress-Cache-Retrieve): originals are stored
210
212
  | `circuitBreakerCooldownMs` | `60000` | How long (ms) the circuit breaker stays open after the threshold is reached. After the cool-down the breaker resets automatically and the next request re-probes the proxy via `/health`. |
211
213
  | `routeCodexViaProxy` | `true` | Rewrite OpenClaw's built-in `openai-codex` provider to use the active Headroom proxy in memory so upstream Codex requests pass through Headroom. |
212
214
  | `gatewayProviderIds` | `[]` | Optional explicit list of OpenClaw provider ids to route through the active Headroom proxy in memory. Friendly aliases `codex`, `claude`, `copilot`, and `gemini` are also accepted. When set, this overrides the default `openai-codex` routing list. |
215
+ | `announceCompression` | `true` | Adds a static, cache-stable notice to the system prompt the first time a session compresses more than 100 tokens, then keeps it unchanged for the rest of the session. Set to `false` to guarantee Headroom never modifies the system prompt. |
213
216
 
214
217
  ## Comparison with lossless-claw
215
218
 
package/dist/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Context compression plugin for [OpenClaw](https://github.com/openclaw/openclaw). Compresses tool outputs, code, logs, and structured data — 70-90% token savings with zero LLM calls.
4
4
 
5
+ Tool-call normalization preserves provider metadata, including thought signatures, when restoring assistant turns after compression.
6
+
5
7
  ## Install
6
8
 
7
9
  Recommended one-command setup:
@@ -210,6 +212,7 @@ Compression is lossless via CCR (Compress-Cache-Retrieve): originals are stored
210
212
  | `circuitBreakerCooldownMs` | `60000` | How long (ms) the circuit breaker stays open after the threshold is reached. After the cool-down the breaker resets automatically and the next request re-probes the proxy via `/health`. |
211
213
  | `routeCodexViaProxy` | `true` | Rewrite OpenClaw's built-in `openai-codex` provider to use the active Headroom proxy in memory so upstream Codex requests pass through Headroom. |
212
214
  | `gatewayProviderIds` | `[]` | Optional explicit list of OpenClaw provider ids to route through the active Headroom proxy in memory. Friendly aliases `codex`, `claude`, `copilot`, and `gemini` are also accepted. When set, this overrides the default `openai-codex` routing list. |
215
+ | `announceCompression` | `true` | Adds a static, cache-stable notice to the system prompt the first time a session compresses more than 100 tokens, then keeps it unchanged for the rest of the session. Set to `false` to guarantee Headroom never modifies the system prompt. |
213
216
 
214
217
  ## Comparison with lossless-claw
215
218
 
package/dist/index.d.ts CHANGED
@@ -123,6 +123,7 @@ interface HeadroomEngineConfig extends ProxyManagerConfig {
123
123
  /** Where to durably record committed turn-advancement keys (see
124
124
  * `DurableAdvancementKeyStore`). Defaults to `defaultCommitLogPath()`. */
125
125
  commitLogPath?: string;
126
+ announceCompression?: boolean;
126
127
  }
127
128
  declare class HeadroomContextEngine {
128
129
  readonly info: {
@@ -145,6 +146,8 @@ declare class HeadroomContextEngine {
145
146
  private proxyStartupError;
146
147
  private stats;
147
148
  private circuit;
149
+ private announcedSessions;
150
+ private static readonly MAX_ANNOUNCED_SESSIONS;
148
151
  constructor(config?: HeadroomEngineConfig, logger?: ProxyManagerLogger);
149
152
  bootstrap(params: {
150
153
  sessionId: string;
@@ -233,6 +236,10 @@ declare class HeadroomContextEngine {
233
236
  private isCircuitOpen;
234
237
  private tripCircuit;
235
238
  private resetCircuit;
239
+ private hasAnnouncedCompression;
240
+ private markCompressionAnnounced;
241
+ /** The sticky notice if this session already earned it and announcements aren't opted out. */
242
+ private sessionCompressionNotice;
236
243
  ensureProxyStarted(): void;
237
244
  onProxyReady(listener: (proxyUrl: string) => void | Promise<void>): () => void;
238
245
  ensureProxyUrl(): Promise<string>;
package/dist/index.js CHANGED
@@ -713,8 +713,10 @@ function normalizeAssistantContent(content) {
713
713
  if (block.type === "text" && typeof block.text === "string") return [block];
714
714
  if (block.type === "thinking" && typeof block.thinking === "string") return [block];
715
715
  if ((block.type === "toolCall" || block.type === "tool_use") && typeof block.name === "string") {
716
+ const { input: _legacyInput, ...metadata } = block;
716
717
  return [
717
718
  {
719
+ ...block.type === "tool_use" ? metadata : block,
718
720
  type: "toolCall",
719
721
  id: typeof block.id === "string" ? block.id : "unknown",
720
722
  name: block.name,
@@ -925,7 +927,8 @@ function withTimeout(promise, ms) {
925
927
  if (timerId !== void 0) clearTimeout(timerId);
926
928
  });
927
929
  }
928
- var HeadroomContextEngine = class {
930
+ var HEADROOM_COMPRESSION_NOTICE = "[Headroom is compressing tool outputs in this session. Use headroom_retrieve if you need the original, uncompressed content.]";
931
+ var HeadroomContextEngine = class _HeadroomContextEngine {
929
932
  info = {
930
933
  id: "headroom",
931
934
  name: "Headroom Context Compression",
@@ -954,6 +957,10 @@ var HeadroomContextEngine = class {
954
957
  compactions: 0
955
958
  };
956
959
  circuit = { errors: 0, openUntilMs: 0 };
960
+ // Sessions shown the compression notice. Bounded via insertion-order
961
+ // eviction — no session lifecycle callback exists here to bound it otherwise.
962
+ announcedSessions = /* @__PURE__ */ new Set();
963
+ static MAX_ANNOUNCED_SESSIONS = 1e3;
957
964
  constructor(config = {}, logger) {
958
965
  this.config = config;
959
966
  this.logger = logger ?? defaultLogger;
@@ -986,11 +993,19 @@ var HeadroomContextEngine = class {
986
993
  async assemble(params) {
987
994
  if (!this.proxyUrl || this.config.enabled === false) {
988
995
  this.ensureProxyStarted();
989
- return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };
996
+ return {
997
+ messages: normalizeAgentMessages(params.messages),
998
+ estimatedTokens: 0,
999
+ systemPromptAddition: this.sessionCompressionNotice(params.sessionId)
1000
+ };
990
1001
  }
991
1002
  if (this.isCircuitOpen()) {
992
1003
  this.logger.warn("[headroom] Circuit open \u2014 using uncompressed messages");
993
- return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };
1004
+ return {
1005
+ messages: normalizeAgentMessages(params.messages),
1006
+ estimatedTokens: 0,
1007
+ systemPromptAddition: this.sessionCompressionNotice(params.sessionId)
1008
+ };
994
1009
  }
995
1010
  try {
996
1011
  const openaiMessages = agentToOpenAIIndexed(params.messages);
@@ -1003,11 +1018,16 @@ var HeadroomContextEngine = class {
1003
1018
  }),
1004
1019
  this.config.requestTimeoutMs ?? 3e4
1005
1020
  );
1021
+ const shouldAnnounceCompression = (this.hasAnnouncedCompression(params.sessionId) || result.tokensSaved > 100) && this.config.announceCompression !== false;
1022
+ if (shouldAnnounceCompression) {
1023
+ this.markCompressionAnnounced(params.sessionId);
1024
+ }
1006
1025
  if (!result.compressed || result.tokensSaved === 0) {
1007
1026
  this.resetCircuit();
1008
1027
  return {
1009
1028
  messages: normalizeAgentMessages(params.messages),
1010
- estimatedTokens: result.tokensBefore
1029
+ estimatedTokens: result.tokensBefore,
1030
+ systemPromptAddition: this.sessionCompressionNotice(params.sessionId)
1011
1031
  };
1012
1032
  }
1013
1033
  const compressedAgentMessages = restoreAgentMessages(params.messages, openaiMessages, result.messages);
@@ -1021,12 +1041,16 @@ var HeadroomContextEngine = class {
1021
1041
  return {
1022
1042
  messages: compressedAgentMessages,
1023
1043
  estimatedTokens: result.tokensAfter,
1024
- systemPromptAddition: result.tokensSaved > 100 ? `[Context compressed by Headroom: ${result.tokensSaved} tokens saved. Use headroom_retrieve with the hash to get full details.]` : void 0
1044
+ systemPromptAddition: this.sessionCompressionNotice(params.sessionId)
1025
1045
  };
1026
1046
  } catch (error) {
1027
1047
  this.logger.error(`Assemble failed: ${error}`);
1028
1048
  this.tripCircuit(error);
1029
- return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };
1049
+ return {
1050
+ messages: normalizeAgentMessages(params.messages),
1051
+ estimatedTokens: 0,
1052
+ systemPromptAddition: this.sessionCompressionNotice(params.sessionId)
1053
+ };
1030
1054
  }
1031
1055
  }
1032
1056
  /** Delegate persistent compaction to OpenClaw's built-in runtime. */
@@ -1097,6 +1121,21 @@ var HeadroomContextEngine = class {
1097
1121
  resetCircuit() {
1098
1122
  this.circuit = { errors: 0, openUntilMs: 0 };
1099
1123
  }
1124
+ hasAnnouncedCompression(sessionId) {
1125
+ return this.announcedSessions.has(sessionId);
1126
+ }
1127
+ markCompressionAnnounced(sessionId) {
1128
+ if (this.announcedSessions.has(sessionId)) return;
1129
+ if (this.announcedSessions.size >= _HeadroomContextEngine.MAX_ANNOUNCED_SESSIONS) {
1130
+ const oldest = this.announcedSessions.values().next().value;
1131
+ if (oldest !== void 0) this.announcedSessions.delete(oldest);
1132
+ }
1133
+ this.announcedSessions.add(sessionId);
1134
+ }
1135
+ /** The sticky notice if this session already earned it and announcements aren't opted out. */
1136
+ sessionCompressionNotice(sessionId) {
1137
+ return this.hasAnnouncedCompression(sessionId) && this.config.announceCompression !== false ? HEADROOM_COMPRESSION_NOTICE : void 0;
1138
+ }
1100
1139
  ensureProxyStarted() {
1101
1140
  if (this.config.enabled === false || this.proxyUrl || this.proxyStartupPromise) {
1102
1141
  return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine.ts","../src/proxy-manager.ts","../src/convert.ts","../src/advancement-key-store.ts","../src/openclaw-compaction.ts","../src/gateway-config.ts","../src/tools/headroom-retrieve.ts","../src/plugin/index.ts"],"sourcesContent":["/**\n * HeadroomContextEngine — ContextEngine implementation for OpenClaw.\n *\n * Compresses tool outputs and conversation context using the Headroom proxy.\n * Zero LLM calls — all compression is algorithmic (SmartCrusher, ContentRouter, etc.)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { compress } from \"headroom-ai\";\nimport { ProxyManager, defaultLogger, type ProxyManagerConfig, type ProxyManagerLogger } from \"./proxy-manager.js\";\nimport { agentToOpenAIIndexed, normalizeAgentMessages, restoreAgentMessages } from \"./convert.js\";\nimport { DurableAdvancementKeyStore, defaultCommitLogPath } from \"./advancement-key-store.js\";\nimport {\n delegateCompactionToRuntime,\n type OpenClawCompactParams,\n type OpenClawCompactResult,\n} from \"./openclaw-compaction.js\";\n\n/** Race a promise against a timeout and always release the timer. */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timerId: ReturnType<typeof setTimeout> | undefined;\n const timer = new Promise<never>((_, reject) => {\n timerId = setTimeout(() => reject(new Error(`headroom compress() timed out after ${ms}ms`)), ms);\n });\n return Promise.race([promise, timer]).finally(() => {\n if (timerId !== undefined) clearTimeout(timerId);\n });\n}\n\nexport interface HeadroomEngineConfig extends ProxyManagerConfig {\n enabled?: boolean;\n requestTimeoutMs?: number;\n circuitBreakerThreshold?: number;\n circuitBreakerCooldownMs?: number;\n /** Where to durably record committed turn-advancement keys (see\n * `DurableAdvancementKeyStore`). Defaults to `defaultCommitLogPath()`. */\n commitLogPath?: string;\n}\n\nexport class HeadroomContextEngine {\n readonly info = {\n id: \"headroom\",\n name: \"Headroom Context Compression\",\n version: \"0.1.0\",\n ownsCompaction: false,\n transcriptSemantics: {\n currentTurnFence: \"before-current-turn-entry-v1\",\n turnAdvancementIdempotency: \"atomic-idempotent-v1\",\n },\n };\n\n // Durable, restart-safe record of committed advancement keys, for\n // commitTurn's idempotent-retry check. See advancement-key-store.ts for\n // why this must survive a process restart and must not evict entries.\n private advancementKeyStore: DurableAdvancementKeyStore;\n\n private proxyManager: ProxyManager;\n private proxyUrl: string | null = null;\n private config: HeadroomEngineConfig;\n private logger: ProxyManagerLogger;\n private proxyReadyListeners = new Set<(proxyUrl: string) => void | Promise<void>>();\n private proxyStartupPromise: Promise<string> | null = null;\n private proxyStartupError: unknown = null;\n private stats = {\n totalCompressions: 0,\n totalTokensSaved: 0,\n totalTokensBefore: 0,\n compactions: 0,\n };\n private circuit = { errors: 0, openUntilMs: 0 };\n\n constructor(config: HeadroomEngineConfig = {}, logger?: ProxyManagerLogger) {\n this.config = config;\n this.logger = logger ?? defaultLogger;\n this.proxyManager = new ProxyManager(config, this.logger);\n this.advancementKeyStore = new DurableAdvancementKeyStore(\n config.commitLogPath ?? defaultCommitLogPath(),\n );\n }\n\n // === ContextEngine Lifecycle ===\n\n async bootstrap(params: {\n sessionId: string;\n sessionKey?: string;\n sessionFile: string;\n }): Promise<{ bootstrapped: boolean; reason?: string }> {\n if (this.config.enabled === false) {\n return { bootstrapped: false, reason: \"disabled\" };\n }\n\n this.ensureProxyStarted();\n return { bootstrapped: true, reason: \"proxy startup scheduled\" };\n }\n\n async ingest(params: {\n sessionId: string;\n message: any;\n isHeartbeat?: boolean;\n }): Promise<{ ingested: boolean }> {\n // No-op: OpenClaw's runtime stores messages. We don't need a separate store.\n return { ingested: true };\n }\n\n async ingestBatch?(params: {\n sessionId: string;\n messages: any[];\n isHeartbeat?: boolean;\n }): Promise<{ ingestedCount: number }> {\n return { ingestedCount: params.messages.length };\n }\n\n /**\n * Assemble context for the model — THE CORE HOOK.\n *\n * Converts AgentMessage[] → OpenAI format → compress() → AgentMessage[]. Only messages the proxy\n * actually changed are rebuilt; the rest are returned exactly as OpenClaw passed them, so the\n * provider prompt cache survives up to the first compressed message (see restoreAgentMessages).\n */\n async assemble(params: {\n sessionId: string;\n messages: any[];\n tokenBudget?: number;\n model?: string;\n prompt?: string;\n }): Promise<{\n messages: any[];\n estimatedTokens: number;\n systemPromptAddition?: string;\n }> {\n if (!this.proxyUrl || this.config.enabled === false) {\n this.ensureProxyStarted();\n // Fallback: return messages unchanged\n return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };\n }\n\n if (this.isCircuitOpen()) {\n this.logger.warn(\"[headroom] Circuit open — using uncompressed messages\");\n return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };\n }\n\n try {\n // Convert AgentMessage → OpenAI format\n const openaiMessages = agentToOpenAIIndexed(params.messages);\n\n // Compress via proxy — pass tokenBudget so RollingWindow enforces it\n const result = await withTimeout(\n compress(openaiMessages, {\n model: params.model ?? \"claude-sonnet-4-5\",\n baseUrl: this.proxyUrl,\n fallback: true,\n tokenBudget: params.tokenBudget,\n } as any),\n this.config.requestTimeoutMs ?? 30_000,\n );\n\n if (!result.compressed || result.tokensSaved === 0) {\n this.resetCircuit();\n return {\n messages: normalizeAgentMessages(params.messages),\n estimatedTokens: result.tokensBefore,\n };\n }\n\n // Convert back to AgentMessage format\n const compressedAgentMessages = restoreAgentMessages(params.messages, openaiMessages, result.messages);\n this.resetCircuit();\n\n // Track stats\n this.stats.totalCompressions++;\n this.stats.totalTokensSaved += result.tokensSaved;\n this.stats.totalTokensBefore += result.tokensBefore;\n\n this.logger.debug(\n `Assembled: ${result.tokensBefore} → ${result.tokensAfter} tokens (saved ${result.tokensSaved})`,\n );\n\n return {\n messages: compressedAgentMessages,\n estimatedTokens: result.tokensAfter,\n systemPromptAddition:\n result.tokensSaved > 100\n ? `[Context compressed by Headroom: ${result.tokensSaved} tokens saved. Use headroom_retrieve with the hash to get full details.]`\n : undefined,\n };\n } catch (error) {\n this.logger.error(`Assemble failed: ${error}`);\n this.tripCircuit(error);\n // Graceful fallback: return original messages\n return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };\n }\n }\n\n /** Delegate persistent compaction to OpenClaw's built-in runtime. */\n async compact(params: OpenClawCompactParams): Promise<OpenClawCompactResult> {\n const result = await delegateCompactionToRuntime(params);\n\n if (result.compacted) {\n this.stats.compactions++;\n }\n this.logger.info(\n `Compaction ${result.compacted ? \"completed\" : \"skipped\"} ` +\n `(budget: ${params.tokenBudget ?? \"none\"}, force: ${params.force ?? false})`,\n );\n\n return result;\n }\n\n /**\n * Durable turn-advancement commit — required by OpenClaw's transcriptSemantics\n * contract. Called only for the accepted, successful turn; failed or aborted\n * turns never reach here. Must be an atomic, idempotent write of the\n * accepted `messages` keyed by `advancementKey` so a host retry with the\n * same key reports \"duplicate\" instead of re-applying the advancement —\n * including a retry that arrives after this process restarted, which is\n * why the record lives on disk (see `DurableAdvancementKeyStore`) rather\n * than in memory, and includes the messages rather than just the key.\n */\n async commitTurn(params: { advancementKey: string; messages: any[] }): Promise<{\n status: \"committed\" | \"duplicate\";\n }> {\n const status = await this.advancementKeyStore.tryCommit(params.advancementKey, params.messages);\n return { status };\n }\n\n async afterTurn?(params: {\n sessionId: string;\n messages: any[];\n prePromptMessageCount: number;\n isHeartbeat?: boolean;\n }): Promise<void> {\n // Optional: could log stats or trigger learning\n }\n\n async prepareSubagentSpawn?(params: {\n parentSessionKey: string;\n childSessionKey: string;\n ttlMs?: number;\n }): Promise<{ rollback: () => Promise<void> } | undefined> {\n // Subagent context is compressed naturally via assemble()\n return undefined;\n }\n\n async onSubagentEnded?(params: {\n childSessionKey: string;\n reason: string;\n }): Promise<void> {\n // No-op\n }\n\n async dispose(): Promise<void> {\n await this.proxyManager.stop();\n this.logger.info(\n `Engine disposed. Stats: ${this.stats.totalCompressions} compressions, ` +\n `${this.stats.totalTokensSaved} tokens saved`,\n );\n }\n\n // --- Public API ---\n\n getStats() {\n return { ...this.stats };\n }\n\n getProxyUrl(): string | null {\n return this.proxyUrl;\n }\n\n getProxyStartupError(): unknown {\n return this.proxyStartupError;\n }\n\n private isCircuitOpen(): boolean {\n const threshold = this.config.circuitBreakerThreshold ?? 3;\n if (this.circuit.errors < threshold) return false;\n if (Date.now() < this.circuit.openUntilMs) return true;\n this.circuit = { errors: 0, openUntilMs: 0 };\n return false;\n }\n\n private tripCircuit(error: unknown): void {\n this.circuit.errors += 1;\n const threshold = this.config.circuitBreakerThreshold ?? 3;\n if (this.circuit.errors < threshold) return;\n const cooldownMs = this.config.circuitBreakerCooldownMs ?? 60_000;\n this.circuit.openUntilMs = Date.now() + cooldownMs;\n this.logger.warn(\n `[headroom] Circuit breaker opened after ${this.circuit.errors} errors ` +\n `(last: ${String(error)}); bypassing compression for ${cooldownMs}ms`,\n );\n }\n\n private resetCircuit(): void {\n this.circuit = { errors: 0, openUntilMs: 0 };\n }\n\n ensureProxyStarted(): void {\n if (this.config.enabled === false || this.proxyUrl || this.proxyStartupPromise) {\n return;\n }\n\n this.proxyStartupError = null;\n this.proxyStartupPromise = this.proxyManager\n .start()\n .then(async (proxyUrl) => {\n this.proxyUrl = proxyUrl;\n this.proxyStartupError = null;\n await this.notifyProxyReady(proxyUrl);\n this.logger.info(`Headroom proxy ready at ${proxyUrl}`);\n return proxyUrl;\n })\n .catch((error) => {\n this.proxyStartupError = error;\n this.logger.warn(`Headroom proxy unavailable: ${error}`);\n throw error;\n })\n .finally(() => {\n this.proxyStartupPromise = null;\n });\n\n // Fire-and-forget lifecycle callers intentionally do not await this promise.\n // Keep the promise rejectable for ensureProxyUrl(), but mark it observed so\n // a missing proxy cannot become a process-level unhandled rejection.\n void this.proxyStartupPromise.catch(() => {});\n }\n\n onProxyReady(listener: (proxyUrl: string) => void | Promise<void>): () => void {\n this.proxyReadyListeners.add(listener);\n return () => {\n this.proxyReadyListeners.delete(listener);\n };\n }\n\n async ensureProxyUrl(): Promise<string> {\n if (this.proxyUrl) {\n return this.proxyUrl;\n }\n\n this.ensureProxyStarted();\n if (!this.proxyStartupPromise) {\n throw new Error(\"Headroom proxy startup is disabled\");\n }\n return this.proxyStartupPromise;\n }\n\n private async notifyProxyReady(proxyUrl: string): Promise<void> {\n for (const listener of this.proxyReadyListeners) {\n try {\n await listener(proxyUrl);\n } catch (error) {\n this.logger.warn(`Headroom proxy ready listener failed: ${error}`);\n }\n }\n }\n}\n","/**\n * Manages connectivity to a Headroom proxy (local or remote).\n *\n * Security model:\n * - Local proxies (127.0.0.1 / localhost) can be auto-started via subprocess\n * - Remote proxies are connect-only: probe and use, never launch\n * - No environment variable access\n */\nimport { spawn } from \"node:child_process\";\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport os from \"os\";\n\nexport interface ProxyManagerConfig {\n proxyUrl?: string;\n proxyPort?: number;\n pythonPath?: string;\n autoStart?: boolean;\n startupTimeoutMs?: number;\n retryMaxAttempts?: number;\n connectTimeoutSeconds?: number;\n}\n\nexport interface ProxyManagerLogger {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n debug(message: string): void;\n}\n\n/** Default logger that prefixes all messages with `[headroom]`. */\nexport const defaultLogger: ProxyManagerLogger = {\n info: (m) => console.log(`[headroom] ${m}`),\n warn: (m) => console.warn(`[headroom] ${m}`),\n error: (m) => console.error(`[headroom] ${m}`),\n debug: () => {},\n};\n\nexport interface ProxyProbeResult {\n reachable: boolean;\n isHeadroom: boolean;\n reason?: string;\n}\n\ninterface LaunchSpec {\n label: string;\n command: string;\n args: string[];\n checkCommand: string;\n checkArgs: string[];\n useShell?: boolean;\n checkUseShell?: boolean;\n}\n\nconst HEADROOM_MODULE_DISCOVERY_SNIPPET =\n \"import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('headroom') else 1)\";\n\nexport class ProxyManager {\n private config: ProxyManagerConfig;\n private logger: ProxyManagerLogger;\n private proxyUrl: string | null = null;\n\n constructor(config: ProxyManagerConfig = {}, logger?: ProxyManagerLogger) {\n this.config = config;\n this.logger = logger ?? defaultLogger;\n }\n\n /**\n * Ensure a proxy is available. Returns the normalized URL origin.\n */\n async start(): Promise<string> {\n const port = this.getProxyPort();\n const rawExplicitUrl =\n typeof this.config.proxyUrl === \"string\" && this.config.proxyUrl.trim().length > 0\n ? normalizeAndValidateProxyUrl(this.config.proxyUrl)\n : null;\n // Only apply proxyPort default to local URLs — remote URLs use their protocol default\n const explicitUrl = rawExplicitUrl\n ? isLocalProxyUrl(rawExplicitUrl) ? withDefaultPort(rawExplicitUrl, port) : rawExplicitUrl\n : null;\n const defaultCandidates = this.getDefaultProxyCandidates(port);\n const candidateUrls = explicitUrl ? [explicitUrl] : [...defaultCandidates];\n const probeByUrl = new Map<string, ProxyProbeResult>();\n\n for (const url of candidateUrls) {\n const probe = await probeHeadroomProxy(url);\n probeByUrl.set(url, probe);\n if (probe.reachable && probe.isHeadroom) {\n this.proxyUrl = url;\n this.logger.info(`Headroom proxy already running at ${url}`);\n return url;\n }\n }\n\n if (explicitUrl) {\n const explicitProbe = probeByUrl.get(explicitUrl);\n if (explicitProbe?.reachable && !explicitProbe.isHeadroom) {\n throw new Error(\n `Service reachable at ${explicitUrl}, but it does not appear to be a Headroom proxy (${explicitProbe.reason ?? \"unknown service\"}).`,\n );\n }\n }\n\n // Remote URLs are connect-only — never auto-start a subprocess for them\n if (explicitUrl && !isLocalProxyUrl(explicitUrl)) {\n throw new Error(\n `Remote Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running at that address.`,\n );\n }\n\n // Auto-start is only available for local proxies\n if (this.config.autoStart === true) {\n const startupUrl = explicitUrl ?? defaultCandidates[0];\n const startupProbe = probeByUrl.get(startupUrl);\n if (startupProbe?.reachable && !startupProbe.isHeadroom) {\n throw new Error(\n `Cannot auto-start Headroom at ${startupUrl}: port is in use by a non-Headroom service (${startupProbe.reason ?? \"unknown service\"}).`,\n );\n }\n\n this.logger.info(\n `No Headroom proxy detected${explicitUrl ? ` at ${startupUrl}` : \" on default local endpoints\"}; attempting to auto-start...`,\n );\n await this.startHeadroomProxy(startupUrl, port);\n\n const startedProbe = await waitForHeadroomProxy(\n startupUrl,\n this.config.startupTimeoutMs ?? 20_000,\n );\n if (startedProbe.reachable && startedProbe.isHeadroom) {\n this.proxyUrl = startupUrl;\n this.logger.info(`Headroom proxy started and reachable at ${startupUrl}`);\n return startupUrl;\n }\n throw new Error(\n `Attempted to start Headroom proxy, but it was not reachable at ${startupUrl} (${startedProbe.reason ?? \"unknown\"}).`,\n );\n }\n\n if (explicitUrl) {\n throw new Error(\n `Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running first.`,\n );\n }\n\n throw new Error(\n `Headroom proxy not detected on default endpoints (${defaultCandidates.join(\", \")}). ` +\n \"Set proxyUrl explicitly or enable autoStart.\",\n );\n }\n\n private getProxyPort(): number {\n const rawPort = this.config.proxyPort;\n if (!Number.isInteger(rawPort) || rawPort === undefined) return 8787;\n if (rawPort < 1 || rawPort > 65535) {\n throw new Error(\"proxyPort must be an integer between 1 and 65535\");\n }\n return rawPort;\n }\n\n private getDefaultProxyCandidates(port: number): string[] {\n return [`http://127.0.0.1:${port}`, `http://localhost:${port}`];\n }\n\n /**\n * Stop manager state. Spawned proxy processes are detached and externally managed.\n */\n async stop(): Promise<void> {\n this.proxyUrl = null;\n }\n\n getUrl(): string | null {\n return this.proxyUrl;\n }\n\n // --- Internal ---\n\n private async startHeadroomProxy(proxyUrl: string, defaultPort: number): Promise<void> {\n const parsed = new URL(proxyUrl);\n const host = parsed.hostname;\n const port = parsed.port || String(defaultPort);\n const specs = this.buildLaunchSpecs(host, port);\n const errors: string[] = [];\n\n for (const spec of specs) {\n if (!this.canExecute(spec.checkCommand, spec.checkArgs, spec.checkUseShell ?? spec.useShell)) {\n this.logger.debug(`Launcher unavailable: ${spec.label}`);\n continue;\n }\n\n try {\n const child = spawn(spec.command, spec.args, {\n detached: true,\n shell: spec.useShell === true,\n stdio: \"ignore\",\n });\n child.unref();\n this.logger.info(`Auto-start launcher selected: ${spec.label}`);\n return;\n } catch (error) {\n errors.push(`${spec.label}: ${String(error)}`);\n }\n }\n\n throw new Error(\n \"No usable Headroom launcher found. Tried PATH, local npm, global npm, and Python. \" +\n \"Install headroom-ai (npm or pip) and ensure one launcher is available.\\n\" +\n (errors.length > 0 ? `Launch errors: ${errors.join(\"; \")}` : \"\"),\n );\n }\n\n private buildLaunchSpecs(host: string, port: string): LaunchSpec[] {\n const commonArgs = [\"proxy\", \"--host\", host, \"--port\", port];\n const retryMaxAttempts = this.config.retryMaxAttempts;\n if (Number.isInteger(retryMaxAttempts)) {\n commonArgs.push(\"--retry-max-attempts\", String(retryMaxAttempts));\n }\n\n const connectTimeoutSeconds = this.config.connectTimeoutSeconds;\n if (Number.isInteger(connectTimeoutSeconds)) {\n commonArgs.push(\"--connect-timeout-seconds\", String(connectTimeoutSeconds));\n }\n\n const specs: LaunchSpec[] = [];\n\n const configuredPython = this.getConfiguredPythonCommand();\n if (configuredPython) {\n specs.push({\n label: `Configured Python: ${configuredPython} -m headroom.cli`,\n command: configuredPython,\n args: [\"-m\", \"headroom.cli\", ...commonArgs],\n checkCommand: configuredPython,\n checkArgs: [\"-c\", HEADROOM_MODULE_DISCOVERY_SNIPPET],\n });\n }\n\n // 2) Windows pyenv: resolve the real executable so we avoid shim .bat wrappers.\n if (process.platform === \"win32\") {\n const pyenvHeadroom = this.getPyenvResolvedHeadroom();\n if (pyenvHeadroom) {\n specs.push({\n label: `pyenv: ${pyenvHeadroom}`,\n command: pyenvHeadroom,\n args: commonArgs,\n checkCommand: pyenvHeadroom,\n checkArgs: [\"--version\"],\n useShell: false,\n });\n }\n }\n\n // 3) PATH\n specs.push({\n label: \"PATH: headroom\",\n command: \"headroom\",\n args: commonArgs,\n checkCommand: process.platform === \"win32\" ? \"where.exe\" : \"sh\",\n checkArgs: process.platform === \"win32\"\n ? [\"headroom\"]\n : [\"-c\", \"command -v headroom >/dev/null 2>&1\"],\n useShell: process.platform === \"win32\",\n checkUseShell: false,\n });\n\n // 4) uv tool install path (~/.local/bin/headroom)\n const uvBin = join(\n os.homedir(),\n \".local\", \"bin\", \"headroom\"\n );\n if (existsSync(uvBin)) {\n specs.push({\n label: `uv tool: ${uvBin}`,\n command: uvBin,\n args: commonArgs,\n checkCommand: uvBin,\n checkArgs: [\"--version\"],\n });\n }\n\n // 5) Local npm install (inside plugin install path)\n const moduleDir = dirname(fileURLToPath(import.meta.url)); // .../dist\n const packageRoot = dirname(moduleDir);\n const localBinDir = join(packageRoot, \"node_modules\", \".bin\");\n const localBins = process.platform === \"win32\"\n ? [join(localBinDir, \"headroom.cmd\"), join(localBinDir, \"headroom\")]\n : [join(localBinDir, \"headroom\")];\n for (const localBin of localBins) {\n if (!existsSync(localBin)) continue;\n specs.push({\n label: `Local npm: ${localBin}`,\n command: localBin,\n args: commonArgs,\n checkCommand: localBin,\n checkArgs: [\"--version\"],\n useShell: process.platform === \"win32\",\n });\n }\n\n // 5) Global npm install\n const npmPrefix = this.getNpmGlobalPrefix();\n if (npmPrefix) {\n const globalBins = process.platform === \"win32\"\n ? [join(npmPrefix, \"headroom.cmd\"), join(npmPrefix, \"headroom\")]\n : [join(npmPrefix, \"bin\", \"headroom\"), join(npmPrefix, \"headroom\")];\n\n for (const globalBin of globalBins) {\n if (!existsSync(globalBin)) continue;\n specs.push({\n label: `Global npm: ${globalBin}`,\n command: globalBin,\n args: commonArgs,\n checkCommand: globalBin,\n checkArgs: [\"--version\"],\n useShell: process.platform === \"win32\",\n });\n }\n }\n\n // 6) Python module fallback\n const pythonCommands = this.getPythonCommands();\n for (const pyCmd of pythonCommands) {\n if (configuredPython && pyCmd === configuredPython) continue;\n specs.push({\n label: `Python: ${pyCmd} -m headroom.cli`,\n command: pyCmd,\n args: [\"-m\", \"headroom.cli\", ...commonArgs],\n checkCommand: pyCmd,\n checkArgs: [\"-c\", HEADROOM_MODULE_DISCOVERY_SNIPPET],\n });\n }\n\n return specs;\n }\n\n private getConfiguredPythonCommand(): string | null {\n const configured = typeof this.config.pythonPath === \"string\"\n ? this.config.pythonPath.trim()\n : \"\";\n return configured.length > 0 ? configured : null;\n }\n\n private getPyenvResolvedHeadroom(): string | null {\n if (process.platform !== \"win32\") return null;\n\n try {\n const result = spawnSync(\"pyenv\", [\"which\", \"headroom\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 5000,\n });\n if (result.error || result.status !== 0) return null;\n const resolved = (result.stdout ?? \"\").trim().split(/\\r?\\n/, 1)[0];\n if (!resolved || !existsSync(resolved)) return null;\n return resolved;\n } catch {\n return null;\n }\n }\n\n private getPythonCommands(): string[] {\n const commands: string[] = [];\n const configured = this.getConfiguredPythonCommand() ?? \"\";\n if (configured.length > 0) {\n commands.push(configured);\n }\n for (const fallback of [\"python\", \"python3\", \"py\"]) {\n if (!commands.includes(fallback)) commands.push(fallback);\n }\n return commands;\n }\n\n private canExecute(command: string, args: string[], useShell = false): boolean {\n try {\n const result = spawnSync(command, args, {\n shell: useShell,\n stdio: \"ignore\",\n timeout: 5000,\n });\n if (result.error) return false;\n return result.status === 0;\n } catch {\n return false;\n }\n }\n\n private getNpmGlobalPrefix(): string | null {\n try {\n const result = spawnSync(\"npm\", [\"prefix\", \"-g\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 5000,\n });\n if (result.error || result.status !== 0) return null;\n const prefix = (result.stdout ?? \"\").trim();\n return prefix.length > 0 ? prefix : null;\n } catch {\n return null;\n }\n }\n}\n\n/** Parse a URL, returning the parsed object or throwing a descriptive error. */\nfunction parseProxyUrl(proxyUrl: string): URL {\n try {\n return new URL(proxyUrl);\n } catch {\n throw new Error(`Invalid proxyUrl: \"${proxyUrl}\"`);\n }\n}\n\nexport function normalizeAndValidateProxyUrl(proxyUrl: string): string {\n const parsed = parseProxyUrl(proxyUrl);\n\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"proxyUrl must use http:// or https://\");\n }\n\n if (parsed.pathname !== \"/\" || parsed.search || parsed.hash) {\n throw new Error(\"proxyUrl must not include a path, query, or hash\");\n }\n\n return parsed.origin;\n}\n\n/** Returns true if the URL points to a local address (localhost or 127.0.0.1). */\nexport function isLocalProxyUrl(proxyUrl: string): boolean {\n try {\n const parsed = new URL(proxyUrl);\n return parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"localhost\";\n } catch {\n return false;\n }\n}\n\nfunction withDefaultPort(proxyUrl: string, defaultPort: number): string {\n const parsed = parseProxyUrl(proxyUrl);\n if (!parsed.port) {\n parsed.port = String(defaultPort);\n }\n return parsed.origin;\n}\n\n/**\n * Probe a configured URL and verify whether it is a running Headroom proxy.\n */\nexport async function probeHeadroomProxy(proxyUrl: string): Promise<ProxyProbeResult> {\n const origin = normalizeAndValidateProxyUrl(proxyUrl);\n const probeEndpoint = async (\n path: string,\n options: { readBody?: boolean } = {},\n ): Promise<{ reachable: boolean; ok: boolean; status?: number; body?: string }> => {\n try {\n const response = await fetch(`${origin}${path}`, {\n signal: AbortSignal.timeout(3_000),\n });\n const body =\n response.ok && options.readBody\n ? await response.text().catch(() => undefined)\n : undefined;\n return { reachable: true, ok: response.ok, status: response.status, body };\n } catch {\n return { reachable: false, ok: false };\n }\n };\n\n const ready = await probeEndpoint(\"/readyz\");\n const retrieveStats = await probeEndpoint(\"/v1/retrieve/stats\", { readBody: true });\n if (retrieveStats.ok && hasHeadroomStatsShape(retrieveStats.body)) {\n return { reachable: true, isHeadroom: true };\n }\n\n const stats = await probeEndpoint(\"/stats\", { readBody: true });\n if (stats.ok && hasHeadroomStatsShape(stats.body)) {\n return { reachable: true, isHeadroom: true };\n }\n\n const health = await probeEndpoint(\"/health\");\n const anyReachable = ready.reachable || retrieveStats.reachable || stats.reachable || health.reachable;\n if (!anyReachable) {\n return { reachable: false, isHeadroom: false, reason: \"proxy probe failed\" };\n }\n\n const reasons = [\n ready.reachable ? `readyz HTTP ${ready.status}` : \"readyz unavailable\",\n retrieveStats.reachable\n ? `retrieve stats HTTP ${retrieveStats.status}`\n : \"retrieve stats endpoint unavailable\",\n stats.reachable ? `stats HTTP ${stats.status}` : \"stats endpoint unavailable\",\n health.reachable ? `health HTTP ${health.status}` : \"health check failed\",\n ];\n return { reachable: true, isHeadroom: false, reason: reasons.join(\"; \") };\n}\n\nfunction hasHeadroomStatsShape(body: string | undefined): boolean {\n if (!body) {\n return false;\n }\n\n try {\n const parsed = JSON.parse(body) as Record<string, unknown>;\n return (\n parsed !== null &&\n typeof parsed === \"object\" &&\n (Object.hasOwn(parsed, \"proxy_inbound\") ||\n Object.hasOwn(parsed, \"api_requests\") ||\n Object.hasOwn(parsed, \"provider_tokens\") ||\n Object.hasOwn(parsed, \"proxy_compression_saved\") ||\n Object.hasOwn(parsed, \"store\") ||\n Object.hasOwn(parsed, \"recent_retrievals\"))\n );\n } catch {\n return false;\n }\n}\n\nasync function waitForHeadroomProxy(proxyUrl: string, timeoutMs: number): Promise<ProxyProbeResult> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const result = await probeHeadroomProxy(proxyUrl);\n if (result.reachable && result.isHeadroom) {\n return result;\n }\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n return probeHeadroomProxy(proxyUrl);\n}\n","/**\n * Convert between OpenClaw's AgentMessage format and OpenAI message format.\n *\n * AgentMessage uses:\n * role: \"user\" | \"assistant\" | \"toolResult\"\n * content: string | ContentBlock[]\n *\n * OpenAI uses:\n * role: \"user\" | \"assistant\" | \"system\" | \"tool\"\n * content: string\n * tool_calls?: ToolCall[]\n * tool_call_id?: string\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface OpenAIMessage {\n role: string;\n content: string | null;\n tool_calls?: any[];\n tool_call_id?: string;\n name?: string;\n _headroomMeta?: Record<string, unknown>;\n}\n\n/**\n * Convert AgentMessage[] to OpenAI message format for compression.\n */\nexport function agentToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n const normalized = normalizeAgentMessage(msg);\n const role = normalized.role;\n\n const buildMeta = (): Record<string, unknown> => {\n const meta = { ...normalized } as Record<string, unknown>;\n delete meta.role;\n delete meta.content;\n return meta;\n };\n\n if (role === \"system\") {\n result.push({\n role: \"system\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : extractText(normalized.content),\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n if (role === \"user\") {\n result.push({\n role: \"user\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : extractText(normalized.content),\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n if (role === \"assistant\") {\n const content = normalized.content;\n if (typeof content === \"string\") {\n result.push({ role: \"assistant\", content, _headroomMeta: buildMeta() });\n continue;\n }\n\n // Content blocks: extract text and tool call blocks.\n // OpenClaw uses `toolCall`; some adapters still emit legacy `tool_use`.\n if (Array.isArray(content)) {\n const textParts: string[] = [];\n const toolCalls: any[] = [];\n\n for (const block of content) {\n if (typeof block === \"string\") {\n textParts.push(block);\n } else if (block.type === \"text\") {\n textParts.push(block.text);\n } else if (block.type === \"tool_use\" || block.type === \"toolCall\") {\n const args =\n block.type === \"toolCall\"\n ? block.arguments\n : block.input;\n toolCalls.push({\n id: block.id,\n type: \"function\",\n function: {\n name: block.name,\n arguments:\n typeof args === \"string\"\n ? args\n : JSON.stringify(args ?? {}),\n },\n });\n }\n }\n\n const openaiMsg: OpenAIMessage = {\n role: \"assistant\",\n content: textParts.length > 0 ? textParts.join(\"\") : null,\n _headroomMeta: buildMeta(),\n };\n if (toolCalls.length > 0) {\n openaiMsg.tool_calls = toolCalls;\n }\n result.push(openaiMsg);\n }\n continue;\n }\n\n if (role === \"toolResult\" || role === \"tool_result\") {\n const content =\n typeof normalized.content === \"string\"\n ? normalized.content\n : Array.isArray(normalized.content)\n ? extractText(normalized.content)\n : JSON.stringify(normalized.content);\n\n result.push({\n role: \"tool\",\n content,\n tool_call_id:\n normalized.toolCallId ??\n normalized.tool_use_id ??\n normalized.id ??\n \"unknown\",\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n // Fallback: pass through as user message\n result.push({\n role: \"user\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : JSON.stringify(normalized.content),\n _headroomMeta: buildMeta(),\n });\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to AgentMessage format.\n */\nexport function openAIToAgent(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n const meta = (msg._headroomMeta ?? {}) as Record<string, unknown>;\n const timestamp =\n typeof meta.timestamp === \"number\" ? meta.timestamp : Date.now();\n\n if (msg.role === \"system\") {\n result.push({\n role: \"system\",\n content: msg.content ?? \"\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"user\") {\n result.push({\n role: \"user\",\n content: msg.content ?? \"\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: any;\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = tc.function.arguments ?? {};\n }\n // Emit OpenClaw-native block shape so downstream transports keep call linkage.\n blocks.push({\n type: \"toolCall\",\n id: tc.id,\n name: tc.function.name,\n arguments: input,\n });\n }\n }\n // OpenClaw's Pi agent expects content to always be an array for assistant messages\n // (it calls .flatMap() on it). Never flatten to a string.\n result.push({\n ...(meta as object),\n role: \"assistant\",\n content: blocks,\n api: typeof meta.api === \"string\" ? meta.api : \"headroom\",\n provider: typeof meta.provider === \"string\" ? meta.provider : \"headroom\",\n model: typeof meta.model === \"string\" ? meta.model : \"headroom\",\n usage:\n isRecord(meta.usage)\n ? meta.usage\n : {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason:\n typeof meta.stopReason === \"string\" ? meta.stopReason : \"stop\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n const textContent =\n typeof msg.content === \"string\"\n ? msg.content\n : msg.content == null\n ? \"\"\n : JSON.stringify(msg.content);\n const toolCallId = msg.tool_call_id ?? \"unknown\";\n result.push({\n ...(meta as object),\n role: \"toolResult\",\n // OpenClaw transport layers expect toolResult content blocks, not a raw string.\n content: [{ type: \"text\", text: textContent }],\n toolCallId:\n typeof meta.toolCallId === \"string\" ? meta.toolCallId : toolCallId,\n tool_use_id:\n typeof meta.tool_use_id === \"string\" ? meta.tool_use_id : toolCallId,\n toolName:\n typeof meta.toolName === \"string\" ? meta.toolName : \"headroom\",\n isError: typeof meta.isError === \"boolean\" ? meta.isError : false,\n timestamp,\n });\n continue;\n }\n }\n\n return result;\n}\n\nexport function normalizeAgentMessages(messages: any[]): any[] {\n return messages.map((message) => normalizeAgentMessage(message));\n}\n\n/** `_headroomMeta` key recording which AgentMessage a converted message came from. */\nconst SOURCE_INDEX_KEY = \"headroomSourceIndex\";\n\n/**\n * Like `agentToOpenAI`, but stamps every converted message with the index of the AgentMessage it came from.\n * The proxy returns `_headroomMeta` unchanged, so `restoreAgentMessages` can map its result back onto the\n * originals even when it drops messages to fit a token budget.\n */\nexport function agentToOpenAIIndexed(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n messages.forEach((message, index) => {\n for (const converted of agentToOpenAI([message])) {\n result.push({\n ...converted,\n _headroomMeta: { ...(converted._headroomMeta ?? {}), [SOURCE_INDEX_KEY]: index },\n });\n }\n });\n return result;\n}\n\n/**\n * Map the proxy's result back onto the AgentMessages OpenClaw passed in.\n *\n * `openAIToAgent(agentToOpenAI(m))` is lossy: it drops `thinking` blocks and flattens user and tool content\n * blocks into one string. Applying it to every message whenever anything is compressed rewrites the whole\n * history, which invalidates the provider's prompt cache from the first message on every compressing turn.\n * So a message the proxy returned unchanged is passed through exactly as it came in, and a message it did\n * change keeps every original field and block except the text that compression replaced.\n *\n * `sent` must be the `agentToOpenAIIndexed(originals)` output that was given to the proxy.\n */\nexport function restoreAgentMessages(\n originals: any[],\n sent: OpenAIMessage[],\n returned: OpenAIMessage[],\n): any[] {\n const sentByIndex = new Map<number, OpenAIMessage>();\n for (const message of sent) {\n const index = sourceIndexOf(message);\n if (index !== undefined) sentByIndex.set(index, message);\n }\n\n const result: any[] = [];\n for (const message of returned) {\n const index = sourceIndexOf(message);\n const original = index === undefined ? undefined : originals[index];\n const before = index === undefined ? undefined : sentByIndex.get(index);\n if (original === undefined || before === undefined) {\n // Not one of ours (e.g. a message the proxy added): convert it the way it always was.\n result.push(...openAIToAgent([withoutSourceIndex(message)]));\n } else if (sameWireMessage(before, message)) {\n result.push(normalizeAgentMessage(original));\n } else {\n result.push(applyCompressedMessage(original, message));\n }\n }\n return result;\n}\n\nfunction sourceIndexOf(message: OpenAIMessage): number | undefined {\n const index = message._headroomMeta?.[SOURCE_INDEX_KEY];\n return typeof index === \"number\" ? index : undefined;\n}\n\nfunction withoutSourceIndex(message: OpenAIMessage): OpenAIMessage {\n if (!message._headroomMeta || !(SOURCE_INDEX_KEY in message._headroomMeta)) return message;\n const { [SOURCE_INDEX_KEY]: _index, ...meta } = message._headroomMeta;\n return { ...message, _headroomMeta: meta };\n}\n\nfunction sameWireMessage(a: OpenAIMessage, b: OpenAIMessage): boolean {\n return (\n a.role === b.role &&\n a.content === b.content &&\n a.tool_call_id === b.tool_call_id &&\n a.name === b.name &&\n JSON.stringify(a.tool_calls ?? null) === JSON.stringify(b.tool_calls ?? null)\n );\n}\n\nfunction applyCompressedMessage(original: any, compressed: OpenAIMessage): any {\n const base = normalizeAgentMessage(original);\n const text = typeof compressed.content === \"string\" ? compressed.content : \"\";\n\n if (base.role === \"assistant\") {\n let content = replaceTextBlocks(base.content, text);\n if (Array.isArray(compressed.tool_calls)) {\n content = content.map((block: any) => withCompressedArguments(block, compressed.tool_calls!));\n }\n return { ...base, content };\n }\n\n if (typeof base.content === \"string\") return { ...base, content: text };\n return { ...base, content: replaceTextBlocks(base.content, text) };\n}\n\n/** Replace the text blocks with one block holding `text`, where the first one was; keep every other block. */\nfunction replaceTextBlocks(content: unknown, text: string): any[] {\n const blocks = Array.isArray(content) ? content : [{ type: \"text\", text: String(content ?? \"\") }];\n const result: any[] = [];\n let placed = false;\n for (const block of blocks) {\n const isText = typeof block === \"string\" || (isRecord(block) && block.type === \"text\");\n if (!isText) {\n result.push(block);\n continue;\n }\n if (!placed) {\n if (text) result.push(isRecord(block) ? { ...block, text } : { type: \"text\", text });\n placed = true;\n }\n }\n if (!placed && text) result.unshift({ type: \"text\", text });\n return result;\n}\n\nfunction withCompressedArguments(block: any, toolCalls: any[]): any {\n if (!isRecord(block) || (block.type !== \"toolCall\" && block.type !== \"tool_use\")) return block;\n const call = toolCalls.find((tc) => tc?.id === block.id);\n const raw = call?.function?.arguments;\n if (typeof raw !== \"string\") return block;\n const current = block.type === \"toolCall\" ? block.arguments : block.input;\n if ((typeof current === \"string\" ? current : JSON.stringify(current ?? {})) === raw) return block;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n parsed = raw;\n }\n return block.type === \"toolCall\" ? { ...block, arguments: parsed } : { ...block, input: parsed };\n}\n\n/**\n * Extract text from content blocks.\n */\nfunction extractText(content: any): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return JSON.stringify(content);\n\n return content\n .map((block: any) => {\n if (typeof block === \"string\") return block;\n if (block.type === \"text\") return block.text;\n if (block.type === \"tool_result\") {\n return typeof block.content === \"string\" ? block.content : JSON.stringify(block.content);\n }\n return \"\";\n })\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction normalizeAgentMessage(message: any): any {\n if (!isRecord(message)) return message;\n\n if (message.role === \"assistant\") {\n return normalizeAssistantMessage(message);\n }\n\n if (message.role === \"toolResult\" || message.role === \"tool_result\") {\n return normalizeToolResultMessage(message);\n }\n\n return message;\n}\n\nfunction normalizeAssistantMessage(message: Record<string, any>): Record<string, any> {\n const normalizedContent = normalizeAssistantContent(message.content);\n\n return {\n ...message,\n content: normalizedContent,\n api: typeof message.api === \"string\" ? message.api : \"headroom\",\n provider: typeof message.provider === \"string\" ? message.provider : \"headroom\",\n model: typeof message.model === \"string\" ? message.model : \"headroom\",\n usage: isRecord(message.usage)\n ? message.usage\n : {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: typeof message.stopReason === \"string\" ? message.stopReason : \"stop\",\n timestamp: typeof message.timestamp === \"number\" ? message.timestamp : Date.now(),\n };\n}\n\nfunction normalizeToolResultMessage(message: Record<string, any>): Record<string, any> {\n const normalizedContent = normalizeToolResultContent(message.content);\n const toolCallId =\n typeof message.toolCallId === \"string\"\n ? message.toolCallId\n : typeof message.tool_use_id === \"string\"\n ? message.tool_use_id\n : typeof message.id === \"string\"\n ? message.id\n : \"unknown\";\n\n return {\n ...message,\n role: \"toolResult\",\n content: normalizedContent,\n toolCallId,\n tool_use_id:\n typeof message.tool_use_id === \"string\" ? message.tool_use_id : toolCallId,\n toolName: typeof message.toolName === \"string\" ? message.toolName : \"headroom\",\n isError: typeof message.isError === \"boolean\" ? message.isError : false,\n timestamp: typeof message.timestamp === \"number\" ? message.timestamp : Date.now(),\n };\n}\n\nfunction normalizeAssistantContent(content: unknown): any[] {\n if (Array.isArray(content)) {\n return content.flatMap((block) => {\n if (typeof block === \"string\") return [{ type: \"text\", text: block }];\n if (!isRecord(block) || typeof block.type !== \"string\") return [];\n if (block.type === \"text\" && typeof block.text === \"string\") return [block];\n if (block.type === \"thinking\" && typeof block.thinking === \"string\") return [block];\n if (\n (block.type === \"toolCall\" || block.type === \"tool_use\") &&\n typeof block.name === \"string\"\n ) {\n return [\n {\n type: \"toolCall\",\n id: typeof block.id === \"string\" ? block.id : \"unknown\",\n name: block.name,\n arguments:\n \"arguments\" in block\n ? block.arguments\n : \"input\" in block\n ? block.input\n : {},\n },\n ];\n }\n return [];\n });\n }\n\n if (typeof content === \"string\" && content.length > 0) {\n return [{ type: \"text\", text: content }];\n }\n\n if (content == null) {\n return [];\n }\n\n return [{ type: \"text\", text: JSON.stringify(content) }];\n}\n\nfunction normalizeToolResultContent(content: unknown): any[] {\n if (Array.isArray(content)) {\n return content.flatMap((block) => {\n if (typeof block === \"string\") return [{ type: \"text\", text: block }];\n if (!isRecord(block) || typeof block.type !== \"string\") return [];\n if (block.type === \"text\" && typeof block.text === \"string\") return [block];\n if (\n block.type === \"image\" &&\n typeof block.data === \"string\" &&\n typeof block.mimeType === \"string\"\n ) {\n return [block];\n }\n if (block.type === \"tool_result\" && \"content\" in block) {\n return normalizeToolResultContent(block.content);\n }\n return [];\n });\n }\n\n if (typeof content === \"string\" && content.length > 0) {\n return [{ type: \"text\", text: content }];\n }\n\n if (content == null) {\n return [];\n }\n\n return [{ type: \"text\", text: JSON.stringify(content) }];\n}\n\nfunction isRecord(value: unknown): value is Record<string, any> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * Durable, atomic, restart-safe, cross-process-safe record of committed\n * turn advancements.\n *\n * OpenClaw's `atomic-idempotent-v1` transcriptSemantics contract requires\n * that a retried `commitTurn` call report \"duplicate\" for a key that was\n * already committed -- including after this process (or the host) restarts\n * -- and that \"the accepted turn and key advance together\" (the committed\n * messages are persisted atomically with the key, not discarded).\n *\n * The default commit-log path is shared by every gateway process using the\n * same Headroom workspace, not just every store instance in one process --\n * an in-process mutex (a JS `Map`/promise queue) does nothing to stop two\n * separate OS processes from each reading the same file, adding a different\n * key, and racing their renames, with the loser's accepted turn silently\n * discarded. The read/check/write transaction is therefore protected by a\n * real inter-process lock: an exclusive lock FILE (`<path>.lock`), acquired\n * via atomic exclusive creation (`open(..., \"wx\")`, which is atomic at the\n * OS level on POSIX and Windows alike) so only one process -- in this one or\n * any other -- can be inside the critical section at a time.\n *\n * The lock is never force-broken by age. An age-based \"the holder must have\n * crashed\" heuristic cannot tell a dead holder apart from one that is simply\n * slow (a large write, a GC pause, disk contention): a second process that\n * reclaims a lock the first still holds can commit and release before the\n * first resumes and overwrites that commit with its own now-stale snapshot\n * -- both processes report \"committed\", but only the second process's write\n * survives, and the first process's genuinely-accepted turn silently\n * vanishes. Node has no built-in binding for a real OS-mediated lock\n * (POSIX `flock`/Windows `LockFileEx`, where the kernel itself releases the\n * lock when the holder's process exits, crash included, so no staleness\n * guess is needed at all), so this fails closed instead: a lock that is\n * never released is never reclaimed, and a caller that can't acquire it\n * within the timeout gets a clear error rather than a silent lost write. A\n * lock file orphaned by a genuinely crashed process requires a human (or an\n * operator script) to remove `<path>.lock` before commits can resume --\n * that operational cost is the trade for never silently dropping an\n * acknowledged commit. Lock ownership is also verified before release (see\n * `withFileLock`): a process only ever deletes the lock file it created.\n *\n * Every `tryCommit` re-reads the file fresh from disk inside the lock (no\n * long-lived in-memory cache), and the in-memory return value is only ever\n * \"committed\" once the atomic (temp file + rename) write recording the key\n * has actually succeeded -- a failed write is never remembered as seen, so\n * retrying the same key attempts the commit again instead of returning a\n * phantom \"duplicate\" for a commit that never reached disk.\n *\n * Keys are never evicted. OpenClaw may retry at any point after a restart or\n * a long stall, so pruning by count or age would risk re-accepting an\n * already-committed turn -- exactly the failure this store exists to avoid.\n */\n\nimport { mkdir, open, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport * as os from \"node:os\";\n\n/**\n * True if `p` exists and is something other than a directory (e.g. a file\n * sitting where a directory needs to be). Some platforms (observed on\n * Windows) report `ENOENT` -- not a distinct \"not a directory\" code -- for\n * `readFile` when a path component like this blocks the rest of the path,\n * which is indistinguishable from \"the commit log just doesn't exist yet\"\n * by error code alone. Checking the parent directly is what tells the two\n * apart: silently treating a broken storage path as an empty store would\n * make every future commit re-accept an already-committed key.\n */\nasync function isBlockingNonDirectory(p: string): Promise<boolean> {\n try {\n const info = await stat(p);\n return !info.isDirectory();\n } catch {\n return false; // Doesn't exist -- not blocking anything.\n }\n}\n\n/** Default commit-log location, honoring the same workspace override the\n * rest of the Headroom workspace convention uses (`HEADROOM_WORKSPACE_DIR`). */\nexport function defaultCommitLogPath(): string {\n const workspaceDir = process.env.HEADROOM_WORKSPACE_DIR?.trim() || join(os.homedir(), \".headroom\");\n return join(workspaceDir, \"openclaw\", \"commit-log.json\");\n}\n\n/** One durably-committed turn: the accepted messages plus when they landed. */\nexport interface CommittedTurn {\n messages: unknown;\n committedAt: string;\n}\n\ntype CommitLog = Record<string, CommittedTurn>;\n\n/** Give up (rather than poll forever) if the lock still can't be acquired after this long. */\nconst LOCK_ACQUIRE_TIMEOUT_MS = 60_000;\nconst LOCK_POLL_BASE_MS = 20;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Run `fn` while holding an exclusive lock on `path` (a sibling `<path>.lock`\n * file). Serializes every caller -- this process or another -- against the\n * same file, not just calls on one object or one process.\n *\n * The lock is held until `fn` returns, however long that takes, and is\n * never reclaimed by another caller on a staleness guess -- see the module\n * docstring for why an age-based heuristic can silently drop a genuinely\n * accepted commit. A caller that can't acquire the lock within\n * `LOCK_ACQUIRE_TIMEOUT_MS` gets a clear timeout error instead of the\n * transaction being allowed to proceed unsafely.\n */\nasync function withFileLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockPath = `${path}.lock`;\n await mkdir(dirname(path), { recursive: true });\n // Unique per acquisition, so release only ever removes the lock file this\n // exact call created -- never one a different (later) holder created.\n const token = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n const deadline = Date.now() + LOCK_ACQUIRE_TIMEOUT_MS;\n let attempt = 0;\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(`${token}\\n${process.pid}\\n${new Date().toISOString()}\\n`);\n } finally {\n await handle.close();\n }\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code !== \"EEXIST\") {\n throw error;\n }\n if (Date.now() > deadline) {\n throw new Error(\n `Timed out waiting for the advancement-key-store lock on ${path}. If the ` +\n `process that created ${lockPath} is no longer running, remove it manually.`,\n );\n }\n attempt += 1;\n await sleep(LOCK_POLL_BASE_MS + Math.random() * LOCK_POLL_BASE_MS * Math.min(attempt, 10));\n }\n }\n try {\n return await fn();\n } finally {\n // Verify the lock file is still the one this call created before\n // removing it -- if it doesn't start with our token, some other process\n // (or a human) replaced or is using it, and it must not be touched.\n try {\n const current = await readFile(lockPath, \"utf8\");\n if (current.startsWith(`${token}\\n`)) {\n await unlink(lockPath);\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n throw error;\n }\n }\n }\n}\n\nexport class DurableAdvancementKeyStore {\n constructor(private readonly path: string) {}\n\n private async readAll(): Promise<CommitLog> {\n let raw: string;\n try {\n raw = await readFile(this.path, \"utf8\");\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n if (code !== \"ENOENT\" && code !== \"ENOTDIR\") {\n throw error;\n }\n if (await isBlockingNonDirectory(dirname(this.path))) {\n throw error;\n }\n return {};\n }\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as CommitLog;\n }\n if (Array.isArray(parsed)) {\n // Migrate the earlier key-only array format (no message payload) so\n // upgrading from it never re-accepts an already-committed key.\n const migrated: CommitLog = {};\n for (const entry of parsed) {\n if (typeof entry === \"string\") {\n migrated[entry] = { messages: null, committedAt: \"\" };\n }\n }\n return migrated;\n }\n return {};\n }\n\n private async writeAll(entries: CommitLog): Promise<void> {\n await mkdir(dirname(this.path), { recursive: true });\n const tmpPath = `${this.path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;\n await writeFile(tmpPath, JSON.stringify(entries), \"utf8\");\n await rename(tmpPath, this.path);\n }\n\n /**\n * Atomically check-and-record `key` together with the `messages` it\n * advances. Holds the cross-process file lock (see `withFileLock`) for\n * the full read/check/write transaction, so two processes -- or two\n * instances in this one -- can't each read the same starting state and\n * race their writes.\n *\n * Throws if the write fails -- the caller sees a rejected commit, not a\n * false \"committed\"/\"duplicate\", and a retry re-reads the (still\n * unchanged) on-disk state and attempts the write again.\n */\n async tryCommit(key: string, messages: unknown): Promise<\"committed\" | \"duplicate\"> {\n return withFileLock(this.path, async () => {\n const entries = await this.readAll();\n if (key in entries) {\n return \"duplicate\";\n }\n const next: CommitLog = {\n ...entries,\n [key]: { messages, committedAt: new Date().toISOString() },\n };\n await this.writeAll(next);\n return \"committed\";\n });\n }\n\n /** Test-only: check membership without recording. */\n async has(key: string): Promise<boolean> {\n const entries = await this.readAll();\n return key in entries;\n }\n\n /** Test-only: read back a committed entry (messages + commit time) without recording. */\n async get(key: string): Promise<CommittedTurn | undefined> {\n const entries = await this.readAll();\n return entries[key];\n }\n}\n","export interface OpenClawCompactParams {\n sessionId: string;\n sessionKey?: string;\n agentId?: string;\n sessionTarget?: unknown;\n sessionFile?: string;\n tokenBudget?: number;\n force?: boolean;\n currentTokenCount?: number;\n compactionTarget?: \"budget\" | \"threshold\";\n customInstructions?: string;\n runtimeSettings?: unknown;\n runtimeContext?: unknown;\n abortSignal?: AbortSignal;\n}\n\nexport interface OpenClawCompactResult {\n ok: boolean;\n compacted: boolean;\n reason?: string;\n result?: {\n summary?: string;\n firstKeptEntryId?: string;\n tokensBefore: number;\n tokensAfter?: number;\n details?: unknown;\n sessionId?: string;\n sessionTarget?: unknown;\n sessionFile?: string;\n };\n}\n\nconst OPENCLAW_PLUGIN_SDK_CORE = \"openclaw/plugin-sdk/core\";\n\n/** Delegate compaction without bundling the optional OpenClaw peer dependency. */\nexport async function delegateCompactionToRuntime(\n params: OpenClawCompactParams,\n): Promise<OpenClawCompactResult> {\n const pluginSdk = (await import(OPENCLAW_PLUGIN_SDK_CORE)) as {\n delegateCompactionToRuntime?: (\n compactParams: OpenClawCompactParams,\n ) => Promise<OpenClawCompactResult>;\n };\n\n if (!pluginSdk.delegateCompactionToRuntime) {\n throw new Error(\"OpenClaw runtime does not expose delegateCompactionToRuntime\");\n }\n\n return pluginSdk.delegateCompactionToRuntime(params);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport const DEFAULT_GATEWAY_PROVIDER_IDS = [\"openai-codex\"] as const;\n\nconst DEFAULT_PROVIDER_BASE_URLS: Readonly<Record<string, string>> = {\n \"openai-codex\": \"https://chatgpt.com/backend-api\",\n};\n\nconst GATEWAY_PROVIDER_ID_ALIASES: Readonly<Record<string, string>> = {\n codex: \"openai-codex\",\n claude: \"anthropic\",\n copilot: \"github-copilot\",\n gemini: \"google\",\n};\n\nconst EXPLICIT_BASE_URL_REQUIRED_PROVIDER_IDS = new Set<string>([\"github-copilot\"]);\n\nexport function resolveGatewayProviderIds(config: Record<string, unknown> | undefined): string[] {\n const configuredProviderIds = normalizeGatewayProviderIds(config?.gatewayProviderIds);\n if (configuredProviderIds.length > 0) {\n return configuredProviderIds;\n }\n\n if (config?.routeCodexViaProxy === false) {\n return [];\n }\n\n return [...DEFAULT_GATEWAY_PROVIDER_IDS];\n}\n\nfunction normalizeGatewayProviderIds(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n const seen = new Set<string>();\n const normalized: string[] = [];\n\n for (const entry of value) {\n if (typeof entry !== \"string\") {\n continue;\n }\n\n const rawProviderId = entry.trim();\n const providerId = GATEWAY_PROVIDER_ID_ALIASES[rawProviderId.toLowerCase()] ?? rawProviderId;\n if (!providerId || seen.has(providerId)) {\n continue;\n }\n\n seen.add(providerId);\n normalized.push(providerId);\n }\n\n return normalized;\n}\n\nexport function applyGatewayProviderBaseUrls<T>(\n cfg: T,\n proxyUrl: string,\n providerIds: readonly string[],\n): { changed: boolean; config: T } {\n const next = structuredClone((cfg ?? {}) as any);\n const changed = applyGatewayProviderBaseUrlsInPlace(next, proxyUrl, providerIds);\n return { changed, config: next as T };\n}\n\nexport function applyGatewayProviderBaseUrlsInPlace(\n cfg: any,\n proxyUrl: string,\n providerIds: readonly string[],\n): boolean {\n if (!cfg || typeof cfg !== \"object\" || providerIds.length === 0) {\n return false;\n }\n\n const models = (cfg.models ??= {});\n const providers = (models.providers ??= {});\n let changed = false;\n\n for (const providerId of providerIds) {\n const currentValue = providers[providerId];\n const currentConfig =\n currentValue && typeof currentValue === \"object\" && !Array.isArray(currentValue)\n ? currentValue\n : {};\n const nextConfig = { ...currentConfig };\n const currentBaseUrl =\n typeof nextConfig.baseUrl === \"string\" && nextConfig.baseUrl.trim().length > 0\n ? nextConfig.baseUrl\n : undefined;\n const defaultBaseUrl = DEFAULT_PROVIDER_BASE_URLS[providerId];\n if (\n !currentBaseUrl &&\n !defaultBaseUrl &&\n EXPLICIT_BASE_URL_REQUIRED_PROVIDER_IDS.has(providerId)\n ) {\n continue;\n }\n const nextBaseUrl = routeBaseUrlThroughProxy({\n providerId,\n proxyUrl,\n currentBaseUrl,\n });\n\n if (!Array.isArray(nextConfig.models)) {\n nextConfig.models = [];\n changed = true;\n }\n\n if (nextConfig.baseUrl === nextBaseUrl) {\n providers[providerId] = nextConfig;\n continue;\n }\n\n nextConfig.baseUrl = nextBaseUrl;\n providers[providerId] = nextConfig;\n changed = true;\n }\n\n return changed;\n}\n\nfunction routeBaseUrlThroughProxy(params: {\n providerId: string;\n proxyUrl: string;\n currentBaseUrl?: string;\n}): string {\n const upstreamBaseUrl = params.currentBaseUrl ?? DEFAULT_PROVIDER_BASE_URLS[params.providerId];\n if (!upstreamBaseUrl) {\n return params.proxyUrl;\n }\n\n try {\n const proxy = new URL(params.proxyUrl);\n const upstream = new URL(upstreamBaseUrl);\n proxy.pathname = upstream.pathname;\n proxy.search = upstream.search;\n proxy.hash = \"\";\n return proxy.toString().replace(/\\/$/, \"\");\n } catch {\n return params.proxyUrl;\n }\n}\n","/**\n * CCR (Compress-Cache-Retrieve) tool for OpenClaw.\n *\n * Allows the agent to retrieve original uncompressed content\n * from the Headroom proxy's compression store.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { normalizeAndValidateProxyUrl } from \"../proxy-manager.js\";\n\nexport interface RetrieveToolConfig {\n proxyUrl: string;\n}\n\nexport function createHeadroomRetrieveTool(config: RetrieveToolConfig) {\n const proxyOrigin = normalizeAndValidateProxyUrl(config.proxyUrl);\n\n return {\n name: \"headroom_retrieve\",\n description:\n \"Retrieve original uncompressed content from Headroom's compression store. \" +\n \"Use when compressed context mentions a hash and you need the full details. \" +\n \"Pass the hash from the compression marker (24 hex characters). \" +\n \"Retrieval is by hash and always returns the full original content.\",\n parameters: {\n type: \"object\" as const,\n properties: {\n hash: {\n type: \"string\",\n description: \"The 24-character hex hash from the compression marker\",\n },\n },\n required: [\"hash\"],\n },\n execute: async (args: { hash: string }): Promise<string> => {\n const { hash } = args;\n\n // Validate hash format\n if (!/^[a-f0-9]{24}$/i.test(hash)) {\n return JSON.stringify({\n error: \"Invalid hash format. Expected 24 hex characters.\",\n });\n }\n\n try {\n const url = `${proxyOrigin}/v1/retrieve/${hash}`;\n\n const resp = await fetch(url, {\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n return JSON.stringify({\n error: `Retrieval failed: HTTP ${resp.status}`,\n details: body,\n });\n }\n\n const data = await resp.json();\n return typeof data === \"string\" ? data : JSON.stringify(data);\n } catch (error) {\n return JSON.stringify({\n error: `Retrieval failed: ${error}`,\n hint: \"The compressed content may have expired (default TTL: 30 minutes)\",\n });\n }\n },\n };\n}\n","/**\n * Headroom OpenClaw Plugin — register ContextEngine + CCR retrieval tool.\n *\n * Usage:\n * openclaw plugins install headroom-ai/openclaw\n *\n * Configuration (in ~/.openclaw/config.json or ~/.clawdbot/clawdbot.json):\n * {\n * \"plugins\": {\n * \"slots\": { \"contextEngine\": \"headroom\" },\n * \"entries\": { \"headroom\": { \"enabled\": true } }\n * }\n * }\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { HeadroomContextEngine } from \"../engine.js\";\nimport {\n applyGatewayProviderBaseUrlsInPlace,\n resolveGatewayProviderIds,\n} from \"../gateway-config.js\";\nimport { normalizeAndValidateProxyUrl, probeHeadroomProxy } from \"../proxy-manager.js\";\nimport { createHeadroomRetrieveTool } from \"../tools/headroom-retrieve.js\";\n\n/**\n * OpenClaw 2026.x plugin API requires a `{ register(api) }` object export.\n * The previous bare-function default export was silently skipped by the loader.\n * See: https://github.com/headroomlabs-ai/headroom/issues/XXX\n */\nexport default {\n register: registerHeadroomPlugin,\n};\n\nexport function registerHeadroomPlugin(api: any) {\n const config = api.config?.plugins?.entries?.headroom?.config ?? {};\n const logger = api.logger ?? console;\n const rawProxyUrl = config.proxyUrl;\n const proxyUrl =\n typeof rawProxyUrl === \"string\" && rawProxyUrl.trim().length > 0\n ? normalizeAndValidateProxyUrl(rawProxyUrl)\n : undefined;\n\n const engine = new HeadroomContextEngine({ ...config, proxyUrl }, {\n info: (m: string) => logger.info(m),\n warn: (m: string) => logger.warn(m),\n error: (m: string) => logger.error(m),\n debug: (m: string) => logger.debug?.(m),\n });\n const gatewayProviderIds = resolveGatewayProviderIds(config);\n let validatedConfiguredProxyUrl: string | null = null;\n let configuredProxyProbePromise: Promise<string | null> | null = null;\n\n const applyGatewayRouting = async (activeProxyUrl: string) => {\n if (gatewayProviderIds.length === 0) {\n return;\n }\n\n try {\n const changed = applyGatewayProviderBaseUrlsInPlace(api.config, activeProxyUrl, gatewayProviderIds);\n\n if (changed) {\n logger.info(\n `[headroom] Routed ${gatewayProviderIds.join(\", \")} through Headroom proxy in memory at ${activeProxyUrl}`,\n );\n } else {\n logger.info(\n `[headroom] Upstream gateway already routed in memory for ${gatewayProviderIds.join(\", \")} at ${activeProxyUrl}`,\n );\n }\n } catch (error) {\n logger.warn(`[headroom] Failed to configure upstream gateway routing: ${error}`);\n }\n };\n\n const getConfiguredRoutingProxyUrl = async (): Promise<string | null> => {\n if (!proxyUrl) {\n return null;\n }\n if (validatedConfiguredProxyUrl === proxyUrl) {\n return validatedConfiguredProxyUrl;\n }\n if (!configuredProxyProbePromise) {\n configuredProxyProbePromise = probeHeadroomProxy(proxyUrl)\n .then((probe) => {\n if (probe.reachable && probe.isHeadroom) {\n validatedConfiguredProxyUrl = proxyUrl;\n return proxyUrl;\n }\n logger.warn(\n `[headroom] Skipping upstream gateway routing: configured proxyUrl is not a ready Headroom proxy at ${proxyUrl}` +\n (probe.reason ? ` (${probe.reason})` : \"\"),\n );\n return null;\n })\n .catch((error) => {\n logger.warn(\n `[headroom] Skipping upstream gateway routing: failed to probe configured proxyUrl ${proxyUrl}: ${error}`,\n );\n return null;\n })\n .finally(() => {\n configuredProxyProbePromise = null;\n });\n }\n return configuredProxyProbePromise;\n };\n\n const ensureGatewayRouting = async () => {\n if (gatewayProviderIds.length === 0) {\n return;\n }\n const activeProxyUrl = engine.getProxyUrl() ?? (await getConfiguredRoutingProxyUrl());\n if (!activeProxyUrl) {\n logger.debug?.(\"[headroom] Deferring upstream gateway routing until proxy is available\");\n return;\n }\n await applyGatewayRouting(activeProxyUrl);\n };\n\n engine.onProxyReady(async (activeProxyUrl) => {\n await applyGatewayRouting(activeProxyUrl);\n });\n\n // Register as context engine\n api.registerContextEngine(\"headroom\", () => engine);\n\n // Register CCR retrieval tool (active once proxy is running)\n api.registerTool((ctx: any) => {\n const activeProxyUrl = engine.getProxyUrl() ?? proxyUrl;\n if (!activeProxyUrl) return null;\n return createHeadroomRetrieveTool({ proxyUrl: activeProxyUrl });\n }, { names: [\"headroom_retrieve\"] });\n\n api.on(\"gateway_start\", async () => {\n await ensureGatewayRouting();\n });\n\n void ensureGatewayRouting();\n\n logger.info(\"[headroom] Plugin registered\");\n}\n"],"mappings":"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","names":["dirname","join","os"]}
1
+ {"version":3,"sources":["../src/engine.ts","../src/proxy-manager.ts","../src/convert.ts","../src/advancement-key-store.ts","../src/openclaw-compaction.ts","../src/gateway-config.ts","../src/tools/headroom-retrieve.ts","../src/plugin/index.ts"],"sourcesContent":["/**\n * HeadroomContextEngine — ContextEngine implementation for OpenClaw.\n *\n * Compresses tool outputs and conversation context using the Headroom proxy.\n * Zero LLM calls — all compression is algorithmic (SmartCrusher, ContentRouter, etc.)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { compress } from \"headroom-ai\";\nimport { ProxyManager, defaultLogger, type ProxyManagerConfig, type ProxyManagerLogger } from \"./proxy-manager.js\";\nimport { agentToOpenAIIndexed, normalizeAgentMessages, restoreAgentMessages } from \"./convert.js\";\nimport { DurableAdvancementKeyStore, defaultCommitLogPath } from \"./advancement-key-store.js\";\nimport {\n delegateCompactionToRuntime,\n type OpenClawCompactParams,\n type OpenClawCompactResult,\n} from \"./openclaw-compaction.js\";\n\n/** Race a promise against a timeout and always release the timer. */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timerId: ReturnType<typeof setTimeout> | undefined;\n const timer = new Promise<never>((_, reject) => {\n timerId = setTimeout(() => reject(new Error(`headroom compress() timed out after ${ms}ms`)), ms);\n });\n return Promise.race([promise, timer]).finally(() => {\n if (timerId !== undefined) clearTimeout(timerId);\n });\n}\n\n// Must stay static — OpenClaw prepends it to the cached system prompt.\nconst HEADROOM_COMPRESSION_NOTICE =\n \"[Headroom is compressing tool outputs in this session. Use headroom_retrieve if you need the original, uncompressed content.]\";\n\nexport interface HeadroomEngineConfig extends ProxyManagerConfig {\n enabled?: boolean;\n requestTimeoutMs?: number;\n circuitBreakerThreshold?: number;\n circuitBreakerCooldownMs?: number;\n /** Where to durably record committed turn-advancement keys (see\n * `DurableAdvancementKeyStore`). Defaults to `defaultCommitLogPath()`. */\n commitLogPath?: string;\n announceCompression?: boolean;\n}\n\nexport class HeadroomContextEngine {\n readonly info = {\n id: \"headroom\",\n name: \"Headroom Context Compression\",\n version: \"0.1.0\",\n ownsCompaction: false,\n transcriptSemantics: {\n currentTurnFence: \"before-current-turn-entry-v1\",\n turnAdvancementIdempotency: \"atomic-idempotent-v1\",\n },\n };\n\n // Durable, restart-safe record of committed advancement keys, for\n // commitTurn's idempotent-retry check. See advancement-key-store.ts for\n // why this must survive a process restart and must not evict entries.\n private advancementKeyStore: DurableAdvancementKeyStore;\n\n private proxyManager: ProxyManager;\n private proxyUrl: string | null = null;\n private config: HeadroomEngineConfig;\n private logger: ProxyManagerLogger;\n private proxyReadyListeners = new Set<(proxyUrl: string) => void | Promise<void>>();\n private proxyStartupPromise: Promise<string> | null = null;\n private proxyStartupError: unknown = null;\n private stats = {\n totalCompressions: 0,\n totalTokensSaved: 0,\n totalTokensBefore: 0,\n compactions: 0,\n };\n private circuit = { errors: 0, openUntilMs: 0 };\n // Sessions shown the compression notice. Bounded via insertion-order\n // eviction — no session lifecycle callback exists here to bound it otherwise.\n private announcedSessions = new Set<string>();\n private static readonly MAX_ANNOUNCED_SESSIONS = 1000;\n\n constructor(config: HeadroomEngineConfig = {}, logger?: ProxyManagerLogger) {\n this.config = config;\n this.logger = logger ?? defaultLogger;\n this.proxyManager = new ProxyManager(config, this.logger);\n this.advancementKeyStore = new DurableAdvancementKeyStore(\n config.commitLogPath ?? defaultCommitLogPath(),\n );\n }\n\n // === ContextEngine Lifecycle ===\n\n async bootstrap(params: {\n sessionId: string;\n sessionKey?: string;\n sessionFile: string;\n }): Promise<{ bootstrapped: boolean; reason?: string }> {\n if (this.config.enabled === false) {\n return { bootstrapped: false, reason: \"disabled\" };\n }\n\n this.ensureProxyStarted();\n return { bootstrapped: true, reason: \"proxy startup scheduled\" };\n }\n\n async ingest(params: {\n sessionId: string;\n message: any;\n isHeartbeat?: boolean;\n }): Promise<{ ingested: boolean }> {\n // No-op: OpenClaw's runtime stores messages. We don't need a separate store.\n return { ingested: true };\n }\n\n async ingestBatch?(params: {\n sessionId: string;\n messages: any[];\n isHeartbeat?: boolean;\n }): Promise<{ ingestedCount: number }> {\n return { ingestedCount: params.messages.length };\n }\n\n /**\n * Assemble context for the model — THE CORE HOOK.\n *\n * Converts AgentMessage[] → OpenAI format → compress() → AgentMessage[]. Only messages the proxy\n * actually changed are rebuilt; the rest are returned exactly as OpenClaw passed them, so the\n * provider prompt cache survives up to the first compressed message (see restoreAgentMessages).\n */\n async assemble(params: {\n sessionId: string;\n messages: any[];\n tokenBudget?: number;\n model?: string;\n prompt?: string;\n }): Promise<{\n messages: any[];\n estimatedTokens: number;\n systemPromptAddition?: string;\n }> {\n if (!this.proxyUrl || this.config.enabled === false) {\n this.ensureProxyStarted();\n // Fallback: return messages unchanged\n return {\n messages: normalizeAgentMessages(params.messages),\n estimatedTokens: 0,\n systemPromptAddition: this.sessionCompressionNotice(params.sessionId),\n };\n }\n\n if (this.isCircuitOpen()) {\n this.logger.warn(\"[headroom] Circuit open — using uncompressed messages\");\n return {\n messages: normalizeAgentMessages(params.messages),\n estimatedTokens: 0,\n systemPromptAddition: this.sessionCompressionNotice(params.sessionId),\n };\n }\n\n try {\n // Convert AgentMessage → OpenAI format\n const openaiMessages = agentToOpenAIIndexed(params.messages);\n\n // Compress via proxy — pass tokenBudget so RollingWindow enforces it\n const result = await withTimeout(\n compress(openaiMessages, {\n model: params.model ?? \"claude-sonnet-4-5\",\n baseUrl: this.proxyUrl,\n fallback: true,\n tokenBudget: params.tokenBudget,\n } as any),\n this.config.requestTimeoutMs ?? 30_000,\n );\n\n const shouldAnnounceCompression =\n (this.hasAnnouncedCompression(params.sessionId) || result.tokensSaved > 100) &&\n this.config.announceCompression !== false;\n if (shouldAnnounceCompression) {\n this.markCompressionAnnounced(params.sessionId);\n }\n\n if (!result.compressed || result.tokensSaved === 0) {\n this.resetCircuit();\n return {\n messages: normalizeAgentMessages(params.messages),\n estimatedTokens: result.tokensBefore,\n systemPromptAddition: this.sessionCompressionNotice(params.sessionId),\n };\n }\n\n // Convert back to AgentMessage format\n const compressedAgentMessages = restoreAgentMessages(params.messages, openaiMessages, result.messages);\n this.resetCircuit();\n\n // Track stats\n this.stats.totalCompressions++;\n this.stats.totalTokensSaved += result.tokensSaved;\n this.stats.totalTokensBefore += result.tokensBefore;\n\n this.logger.debug(\n `Assembled: ${result.tokensBefore} → ${result.tokensAfter} tokens (saved ${result.tokensSaved})`,\n );\n\n return {\n messages: compressedAgentMessages,\n estimatedTokens: result.tokensAfter,\n systemPromptAddition: this.sessionCompressionNotice(params.sessionId),\n };\n } catch (error) {\n this.logger.error(`Assemble failed: ${error}`);\n this.tripCircuit(error);\n // Graceful fallback: return original messages\n return {\n messages: normalizeAgentMessages(params.messages),\n estimatedTokens: 0,\n systemPromptAddition: this.sessionCompressionNotice(params.sessionId),\n };\n }\n }\n\n /** Delegate persistent compaction to OpenClaw's built-in runtime. */\n async compact(params: OpenClawCompactParams): Promise<OpenClawCompactResult> {\n const result = await delegateCompactionToRuntime(params);\n\n if (result.compacted) {\n this.stats.compactions++;\n }\n this.logger.info(\n `Compaction ${result.compacted ? \"completed\" : \"skipped\"} ` +\n `(budget: ${params.tokenBudget ?? \"none\"}, force: ${params.force ?? false})`,\n );\n\n return result;\n }\n\n /**\n * Durable turn-advancement commit — required by OpenClaw's transcriptSemantics\n * contract. Called only for the accepted, successful turn; failed or aborted\n * turns never reach here. Must be an atomic, idempotent write of the\n * accepted `messages` keyed by `advancementKey` so a host retry with the\n * same key reports \"duplicate\" instead of re-applying the advancement —\n * including a retry that arrives after this process restarted, which is\n * why the record lives on disk (see `DurableAdvancementKeyStore`) rather\n * than in memory, and includes the messages rather than just the key.\n */\n async commitTurn(params: { advancementKey: string; messages: any[] }): Promise<{\n status: \"committed\" | \"duplicate\";\n }> {\n const status = await this.advancementKeyStore.tryCommit(params.advancementKey, params.messages);\n return { status };\n }\n\n async afterTurn?(params: {\n sessionId: string;\n messages: any[];\n prePromptMessageCount: number;\n isHeartbeat?: boolean;\n }): Promise<void> {\n // Optional: could log stats or trigger learning\n }\n\n async prepareSubagentSpawn?(params: {\n parentSessionKey: string;\n childSessionKey: string;\n ttlMs?: number;\n }): Promise<{ rollback: () => Promise<void> } | undefined> {\n // Subagent context is compressed naturally via assemble()\n return undefined;\n }\n\n async onSubagentEnded?(params: {\n childSessionKey: string;\n reason: string;\n }): Promise<void> {\n // No-op\n }\n\n async dispose(): Promise<void> {\n await this.proxyManager.stop();\n this.logger.info(\n `Engine disposed. Stats: ${this.stats.totalCompressions} compressions, ` +\n `${this.stats.totalTokensSaved} tokens saved`,\n );\n }\n\n // --- Public API ---\n\n getStats() {\n return { ...this.stats };\n }\n\n getProxyUrl(): string | null {\n return this.proxyUrl;\n }\n\n getProxyStartupError(): unknown {\n return this.proxyStartupError;\n }\n\n private isCircuitOpen(): boolean {\n const threshold = this.config.circuitBreakerThreshold ?? 3;\n if (this.circuit.errors < threshold) return false;\n if (Date.now() < this.circuit.openUntilMs) return true;\n this.circuit = { errors: 0, openUntilMs: 0 };\n return false;\n }\n\n private tripCircuit(error: unknown): void {\n this.circuit.errors += 1;\n const threshold = this.config.circuitBreakerThreshold ?? 3;\n if (this.circuit.errors < threshold) return;\n const cooldownMs = this.config.circuitBreakerCooldownMs ?? 60_000;\n this.circuit.openUntilMs = Date.now() + cooldownMs;\n this.logger.warn(\n `[headroom] Circuit breaker opened after ${this.circuit.errors} errors ` +\n `(last: ${String(error)}); bypassing compression for ${cooldownMs}ms`,\n );\n }\n\n private resetCircuit(): void {\n this.circuit = { errors: 0, openUntilMs: 0 };\n }\n\n private hasAnnouncedCompression(sessionId: string): boolean {\n return this.announcedSessions.has(sessionId);\n }\n\n private markCompressionAnnounced(sessionId: string): void {\n if (this.announcedSessions.has(sessionId)) return;\n if (this.announcedSessions.size >= HeadroomContextEngine.MAX_ANNOUNCED_SESSIONS) {\n const oldest = this.announcedSessions.values().next().value;\n if (oldest !== undefined) this.announcedSessions.delete(oldest);\n }\n this.announcedSessions.add(sessionId);\n }\n\n /** The sticky notice if this session already earned it and announcements aren't opted out. */\n private sessionCompressionNotice(sessionId: string): string | undefined {\n return this.hasAnnouncedCompression(sessionId) && this.config.announceCompression !== false\n ? HEADROOM_COMPRESSION_NOTICE\n : undefined;\n }\n\n ensureProxyStarted(): void {\n if (this.config.enabled === false || this.proxyUrl || this.proxyStartupPromise) {\n return;\n }\n\n this.proxyStartupError = null;\n this.proxyStartupPromise = this.proxyManager\n .start()\n .then(async (proxyUrl) => {\n this.proxyUrl = proxyUrl;\n this.proxyStartupError = null;\n await this.notifyProxyReady(proxyUrl);\n this.logger.info(`Headroom proxy ready at ${proxyUrl}`);\n return proxyUrl;\n })\n .catch((error) => {\n this.proxyStartupError = error;\n this.logger.warn(`Headroom proxy unavailable: ${error}`);\n throw error;\n })\n .finally(() => {\n this.proxyStartupPromise = null;\n });\n\n // Fire-and-forget lifecycle callers intentionally do not await this promise.\n // Keep the promise rejectable for ensureProxyUrl(), but mark it observed so\n // a missing proxy cannot become a process-level unhandled rejection.\n void this.proxyStartupPromise.catch(() => {});\n }\n\n onProxyReady(listener: (proxyUrl: string) => void | Promise<void>): () => void {\n this.proxyReadyListeners.add(listener);\n return () => {\n this.proxyReadyListeners.delete(listener);\n };\n }\n\n async ensureProxyUrl(): Promise<string> {\n if (this.proxyUrl) {\n return this.proxyUrl;\n }\n\n this.ensureProxyStarted();\n if (!this.proxyStartupPromise) {\n throw new Error(\"Headroom proxy startup is disabled\");\n }\n return this.proxyStartupPromise;\n }\n\n private async notifyProxyReady(proxyUrl: string): Promise<void> {\n for (const listener of this.proxyReadyListeners) {\n try {\n await listener(proxyUrl);\n } catch (error) {\n this.logger.warn(`Headroom proxy ready listener failed: ${error}`);\n }\n }\n }\n}\n","/**\n * Manages connectivity to a Headroom proxy (local or remote).\n *\n * Security model:\n * - Local proxies (127.0.0.1 / localhost) can be auto-started via subprocess\n * - Remote proxies are connect-only: probe and use, never launch\n * - No environment variable access\n */\nimport { spawn } from \"node:child_process\";\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport os from \"os\";\n\nexport interface ProxyManagerConfig {\n proxyUrl?: string;\n proxyPort?: number;\n pythonPath?: string;\n autoStart?: boolean;\n startupTimeoutMs?: number;\n retryMaxAttempts?: number;\n connectTimeoutSeconds?: number;\n}\n\nexport interface ProxyManagerLogger {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n debug(message: string): void;\n}\n\n/** Default logger that prefixes all messages with `[headroom]`. */\nexport const defaultLogger: ProxyManagerLogger = {\n info: (m) => console.log(`[headroom] ${m}`),\n warn: (m) => console.warn(`[headroom] ${m}`),\n error: (m) => console.error(`[headroom] ${m}`),\n debug: () => {},\n};\n\nexport interface ProxyProbeResult {\n reachable: boolean;\n isHeadroom: boolean;\n reason?: string;\n}\n\ninterface LaunchSpec {\n label: string;\n command: string;\n args: string[];\n checkCommand: string;\n checkArgs: string[];\n useShell?: boolean;\n checkUseShell?: boolean;\n}\n\nconst HEADROOM_MODULE_DISCOVERY_SNIPPET =\n \"import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('headroom') else 1)\";\n\nexport class ProxyManager {\n private config: ProxyManagerConfig;\n private logger: ProxyManagerLogger;\n private proxyUrl: string | null = null;\n\n constructor(config: ProxyManagerConfig = {}, logger?: ProxyManagerLogger) {\n this.config = config;\n this.logger = logger ?? defaultLogger;\n }\n\n /**\n * Ensure a proxy is available. Returns the normalized URL origin.\n */\n async start(): Promise<string> {\n const port = this.getProxyPort();\n const rawExplicitUrl =\n typeof this.config.proxyUrl === \"string\" && this.config.proxyUrl.trim().length > 0\n ? normalizeAndValidateProxyUrl(this.config.proxyUrl)\n : null;\n // Only apply proxyPort default to local URLs — remote URLs use their protocol default\n const explicitUrl = rawExplicitUrl\n ? isLocalProxyUrl(rawExplicitUrl) ? withDefaultPort(rawExplicitUrl, port) : rawExplicitUrl\n : null;\n const defaultCandidates = this.getDefaultProxyCandidates(port);\n const candidateUrls = explicitUrl ? [explicitUrl] : [...defaultCandidates];\n const probeByUrl = new Map<string, ProxyProbeResult>();\n\n for (const url of candidateUrls) {\n const probe = await probeHeadroomProxy(url);\n probeByUrl.set(url, probe);\n if (probe.reachable && probe.isHeadroom) {\n this.proxyUrl = url;\n this.logger.info(`Headroom proxy already running at ${url}`);\n return url;\n }\n }\n\n if (explicitUrl) {\n const explicitProbe = probeByUrl.get(explicitUrl);\n if (explicitProbe?.reachable && !explicitProbe.isHeadroom) {\n throw new Error(\n `Service reachable at ${explicitUrl}, but it does not appear to be a Headroom proxy (${explicitProbe.reason ?? \"unknown service\"}).`,\n );\n }\n }\n\n // Remote URLs are connect-only — never auto-start a subprocess for them\n if (explicitUrl && !isLocalProxyUrl(explicitUrl)) {\n throw new Error(\n `Remote Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running at that address.`,\n );\n }\n\n // Auto-start is only available for local proxies\n if (this.config.autoStart === true) {\n const startupUrl = explicitUrl ?? defaultCandidates[0];\n const startupProbe = probeByUrl.get(startupUrl);\n if (startupProbe?.reachable && !startupProbe.isHeadroom) {\n throw new Error(\n `Cannot auto-start Headroom at ${startupUrl}: port is in use by a non-Headroom service (${startupProbe.reason ?? \"unknown service\"}).`,\n );\n }\n\n this.logger.info(\n `No Headroom proxy detected${explicitUrl ? ` at ${startupUrl}` : \" on default local endpoints\"}; attempting to auto-start...`,\n );\n await this.startHeadroomProxy(startupUrl, port);\n\n const startedProbe = await waitForHeadroomProxy(\n startupUrl,\n this.config.startupTimeoutMs ?? 20_000,\n );\n if (startedProbe.reachable && startedProbe.isHeadroom) {\n this.proxyUrl = startupUrl;\n this.logger.info(`Headroom proxy started and reachable at ${startupUrl}`);\n return startupUrl;\n }\n throw new Error(\n `Attempted to start Headroom proxy, but it was not reachable at ${startupUrl} (${startedProbe.reason ?? \"unknown\"}).`,\n );\n }\n\n if (explicitUrl) {\n throw new Error(\n `Headroom proxy not reachable at ${explicitUrl}. Ensure the proxy is running first.`,\n );\n }\n\n throw new Error(\n `Headroom proxy not detected on default endpoints (${defaultCandidates.join(\", \")}). ` +\n \"Set proxyUrl explicitly or enable autoStart.\",\n );\n }\n\n private getProxyPort(): number {\n const rawPort = this.config.proxyPort;\n if (!Number.isInteger(rawPort) || rawPort === undefined) return 8787;\n if (rawPort < 1 || rawPort > 65535) {\n throw new Error(\"proxyPort must be an integer between 1 and 65535\");\n }\n return rawPort;\n }\n\n private getDefaultProxyCandidates(port: number): string[] {\n return [`http://127.0.0.1:${port}`, `http://localhost:${port}`];\n }\n\n /**\n * Stop manager state. Spawned proxy processes are detached and externally managed.\n */\n async stop(): Promise<void> {\n this.proxyUrl = null;\n }\n\n getUrl(): string | null {\n return this.proxyUrl;\n }\n\n // --- Internal ---\n\n private async startHeadroomProxy(proxyUrl: string, defaultPort: number): Promise<void> {\n const parsed = new URL(proxyUrl);\n const host = parsed.hostname;\n const port = parsed.port || String(defaultPort);\n const specs = this.buildLaunchSpecs(host, port);\n const errors: string[] = [];\n\n for (const spec of specs) {\n if (!this.canExecute(spec.checkCommand, spec.checkArgs, spec.checkUseShell ?? spec.useShell)) {\n this.logger.debug(`Launcher unavailable: ${spec.label}`);\n continue;\n }\n\n try {\n const child = spawn(spec.command, spec.args, {\n detached: true,\n shell: spec.useShell === true,\n stdio: \"ignore\",\n });\n child.unref();\n this.logger.info(`Auto-start launcher selected: ${spec.label}`);\n return;\n } catch (error) {\n errors.push(`${spec.label}: ${String(error)}`);\n }\n }\n\n throw new Error(\n \"No usable Headroom launcher found. Tried PATH, local npm, global npm, and Python. \" +\n \"Install headroom-ai (npm or pip) and ensure one launcher is available.\\n\" +\n (errors.length > 0 ? `Launch errors: ${errors.join(\"; \")}` : \"\"),\n );\n }\n\n private buildLaunchSpecs(host: string, port: string): LaunchSpec[] {\n const commonArgs = [\"proxy\", \"--host\", host, \"--port\", port];\n const retryMaxAttempts = this.config.retryMaxAttempts;\n if (Number.isInteger(retryMaxAttempts)) {\n commonArgs.push(\"--retry-max-attempts\", String(retryMaxAttempts));\n }\n\n const connectTimeoutSeconds = this.config.connectTimeoutSeconds;\n if (Number.isInteger(connectTimeoutSeconds)) {\n commonArgs.push(\"--connect-timeout-seconds\", String(connectTimeoutSeconds));\n }\n\n const specs: LaunchSpec[] = [];\n\n const configuredPython = this.getConfiguredPythonCommand();\n if (configuredPython) {\n specs.push({\n label: `Configured Python: ${configuredPython} -m headroom.cli`,\n command: configuredPython,\n args: [\"-m\", \"headroom.cli\", ...commonArgs],\n checkCommand: configuredPython,\n checkArgs: [\"-c\", HEADROOM_MODULE_DISCOVERY_SNIPPET],\n });\n }\n\n // 2) Windows pyenv: resolve the real executable so we avoid shim .bat wrappers.\n if (process.platform === \"win32\") {\n const pyenvHeadroom = this.getPyenvResolvedHeadroom();\n if (pyenvHeadroom) {\n specs.push({\n label: `pyenv: ${pyenvHeadroom}`,\n command: pyenvHeadroom,\n args: commonArgs,\n checkCommand: pyenvHeadroom,\n checkArgs: [\"--version\"],\n useShell: false,\n });\n }\n }\n\n // 3) PATH\n specs.push({\n label: \"PATH: headroom\",\n command: \"headroom\",\n args: commonArgs,\n checkCommand: process.platform === \"win32\" ? \"where.exe\" : \"sh\",\n checkArgs: process.platform === \"win32\"\n ? [\"headroom\"]\n : [\"-c\", \"command -v headroom >/dev/null 2>&1\"],\n useShell: process.platform === \"win32\",\n checkUseShell: false,\n });\n\n // 4) uv tool install path (~/.local/bin/headroom)\n const uvBin = join(\n os.homedir(),\n \".local\", \"bin\", \"headroom\"\n );\n if (existsSync(uvBin)) {\n specs.push({\n label: `uv tool: ${uvBin}`,\n command: uvBin,\n args: commonArgs,\n checkCommand: uvBin,\n checkArgs: [\"--version\"],\n });\n }\n\n // 5) Local npm install (inside plugin install path)\n const moduleDir = dirname(fileURLToPath(import.meta.url)); // .../dist\n const packageRoot = dirname(moduleDir);\n const localBinDir = join(packageRoot, \"node_modules\", \".bin\");\n const localBins = process.platform === \"win32\"\n ? [join(localBinDir, \"headroom.cmd\"), join(localBinDir, \"headroom\")]\n : [join(localBinDir, \"headroom\")];\n for (const localBin of localBins) {\n if (!existsSync(localBin)) continue;\n specs.push({\n label: `Local npm: ${localBin}`,\n command: localBin,\n args: commonArgs,\n checkCommand: localBin,\n checkArgs: [\"--version\"],\n useShell: process.platform === \"win32\",\n });\n }\n\n // 5) Global npm install\n const npmPrefix = this.getNpmGlobalPrefix();\n if (npmPrefix) {\n const globalBins = process.platform === \"win32\"\n ? [join(npmPrefix, \"headroom.cmd\"), join(npmPrefix, \"headroom\")]\n : [join(npmPrefix, \"bin\", \"headroom\"), join(npmPrefix, \"headroom\")];\n\n for (const globalBin of globalBins) {\n if (!existsSync(globalBin)) continue;\n specs.push({\n label: `Global npm: ${globalBin}`,\n command: globalBin,\n args: commonArgs,\n checkCommand: globalBin,\n checkArgs: [\"--version\"],\n useShell: process.platform === \"win32\",\n });\n }\n }\n\n // 6) Python module fallback\n const pythonCommands = this.getPythonCommands();\n for (const pyCmd of pythonCommands) {\n if (configuredPython && pyCmd === configuredPython) continue;\n specs.push({\n label: `Python: ${pyCmd} -m headroom.cli`,\n command: pyCmd,\n args: [\"-m\", \"headroom.cli\", ...commonArgs],\n checkCommand: pyCmd,\n checkArgs: [\"-c\", HEADROOM_MODULE_DISCOVERY_SNIPPET],\n });\n }\n\n return specs;\n }\n\n private getConfiguredPythonCommand(): string | null {\n const configured = typeof this.config.pythonPath === \"string\"\n ? this.config.pythonPath.trim()\n : \"\";\n return configured.length > 0 ? configured : null;\n }\n\n private getPyenvResolvedHeadroom(): string | null {\n if (process.platform !== \"win32\") return null;\n\n try {\n const result = spawnSync(\"pyenv\", [\"which\", \"headroom\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 5000,\n });\n if (result.error || result.status !== 0) return null;\n const resolved = (result.stdout ?? \"\").trim().split(/\\r?\\n/, 1)[0];\n if (!resolved || !existsSync(resolved)) return null;\n return resolved;\n } catch {\n return null;\n }\n }\n\n private getPythonCommands(): string[] {\n const commands: string[] = [];\n const configured = this.getConfiguredPythonCommand() ?? \"\";\n if (configured.length > 0) {\n commands.push(configured);\n }\n for (const fallback of [\"python\", \"python3\", \"py\"]) {\n if (!commands.includes(fallback)) commands.push(fallback);\n }\n return commands;\n }\n\n private canExecute(command: string, args: string[], useShell = false): boolean {\n try {\n const result = spawnSync(command, args, {\n shell: useShell,\n stdio: \"ignore\",\n timeout: 5000,\n });\n if (result.error) return false;\n return result.status === 0;\n } catch {\n return false;\n }\n }\n\n private getNpmGlobalPrefix(): string | null {\n try {\n const result = spawnSync(\"npm\", [\"prefix\", \"-g\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 5000,\n });\n if (result.error || result.status !== 0) return null;\n const prefix = (result.stdout ?? \"\").trim();\n return prefix.length > 0 ? prefix : null;\n } catch {\n return null;\n }\n }\n}\n\n/** Parse a URL, returning the parsed object or throwing a descriptive error. */\nfunction parseProxyUrl(proxyUrl: string): URL {\n try {\n return new URL(proxyUrl);\n } catch {\n throw new Error(`Invalid proxyUrl: \"${proxyUrl}\"`);\n }\n}\n\nexport function normalizeAndValidateProxyUrl(proxyUrl: string): string {\n const parsed = parseProxyUrl(proxyUrl);\n\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"proxyUrl must use http:// or https://\");\n }\n\n if (parsed.pathname !== \"/\" || parsed.search || parsed.hash) {\n throw new Error(\"proxyUrl must not include a path, query, or hash\");\n }\n\n return parsed.origin;\n}\n\n/** Returns true if the URL points to a local address (localhost or 127.0.0.1). */\nexport function isLocalProxyUrl(proxyUrl: string): boolean {\n try {\n const parsed = new URL(proxyUrl);\n return parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"localhost\";\n } catch {\n return false;\n }\n}\n\nfunction withDefaultPort(proxyUrl: string, defaultPort: number): string {\n const parsed = parseProxyUrl(proxyUrl);\n if (!parsed.port) {\n parsed.port = String(defaultPort);\n }\n return parsed.origin;\n}\n\n/**\n * Probe a configured URL and verify whether it is a running Headroom proxy.\n */\nexport async function probeHeadroomProxy(proxyUrl: string): Promise<ProxyProbeResult> {\n const origin = normalizeAndValidateProxyUrl(proxyUrl);\n const probeEndpoint = async (\n path: string,\n options: { readBody?: boolean } = {},\n ): Promise<{ reachable: boolean; ok: boolean; status?: number; body?: string }> => {\n try {\n const response = await fetch(`${origin}${path}`, {\n signal: AbortSignal.timeout(3_000),\n });\n const body =\n response.ok && options.readBody\n ? await response.text().catch(() => undefined)\n : undefined;\n return { reachable: true, ok: response.ok, status: response.status, body };\n } catch {\n return { reachable: false, ok: false };\n }\n };\n\n const ready = await probeEndpoint(\"/readyz\");\n const retrieveStats = await probeEndpoint(\"/v1/retrieve/stats\", { readBody: true });\n if (retrieveStats.ok && hasHeadroomStatsShape(retrieveStats.body)) {\n return { reachable: true, isHeadroom: true };\n }\n\n const stats = await probeEndpoint(\"/stats\", { readBody: true });\n if (stats.ok && hasHeadroomStatsShape(stats.body)) {\n return { reachable: true, isHeadroom: true };\n }\n\n const health = await probeEndpoint(\"/health\");\n const anyReachable = ready.reachable || retrieveStats.reachable || stats.reachable || health.reachable;\n if (!anyReachable) {\n return { reachable: false, isHeadroom: false, reason: \"proxy probe failed\" };\n }\n\n const reasons = [\n ready.reachable ? `readyz HTTP ${ready.status}` : \"readyz unavailable\",\n retrieveStats.reachable\n ? `retrieve stats HTTP ${retrieveStats.status}`\n : \"retrieve stats endpoint unavailable\",\n stats.reachable ? `stats HTTP ${stats.status}` : \"stats endpoint unavailable\",\n health.reachable ? `health HTTP ${health.status}` : \"health check failed\",\n ];\n return { reachable: true, isHeadroom: false, reason: reasons.join(\"; \") };\n}\n\nfunction hasHeadroomStatsShape(body: string | undefined): boolean {\n if (!body) {\n return false;\n }\n\n try {\n const parsed = JSON.parse(body) as Record<string, unknown>;\n return (\n parsed !== null &&\n typeof parsed === \"object\" &&\n (Object.hasOwn(parsed, \"proxy_inbound\") ||\n Object.hasOwn(parsed, \"api_requests\") ||\n Object.hasOwn(parsed, \"provider_tokens\") ||\n Object.hasOwn(parsed, \"proxy_compression_saved\") ||\n Object.hasOwn(parsed, \"store\") ||\n Object.hasOwn(parsed, \"recent_retrievals\"))\n );\n } catch {\n return false;\n }\n}\n\nasync function waitForHeadroomProxy(proxyUrl: string, timeoutMs: number): Promise<ProxyProbeResult> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const result = await probeHeadroomProxy(proxyUrl);\n if (result.reachable && result.isHeadroom) {\n return result;\n }\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n return probeHeadroomProxy(proxyUrl);\n}\n","/**\n * Convert between OpenClaw's AgentMessage format and OpenAI message format.\n *\n * AgentMessage uses:\n * role: \"user\" | \"assistant\" | \"toolResult\"\n * content: string | ContentBlock[]\n *\n * OpenAI uses:\n * role: \"user\" | \"assistant\" | \"system\" | \"tool\"\n * content: string\n * tool_calls?: ToolCall[]\n * tool_call_id?: string\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface OpenAIMessage {\n role: string;\n content: string | null;\n tool_calls?: any[];\n tool_call_id?: string;\n name?: string;\n _headroomMeta?: Record<string, unknown>;\n}\n\n/**\n * Convert AgentMessage[] to OpenAI message format for compression.\n */\nexport function agentToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n const normalized = normalizeAgentMessage(msg);\n const role = normalized.role;\n\n const buildMeta = (): Record<string, unknown> => {\n const meta = { ...normalized } as Record<string, unknown>;\n delete meta.role;\n delete meta.content;\n return meta;\n };\n\n if (role === \"system\") {\n result.push({\n role: \"system\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : extractText(normalized.content),\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n if (role === \"user\") {\n result.push({\n role: \"user\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : extractText(normalized.content),\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n if (role === \"assistant\") {\n const content = normalized.content;\n if (typeof content === \"string\") {\n result.push({ role: \"assistant\", content, _headroomMeta: buildMeta() });\n continue;\n }\n\n // Content blocks: extract text and tool call blocks.\n // OpenClaw uses `toolCall`; some adapters still emit legacy `tool_use`.\n if (Array.isArray(content)) {\n const textParts: string[] = [];\n const toolCalls: any[] = [];\n\n for (const block of content) {\n if (typeof block === \"string\") {\n textParts.push(block);\n } else if (block.type === \"text\") {\n textParts.push(block.text);\n } else if (block.type === \"tool_use\" || block.type === \"toolCall\") {\n const args =\n block.type === \"toolCall\"\n ? block.arguments\n : block.input;\n toolCalls.push({\n id: block.id,\n type: \"function\",\n function: {\n name: block.name,\n arguments:\n typeof args === \"string\"\n ? args\n : JSON.stringify(args ?? {}),\n },\n });\n }\n }\n\n const openaiMsg: OpenAIMessage = {\n role: \"assistant\",\n content: textParts.length > 0 ? textParts.join(\"\") : null,\n _headroomMeta: buildMeta(),\n };\n if (toolCalls.length > 0) {\n openaiMsg.tool_calls = toolCalls;\n }\n result.push(openaiMsg);\n }\n continue;\n }\n\n if (role === \"toolResult\" || role === \"tool_result\") {\n const content =\n typeof normalized.content === \"string\"\n ? normalized.content\n : Array.isArray(normalized.content)\n ? extractText(normalized.content)\n : JSON.stringify(normalized.content);\n\n result.push({\n role: \"tool\",\n content,\n tool_call_id:\n normalized.toolCallId ??\n normalized.tool_use_id ??\n normalized.id ??\n \"unknown\",\n _headroomMeta: buildMeta(),\n });\n continue;\n }\n\n // Fallback: pass through as user message\n result.push({\n role: \"user\",\n content:\n typeof normalized.content === \"string\"\n ? normalized.content\n : JSON.stringify(normalized.content),\n _headroomMeta: buildMeta(),\n });\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to AgentMessage format.\n */\nexport function openAIToAgent(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n const meta = (msg._headroomMeta ?? {}) as Record<string, unknown>;\n const timestamp =\n typeof meta.timestamp === \"number\" ? meta.timestamp : Date.now();\n\n if (msg.role === \"system\") {\n result.push({\n role: \"system\",\n content: msg.content ?? \"\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"user\") {\n result.push({\n role: \"user\",\n content: msg.content ?? \"\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: any;\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = tc.function.arguments ?? {};\n }\n // Emit OpenClaw-native block shape so downstream transports keep call linkage.\n blocks.push({\n type: \"toolCall\",\n id: tc.id,\n name: tc.function.name,\n arguments: input,\n });\n }\n }\n // OpenClaw's Pi agent expects content to always be an array for assistant messages\n // (it calls .flatMap() on it). Never flatten to a string.\n result.push({\n ...(meta as object),\n role: \"assistant\",\n content: blocks,\n api: typeof meta.api === \"string\" ? meta.api : \"headroom\",\n provider: typeof meta.provider === \"string\" ? meta.provider : \"headroom\",\n model: typeof meta.model === \"string\" ? meta.model : \"headroom\",\n usage:\n isRecord(meta.usage)\n ? meta.usage\n : {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason:\n typeof meta.stopReason === \"string\" ? meta.stopReason : \"stop\",\n timestamp,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n const textContent =\n typeof msg.content === \"string\"\n ? msg.content\n : msg.content == null\n ? \"\"\n : JSON.stringify(msg.content);\n const toolCallId = msg.tool_call_id ?? \"unknown\";\n result.push({\n ...(meta as object),\n role: \"toolResult\",\n // OpenClaw transport layers expect toolResult content blocks, not a raw string.\n content: [{ type: \"text\", text: textContent }],\n toolCallId:\n typeof meta.toolCallId === \"string\" ? meta.toolCallId : toolCallId,\n tool_use_id:\n typeof meta.tool_use_id === \"string\" ? meta.tool_use_id : toolCallId,\n toolName:\n typeof meta.toolName === \"string\" ? meta.toolName : \"headroom\",\n isError: typeof meta.isError === \"boolean\" ? meta.isError : false,\n timestamp,\n });\n continue;\n }\n }\n\n return result;\n}\n\nexport function normalizeAgentMessages(messages: any[]): any[] {\n return messages.map((message) => normalizeAgentMessage(message));\n}\n\n/** `_headroomMeta` key recording which AgentMessage a converted message came from. */\nconst SOURCE_INDEX_KEY = \"headroomSourceIndex\";\n\n/**\n * Like `agentToOpenAI`, but stamps every converted message with the index of the AgentMessage it came from.\n * The proxy returns `_headroomMeta` unchanged, so `restoreAgentMessages` can map its result back onto the\n * originals even when it drops messages to fit a token budget.\n */\nexport function agentToOpenAIIndexed(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n messages.forEach((message, index) => {\n for (const converted of agentToOpenAI([message])) {\n result.push({\n ...converted,\n _headroomMeta: { ...(converted._headroomMeta ?? {}), [SOURCE_INDEX_KEY]: index },\n });\n }\n });\n return result;\n}\n\n/**\n * Map the proxy's result back onto the AgentMessages OpenClaw passed in.\n *\n * `openAIToAgent(agentToOpenAI(m))` is lossy: it drops `thinking` blocks and flattens user and tool content\n * blocks into one string. Applying it to every message whenever anything is compressed rewrites the whole\n * history, which invalidates the provider's prompt cache from the first message on every compressing turn.\n * So a message the proxy returned unchanged is passed through exactly as it came in, and a message it did\n * change keeps every original field and block except the text that compression replaced.\n *\n * `sent` must be the `agentToOpenAIIndexed(originals)` output that was given to the proxy.\n */\nexport function restoreAgentMessages(\n originals: any[],\n sent: OpenAIMessage[],\n returned: OpenAIMessage[],\n): any[] {\n const sentByIndex = new Map<number, OpenAIMessage>();\n for (const message of sent) {\n const index = sourceIndexOf(message);\n if (index !== undefined) sentByIndex.set(index, message);\n }\n\n const result: any[] = [];\n for (const message of returned) {\n const index = sourceIndexOf(message);\n const original = index === undefined ? undefined : originals[index];\n const before = index === undefined ? undefined : sentByIndex.get(index);\n if (original === undefined || before === undefined) {\n // Not one of ours (e.g. a message the proxy added): convert it the way it always was.\n result.push(...openAIToAgent([withoutSourceIndex(message)]));\n } else if (sameWireMessage(before, message)) {\n result.push(normalizeAgentMessage(original));\n } else {\n result.push(applyCompressedMessage(original, message));\n }\n }\n return result;\n}\n\nfunction sourceIndexOf(message: OpenAIMessage): number | undefined {\n const index = message._headroomMeta?.[SOURCE_INDEX_KEY];\n return typeof index === \"number\" ? index : undefined;\n}\n\nfunction withoutSourceIndex(message: OpenAIMessage): OpenAIMessage {\n if (!message._headroomMeta || !(SOURCE_INDEX_KEY in message._headroomMeta)) return message;\n const { [SOURCE_INDEX_KEY]: _index, ...meta } = message._headroomMeta;\n return { ...message, _headroomMeta: meta };\n}\n\nfunction sameWireMessage(a: OpenAIMessage, b: OpenAIMessage): boolean {\n return (\n a.role === b.role &&\n a.content === b.content &&\n a.tool_call_id === b.tool_call_id &&\n a.name === b.name &&\n JSON.stringify(a.tool_calls ?? null) === JSON.stringify(b.tool_calls ?? null)\n );\n}\n\nfunction applyCompressedMessage(original: any, compressed: OpenAIMessage): any {\n const base = normalizeAgentMessage(original);\n const text = typeof compressed.content === \"string\" ? compressed.content : \"\";\n\n if (base.role === \"assistant\") {\n let content = replaceTextBlocks(base.content, text);\n if (Array.isArray(compressed.tool_calls)) {\n content = content.map((block: any) => withCompressedArguments(block, compressed.tool_calls!));\n }\n return { ...base, content };\n }\n\n if (typeof base.content === \"string\") return { ...base, content: text };\n return { ...base, content: replaceTextBlocks(base.content, text) };\n}\n\n/** Replace the text blocks with one block holding `text`, where the first one was; keep every other block. */\nfunction replaceTextBlocks(content: unknown, text: string): any[] {\n const blocks = Array.isArray(content) ? content : [{ type: \"text\", text: String(content ?? \"\") }];\n const result: any[] = [];\n let placed = false;\n for (const block of blocks) {\n const isText = typeof block === \"string\" || (isRecord(block) && block.type === \"text\");\n if (!isText) {\n result.push(block);\n continue;\n }\n if (!placed) {\n if (text) result.push(isRecord(block) ? { ...block, text } : { type: \"text\", text });\n placed = true;\n }\n }\n if (!placed && text) result.unshift({ type: \"text\", text });\n return result;\n}\n\nfunction withCompressedArguments(block: any, toolCalls: any[]): any {\n if (!isRecord(block) || (block.type !== \"toolCall\" && block.type !== \"tool_use\")) return block;\n const call = toolCalls.find((tc) => tc?.id === block.id);\n const raw = call?.function?.arguments;\n if (typeof raw !== \"string\") return block;\n const current = block.type === \"toolCall\" ? block.arguments : block.input;\n if ((typeof current === \"string\" ? current : JSON.stringify(current ?? {})) === raw) return block;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n parsed = raw;\n }\n return block.type === \"toolCall\" ? { ...block, arguments: parsed } : { ...block, input: parsed };\n}\n\n/**\n * Extract text from content blocks.\n */\nfunction extractText(content: any): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return JSON.stringify(content);\n\n return content\n .map((block: any) => {\n if (typeof block === \"string\") return block;\n if (block.type === \"text\") return block.text;\n if (block.type === \"tool_result\") {\n return typeof block.content === \"string\" ? block.content : JSON.stringify(block.content);\n }\n return \"\";\n })\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction normalizeAgentMessage(message: any): any {\n if (!isRecord(message)) return message;\n\n if (message.role === \"assistant\") {\n return normalizeAssistantMessage(message);\n }\n\n if (message.role === \"toolResult\" || message.role === \"tool_result\") {\n return normalizeToolResultMessage(message);\n }\n\n return message;\n}\n\nfunction normalizeAssistantMessage(message: Record<string, any>): Record<string, any> {\n const normalizedContent = normalizeAssistantContent(message.content);\n\n return {\n ...message,\n content: normalizedContent,\n api: typeof message.api === \"string\" ? message.api : \"headroom\",\n provider: typeof message.provider === \"string\" ? message.provider : \"headroom\",\n model: typeof message.model === \"string\" ? message.model : \"headroom\",\n usage: isRecord(message.usage)\n ? message.usage\n : {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: typeof message.stopReason === \"string\" ? message.stopReason : \"stop\",\n timestamp: typeof message.timestamp === \"number\" ? message.timestamp : Date.now(),\n };\n}\n\nfunction normalizeToolResultMessage(message: Record<string, any>): Record<string, any> {\n const normalizedContent = normalizeToolResultContent(message.content);\n const toolCallId =\n typeof message.toolCallId === \"string\"\n ? message.toolCallId\n : typeof message.tool_use_id === \"string\"\n ? message.tool_use_id\n : typeof message.id === \"string\"\n ? message.id\n : \"unknown\";\n\n return {\n ...message,\n role: \"toolResult\",\n content: normalizedContent,\n toolCallId,\n tool_use_id:\n typeof message.tool_use_id === \"string\" ? message.tool_use_id : toolCallId,\n toolName: typeof message.toolName === \"string\" ? message.toolName : \"headroom\",\n isError: typeof message.isError === \"boolean\" ? message.isError : false,\n timestamp: typeof message.timestamp === \"number\" ? message.timestamp : Date.now(),\n };\n}\n\nfunction normalizeAssistantContent(content: unknown): any[] {\n if (Array.isArray(content)) {\n return content.flatMap((block) => {\n if (typeof block === \"string\") return [{ type: \"text\", text: block }];\n if (!isRecord(block) || typeof block.type !== \"string\") return [];\n if (block.type === \"text\" && typeof block.text === \"string\") return [block];\n if (block.type === \"thinking\" && typeof block.thinking === \"string\") return [block];\n if (\n (block.type === \"toolCall\" || block.type === \"tool_use\") &&\n typeof block.name === \"string\"\n ) {\n // Legacy input becomes arguments; do not retain an alias that can go stale after compression.\n const { input: _legacyInput, ...metadata } = block;\n return [\n {\n ...(block.type === \"tool_use\" ? metadata : block),\n type: \"toolCall\",\n id: typeof block.id === \"string\" ? block.id : \"unknown\",\n name: block.name,\n arguments:\n \"arguments\" in block\n ? block.arguments\n : \"input\" in block\n ? block.input\n : {},\n },\n ];\n }\n return [];\n });\n }\n\n if (typeof content === \"string\" && content.length > 0) {\n return [{ type: \"text\", text: content }];\n }\n\n if (content == null) {\n return [];\n }\n\n return [{ type: \"text\", text: JSON.stringify(content) }];\n}\n\nfunction normalizeToolResultContent(content: unknown): any[] {\n if (Array.isArray(content)) {\n return content.flatMap((block) => {\n if (typeof block === \"string\") return [{ type: \"text\", text: block }];\n if (!isRecord(block) || typeof block.type !== \"string\") return [];\n if (block.type === \"text\" && typeof block.text === \"string\") return [block];\n if (\n block.type === \"image\" &&\n typeof block.data === \"string\" &&\n typeof block.mimeType === \"string\"\n ) {\n return [block];\n }\n if (block.type === \"tool_result\" && \"content\" in block) {\n return normalizeToolResultContent(block.content);\n }\n return [];\n });\n }\n\n if (typeof content === \"string\" && content.length > 0) {\n return [{ type: \"text\", text: content }];\n }\n\n if (content == null) {\n return [];\n }\n\n return [{ type: \"text\", text: JSON.stringify(content) }];\n}\n\nfunction isRecord(value: unknown): value is Record<string, any> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * Durable, atomic, restart-safe, cross-process-safe record of committed\n * turn advancements.\n *\n * OpenClaw's `atomic-idempotent-v1` transcriptSemantics contract requires\n * that a retried `commitTurn` call report \"duplicate\" for a key that was\n * already committed -- including after this process (or the host) restarts\n * -- and that \"the accepted turn and key advance together\" (the committed\n * messages are persisted atomically with the key, not discarded).\n *\n * The default commit-log path is shared by every gateway process using the\n * same Headroom workspace, not just every store instance in one process --\n * an in-process mutex (a JS `Map`/promise queue) does nothing to stop two\n * separate OS processes from each reading the same file, adding a different\n * key, and racing their renames, with the loser's accepted turn silently\n * discarded. The read/check/write transaction is therefore protected by a\n * real inter-process lock: an exclusive lock FILE (`<path>.lock`), acquired\n * via atomic exclusive creation (`open(..., \"wx\")`, which is atomic at the\n * OS level on POSIX and Windows alike) so only one process -- in this one or\n * any other -- can be inside the critical section at a time.\n *\n * The lock is never force-broken by age. An age-based \"the holder must have\n * crashed\" heuristic cannot tell a dead holder apart from one that is simply\n * slow (a large write, a GC pause, disk contention): a second process that\n * reclaims a lock the first still holds can commit and release before the\n * first resumes and overwrites that commit with its own now-stale snapshot\n * -- both processes report \"committed\", but only the second process's write\n * survives, and the first process's genuinely-accepted turn silently\n * vanishes. Node has no built-in binding for a real OS-mediated lock\n * (POSIX `flock`/Windows `LockFileEx`, where the kernel itself releases the\n * lock when the holder's process exits, crash included, so no staleness\n * guess is needed at all), so this fails closed instead: a lock that is\n * never released is never reclaimed, and a caller that can't acquire it\n * within the timeout gets a clear error rather than a silent lost write. A\n * lock file orphaned by a genuinely crashed process requires a human (or an\n * operator script) to remove `<path>.lock` before commits can resume --\n * that operational cost is the trade for never silently dropping an\n * acknowledged commit. Lock ownership is also verified before release (see\n * `withFileLock`): a process only ever deletes the lock file it created.\n *\n * Every `tryCommit` re-reads the file fresh from disk inside the lock (no\n * long-lived in-memory cache), and the in-memory return value is only ever\n * \"committed\" once the atomic (temp file + rename) write recording the key\n * has actually succeeded -- a failed write is never remembered as seen, so\n * retrying the same key attempts the commit again instead of returning a\n * phantom \"duplicate\" for a commit that never reached disk.\n *\n * Keys are never evicted. OpenClaw may retry at any point after a restart or\n * a long stall, so pruning by count or age would risk re-accepting an\n * already-committed turn -- exactly the failure this store exists to avoid.\n */\n\nimport { mkdir, open, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport * as os from \"node:os\";\n\n/**\n * True if `p` exists and is something other than a directory (e.g. a file\n * sitting where a directory needs to be). Some platforms (observed on\n * Windows) report `ENOENT` -- not a distinct \"not a directory\" code -- for\n * `readFile` when a path component like this blocks the rest of the path,\n * which is indistinguishable from \"the commit log just doesn't exist yet\"\n * by error code alone. Checking the parent directly is what tells the two\n * apart: silently treating a broken storage path as an empty store would\n * make every future commit re-accept an already-committed key.\n */\nasync function isBlockingNonDirectory(p: string): Promise<boolean> {\n try {\n const info = await stat(p);\n return !info.isDirectory();\n } catch {\n return false; // Doesn't exist -- not blocking anything.\n }\n}\n\n/** Default commit-log location, honoring the same workspace override the\n * rest of the Headroom workspace convention uses (`HEADROOM_WORKSPACE_DIR`). */\nexport function defaultCommitLogPath(): string {\n const workspaceDir = process.env.HEADROOM_WORKSPACE_DIR?.trim() || join(os.homedir(), \".headroom\");\n return join(workspaceDir, \"openclaw\", \"commit-log.json\");\n}\n\n/** One durably-committed turn: the accepted messages plus when they landed. */\nexport interface CommittedTurn {\n messages: unknown;\n committedAt: string;\n}\n\ntype CommitLog = Record<string, CommittedTurn>;\n\n/** Give up (rather than poll forever) if the lock still can't be acquired after this long. */\nconst LOCK_ACQUIRE_TIMEOUT_MS = 60_000;\nconst LOCK_POLL_BASE_MS = 20;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Run `fn` while holding an exclusive lock on `path` (a sibling `<path>.lock`\n * file). Serializes every caller -- this process or another -- against the\n * same file, not just calls on one object or one process.\n *\n * The lock is held until `fn` returns, however long that takes, and is\n * never reclaimed by another caller on a staleness guess -- see the module\n * docstring for why an age-based heuristic can silently drop a genuinely\n * accepted commit. A caller that can't acquire the lock within\n * `LOCK_ACQUIRE_TIMEOUT_MS` gets a clear timeout error instead of the\n * transaction being allowed to proceed unsafely.\n */\nasync function withFileLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockPath = `${path}.lock`;\n await mkdir(dirname(path), { recursive: true });\n // Unique per acquisition, so release only ever removes the lock file this\n // exact call created -- never one a different (later) holder created.\n const token = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n const deadline = Date.now() + LOCK_ACQUIRE_TIMEOUT_MS;\n let attempt = 0;\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(`${token}\\n${process.pid}\\n${new Date().toISOString()}\\n`);\n } finally {\n await handle.close();\n }\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code !== \"EEXIST\") {\n throw error;\n }\n if (Date.now() > deadline) {\n throw new Error(\n `Timed out waiting for the advancement-key-store lock on ${path}. If the ` +\n `process that created ${lockPath} is no longer running, remove it manually.`,\n );\n }\n attempt += 1;\n await sleep(LOCK_POLL_BASE_MS + Math.random() * LOCK_POLL_BASE_MS * Math.min(attempt, 10));\n }\n }\n try {\n return await fn();\n } finally {\n // Verify the lock file is still the one this call created before\n // removing it -- if it doesn't start with our token, some other process\n // (or a human) replaced or is using it, and it must not be touched.\n try {\n const current = await readFile(lockPath, \"utf8\");\n if (current.startsWith(`${token}\\n`)) {\n await unlink(lockPath);\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n throw error;\n }\n }\n }\n}\n\nexport class DurableAdvancementKeyStore {\n constructor(private readonly path: string) {}\n\n private async readAll(): Promise<CommitLog> {\n let raw: string;\n try {\n raw = await readFile(this.path, \"utf8\");\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n if (code !== \"ENOENT\" && code !== \"ENOTDIR\") {\n throw error;\n }\n if (await isBlockingNonDirectory(dirname(this.path))) {\n throw error;\n }\n return {};\n }\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as CommitLog;\n }\n if (Array.isArray(parsed)) {\n // Migrate the earlier key-only array format (no message payload) so\n // upgrading from it never re-accepts an already-committed key.\n const migrated: CommitLog = {};\n for (const entry of parsed) {\n if (typeof entry === \"string\") {\n migrated[entry] = { messages: null, committedAt: \"\" };\n }\n }\n return migrated;\n }\n return {};\n }\n\n private async writeAll(entries: CommitLog): Promise<void> {\n await mkdir(dirname(this.path), { recursive: true });\n const tmpPath = `${this.path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;\n await writeFile(tmpPath, JSON.stringify(entries), \"utf8\");\n await rename(tmpPath, this.path);\n }\n\n /**\n * Atomically check-and-record `key` together with the `messages` it\n * advances. Holds the cross-process file lock (see `withFileLock`) for\n * the full read/check/write transaction, so two processes -- or two\n * instances in this one -- can't each read the same starting state and\n * race their writes.\n *\n * Throws if the write fails -- the caller sees a rejected commit, not a\n * false \"committed\"/\"duplicate\", and a retry re-reads the (still\n * unchanged) on-disk state and attempts the write again.\n */\n async tryCommit(key: string, messages: unknown): Promise<\"committed\" | \"duplicate\"> {\n return withFileLock(this.path, async () => {\n const entries = await this.readAll();\n if (key in entries) {\n return \"duplicate\";\n }\n const next: CommitLog = {\n ...entries,\n [key]: { messages, committedAt: new Date().toISOString() },\n };\n await this.writeAll(next);\n return \"committed\";\n });\n }\n\n /** Test-only: check membership without recording. */\n async has(key: string): Promise<boolean> {\n const entries = await this.readAll();\n return key in entries;\n }\n\n /** Test-only: read back a committed entry (messages + commit time) without recording. */\n async get(key: string): Promise<CommittedTurn | undefined> {\n const entries = await this.readAll();\n return entries[key];\n }\n}\n","export interface OpenClawCompactParams {\n sessionId: string;\n sessionKey?: string;\n agentId?: string;\n sessionTarget?: unknown;\n sessionFile?: string;\n tokenBudget?: number;\n force?: boolean;\n currentTokenCount?: number;\n compactionTarget?: \"budget\" | \"threshold\";\n customInstructions?: string;\n runtimeSettings?: unknown;\n runtimeContext?: unknown;\n abortSignal?: AbortSignal;\n}\n\nexport interface OpenClawCompactResult {\n ok: boolean;\n compacted: boolean;\n reason?: string;\n result?: {\n summary?: string;\n firstKeptEntryId?: string;\n tokensBefore: number;\n tokensAfter?: number;\n details?: unknown;\n sessionId?: string;\n sessionTarget?: unknown;\n sessionFile?: string;\n };\n}\n\nconst OPENCLAW_PLUGIN_SDK_CORE = \"openclaw/plugin-sdk/core\";\n\n/** Delegate compaction without bundling the optional OpenClaw peer dependency. */\nexport async function delegateCompactionToRuntime(\n params: OpenClawCompactParams,\n): Promise<OpenClawCompactResult> {\n const pluginSdk = (await import(OPENCLAW_PLUGIN_SDK_CORE)) as {\n delegateCompactionToRuntime?: (\n compactParams: OpenClawCompactParams,\n ) => Promise<OpenClawCompactResult>;\n };\n\n if (!pluginSdk.delegateCompactionToRuntime) {\n throw new Error(\"OpenClaw runtime does not expose delegateCompactionToRuntime\");\n }\n\n return pluginSdk.delegateCompactionToRuntime(params);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport const DEFAULT_GATEWAY_PROVIDER_IDS = [\"openai-codex\"] as const;\n\nconst DEFAULT_PROVIDER_BASE_URLS: Readonly<Record<string, string>> = {\n \"openai-codex\": \"https://chatgpt.com/backend-api\",\n};\n\nconst GATEWAY_PROVIDER_ID_ALIASES: Readonly<Record<string, string>> = {\n codex: \"openai-codex\",\n claude: \"anthropic\",\n copilot: \"github-copilot\",\n gemini: \"google\",\n};\n\nconst EXPLICIT_BASE_URL_REQUIRED_PROVIDER_IDS = new Set<string>([\"github-copilot\"]);\n\nexport function resolveGatewayProviderIds(config: Record<string, unknown> | undefined): string[] {\n const configuredProviderIds = normalizeGatewayProviderIds(config?.gatewayProviderIds);\n if (configuredProviderIds.length > 0) {\n return configuredProviderIds;\n }\n\n if (config?.routeCodexViaProxy === false) {\n return [];\n }\n\n return [...DEFAULT_GATEWAY_PROVIDER_IDS];\n}\n\nfunction normalizeGatewayProviderIds(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n const seen = new Set<string>();\n const normalized: string[] = [];\n\n for (const entry of value) {\n if (typeof entry !== \"string\") {\n continue;\n }\n\n const rawProviderId = entry.trim();\n const providerId = GATEWAY_PROVIDER_ID_ALIASES[rawProviderId.toLowerCase()] ?? rawProviderId;\n if (!providerId || seen.has(providerId)) {\n continue;\n }\n\n seen.add(providerId);\n normalized.push(providerId);\n }\n\n return normalized;\n}\n\nexport function applyGatewayProviderBaseUrls<T>(\n cfg: T,\n proxyUrl: string,\n providerIds: readonly string[],\n): { changed: boolean; config: T } {\n const next = structuredClone((cfg ?? {}) as any);\n const changed = applyGatewayProviderBaseUrlsInPlace(next, proxyUrl, providerIds);\n return { changed, config: next as T };\n}\n\nexport function applyGatewayProviderBaseUrlsInPlace(\n cfg: any,\n proxyUrl: string,\n providerIds: readonly string[],\n): boolean {\n if (!cfg || typeof cfg !== \"object\" || providerIds.length === 0) {\n return false;\n }\n\n const models = (cfg.models ??= {});\n const providers = (models.providers ??= {});\n let changed = false;\n\n for (const providerId of providerIds) {\n const currentValue = providers[providerId];\n const currentConfig =\n currentValue && typeof currentValue === \"object\" && !Array.isArray(currentValue)\n ? currentValue\n : {};\n const nextConfig = { ...currentConfig };\n const currentBaseUrl =\n typeof nextConfig.baseUrl === \"string\" && nextConfig.baseUrl.trim().length > 0\n ? nextConfig.baseUrl\n : undefined;\n const defaultBaseUrl = DEFAULT_PROVIDER_BASE_URLS[providerId];\n if (\n !currentBaseUrl &&\n !defaultBaseUrl &&\n EXPLICIT_BASE_URL_REQUIRED_PROVIDER_IDS.has(providerId)\n ) {\n continue;\n }\n const nextBaseUrl = routeBaseUrlThroughProxy({\n providerId,\n proxyUrl,\n currentBaseUrl,\n });\n\n if (!Array.isArray(nextConfig.models)) {\n nextConfig.models = [];\n changed = true;\n }\n\n if (nextConfig.baseUrl === nextBaseUrl) {\n providers[providerId] = nextConfig;\n continue;\n }\n\n nextConfig.baseUrl = nextBaseUrl;\n providers[providerId] = nextConfig;\n changed = true;\n }\n\n return changed;\n}\n\nfunction routeBaseUrlThroughProxy(params: {\n providerId: string;\n proxyUrl: string;\n currentBaseUrl?: string;\n}): string {\n const upstreamBaseUrl = params.currentBaseUrl ?? DEFAULT_PROVIDER_BASE_URLS[params.providerId];\n if (!upstreamBaseUrl) {\n return params.proxyUrl;\n }\n\n try {\n const proxy = new URL(params.proxyUrl);\n const upstream = new URL(upstreamBaseUrl);\n proxy.pathname = upstream.pathname;\n proxy.search = upstream.search;\n proxy.hash = \"\";\n return proxy.toString().replace(/\\/$/, \"\");\n } catch {\n return params.proxyUrl;\n }\n}\n","/**\n * CCR (Compress-Cache-Retrieve) tool for OpenClaw.\n *\n * Allows the agent to retrieve original uncompressed content\n * from the Headroom proxy's compression store.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { normalizeAndValidateProxyUrl } from \"../proxy-manager.js\";\n\nexport interface RetrieveToolConfig {\n proxyUrl: string;\n}\n\nexport function createHeadroomRetrieveTool(config: RetrieveToolConfig) {\n const proxyOrigin = normalizeAndValidateProxyUrl(config.proxyUrl);\n\n return {\n name: \"headroom_retrieve\",\n description:\n \"Retrieve original uncompressed content from Headroom's compression store. \" +\n \"Use when compressed context mentions a hash and you need the full details. \" +\n \"Pass the hash from the compression marker (24 hex characters). \" +\n \"Retrieval is by hash and always returns the full original content.\",\n parameters: {\n type: \"object\" as const,\n properties: {\n hash: {\n type: \"string\",\n description: \"The 24-character hex hash from the compression marker\",\n },\n },\n required: [\"hash\"],\n },\n execute: async (args: { hash: string }): Promise<string> => {\n const { hash } = args;\n\n // Validate hash format\n if (!/^[a-f0-9]{24}$/i.test(hash)) {\n return JSON.stringify({\n error: \"Invalid hash format. Expected 24 hex characters.\",\n });\n }\n\n try {\n const url = `${proxyOrigin}/v1/retrieve/${hash}`;\n\n const resp = await fetch(url, {\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n return JSON.stringify({\n error: `Retrieval failed: HTTP ${resp.status}`,\n details: body,\n });\n }\n\n const data = await resp.json();\n return typeof data === \"string\" ? data : JSON.stringify(data);\n } catch (error) {\n return JSON.stringify({\n error: `Retrieval failed: ${error}`,\n hint: \"The compressed content may have expired (default TTL: 30 minutes)\",\n });\n }\n },\n };\n}\n","/**\n * Headroom OpenClaw Plugin — register ContextEngine + CCR retrieval tool.\n *\n * Usage:\n * openclaw plugins install headroom-ai/openclaw\n *\n * Configuration (in ~/.openclaw/config.json or ~/.clawdbot/clawdbot.json):\n * {\n * \"plugins\": {\n * \"slots\": { \"contextEngine\": \"headroom\" },\n * \"entries\": { \"headroom\": { \"enabled\": true } }\n * }\n * }\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { HeadroomContextEngine } from \"../engine.js\";\nimport {\n applyGatewayProviderBaseUrlsInPlace,\n resolveGatewayProviderIds,\n} from \"../gateway-config.js\";\nimport { normalizeAndValidateProxyUrl, probeHeadroomProxy } from \"../proxy-manager.js\";\nimport { createHeadroomRetrieveTool } from \"../tools/headroom-retrieve.js\";\n\n/**\n * OpenClaw 2026.x plugin API requires a `{ register(api) }` object export.\n * The previous bare-function default export was silently skipped by the loader.\n * See: https://github.com/headroomlabs-ai/headroom/issues/XXX\n */\nexport default {\n register: registerHeadroomPlugin,\n};\n\nexport function registerHeadroomPlugin(api: any) {\n const config = api.config?.plugins?.entries?.headroom?.config ?? {};\n const logger = api.logger ?? console;\n const rawProxyUrl = config.proxyUrl;\n const proxyUrl =\n typeof rawProxyUrl === \"string\" && rawProxyUrl.trim().length > 0\n ? normalizeAndValidateProxyUrl(rawProxyUrl)\n : undefined;\n\n const engine = new HeadroomContextEngine({ ...config, proxyUrl }, {\n info: (m: string) => logger.info(m),\n warn: (m: string) => logger.warn(m),\n error: (m: string) => logger.error(m),\n debug: (m: string) => logger.debug?.(m),\n });\n const gatewayProviderIds = resolveGatewayProviderIds(config);\n let validatedConfiguredProxyUrl: string | null = null;\n let configuredProxyProbePromise: Promise<string | null> | null = null;\n\n const applyGatewayRouting = async (activeProxyUrl: string) => {\n if (gatewayProviderIds.length === 0) {\n return;\n }\n\n try {\n const changed = applyGatewayProviderBaseUrlsInPlace(api.config, activeProxyUrl, gatewayProviderIds);\n\n if (changed) {\n logger.info(\n `[headroom] Routed ${gatewayProviderIds.join(\", \")} through Headroom proxy in memory at ${activeProxyUrl}`,\n );\n } else {\n logger.info(\n `[headroom] Upstream gateway already routed in memory for ${gatewayProviderIds.join(\", \")} at ${activeProxyUrl}`,\n );\n }\n } catch (error) {\n logger.warn(`[headroom] Failed to configure upstream gateway routing: ${error}`);\n }\n };\n\n const getConfiguredRoutingProxyUrl = async (): Promise<string | null> => {\n if (!proxyUrl) {\n return null;\n }\n if (validatedConfiguredProxyUrl === proxyUrl) {\n return validatedConfiguredProxyUrl;\n }\n if (!configuredProxyProbePromise) {\n configuredProxyProbePromise = probeHeadroomProxy(proxyUrl)\n .then((probe) => {\n if (probe.reachable && probe.isHeadroom) {\n validatedConfiguredProxyUrl = proxyUrl;\n return proxyUrl;\n }\n logger.warn(\n `[headroom] Skipping upstream gateway routing: configured proxyUrl is not a ready Headroom proxy at ${proxyUrl}` +\n (probe.reason ? ` (${probe.reason})` : \"\"),\n );\n return null;\n })\n .catch((error) => {\n logger.warn(\n `[headroom] Skipping upstream gateway routing: failed to probe configured proxyUrl ${proxyUrl}: ${error}`,\n );\n return null;\n })\n .finally(() => {\n configuredProxyProbePromise = null;\n });\n }\n return configuredProxyProbePromise;\n };\n\n const ensureGatewayRouting = async () => {\n if (gatewayProviderIds.length === 0) {\n return;\n }\n const activeProxyUrl = engine.getProxyUrl() ?? (await getConfiguredRoutingProxyUrl());\n if (!activeProxyUrl) {\n logger.debug?.(\"[headroom] Deferring upstream gateway routing until proxy is available\");\n return;\n }\n await applyGatewayRouting(activeProxyUrl);\n };\n\n engine.onProxyReady(async (activeProxyUrl) => {\n await applyGatewayRouting(activeProxyUrl);\n });\n\n // Register as context engine\n api.registerContextEngine(\"headroom\", () => engine);\n\n // Register CCR retrieval tool (active once proxy is running)\n api.registerTool((ctx: any) => {\n const activeProxyUrl = engine.getProxyUrl() ?? proxyUrl;\n if (!activeProxyUrl) return null;\n return createHeadroomRetrieveTool({ proxyUrl: activeProxyUrl });\n }, { names: [\"headroom_retrieve\"] });\n\n api.on(\"gateway_start\", async () => {\n await ensureGatewayRouting();\n });\n\n void ensureGatewayRouting();\n\n logger.info(\"[headroom] Plugin registered\");\n}\n"],"mappings":"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","names":["dirname","join","os"]}
@@ -41,6 +41,10 @@
41
41
  "gatewayProviderIds": {
42
42
  "label": "Gateway Provider IDs",
43
43
  "help": "Optional list of OpenClaw provider ids to route through the active Headroom proxy in memory. Friendly aliases codex, claude, copilot, and gemini are also accepted. When set, this overrides the default openai-codex-only routing."
44
+ },
45
+ "announceCompression": {
46
+ "label": "Announce Compression In System Prompt",
47
+ "help": "When enabled, a static, cache-stable notice is added to the system prompt the first time a session compresses more than 100 tokens. Disable to guarantee the plugin never modifies the system prompt."
44
48
  }
45
49
  },
46
50
  "configSchema": {
@@ -106,6 +110,10 @@
106
110
  "type": "string"
107
111
  },
108
112
  "default": []
113
+ },
114
+ "announceCompression": {
115
+ "type": "boolean",
116
+ "default": true
109
117
  }
110
118
  }
111
119
  },
package/dist/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "headroom-openclaw",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Headroom context compression plugin for OpenClaw — 70-90% token savings with zero LLM calls",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
7
  "types": "./index.d.ts",
8
8
  "license": "Apache-2.0",
9
9
  "dependencies": {
10
- "headroom-ai": "^0.40.0"
10
+ "headroom-ai": "^0.41.0"
11
11
  },
12
12
  "peerDependencies": {
13
13
  "openclaw": "*"
@@ -41,6 +41,10 @@
41
41
  "gatewayProviderIds": {
42
42
  "label": "Gateway Provider IDs",
43
43
  "help": "Optional list of OpenClaw provider ids to route through the active Headroom proxy in memory. Friendly aliases codex, claude, copilot, and gemini are also accepted. When set, this overrides the default openai-codex-only routing."
44
+ },
45
+ "announceCompression": {
46
+ "label": "Announce Compression In System Prompt",
47
+ "help": "When enabled, a static, cache-stable notice is added to the system prompt the first time a session compresses more than 100 tokens. Disable to guarantee the plugin never modifies the system prompt."
44
48
  }
45
49
  },
46
50
  "configSchema": {
@@ -106,6 +110,10 @@
106
110
  "type": "string"
107
111
  },
108
112
  "default": []
113
+ },
114
+ "announceCompression": {
115
+ "type": "boolean",
116
+ "default": true
109
117
  }
110
118
  }
111
119
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "headroom-openclaw",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Headroom context compression plugin for OpenClaw — 70-90% token savings with zero LLM calls",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "typecheck": "node node_modules/@typescript/native/bin/tsc --noEmit"
19
19
  },
20
20
  "dependencies": {
21
- "headroom-ai": "^0.40.0"
21
+ "headroom-ai": "^0.41.0"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "openclaw": "*"