headroom-openclaw 0.37.0 → 0.39.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 +2 -2
- package/dist/README.md +2 -2
- package/dist/index.d.ts +54 -23
- package/dist/index.js +187 -21
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -215,8 +215,8 @@ Compression is lossless via CCR (Compress-Cache-Retrieve): originals are stored
|
|
|
215
215
|
|
|
216
216
|
| | lossless-claw | headroom |
|
|
217
217
|
|---|---|---|
|
|
218
|
-
| Compaction method | LLM summarization (DAG) |
|
|
219
|
-
| Cost of compaction | Tokens (LLM calls) |
|
|
218
|
+
| Compaction method | LLM summarization (DAG) | OpenClaw native compaction (delegated) |
|
|
219
|
+
| Cost of compaction | Tokens (LLM calls) | OpenClaw configuration-dependent |
|
|
220
220
|
| Best for | Long conversations | Tool-heavy agents with large outputs |
|
|
221
221
|
| Retrieval | `lcm_grep`, `lcm_expand` | `headroom_retrieve` (instant) |
|
|
222
222
|
|
package/dist/README.md
CHANGED
|
@@ -215,8 +215,8 @@ Compression is lossless via CCR (Compress-Cache-Retrieve): originals are stored
|
|
|
215
215
|
|
|
216
216
|
| | lossless-claw | headroom |
|
|
217
217
|
|---|---|---|
|
|
218
|
-
| Compaction method | LLM summarization (DAG) |
|
|
219
|
-
| Cost of compaction | Tokens (LLM calls) |
|
|
218
|
+
| Compaction method | LLM summarization (DAG) | OpenClaw native compaction (delegated) |
|
|
219
|
+
| Cost of compaction | Tokens (LLM calls) | OpenClaw configuration-dependent |
|
|
220
220
|
| Best for | Long conversations | Tool-heavy agents with large outputs |
|
|
221
221
|
| Retrieval | `lcm_grep`, `lcm_expand` | `headroom_retrieve` (instant) |
|
|
222
222
|
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
/**
|
|
16
16
|
* OpenClaw 2026.x plugin API requires a `{ register(api) }` object export.
|
|
17
17
|
* The previous bare-function default export was silently skipped by the loader.
|
|
18
|
-
* See: https://github.com/
|
|
18
|
+
* See: https://github.com/headroomlabs-ai/headroom/issues/XXX
|
|
19
19
|
*/
|
|
20
20
|
declare const _default: {
|
|
21
21
|
register: typeof registerHeadroomPlugin;
|
|
@@ -77,6 +77,37 @@ declare function isLocalProxyUrl(proxyUrl: string): boolean;
|
|
|
77
77
|
*/
|
|
78
78
|
declare function probeHeadroomProxy(proxyUrl: string): Promise<ProxyProbeResult>;
|
|
79
79
|
|
|
80
|
+
interface OpenClawCompactParams {
|
|
81
|
+
sessionId: string;
|
|
82
|
+
sessionKey?: string;
|
|
83
|
+
agentId?: string;
|
|
84
|
+
sessionTarget?: unknown;
|
|
85
|
+
sessionFile?: string;
|
|
86
|
+
tokenBudget?: number;
|
|
87
|
+
force?: boolean;
|
|
88
|
+
currentTokenCount?: number;
|
|
89
|
+
compactionTarget?: "budget" | "threshold";
|
|
90
|
+
customInstructions?: string;
|
|
91
|
+
runtimeSettings?: unknown;
|
|
92
|
+
runtimeContext?: unknown;
|
|
93
|
+
abortSignal?: AbortSignal;
|
|
94
|
+
}
|
|
95
|
+
interface OpenClawCompactResult {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
compacted: boolean;
|
|
98
|
+
reason?: string;
|
|
99
|
+
result?: {
|
|
100
|
+
summary?: string;
|
|
101
|
+
firstKeptEntryId?: string;
|
|
102
|
+
tokensBefore: number;
|
|
103
|
+
tokensAfter?: number;
|
|
104
|
+
details?: unknown;
|
|
105
|
+
sessionId?: string;
|
|
106
|
+
sessionTarget?: unknown;
|
|
107
|
+
sessionFile?: string;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
80
111
|
/**
|
|
81
112
|
* HeadroomContextEngine — ContextEngine implementation for OpenClaw.
|
|
82
113
|
*
|
|
@@ -89,6 +120,9 @@ interface HeadroomEngineConfig extends ProxyManagerConfig {
|
|
|
89
120
|
requestTimeoutMs?: number;
|
|
90
121
|
circuitBreakerThreshold?: number;
|
|
91
122
|
circuitBreakerCooldownMs?: number;
|
|
123
|
+
/** Where to durably record committed turn-advancement keys (see
|
|
124
|
+
* `DurableAdvancementKeyStore`). Defaults to `defaultCommitLogPath()`. */
|
|
125
|
+
commitLogPath?: string;
|
|
92
126
|
}
|
|
93
127
|
declare class HeadroomContextEngine {
|
|
94
128
|
readonly info: {
|
|
@@ -96,7 +130,12 @@ declare class HeadroomContextEngine {
|
|
|
96
130
|
name: string;
|
|
97
131
|
version: string;
|
|
98
132
|
ownsCompaction: boolean;
|
|
133
|
+
transcriptSemantics: {
|
|
134
|
+
currentTurnFence: string;
|
|
135
|
+
turnAdvancementIdempotency: string;
|
|
136
|
+
};
|
|
99
137
|
};
|
|
138
|
+
private advancementKeyStore;
|
|
100
139
|
private proxyManager;
|
|
101
140
|
private proxyUrl;
|
|
102
141
|
private config;
|
|
@@ -145,31 +184,23 @@ declare class HeadroomContextEngine {
|
|
|
145
184
|
estimatedTokens: number;
|
|
146
185
|
systemPromptAddition?: string;
|
|
147
186
|
}>;
|
|
187
|
+
/** Delegate persistent compaction to OpenClaw's built-in runtime. */
|
|
188
|
+
compact(params: OpenClawCompactParams): Promise<OpenClawCompactResult>;
|
|
148
189
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* Zero LLM calls. All algorithmic.
|
|
190
|
+
* Durable turn-advancement commit — required by OpenClaw's transcriptSemantics
|
|
191
|
+
* contract. Called only for the accepted, successful turn; failed or aborted
|
|
192
|
+
* turns never reach here. Must be an atomic, idempotent write of the
|
|
193
|
+
* accepted `messages` keyed by `advancementKey` so a host retry with the
|
|
194
|
+
* same key reports "duplicate" instead of re-applying the advancement —
|
|
195
|
+
* including a retry that arrives after this process restarted, which is
|
|
196
|
+
* why the record lives on disk (see `DurableAdvancementKeyStore`) rather
|
|
197
|
+
* than in memory, and includes the messages rather than just the key.
|
|
158
198
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
tokenBudget?: number;
|
|
163
|
-
force?: boolean;
|
|
164
|
-
runtimeContext?: any;
|
|
199
|
+
commitTurn(params: {
|
|
200
|
+
advancementKey: string;
|
|
201
|
+
messages: any[];
|
|
165
202
|
}): Promise<{
|
|
166
|
-
|
|
167
|
-
compacted: boolean;
|
|
168
|
-
reason?: string;
|
|
169
|
-
result?: {
|
|
170
|
-
tokensBefore: number;
|
|
171
|
-
tokensAfter?: number;
|
|
172
|
-
};
|
|
203
|
+
status: "committed" | "duplicate";
|
|
173
204
|
}>;
|
|
174
205
|
afterTurn?(params: {
|
|
175
206
|
sessionId: string;
|
package/dist/index.js
CHANGED
|
@@ -668,6 +668,161 @@ function isRecord(value) {
|
|
|
668
668
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
669
669
|
}
|
|
670
670
|
|
|
671
|
+
// src/advancement-key-store.ts
|
|
672
|
+
import { mkdir, open, readFile, rename, stat, unlink, writeFile } from "fs/promises";
|
|
673
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
674
|
+
import * as os2 from "os";
|
|
675
|
+
async function isBlockingNonDirectory(p) {
|
|
676
|
+
try {
|
|
677
|
+
const info = await stat(p);
|
|
678
|
+
return !info.isDirectory();
|
|
679
|
+
} catch {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function defaultCommitLogPath() {
|
|
684
|
+
const workspaceDir = process.env.HEADROOM_WORKSPACE_DIR?.trim() || join2(os2.homedir(), ".headroom");
|
|
685
|
+
return join2(workspaceDir, "openclaw", "commit-log.json");
|
|
686
|
+
}
|
|
687
|
+
var LOCK_ACQUIRE_TIMEOUT_MS = 6e4;
|
|
688
|
+
var LOCK_POLL_BASE_MS = 20;
|
|
689
|
+
function sleep(ms) {
|
|
690
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
691
|
+
}
|
|
692
|
+
async function withFileLock(path, fn) {
|
|
693
|
+
const lockPath = `${path}.lock`;
|
|
694
|
+
await mkdir(dirname2(path), { recursive: true });
|
|
695
|
+
const token = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
696
|
+
const deadline = Date.now() + LOCK_ACQUIRE_TIMEOUT_MS;
|
|
697
|
+
let attempt = 0;
|
|
698
|
+
for (; ; ) {
|
|
699
|
+
try {
|
|
700
|
+
const handle = await open(lockPath, "wx");
|
|
701
|
+
try {
|
|
702
|
+
await handle.writeFile(`${token}
|
|
703
|
+
${process.pid}
|
|
704
|
+
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
705
|
+
`);
|
|
706
|
+
} finally {
|
|
707
|
+
await handle.close();
|
|
708
|
+
}
|
|
709
|
+
break;
|
|
710
|
+
} catch (error) {
|
|
711
|
+
if (error?.code !== "EEXIST") {
|
|
712
|
+
throw error;
|
|
713
|
+
}
|
|
714
|
+
if (Date.now() > deadline) {
|
|
715
|
+
throw new Error(
|
|
716
|
+
`Timed out waiting for the advancement-key-store lock on ${path}. If the process that created ${lockPath} is no longer running, remove it manually.`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
attempt += 1;
|
|
720
|
+
await sleep(LOCK_POLL_BASE_MS + Math.random() * LOCK_POLL_BASE_MS * Math.min(attempt, 10));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
return await fn();
|
|
725
|
+
} finally {
|
|
726
|
+
try {
|
|
727
|
+
const current = await readFile(lockPath, "utf8");
|
|
728
|
+
if (current.startsWith(`${token}
|
|
729
|
+
`)) {
|
|
730
|
+
await unlink(lockPath);
|
|
731
|
+
}
|
|
732
|
+
} catch (error) {
|
|
733
|
+
if (error?.code !== "ENOENT") {
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
var DurableAdvancementKeyStore = class {
|
|
740
|
+
constructor(path) {
|
|
741
|
+
this.path = path;
|
|
742
|
+
}
|
|
743
|
+
path;
|
|
744
|
+
async readAll() {
|
|
745
|
+
let raw;
|
|
746
|
+
try {
|
|
747
|
+
raw = await readFile(this.path, "utf8");
|
|
748
|
+
} catch (error) {
|
|
749
|
+
const code = error?.code;
|
|
750
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
if (await isBlockingNonDirectory(dirname2(this.path))) {
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
756
|
+
return {};
|
|
757
|
+
}
|
|
758
|
+
const parsed = JSON.parse(raw);
|
|
759
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
760
|
+
return parsed;
|
|
761
|
+
}
|
|
762
|
+
if (Array.isArray(parsed)) {
|
|
763
|
+
const migrated = {};
|
|
764
|
+
for (const entry of parsed) {
|
|
765
|
+
if (typeof entry === "string") {
|
|
766
|
+
migrated[entry] = { messages: null, committedAt: "" };
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return migrated;
|
|
770
|
+
}
|
|
771
|
+
return {};
|
|
772
|
+
}
|
|
773
|
+
async writeAll(entries) {
|
|
774
|
+
await mkdir(dirname2(this.path), { recursive: true });
|
|
775
|
+
const tmpPath = `${this.path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
776
|
+
await writeFile(tmpPath, JSON.stringify(entries), "utf8");
|
|
777
|
+
await rename(tmpPath, this.path);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Atomically check-and-record `key` together with the `messages` it
|
|
781
|
+
* advances. Holds the cross-process file lock (see `withFileLock`) for
|
|
782
|
+
* the full read/check/write transaction, so two processes -- or two
|
|
783
|
+
* instances in this one -- can't each read the same starting state and
|
|
784
|
+
* race their writes.
|
|
785
|
+
*
|
|
786
|
+
* Throws if the write fails -- the caller sees a rejected commit, not a
|
|
787
|
+
* false "committed"/"duplicate", and a retry re-reads the (still
|
|
788
|
+
* unchanged) on-disk state and attempts the write again.
|
|
789
|
+
*/
|
|
790
|
+
async tryCommit(key, messages) {
|
|
791
|
+
return withFileLock(this.path, async () => {
|
|
792
|
+
const entries = await this.readAll();
|
|
793
|
+
if (key in entries) {
|
|
794
|
+
return "duplicate";
|
|
795
|
+
}
|
|
796
|
+
const next = {
|
|
797
|
+
...entries,
|
|
798
|
+
[key]: { messages, committedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
799
|
+
};
|
|
800
|
+
await this.writeAll(next);
|
|
801
|
+
return "committed";
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
/** Test-only: check membership without recording. */
|
|
805
|
+
async has(key) {
|
|
806
|
+
const entries = await this.readAll();
|
|
807
|
+
return key in entries;
|
|
808
|
+
}
|
|
809
|
+
/** Test-only: read back a committed entry (messages + commit time) without recording. */
|
|
810
|
+
async get(key) {
|
|
811
|
+
const entries = await this.readAll();
|
|
812
|
+
return entries[key];
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// src/openclaw-compaction.ts
|
|
817
|
+
var OPENCLAW_PLUGIN_SDK_CORE = "openclaw/plugin-sdk/core";
|
|
818
|
+
async function delegateCompactionToRuntime(params) {
|
|
819
|
+
const pluginSdk = await import(OPENCLAW_PLUGIN_SDK_CORE);
|
|
820
|
+
if (!pluginSdk.delegateCompactionToRuntime) {
|
|
821
|
+
throw new Error("OpenClaw runtime does not expose delegateCompactionToRuntime");
|
|
822
|
+
}
|
|
823
|
+
return pluginSdk.delegateCompactionToRuntime(params);
|
|
824
|
+
}
|
|
825
|
+
|
|
671
826
|
// src/engine.ts
|
|
672
827
|
function withTimeout(promise, ms) {
|
|
673
828
|
let timerId;
|
|
@@ -683,8 +838,16 @@ var HeadroomContextEngine = class {
|
|
|
683
838
|
id: "headroom",
|
|
684
839
|
name: "Headroom Context Compression",
|
|
685
840
|
version: "0.1.0",
|
|
686
|
-
ownsCompaction:
|
|
841
|
+
ownsCompaction: false,
|
|
842
|
+
transcriptSemantics: {
|
|
843
|
+
currentTurnFence: "before-current-turn-entry-v1",
|
|
844
|
+
turnAdvancementIdempotency: "atomic-idempotent-v1"
|
|
845
|
+
}
|
|
687
846
|
};
|
|
847
|
+
// Durable, restart-safe record of committed advancement keys, for
|
|
848
|
+
// commitTurn's idempotent-retry check. See advancement-key-store.ts for
|
|
849
|
+
// why this must survive a process restart and must not evict entries.
|
|
850
|
+
advancementKeyStore;
|
|
688
851
|
proxyManager;
|
|
689
852
|
proxyUrl = null;
|
|
690
853
|
config;
|
|
@@ -703,6 +866,9 @@ var HeadroomContextEngine = class {
|
|
|
703
866
|
this.config = config;
|
|
704
867
|
this.logger = logger ?? defaultLogger;
|
|
705
868
|
this.proxyManager = new ProxyManager(config, this.logger);
|
|
869
|
+
this.advancementKeyStore = new DurableAdvancementKeyStore(
|
|
870
|
+
config.commitLogPath ?? defaultCommitLogPath()
|
|
871
|
+
);
|
|
706
872
|
}
|
|
707
873
|
// === ContextEngine Lifecycle ===
|
|
708
874
|
async bootstrap(params) {
|
|
@@ -769,30 +935,30 @@ var HeadroomContextEngine = class {
|
|
|
769
935
|
return { messages: normalizeAgentMessages(params.messages), estimatedTokens: 0 };
|
|
770
936
|
}
|
|
771
937
|
}
|
|
772
|
-
/**
|
|
773
|
-
* Compact context — zero-cost alternative to LLM summarization.
|
|
774
|
-
*
|
|
775
|
-
* Calls compress() with the token budget, which triggers:
|
|
776
|
-
* - SmartCrusher: aggressive JSON compression (70-90% on tool outputs)
|
|
777
|
-
* - Kompress: ModernBERT text compression (40-60% on assistant text)
|
|
778
|
-
* - RollingWindow: drops oldest messages if still over budget
|
|
779
|
-
* - CCR: stores originals for retrieval via headroom_retrieve tool
|
|
780
|
-
*
|
|
781
|
-
* Zero LLM calls. All algorithmic.
|
|
782
|
-
*/
|
|
938
|
+
/** Delegate persistent compaction to OpenClaw's built-in runtime. */
|
|
783
939
|
async compact(params) {
|
|
784
|
-
|
|
785
|
-
|
|
940
|
+
const result = await delegateCompactionToRuntime(params);
|
|
941
|
+
if (result.compacted) {
|
|
942
|
+
this.stats.compactions++;
|
|
786
943
|
}
|
|
787
|
-
this.stats.compactions++;
|
|
788
944
|
this.logger.info(
|
|
789
|
-
`
|
|
945
|
+
`Compaction ${result.compacted ? "completed" : "skipped"} (budget: ${params.tokenBudget ?? "none"}, force: ${params.force ?? false})`
|
|
790
946
|
);
|
|
791
|
-
return
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
947
|
+
return result;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Durable turn-advancement commit — required by OpenClaw's transcriptSemantics
|
|
951
|
+
* contract. Called only for the accepted, successful turn; failed or aborted
|
|
952
|
+
* turns never reach here. Must be an atomic, idempotent write of the
|
|
953
|
+
* accepted `messages` keyed by `advancementKey` so a host retry with the
|
|
954
|
+
* same key reports "duplicate" instead of re-applying the advancement —
|
|
955
|
+
* including a retry that arrives after this process restarted, which is
|
|
956
|
+
* why the record lives on disk (see `DurableAdvancementKeyStore`) rather
|
|
957
|
+
* than in memory, and includes the messages rather than just the key.
|
|
958
|
+
*/
|
|
959
|
+
async commitTurn(params) {
|
|
960
|
+
const status = await this.advancementKeyStore.tryCommit(params.advancementKey, params.messages);
|
|
961
|
+
return { status };
|
|
796
962
|
}
|
|
797
963
|
async afterTurn(params) {
|
|
798
964
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts","../src/proxy-manager.ts","../src/convert.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 { agentToOpenAI, normalizeAgentMessages, openAIToAgent } from \"./convert.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}\n\nexport class HeadroomContextEngine {\n readonly info = {\n id: \"headroom\",\n name: \"Headroom Context Compression\",\n version: \"0.1.0\",\n ownsCompaction: true,\n };\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 }\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[]\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 = agentToOpenAI(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 = openAIToAgent(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 /**\n * Compact context — zero-cost alternative to LLM summarization.\n *\n * Calls compress() with the token budget, which triggers:\n * - SmartCrusher: aggressive JSON compression (70-90% on tool outputs)\n * - Kompress: ModernBERT text compression (40-60% on assistant text)\n * - RollingWindow: drops oldest messages if still over budget\n * - CCR: stores originals for retrieval via headroom_retrieve tool\n *\n * Zero LLM calls. All algorithmic.\n */\n async compact(params: {\n sessionId: string;\n sessionFile: string;\n tokenBudget?: number;\n force?: boolean;\n runtimeContext?: any;\n }): Promise<{\n ok: boolean;\n compacted: boolean;\n reason?: string;\n result?: {\n tokensBefore: number;\n tokensAfter?: number;\n };\n }> {\n if (!this.proxyUrl) {\n return { ok: false, compacted: false, reason: \"Proxy not available\" };\n }\n\n // Read current messages from session file if available\n // For now, compact() works in tandem with assemble() — the next assemble()\n // call will compress with the token budget. When compact() is called\n // independently, we report success since our pipeline handles it.\n //\n // TODO: Read session file, extract messages, call compress() with tokenBudget,\n // write back compacted messages.\n\n this.stats.compactions++;\n this.logger.info(\n `Compact called (budget: ${params.tokenBudget ?? \"none\"}, force: ${params.force ?? false})`,\n );\n\n return {\n ok: true,\n compacted: true,\n reason: \"Headroom applies SmartCrusher + Kompress + RollingWindow on next assemble()\",\n };\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/**\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","/* 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/chopratejas/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":[]}
|
|
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 { agentToOpenAI, normalizeAgentMessages, openAIToAgent } 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[]\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 = agentToOpenAI(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 = openAIToAgent(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/**\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"]}
|
package/dist/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "headroom-openclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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.
|
|
10
|
+
"headroom-ai": "^0.39.0"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"openclaw": "*"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "headroom-openclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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.
|
|
21
|
+
"headroom-ai": "^0.39.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"openclaw": "*"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@types/node": "^26.1.1",
|
|
34
34
|
"tsup": "^8.0.0",
|
|
35
35
|
"typescript": "^6.0.3",
|
|
36
|
-
"vitest": "^
|
|
36
|
+
"vitest": "^5.0.0"
|
|
37
37
|
},
|
|
38
38
|
"overrides": {
|
|
39
39
|
"esbuild": "^0.28.1"
|