@remnic/plugin-openclaw 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,6 +38,42 @@ Then restart the gateway:
38
38
  launchctl kickstart -k gui/501/ai.openclaw.gateway
39
39
  ```
40
40
 
41
+ ## Benchmarking The OpenClaw Chain
42
+
43
+ The benchmark CLI can now exercise the real OpenClaw-backed answer path instead
44
+ of only the stripped retrieval harness. Use the `openclaw-chain` runtime
45
+ profile to load the Remnic plugin config from `openclaw.json`, route answer
46
+ generation through the configured gateway chain, and optionally attach a
47
+ provider-backed judge:
48
+
49
+ ```bash
50
+ remnic bench run longmemeval \
51
+ --runtime-profile openclaw-chain \
52
+ --openclaw-config ~/.openclaw/openclaw.json \
53
+ --gateway-agent-id memory-primary
54
+
55
+ remnic bench run longmemeval \
56
+ --runtime-profile openclaw-chain \
57
+ --openclaw-config ~/.openclaw/openclaw.json \
58
+ --gateway-agent-id memory-primary \
59
+ --judge-provider openai \
60
+ --judge-model gpt-5.4-mini
61
+ ```
62
+
63
+ To compare the stripped harness, direct Remnic runtime, and OpenClaw chain in a
64
+ single pass, run a profile matrix:
65
+
66
+ ```bash
67
+ remnic bench run longmemeval \
68
+ --matrix baseline,real,openclaw-chain \
69
+ --openclaw-config ~/.openclaw/openclaw.json \
70
+ --remnic-config ~/.config/remnic/config.json
71
+ ```
72
+
73
+ Each stored result records its `runtimeProfile`, provider metadata, and the
74
+ resolved Remnic config so benchmark comparisons can distinguish retrieval-only
75
+ runs from real runtime and OpenClaw chain runs.
76
+
41
77
  ## What it does
42
78
 
43
79
  This plugin hooks into the OpenClaw gateway lifecycle:
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  FallbackLlmClient
3
- } from "./chunk-3SA5F4WT.js";
3
+ } from "./chunk-NXLHSCLU.js";
4
+ import "./chunk-3A5ELHTT.js";
4
5
  import {
5
6
  listJsonFiles
6
7
  } from "./chunk-5LE4HTVL.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  buildExtensionsBlockForConsolidation,
3
3
  runPostConsolidationMaterialize
4
- } from "./chunk-J2FCINY7.js";
5
- import "./chunk-5VTGFKKU.js";
4
+ } from "./chunk-QHMR3D7U.js";
5
+ import "./chunk-KPMXWORS.js";
6
6
  import {
7
7
  readChainIndex,
8
8
  resolveChainsDir
@@ -12,7 +12,8 @@ import {
12
12
  } from "./chunk-YHH3SXKD.js";
13
13
  import {
14
14
  FallbackLlmClient
15
- } from "./chunk-3SA5F4WT.js";
15
+ } from "./chunk-NXLHSCLU.js";
16
+ import "./chunk-3A5ELHTT.js";
16
17
  import {
17
18
  listJsonFiles,
18
19
  readJsonFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  searchCausalTrajectories
3
- } from "./chunk-RMFPW4VK.js";
3
+ } from "./chunk-LN5UZQVG.js";
4
4
  import {
5
5
  readChainIndex,
6
6
  resolveChainsDir
@@ -0,0 +1,61 @@
1
+ // ../remnic-core/src/json-extract.ts
2
+ function stripCodeFences(text) {
3
+ return text.replace(/```(?:json)?\s*([\s\S]*?)```/gi, (_m, inner) => String(inner).trim());
4
+ }
5
+ function extractJsonCandidates(text) {
6
+ const trimmed = text.trim();
7
+ const cleaned = stripCodeFences(trimmed);
8
+ const candidates = [];
9
+ if (cleaned.length > 0) candidates.push(cleaned);
10
+ candidates.push(...scanBalancedJsonBlocks(cleaned));
11
+ const objMatch = cleaned.match(/\{[\s\S]*\}/);
12
+ if (objMatch) candidates.push(objMatch[0]);
13
+ const seen = /* @__PURE__ */ new Set();
14
+ return candidates.map((c) => c.trim()).filter((c) => c.length > 0).filter((c) => {
15
+ if (seen.has(c)) return false;
16
+ seen.add(c);
17
+ return true;
18
+ });
19
+ }
20
+ function scanBalancedJsonBlocks(text) {
21
+ const out = [];
22
+ const opens = /* @__PURE__ */ new Set(["{", "["]);
23
+ const closes = { "{": "}", "[": "]" };
24
+ for (let i = 0; i < text.length; i++) {
25
+ const start = text[i];
26
+ if (!opens.has(start)) continue;
27
+ const expectedClose = closes[start];
28
+ let depth = 0;
29
+ let inString = false;
30
+ let escape = false;
31
+ for (let j = i; j < text.length; j++) {
32
+ const ch = text[j];
33
+ if (inString) {
34
+ if (escape) {
35
+ escape = false;
36
+ } else if (ch === "\\") {
37
+ escape = true;
38
+ } else if (ch === '"') {
39
+ inString = false;
40
+ }
41
+ continue;
42
+ }
43
+ if (ch === '"') {
44
+ inString = true;
45
+ continue;
46
+ }
47
+ if (ch === start) depth++;
48
+ if (ch === expectedClose) depth--;
49
+ if (depth === 0) {
50
+ out.push(text.slice(i, j + 1).trim());
51
+ i = j;
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ return out;
57
+ }
58
+
59
+ export {
60
+ extractJsonCandidates
61
+ };
@@ -0,0 +1,136 @@
1
+ // ../remnic-core/src/contradiction/contradiction-review.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { createHash } from "crypto";
5
+ function computePairId(memoryIdA, memoryIdB) {
6
+ const sorted = [memoryIdA, memoryIdB].sort();
7
+ return createHash("sha256").update(sorted.join("::")).digest("hex").slice(0, 24);
8
+ }
9
+ function reviewDir(memoryDir) {
10
+ return path.join(memoryDir, ".review", "contradictions");
11
+ }
12
+ function pairPath(memoryDir, pairId) {
13
+ if (pairId.includes("/") || pairId.includes("\\") || pairId.includes("..")) {
14
+ throw new Error(`Invalid pairId: ${pairId}`);
15
+ }
16
+ return path.join(reviewDir(memoryDir), `${pairId}.json`);
17
+ }
18
+ function ensureDir(memoryDir) {
19
+ const dir = reviewDir(memoryDir);
20
+ if (!fs.existsSync(dir)) {
21
+ fs.mkdirSync(dir, { recursive: true });
22
+ }
23
+ }
24
+ function writePair(memoryDir, pair) {
25
+ ensureDir(memoryDir);
26
+ const pairId = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
27
+ const existing = readPair(memoryDir, pairId);
28
+ if (existing?.resolution) {
29
+ return existing;
30
+ }
31
+ if (existing && existing.confidence >= pair.confidence) {
32
+ return existing;
33
+ }
34
+ const full = {
35
+ ...pair,
36
+ pairId,
37
+ lastReviewedAt: existing?.lastReviewedAt,
38
+ resolution: existing?.resolution
39
+ };
40
+ const filePath = pairPath(memoryDir, pairId);
41
+ const tmpPath = `${filePath}.tmp`;
42
+ fs.writeFileSync(tmpPath, JSON.stringify(full, null, 2), "utf-8");
43
+ fs.renameSync(tmpPath, filePath);
44
+ return full;
45
+ }
46
+ function writePairs(memoryDir, pairs) {
47
+ const seen = /* @__PURE__ */ new Set();
48
+ const results = [];
49
+ for (const pair of pairs) {
50
+ const key = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
51
+ if (seen.has(key)) continue;
52
+ seen.add(key);
53
+ results.push(writePair(memoryDir, pair));
54
+ }
55
+ return results;
56
+ }
57
+ function readPair(memoryDir, pairId) {
58
+ const filePath = pairPath(memoryDir, pairId);
59
+ try {
60
+ const raw = fs.readFileSync(filePath, "utf-8");
61
+ const parsed = JSON.parse(raw);
62
+ if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.memoryIds)) {
63
+ return parsed;
64
+ }
65
+ return null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ function listPairs(memoryDir, options) {
71
+ const startTime = Date.now();
72
+ const dir = reviewDir(memoryDir);
73
+ const { filter = "all", namespace, limit = 50 } = options ?? {};
74
+ const pairs = [];
75
+ let total = 0;
76
+ if (!fs.existsSync(dir)) {
77
+ return { pairs: [], total: 0, durationMs: Date.now() - startTime };
78
+ }
79
+ for (const entry of fs.readdirSync(dir)) {
80
+ if (!entry.endsWith(".json")) continue;
81
+ try {
82
+ const raw = fs.readFileSync(path.join(dir, entry), "utf-8");
83
+ const pair = JSON.parse(raw);
84
+ if (typeof pair !== "object" || pair === null) continue;
85
+ if (!Array.isArray(pair.memoryIds)) continue;
86
+ if (namespace && pair.namespace !== namespace) continue;
87
+ if (filter === "unresolved") {
88
+ if (pair.resolution) continue;
89
+ if (pair.verdict === "independent") continue;
90
+ } else if (filter !== "all" && pair.verdict !== filter) {
91
+ continue;
92
+ }
93
+ total++;
94
+ if (pairs.length < limit) pairs.push(pair);
95
+ } catch {
96
+ continue;
97
+ }
98
+ }
99
+ return { pairs, total, durationMs: Date.now() - startTime };
100
+ }
101
+ function isCoolingDown(pair, cooldownDays) {
102
+ if (cooldownDays <= 0) return false;
103
+ if (!pair.lastReviewedAt) return false;
104
+ const lastReviewed = new Date(pair.lastReviewedAt).getTime();
105
+ if (!Number.isFinite(lastReviewed)) return false;
106
+ const cooldownMs = cooldownDays * 24 * 60 * 60 * 1e3;
107
+ return Date.now() < lastReviewed + cooldownMs;
108
+ }
109
+ function resolvePair(memoryDir, pairId, verb) {
110
+ const existing = readPair(memoryDir, pairId);
111
+ if (!existing) return null;
112
+ const updated = {
113
+ ...existing,
114
+ lastReviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
115
+ resolution: verb
116
+ };
117
+ const filePath = pairPath(memoryDir, pairId);
118
+ const tmpPath = `${filePath}.tmp`;
119
+ fs.writeFileSync(tmpPath, JSON.stringify(updated, null, 2), "utf-8");
120
+ fs.renameSync(tmpPath, filePath);
121
+ return updated;
122
+ }
123
+ function memoryHashesChanged(_memoryDir, _pair, _getCurrentHash) {
124
+ return false;
125
+ }
126
+
127
+ export {
128
+ computePairId,
129
+ writePair,
130
+ writePairs,
131
+ readPair,
132
+ listPairs,
133
+ isCoolingDown,
134
+ resolvePair,
135
+ memoryHashesChanged
136
+ };
@@ -3038,6 +3038,9 @@ var StorageManager = class _StorageManager {
3038
3038
  get correctionsDir() {
3039
3039
  return path5.join(this.baseDir, "corrections");
3040
3040
  }
3041
+ get proceduresDir() {
3042
+ return path5.join(this.baseDir, "procedures");
3043
+ }
3041
3044
  get entitiesDir() {
3042
3045
  return path5.join(this.baseDir, "entities");
3043
3046
  }
@@ -3194,6 +3197,7 @@ var StorageManager = class _StorageManager {
3194
3197
  async ensureDirectories() {
3195
3198
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3196
3199
  await mkdir3(path5.join(this.factsDir, today), { recursive: true });
3200
+ await mkdir3(path5.join(this.proceduresDir, today), { recursive: true });
3197
3201
  await mkdir3(this.correctionsDir, { recursive: true });
3198
3202
  await mkdir3(this.entitiesDir, { recursive: true });
3199
3203
  await mkdir3(this.stateDir, { recursive: true });
@@ -3243,6 +3247,9 @@ var StorageManager = class _StorageManager {
3243
3247
  memoryKind: options.memoryKind,
3244
3248
  structuredAttributes: options.structuredAttributes
3245
3249
  };
3250
+ if (options.status !== void 0) {
3251
+ fm.status = options.status;
3252
+ }
3246
3253
  let enrichedContent = content;
3247
3254
  if (options.structuredAttributes && Object.keys(options.structuredAttributes).length > 0) {
3248
3255
  enrichedContent = `${content}
@@ -3263,6 +3270,9 @@ ${sanitized.text}
3263
3270
  let filePath;
3264
3271
  if (category === "correction") {
3265
3272
  filePath = path5.join(this.correctionsDir, `${id}.md`);
3273
+ } else if (category === "procedure") {
3274
+ await mkdir3(path5.join(this.proceduresDir, today), { recursive: true });
3275
+ filePath = path5.join(this.proceduresDir, today, `${id}.md`);
3266
3276
  } else {
3267
3277
  filePath = path5.join(this.factsDir, today, `${id}.md`);
3268
3278
  }
@@ -3682,6 +3692,7 @@ ${sanitized.text}
3682
3692
  }
3683
3693
  };
3684
3694
  await collectPaths(this.factsDir);
3695
+ await collectPaths(this.proceduresDir);
3685
3696
  await collectPaths(this.correctionsDir);
3686
3697
  return filePaths;
3687
3698
  }
@@ -3989,6 +4000,9 @@ ${sanitized.text}
3989
4000
  if (memory.frontmatter.category === "correction") {
3990
4001
  return path5.join(root, "corrections", `${memory.frontmatter.id}.md`);
3991
4002
  }
4003
+ if (memory.frontmatter.category === "procedure") {
4004
+ return path5.join(root, "procedures", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
4005
+ }
3992
4006
  return path5.join(root, "facts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
3993
4007
  }
3994
4008
  async writeMemoryFileAtomic(targetPath, memory) {
@@ -5687,6 +5701,9 @@ ${sanitized.text}
5687
5701
  let filePath;
5688
5702
  if (category === "correction") {
5689
5703
  filePath = path5.join(this.correctionsDir, `${id}.md`);
5704
+ } else if (category === "procedure") {
5705
+ await mkdir3(path5.join(this.proceduresDir, today), { recursive: true });
5706
+ filePath = path5.join(this.proceduresDir, today, `${id}.md`);
5690
5707
  } else {
5691
5708
  filePath = path5.join(this.factsDir, today, `${id}.md`);
5692
5709
  }
@@ -67,6 +67,14 @@ async function readCausalTrajectoryRecords(options) {
67
67
  }
68
68
  return { files, trajectories, invalidTrajectories };
69
69
  }
70
+ function filterTrajectoriesByLookbackDays(trajectories, lookbackDays, nowMs = Date.now()) {
71
+ const days = Math.max(1, Math.floor(lookbackDays));
72
+ const cutoff = nowMs - days * 864e5;
73
+ return trajectories.filter((t) => {
74
+ const ms = Date.parse(t.recordedAt);
75
+ return Number.isFinite(ms) && ms >= cutoff;
76
+ });
77
+ }
70
78
  async function getCausalTrajectoryStoreStatus(options) {
71
79
  const rootDir = resolveCausalTrajectoryStoreDir(options.memoryDir, options.causalTrajectoryStoreDir);
72
80
  const trajectoriesDir = path.join(rootDir, "trajectories");
@@ -153,6 +161,8 @@ async function searchCausalTrajectories(options) {
153
161
  }
154
162
 
155
163
  export {
164
+ readCausalTrajectoryRecords,
165
+ filterTrajectoriesByLookbackDays,
156
166
  getCausalTrajectoryStoreStatus,
157
167
  searchCausalTrajectories
158
168
  };
@@ -1,65 +1,10 @@
1
+ import {
2
+ extractJsonCandidates
3
+ } from "./chunk-3A5ELHTT.js";
1
4
  import {
2
5
  log
3
6
  } from "./chunk-UFU5GGGA.js";
4
7
 
5
- // ../remnic-core/src/json-extract.ts
6
- function stripCodeFences(text) {
7
- return text.replace(/```(?:json)?\s*([\s\S]*?)```/gi, (_m, inner) => String(inner).trim());
8
- }
9
- function extractJsonCandidates(text) {
10
- const trimmed = text.trim();
11
- const cleaned = stripCodeFences(trimmed);
12
- const candidates = [];
13
- if (cleaned.length > 0) candidates.push(cleaned);
14
- candidates.push(...scanBalancedJsonBlocks(cleaned));
15
- const objMatch = cleaned.match(/\{[\s\S]*\}/);
16
- if (objMatch) candidates.push(objMatch[0]);
17
- const seen = /* @__PURE__ */ new Set();
18
- return candidates.map((c) => c.trim()).filter((c) => c.length > 0).filter((c) => {
19
- if (seen.has(c)) return false;
20
- seen.add(c);
21
- return true;
22
- });
23
- }
24
- function scanBalancedJsonBlocks(text) {
25
- const out = [];
26
- const opens = /* @__PURE__ */ new Set(["{", "["]);
27
- const closes = { "{": "}", "[": "]" };
28
- for (let i = 0; i < text.length; i++) {
29
- const start = text[i];
30
- if (!opens.has(start)) continue;
31
- const expectedClose = closes[start];
32
- let depth = 0;
33
- let inString = false;
34
- let escape = false;
35
- for (let j = i; j < text.length; j++) {
36
- const ch = text[j];
37
- if (inString) {
38
- if (escape) {
39
- escape = false;
40
- } else if (ch === "\\") {
41
- escape = true;
42
- } else if (ch === '"') {
43
- inString = false;
44
- }
45
- continue;
46
- }
47
- if (ch === '"') {
48
- inString = true;
49
- continue;
50
- }
51
- if (ch === start) depth++;
52
- if (ch === expectedClose) depth--;
53
- if (depth === 0) {
54
- out.push(text.slice(i, j + 1).trim());
55
- i = j;
56
- break;
57
- }
58
- }
59
- }
60
- return out;
61
- }
62
-
63
8
  // ../remnic-core/src/openai-chat-compat.ts
64
9
  function normalizedModel(model) {
65
10
  return model.trim().toLowerCase();
@@ -183,13 +128,25 @@ async function getGatewayResolver() {
183
128
  async function findRuntimeModules() {
184
129
  const { readdirSync } = await import("fs");
185
130
  const { createRequire } = await import("module");
131
+ const { execFileSync } = await import("child_process");
186
132
  const candidates = [];
187
133
  const distDirs = [];
134
+ const pushDistDirs = (entryPath) => {
135
+ const resolvedEntryDir = path.dirname(entryPath);
136
+ const packageRoot = path.basename(resolvedEntryDir) === "dist" ? path.resolve(resolvedEntryDir, "..") : resolvedEntryDir;
137
+ const candidateDistDirs = [
138
+ path.join(packageRoot, "dist"),
139
+ path.join(packageRoot, "..", "dist")
140
+ ];
141
+ for (const candidate of candidateDistDirs) {
142
+ const resolved = path.resolve(candidate);
143
+ if (!distDirs.includes(resolved)) distDirs.push(resolved);
144
+ }
145
+ };
188
146
  try {
189
147
  const req = createRequire(import.meta.url);
190
148
  const openclawMain = req.resolve("openclaw");
191
- const openclawDist = path.join(path.dirname(openclawMain), "..", "dist");
192
- if (openclawDist) distDirs.push(path.resolve(openclawDist));
149
+ pushDistDirs(openclawMain);
193
150
  } catch {
194
151
  }
195
152
  try {
@@ -198,12 +155,22 @@ async function findRuntimeModules() {
198
155
  if (mainScript) {
199
156
  const realScript = realpathSync(mainScript);
200
157
  if (realScript.includes("openclaw")) {
201
- const distDir = path.dirname(realScript);
202
- if (!distDirs.includes(distDir)) distDirs.push(distDir);
158
+ pushDistDirs(realScript);
203
159
  }
204
160
  }
205
161
  } catch {
206
162
  }
163
+ try {
164
+ const openclawBin = execFileSync("which", ["openclaw"], {
165
+ encoding: "utf8",
166
+ stdio: ["ignore", "pipe", "ignore"]
167
+ }).trim();
168
+ if (openclawBin) {
169
+ const { realpathSync } = await import("fs");
170
+ pushDistDirs(realpathSync(openclawBin));
171
+ }
172
+ } catch {
173
+ }
207
174
  for (const dir of distDirs) {
208
175
  try {
209
176
  const files = readdirSync(dir);
@@ -218,7 +185,10 @@ async function findRuntimeModules() {
218
185
  return candidates;
219
186
  }
220
187
  async function resolveProviderApiKey(providerId, apiKeyValue, gatewayConfig, agentDir) {
221
- const cacheKey = `provider:${providerId}`;
188
+ const resolvedAgentDir = path.resolve(
189
+ agentDir ?? path.join(os2.homedir(), ".openclaw", "agents", "main", "agent")
190
+ );
191
+ const cacheKey = `provider:${providerId}:agentDir:${resolvedAgentDir}`;
222
192
  if (resolvedCache.has(cacheKey)) {
223
193
  return resolvedCache.get(cacheKey);
224
194
  }
@@ -234,7 +204,6 @@ async function resolveProviderApiKey(providerId, apiKeyValue, gatewayConfig, age
234
204
  const resolver = await getGatewayResolver();
235
205
  if (resolver) {
236
206
  try {
237
- const resolvedAgentDir = agentDir ?? path.join(os2.homedir(), ".openclaw", "agents", "main", "agent");
238
207
  const auth = await resolver({ provider: providerId, cfg: gatewayConfig, agentDir: resolvedAgentDir });
239
208
  if (auth?.apiKey) {
240
209
  resolved = auth.apiKey;
@@ -310,8 +279,10 @@ function loadModelsJsonProviders() {
310
279
  // ../remnic-core/src/fallback-llm.ts
311
280
  var FallbackLlmClient = class {
312
281
  gatewayConfig;
313
- constructor(gatewayConfig) {
282
+ runtimeContext;
283
+ constructor(gatewayConfig, runtimeContext = {}) {
314
284
  this.gatewayConfig = gatewayConfig;
285
+ this.runtimeContext = runtimeContext;
315
286
  }
316
287
  /**
317
288
  * Check if fallback is available (gateway config has at least one model).
@@ -513,6 +484,14 @@ var FallbackLlmClient = class {
513
484
  if (model.providerConfig.api === "anthropic-messages") {
514
485
  return await this.callAnthropic(effectiveConfig, model.modelId, messages, options);
515
486
  }
487
+ if (model.providerConfig.api === "openai-responses" || model.providerConfig.api === "openai-codex-responses" || model.providerConfig.api === "azure-openai-responses") {
488
+ return await this.callOpenAIResponses(
489
+ effectiveConfig,
490
+ model.modelId,
491
+ messages,
492
+ options
493
+ );
494
+ }
516
495
  return await this.callOpenAI(
517
496
  effectiveConfig,
518
497
  model.modelId,
@@ -537,7 +516,8 @@ var FallbackLlmClient = class {
537
516
  api: model.providerConfig.api,
538
517
  baseUrl: model.providerConfig.baseUrl
539
518
  },
540
- cfg: this.gatewayConfig
519
+ cfg: this.gatewayConfig,
520
+ workspaceDir: this.runtimeContext.workspaceDir
541
521
  });
542
522
  if (result?.apiKey || result?.baseUrl) {
543
523
  log.debug(
@@ -557,7 +537,12 @@ var FallbackLlmClient = class {
557
537
  * secret refs, etc.). Used as fallback when gateway runtime auth isn't available.
558
538
  */
559
539
  async resolveFallbackApiKey(model) {
560
- return resolveProviderApiKey(model.providerId, model.providerConfig.apiKey, this.gatewayConfig);
540
+ return resolveProviderApiKey(
541
+ model.providerId,
542
+ model.providerConfig.apiKey,
543
+ this.gatewayConfig,
544
+ this.runtimeContext.agentDir
545
+ );
561
546
  }
562
547
  /**
563
548
  * Call OpenAI-compatible API.
@@ -605,11 +590,65 @@ var FallbackLlmClient = class {
605
590
  } : void 0
606
591
  };
607
592
  }
593
+ /**
594
+ * Call an OpenAI-compatible Responses API.
595
+ */
596
+ async callOpenAIResponses(config, modelId, messages, options) {
597
+ const base = config.baseUrl.replace(/\/$/, "");
598
+ const url = base.endsWith("/v1") ? `${base}/responses` : `${base}/v1/responses`;
599
+ const headers = {
600
+ "Content-Type": "application/json",
601
+ ...config.headers
602
+ };
603
+ if (config.apiKey && typeof config.apiKey === "string" && config.authHeader !== false) {
604
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
605
+ }
606
+ const instructions = messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
607
+ const input = messages.filter((message) => message.role !== "system").map((message) => ({
608
+ role: message.role,
609
+ content: [{
610
+ type: message.role === "assistant" ? "output_text" : "input_text",
611
+ text: message.content
612
+ }]
613
+ }));
614
+ const body = {
615
+ model: modelId,
616
+ input,
617
+ max_output_tokens: Math.max(0, Math.floor(options.maxTokens ?? 4096)),
618
+ temperature: options.temperature ?? 0.3
619
+ };
620
+ if (instructions.length > 0) {
621
+ body.instructions = instructions;
622
+ }
623
+ const response = await fetch(url, {
624
+ method: "POST",
625
+ headers,
626
+ body: JSON.stringify(body)
627
+ });
628
+ if (!response.ok) {
629
+ const error = await response.text();
630
+ throw new Error(`OpenAI Responses API error: ${response.status} ${error}`);
631
+ }
632
+ const data = await response.json();
633
+ const outputText = extractResponsesOutputText(data);
634
+ if (!outputText) {
635
+ throw new Error("Empty response from OpenAI Responses API");
636
+ }
637
+ return {
638
+ content: outputText,
639
+ usage: data.usage ? {
640
+ inputTokens: data.usage.input_tokens,
641
+ outputTokens: data.usage.output_tokens,
642
+ totalTokens: data.usage.total_tokens
643
+ } : void 0
644
+ };
645
+ }
608
646
  /**
609
647
  * Call Anthropic Messages API.
610
648
  */
611
649
  async callAnthropic(config, modelId, messages, options) {
612
- const url = `${config.baseUrl.replace(/\/$/, "")}/messages`;
650
+ const base = config.baseUrl.replace(/\/$/, "");
651
+ const url = base.endsWith("/v1") ? `${base}/messages` : `${base}/v1/messages`;
613
652
  const headers = {
614
653
  "Content-Type": "application/json",
615
654
  "anthropic-version": "2023-06-01",
@@ -657,12 +696,29 @@ var FallbackLlmClient = class {
657
696
  };
658
697
  }
659
698
  };
699
+ function extractResponsesOutputText(data) {
700
+ if (typeof data.output_text === "string" && data.output_text.trim().length > 0) {
701
+ return data.output_text;
702
+ }
703
+ const chunks = [];
704
+ for (const item of data.output ?? []) {
705
+ if (typeof item.text === "string" && item.text.trim().length > 0) {
706
+ chunks.push(item.text);
707
+ }
708
+ for (const part of item.content ?? []) {
709
+ if ((part.type === "output_text" || part.type === "text") && typeof part.text === "string" && part.text.trim().length > 0) {
710
+ chunks.push(part.text);
711
+ }
712
+ }
713
+ }
714
+ const joined = chunks.join("\n").trim();
715
+ return joined.length > 0 ? joined : null;
716
+ }
660
717
 
661
718
  export {
662
719
  readEnvVar,
663
720
  resolveHomeDir,
664
721
  mergeEnv,
665
- extractJsonCandidates,
666
722
  shouldAssumeOpenAiChatCompletions,
667
723
  buildChatCompletionTokenLimit,
668
724
  FallbackLlmClient
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-5VTGFKKU.js";
3
+ } from "./chunk-KPMXWORS.js";
4
4
  import {
5
5
  countRecallTokenOverlap,
6
6
  normalizeRecallTokens
@@ -3,7 +3,7 @@ import {
3
3
  parseContinuityImprovementLoops,
4
4
  parseContinuityIncident,
5
5
  sanitizeMemoryContent
6
- } from "./chunk-5VTGFKKU.js";
6
+ } from "./chunk-KPMXWORS.js";
7
7
  import {
8
8
  log
9
9
  } from "./chunk-UFU5GGGA.js";
@@ -4881,7 +4881,7 @@ var CompoundingEngine = class {
4881
4881
  let promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary, mistakes.registry, rubrics) : [];
4882
4882
  if (this.config.cmcConsolidationEnabled) {
4883
4883
  try {
4884
- const { deriveCausalPromotionCandidates, materializeAfterCausalConsolidation } = await import("./causal-consolidation-Z3PHIFTL.js");
4884
+ const { deriveCausalPromotionCandidates, materializeAfterCausalConsolidation } = await import("./causal-consolidation-S6M7UTZG.js");
4885
4885
  const causalCandidates = await deriveCausalPromotionCandidates({
4886
4886
  memoryDir: this.config.memoryDir,
4887
4887
  causalTrajectoryStoreDir: this.config.causalTrajectoryStoreDir,
@@ -4913,7 +4913,7 @@ var CompoundingEngine = class {
4913
4913
  }
4914
4914
  if (this.config.calibrationEnabled) {
4915
4915
  try {
4916
- const { runCalibrationConsolidation } = await import("./calibration-3JHF25QT.js");
4916
+ const { runCalibrationConsolidation } = await import("./calibration-BAC7KNKR.js");
4917
4917
  const calRules = await runCalibrationConsolidation({
4918
4918
  memoryDir: this.config.memoryDir,
4919
4919
  gatewayConfig: this.config.gatewayConfig,