@joshuaswarren/openclaw-engram 9.1.22 → 9.1.24
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 +27 -6
- package/dist/access-cli.js +3 -3
- package/dist/admin-console/public/app.js +222 -0
- package/dist/admin-console/public/index.html +72 -0
- package/dist/{calibration-UY5SQU5K.js → calibration-WH4XEHIA.js} +2 -2
- package/dist/{causal-consolidation-5RFIDR74.js → causal-consolidation-62M3TOOX.js} +2 -2
- package/dist/{chunk-FP3MUEPD.js → chunk-4722S3A6.js} +2 -2
- package/dist/chunk-4722S3A6.js.map +1 -0
- package/dist/{chunk-EM2BU45W.js → chunk-PIQWOQEX.js} +3 -3
- package/dist/{chunk-3ILF2OUT.js → chunk-XKT3IT22.js} +445 -28
- package/dist/chunk-XKT3IT22.js.map +1 -0
- package/dist/{engine-ZPZYNGSE.js → engine-UGYPARP3.js} +2 -2
- package/dist/{fallback-llm-3DXDWI5H.js → fallback-llm-PKOQ6PB5.js} +2 -2
- package/dist/index.js +132 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3ILF2OUT.js.map +0 -1
- package/dist/chunk-FP3MUEPD.js.map +0 -1
- /package/dist/{calibration-UY5SQU5K.js.map → calibration-WH4XEHIA.js.map} +0 -0
- /package/dist/{causal-consolidation-5RFIDR74.js.map → causal-consolidation-62M3TOOX.js.map} +0 -0
- /package/dist/{chunk-EM2BU45W.js.map → chunk-PIQWOQEX.js.map} +0 -0
- /package/dist/{engine-ZPZYNGSE.js.map → engine-UGYPARP3.js.map} +0 -0
- /package/dist/{fallback-llm-3DXDWI5H.js.map → fallback-llm-PKOQ6PB5.js.map} +0 -0
package/README.md
CHANGED
|
@@ -71,12 +71,12 @@ After installation, add Engram to your `openclaw.json`:
|
|
|
71
71
|
// Option 1: Use OpenAI for extraction:
|
|
72
72
|
"openaiApiKey": "${OPENAI_API_KEY}"
|
|
73
73
|
|
|
74
|
-
// Option 2: Use
|
|
74
|
+
// Option 2: Use Engram's local LLM path (plugin mode only; no API key needed):
|
|
75
75
|
// "localLlmEnabled": true,
|
|
76
76
|
// "localLlmUrl": "http://localhost:1234/v1",
|
|
77
77
|
// "localLlmModel": "qwen2.5-32b-instruct"
|
|
78
78
|
|
|
79
|
-
// Option 3: Use the gateway model chain (
|
|
79
|
+
// Option 3: Use the gateway model chain (primary path in gateway mode):
|
|
80
80
|
// "modelSource": "gateway",
|
|
81
81
|
// "gatewayAgentId": "engram-llm",
|
|
82
82
|
// "fastGatewayAgentId": "engram-llm-fast"
|
|
@@ -87,7 +87,7 @@ After installation, add Engram to your `openclaw.json`:
|
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
> **Gateway model source:** When `modelSource` is `"gateway"`, Engram routes all LLM calls (extraction, consolidation, reranking) through an OpenClaw agent persona's model chain instead of its own config. Define agent personas in `openclaw.json → agents.list[]` with a `primary` model and `fallbacks[]` array — Engram tries each in order until one succeeds. This lets you build multi-provider fallback chains like Fireworks → local LLM → cloud OpenAI. See the [Gateway Model Source](docs/config-reference.md#gateway-model-source) guide for full setup.
|
|
90
|
+
> **Gateway model source:** When `modelSource` is `"gateway"`, Engram routes all LLM calls (extraction, consolidation, reranking) through an OpenClaw agent persona's model chain instead of its own config. Extraction starts on the `gatewayAgentId` chain directly in this mode; `localLlm*` settings do not control primary extraction order. Define agent personas in `openclaw.json → agents.list[]` with a `primary` model and `fallbacks[]` array — Engram tries each in order until one succeeds. This lets you build multi-provider fallback chains like Fireworks → local LLM → cloud OpenAI. See the [Gateway Model Source](docs/config-reference.md#gateway-model-source) guide for full setup.
|
|
91
91
|
|
|
92
92
|
Restart the gateway:
|
|
93
93
|
|
|
@@ -409,7 +409,7 @@ openclaw engram access http-serve --token "$OPENCLAW_ENGRAM_ACCESS_TOKEN"
|
|
|
409
409
|
|
|
410
410
|
Key endpoints: `GET /engram/v1/health`, `POST /engram/v1/recall`, `POST /engram/v1/memories`, `GET /engram/v1/entities/:name`, and more. Full reference in [API docs](docs/api.md).
|
|
411
411
|
|
|
412
|
-
The HTTP server also hosts a lightweight operator UI at `http://127.0.0.1:4318/engram/ui/` for memory browsing, recall inspection, governance review, and entity exploration.
|
|
412
|
+
The HTTP server also hosts a lightweight operator UI at `http://127.0.0.1:4318/engram/ui/` for memory browsing, recall inspection, governance review, trust-zone promotion, and entity exploration.
|
|
413
413
|
|
|
414
414
|
### MCP Tools
|
|
415
415
|
|
|
@@ -473,8 +473,29 @@ openclaw engram semantic-consolidate --dry-run # Preview without changes
|
|
|
473
473
|
# Access layer
|
|
474
474
|
openclaw engram access http-serve --token "$TOKEN" # Start HTTP API
|
|
475
475
|
openclaw engram access mcp-serve # Start stdio MCP server
|
|
476
|
+
|
|
477
|
+
# Trust-zone demos
|
|
478
|
+
openclaw engram trust-zone-demo-seed --dry-run # Preview the opt-in buyer demo dataset
|
|
479
|
+
openclaw engram trust-zone-demo-seed # Explicitly seed the demo dataset
|
|
480
|
+
openclaw engram trust-zone-promote --record-id <id> --target-zone working --reason "Operator review"
|
|
476
481
|
```
|
|
477
482
|
|
|
483
|
+
### Trust-zone demo workflow
|
|
484
|
+
|
|
485
|
+
Trust zones now ship with a dedicated admin-console view plus an explicit demo seeding path for buyer-facing walkthroughs.
|
|
486
|
+
|
|
487
|
+
- **Never automatic** — Engram does not seed sample trust-zone records on install, startup, or feature enablement.
|
|
488
|
+
- **Explicit only** — demo records appear only after you run `openclaw engram trust-zone-demo-seed` or trigger the matching admin-console action.
|
|
489
|
+
- **Buyer-friendly story** — the trust-zone view surfaces provenance strength, promotion readiness, corroboration requirements, and operator promotion actions in one place.
|
|
490
|
+
|
|
491
|
+
The seeded scenario is `enterprise-buyer-v1`, which creates a small, opinionated dataset covering:
|
|
492
|
+
|
|
493
|
+
- quarantine records that are ready for review
|
|
494
|
+
- working records that are blocked on missing provenance
|
|
495
|
+
- working records that still need corroboration
|
|
496
|
+
- working records with independent corroboration support
|
|
497
|
+
- a trusted operator policy record
|
|
498
|
+
|
|
478
499
|
See the [full CLI reference](docs/api.md#cli-commands) for all commands.
|
|
479
500
|
|
|
480
501
|
## Configuration
|
|
@@ -484,10 +505,10 @@ All settings live in `openclaw.json` under `plugins.entries.openclaw-engram.conf
|
|
|
484
505
|
| Setting | Default | Description |
|
|
485
506
|
|---------|---------|-------------|
|
|
486
507
|
| `openaiApiKey` | `(env)` | OpenAI API key (optional when using a local LLM) |
|
|
487
|
-
| `localLlmEnabled` | `false` |
|
|
508
|
+
| `localLlmEnabled` | `false` | Enable Engram's local LLM path when `modelSource` is `plugin` |
|
|
488
509
|
| `localLlmUrl` | unset | Local LLM endpoint (e.g., `http://localhost:1234/v1`) |
|
|
489
510
|
| `localLlmModel` | unset | Local model name (e.g., `qwen2.5-32b-instruct`) |
|
|
490
|
-
| `model` | `gpt-5.2` | OpenAI model for extraction
|
|
511
|
+
| `model` | `gpt-5.2` | OpenAI model for extraction when `modelSource` is `plugin` and local LLM is disabled |
|
|
491
512
|
| `searchBackend` | `"qmd"` | Search engine: `qmd`, `orama`, `lancedb`, `meilisearch`, `remote`, `noop` |
|
|
492
513
|
| `captureMode` | `implicit` | Memory write policy: `implicit`, `explicit`, `hybrid` |
|
|
493
514
|
| `recallBudgetChars` | `maxMemoryTokens * 4` | Recall budget (default ~8K chars; set 64K+ for large-context models) |
|
package/dist/access-cli.js
CHANGED
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
EngramAccessService,
|
|
4
4
|
Orchestrator,
|
|
5
5
|
parseConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-XKT3IT22.js";
|
|
7
|
+
import "./chunk-PIQWOQEX.js";
|
|
8
8
|
import "./chunk-IMMYYNXG.js";
|
|
9
9
|
import "./chunk-Y6DMH5LN.js";
|
|
10
10
|
import "./chunk-6KX4XLQJ.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-4722S3A6.js";
|
|
12
12
|
import "./chunk-MKM2BCQH.js";
|
|
13
13
|
import "./chunk-DEIBZP3O.js";
|
|
14
14
|
import "./chunk-SSIIJJKA.js";
|
|
@@ -5,6 +5,11 @@ const browserState = {
|
|
|
5
5
|
offset: 0,
|
|
6
6
|
total: 0,
|
|
7
7
|
};
|
|
8
|
+
const trustZoneState = {
|
|
9
|
+
limit: 12,
|
|
10
|
+
offset: 0,
|
|
11
|
+
total: 0,
|
|
12
|
+
};
|
|
8
13
|
|
|
9
14
|
function $(id) {
|
|
10
15
|
return document.getElementById(id);
|
|
@@ -109,12 +114,43 @@ function readMemoryPageSize() {
|
|
|
109
114
|
return Number.parseInt($("memoryPageSize")?.value || String(browserState.limit || 25), 10) || 25;
|
|
110
115
|
}
|
|
111
116
|
|
|
117
|
+
function readTrustZonePageSize() {
|
|
118
|
+
return Number.parseInt($("trustZonePageSize")?.value || String(trustZoneState.limit || 12), 10) || 12;
|
|
119
|
+
}
|
|
120
|
+
|
|
112
121
|
function stepMemoryPage(direction) {
|
|
113
122
|
const pageSize = readMemoryPageSize();
|
|
114
123
|
browserState.limit = pageSize;
|
|
115
124
|
browserState.offset = Math.max(0, browserState.offset + direction * pageSize);
|
|
116
125
|
}
|
|
117
126
|
|
|
127
|
+
function syncTrustZoneControls() {
|
|
128
|
+
const prevButton = $("trustZonePrevButton");
|
|
129
|
+
const nextButton = $("trustZoneNextButton");
|
|
130
|
+
if (prevButton) prevButton.disabled = trustZoneState.offset <= 0;
|
|
131
|
+
if (nextButton) nextButton.disabled = trustZoneState.offset + trustZoneState.limit >= trustZoneState.total;
|
|
132
|
+
|
|
133
|
+
const pageStatus = $("trustZonePageStatus");
|
|
134
|
+
if (!pageStatus) return;
|
|
135
|
+
if (trustZoneState.total === 0) {
|
|
136
|
+
pageStatus.textContent = "No results";
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const pageOffset = Math.min(
|
|
140
|
+
trustZoneState.offset,
|
|
141
|
+
Math.max(0, trustZoneState.total - 1),
|
|
142
|
+
);
|
|
143
|
+
const start = pageOffset + 1;
|
|
144
|
+
const end = Math.min(pageOffset + trustZoneState.limit, trustZoneState.total);
|
|
145
|
+
pageStatus.textContent = `${start}-${end} of ${trustZoneState.total}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function stepTrustZonePage(direction) {
|
|
149
|
+
const pageSize = readTrustZonePageSize();
|
|
150
|
+
trustZoneState.limit = pageSize;
|
|
151
|
+
trustZoneState.offset = Math.max(0, trustZoneState.offset + direction * pageSize);
|
|
152
|
+
}
|
|
153
|
+
|
|
118
154
|
function renderMemoryList(memories) {
|
|
119
155
|
const list = $("memoryList");
|
|
120
156
|
if (!list) return;
|
|
@@ -254,6 +290,86 @@ function renderEntityList(entities) {
|
|
|
254
290
|
});
|
|
255
291
|
}
|
|
256
292
|
|
|
293
|
+
function renderTrustZoneList(records) {
|
|
294
|
+
const list = $("trustZoneList");
|
|
295
|
+
if (!list) return;
|
|
296
|
+
if (!Array.isArray(records) || records.length === 0) {
|
|
297
|
+
renderEmptyState(list, "No trust-zone records matched.");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
clearChildren(list);
|
|
301
|
+
records.forEach((record) => {
|
|
302
|
+
const article = createItem();
|
|
303
|
+
const meta = document.createElement("div");
|
|
304
|
+
meta.className = "meta";
|
|
305
|
+
appendPill(meta, record.zone);
|
|
306
|
+
appendPill(meta, record.kind);
|
|
307
|
+
appendPill(meta, record.sourceClass);
|
|
308
|
+
appendPill(meta, record.anchored ? "anchored" : "unanchored");
|
|
309
|
+
if (record.trustScore) {
|
|
310
|
+
appendPill(meta, `trust ${record.trustScore.total} (${record.trustScore.band})`);
|
|
311
|
+
}
|
|
312
|
+
article.appendChild(meta);
|
|
313
|
+
|
|
314
|
+
const heading = document.createElement("h3");
|
|
315
|
+
heading.style.marginTop = "10px";
|
|
316
|
+
heading.textContent = record.recordId;
|
|
317
|
+
article.appendChild(heading);
|
|
318
|
+
|
|
319
|
+
const pathText = document.createElement("div");
|
|
320
|
+
pathText.className = "status";
|
|
321
|
+
pathText.textContent = `${record.recordedAt} · ${record.filePath}`;
|
|
322
|
+
article.appendChild(pathText);
|
|
323
|
+
|
|
324
|
+
const preview = document.createElement("p");
|
|
325
|
+
preview.textContent = record.summary;
|
|
326
|
+
article.appendChild(preview);
|
|
327
|
+
|
|
328
|
+
const readiness = document.createElement("div");
|
|
329
|
+
readiness.className = "status";
|
|
330
|
+
if (record.nextPromotionTarget) {
|
|
331
|
+
readiness.textContent = record.nextPromotionAllowed
|
|
332
|
+
? `Ready for promotion to ${record.nextPromotionTarget}.`
|
|
333
|
+
: `Blocked on ${record.nextPromotionTarget}: ${(record.nextPromotionReasons || []).join("; ") || "operator review required"}`;
|
|
334
|
+
} else {
|
|
335
|
+
readiness.textContent = "No further promotion path.";
|
|
336
|
+
}
|
|
337
|
+
article.appendChild(readiness);
|
|
338
|
+
|
|
339
|
+
const toolbar = document.createElement("div");
|
|
340
|
+
toolbar.className = "toolbar";
|
|
341
|
+
toolbar.style.marginTop = "12px";
|
|
342
|
+
|
|
343
|
+
const inspectButton = document.createElement("button");
|
|
344
|
+
inspectButton.className = "secondary";
|
|
345
|
+
inspectButton.textContent = "Inspect";
|
|
346
|
+
inspectButton.addEventListener("click", () => {
|
|
347
|
+
$("trustZoneDetail").textContent = JSON.stringify(record, null, 2);
|
|
348
|
+
setStatus("trustZoneDetailStatus", `Loaded ${record.recordId}.`, "ok");
|
|
349
|
+
});
|
|
350
|
+
toolbar.appendChild(inspectButton);
|
|
351
|
+
|
|
352
|
+
if (record.nextPromotionTarget) {
|
|
353
|
+
const previewButton = document.createElement("button");
|
|
354
|
+
previewButton.className = "secondary";
|
|
355
|
+
previewButton.textContent = `Preview → ${record.nextPromotionTarget}`;
|
|
356
|
+
previewButton.addEventListener("click", () => void promoteTrustZone(record.recordId, record.nextPromotionTarget, true));
|
|
357
|
+
toolbar.appendChild(previewButton);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (record.nextPromotionTarget && record.nextPromotionAllowed) {
|
|
361
|
+
const promoteButton = document.createElement("button");
|
|
362
|
+
promoteButton.className = "accent";
|
|
363
|
+
promoteButton.textContent = `Promote → ${record.nextPromotionTarget}`;
|
|
364
|
+
promoteButton.addEventListener("click", () => void promoteTrustZone(record.recordId, record.nextPromotionTarget, false));
|
|
365
|
+
toolbar.appendChild(promoteButton);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
article.appendChild(toolbar);
|
|
369
|
+
list.appendChild(article);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
257
373
|
function renderQuality(response) {
|
|
258
374
|
const summary = $("qualitySummary");
|
|
259
375
|
if (!summary) return;
|
|
@@ -418,6 +534,99 @@ async function loadMaintenance() {
|
|
|
418
534
|
setStatus("maintenanceStatus", "Maintenance summary loaded.", "ok");
|
|
419
535
|
}
|
|
420
536
|
|
|
537
|
+
async function loadTrustZones(resetOffset = false) {
|
|
538
|
+
if (resetOffset) trustZoneState.offset = 0;
|
|
539
|
+
trustZoneState.limit = readTrustZonePageSize();
|
|
540
|
+
setStatus("trustZoneStatus", "Loading trust-zone state...");
|
|
541
|
+
const params = new URLSearchParams();
|
|
542
|
+
const query = $("trustZoneQuery")?.value?.trim();
|
|
543
|
+
const zone = $("trustZoneZone")?.value?.trim();
|
|
544
|
+
const sourceClass = $("trustZoneSourceClass")?.value?.trim();
|
|
545
|
+
if (query) params.set("q", query);
|
|
546
|
+
if (zone) params.set("zone", zone);
|
|
547
|
+
if (sourceClass) params.set("sourceClass", sourceClass);
|
|
548
|
+
params.set("limit", String(trustZoneState.limit));
|
|
549
|
+
params.set("offset", String(trustZoneState.offset));
|
|
550
|
+
|
|
551
|
+
const [statusResponse, browseResponse] = await Promise.all([
|
|
552
|
+
fetchJson("/engram/v1/trust-zones/status"),
|
|
553
|
+
fetchJson(`/engram/v1/trust-zones/records?${params.toString()}`),
|
|
554
|
+
]);
|
|
555
|
+
trustZoneState.total = browseResponse.total || 0;
|
|
556
|
+
const maxOffset = trustZoneState.total > 0
|
|
557
|
+
? Math.floor((trustZoneState.total - 1) / trustZoneState.limit) * trustZoneState.limit
|
|
558
|
+
: 0;
|
|
559
|
+
if (!resetOffset && trustZoneState.offset > maxOffset) {
|
|
560
|
+
trustZoneState.offset = maxOffset;
|
|
561
|
+
return loadTrustZones(false);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
renderTrustZoneList(browseResponse.records);
|
|
565
|
+
syncTrustZoneControls();
|
|
566
|
+
const byZone = statusResponse?.status?.records?.byZone || {};
|
|
567
|
+
const zoneSummary = ["quarantine", "working", "trusted"]
|
|
568
|
+
.filter((name) => typeof byZone[name] === "number")
|
|
569
|
+
.map((name) => `${name} ${byZone[name]}`)
|
|
570
|
+
.join(" · ");
|
|
571
|
+
setStatus(
|
|
572
|
+
"trustZoneStatus",
|
|
573
|
+
`Loaded ${browseResponse.count} of ${browseResponse.total} trust-zone records.${zoneSummary ? ` ${zoneSummary}.` : ""}`,
|
|
574
|
+
"ok",
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async function promoteTrustZone(recordId, targetZone, dryRun) {
|
|
579
|
+
if (!recordId || !targetZone) return;
|
|
580
|
+
setStatus("trustZoneDetailStatus", `${dryRun ? "Previewing" : "Applying"} ${targetZone} promotion for ${recordId}...`);
|
|
581
|
+
const response = await fetchJson("/engram/v1/trust-zones/promote", {
|
|
582
|
+
method: "POST",
|
|
583
|
+
body: JSON.stringify({
|
|
584
|
+
recordId,
|
|
585
|
+
targetZone,
|
|
586
|
+
promotionReason: dryRun
|
|
587
|
+
? `Previewed in Engram admin console for ${recordId}.`
|
|
588
|
+
: `Promoted in Engram admin console for ${recordId}.`,
|
|
589
|
+
dryRun,
|
|
590
|
+
}),
|
|
591
|
+
});
|
|
592
|
+
$("trustZoneSeedResult").textContent = JSON.stringify(response, null, 2);
|
|
593
|
+
$("trustZoneDetail").textContent = JSON.stringify(response.record, null, 2);
|
|
594
|
+
await loadTrustZones(false);
|
|
595
|
+
setStatus(
|
|
596
|
+
"trustZoneDetailStatus",
|
|
597
|
+
dryRun ? `Previewed ${targetZone} promotion for ${recordId}.` : `Applied ${targetZone} promotion for ${recordId}.`,
|
|
598
|
+
"ok",
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
async function seedTrustZoneDemo(dryRun) {
|
|
603
|
+
if (!dryRun && typeof window.confirm === "function") {
|
|
604
|
+
const confirmed = window.confirm(
|
|
605
|
+
"Seed the explicit trust-zone demo dataset into the current namespace? This is opt-in demo data for buyer-facing walkthroughs.",
|
|
606
|
+
);
|
|
607
|
+
if (!confirmed) return;
|
|
608
|
+
}
|
|
609
|
+
setStatus("trustZoneStatus", dryRun ? "Previewing trust-zone demo seed..." : "Seeding trust-zone demo dataset...");
|
|
610
|
+
const response = await fetchJson("/engram/v1/trust-zones/demo-seed", {
|
|
611
|
+
method: "POST",
|
|
612
|
+
body: JSON.stringify({
|
|
613
|
+
scenario: "enterprise-buyer-v1",
|
|
614
|
+
dryRun,
|
|
615
|
+
}),
|
|
616
|
+
});
|
|
617
|
+
$("trustZoneSeedResult").textContent = JSON.stringify(response, null, 2);
|
|
618
|
+
if (!dryRun) {
|
|
619
|
+
await loadTrustZones(true);
|
|
620
|
+
}
|
|
621
|
+
setStatus(
|
|
622
|
+
"trustZoneStatus",
|
|
623
|
+
dryRun
|
|
624
|
+
? `Previewed ${response.records.length} trust-zone demo records.`
|
|
625
|
+
: `Seeded ${response.recordsWritten} trust-zone demo records into ${response.namespace}.`,
|
|
626
|
+
"ok",
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
|
|
421
630
|
async function connectAndBootstrap() {
|
|
422
631
|
const input = $("tokenInput");
|
|
423
632
|
const token = input?.value?.trim() || readToken();
|
|
@@ -433,6 +642,7 @@ async function connectAndBootstrap() {
|
|
|
433
642
|
setStatus("authStatus", "Connected to Engram access API.", "ok");
|
|
434
643
|
await Promise.allSettled([
|
|
435
644
|
loadMemoryBrowser(true),
|
|
645
|
+
loadTrustZones(true),
|
|
436
646
|
loadReviewQueue(),
|
|
437
647
|
loadEntities(),
|
|
438
648
|
loadQuality(),
|
|
@@ -485,6 +695,17 @@ function bootstrap() {
|
|
|
485
695
|
void loadMemoryBrowser(false);
|
|
486
696
|
});
|
|
487
697
|
$("runRecallButton")?.addEventListener("click", () => void runRecallDebugger());
|
|
698
|
+
$("refreshTrustZonesButton")?.addEventListener("click", () => void loadTrustZones(true));
|
|
699
|
+
$("trustZonePrevButton")?.addEventListener("click", () => {
|
|
700
|
+
stepTrustZonePage(-1);
|
|
701
|
+
void loadTrustZones(false);
|
|
702
|
+
});
|
|
703
|
+
$("trustZoneNextButton")?.addEventListener("click", () => {
|
|
704
|
+
stepTrustZonePage(1);
|
|
705
|
+
void loadTrustZones(false);
|
|
706
|
+
});
|
|
707
|
+
$("previewTrustZoneSeedButton")?.addEventListener("click", () => void seedTrustZoneDemo(true));
|
|
708
|
+
$("seedTrustZoneDemoButton")?.addEventListener("click", () => void seedTrustZoneDemo(false));
|
|
488
709
|
$("refreshQueueButton")?.addEventListener("click", () => void loadReviewQueue());
|
|
489
710
|
$("searchEntitiesButton")?.addEventListener("click", () => void loadEntities());
|
|
490
711
|
$("copyMemoryPathButton")?.addEventListener("click", copyMemoryPath);
|
|
@@ -493,6 +714,7 @@ function bootstrap() {
|
|
|
493
714
|
void connectAndBootstrap();
|
|
494
715
|
} else {
|
|
495
716
|
syncBrowserControls();
|
|
717
|
+
syncTrustZoneControls();
|
|
496
718
|
}
|
|
497
719
|
}
|
|
498
720
|
|
|
@@ -194,6 +194,15 @@
|
|
|
194
194
|
font-size: 0.84rem;
|
|
195
195
|
color: var(--muted);
|
|
196
196
|
}
|
|
197
|
+
.callout {
|
|
198
|
+
border: 1px dashed var(--line);
|
|
199
|
+
border-radius: 14px;
|
|
200
|
+
padding: 12px 14px;
|
|
201
|
+
background: rgba(255, 255, 255, 0.62);
|
|
202
|
+
color: var(--muted);
|
|
203
|
+
font-size: 0.88rem;
|
|
204
|
+
line-height: 1.45;
|
|
205
|
+
}
|
|
197
206
|
.status.ok {
|
|
198
207
|
color: var(--ok);
|
|
199
208
|
}
|
|
@@ -360,6 +369,69 @@
|
|
|
360
369
|
</section>
|
|
361
370
|
</div>
|
|
362
371
|
|
|
372
|
+
<div class="grid" style="margin-top: 16px;">
|
|
373
|
+
<section class="card">
|
|
374
|
+
<h2>Trust Zones</h2>
|
|
375
|
+
<div class="callout">
|
|
376
|
+
Demo data is never seeded automatically. Use the explicit seed action below only when you want a buyer-facing trust-zone walkthrough dataset.
|
|
377
|
+
</div>
|
|
378
|
+
<div class="toolbar" style="margin-top: 12px;">
|
|
379
|
+
<label style="flex: 1;">
|
|
380
|
+
Query
|
|
381
|
+
<input id="trustZoneQuery" type="text" placeholder="Search summaries, tags, metadata, or entity refs" />
|
|
382
|
+
</label>
|
|
383
|
+
<label>
|
|
384
|
+
Zone
|
|
385
|
+
<select id="trustZoneZone">
|
|
386
|
+
<option value="">All</option>
|
|
387
|
+
<option value="quarantine">quarantine</option>
|
|
388
|
+
<option value="working">working</option>
|
|
389
|
+
<option value="trusted">trusted</option>
|
|
390
|
+
</select>
|
|
391
|
+
</label>
|
|
392
|
+
<label>
|
|
393
|
+
Source
|
|
394
|
+
<select id="trustZoneSourceClass">
|
|
395
|
+
<option value="">All</option>
|
|
396
|
+
<option value="tool_output">tool_output</option>
|
|
397
|
+
<option value="web_content">web_content</option>
|
|
398
|
+
<option value="subagent_trace">subagent_trace</option>
|
|
399
|
+
<option value="system_memory">system_memory</option>
|
|
400
|
+
<option value="user_input">user_input</option>
|
|
401
|
+
<option value="manual">manual</option>
|
|
402
|
+
</select>
|
|
403
|
+
</label>
|
|
404
|
+
<label>
|
|
405
|
+
Page Size
|
|
406
|
+
<select id="trustZonePageSize">
|
|
407
|
+
<option value="8">8</option>
|
|
408
|
+
<option value="12" selected>12</option>
|
|
409
|
+
<option value="20">20</option>
|
|
410
|
+
</select>
|
|
411
|
+
</label>
|
|
412
|
+
<button id="refreshTrustZonesButton">Refresh</button>
|
|
413
|
+
<button class="secondary" id="previewTrustZoneSeedButton">Preview Demo Seed</button>
|
|
414
|
+
<button class="accent" id="seedTrustZoneDemoButton">Seed Demo Dataset</button>
|
|
415
|
+
</div>
|
|
416
|
+
<div class="toolbar pager" style="margin-top: 12px;">
|
|
417
|
+
<div class="status" id="trustZoneStatus">No trust-zone data loaded yet.</div>
|
|
418
|
+
<div class="toolbar">
|
|
419
|
+
<button class="secondary" id="trustZonePrevButton">Previous</button>
|
|
420
|
+
<div class="status" id="trustZonePageStatus">Page 1</div>
|
|
421
|
+
<button class="secondary" id="trustZoneNextButton">Next</button>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
<div class="list" id="trustZoneList"></div>
|
|
425
|
+
</section>
|
|
426
|
+
|
|
427
|
+
<section class="card">
|
|
428
|
+
<h2>Trust Zone Detail</h2>
|
|
429
|
+
<div class="status" id="trustZoneDetailStatus">Select a trust-zone record to inspect raw lineage and promotion state.</div>
|
|
430
|
+
<div class="mono-box" style="margin-top: 12px;"><pre id="trustZoneDetail">Trust-zone detail will appear here.</pre></div>
|
|
431
|
+
<div class="mono-box" style="margin-top: 12px;"><pre id="trustZoneSeedResult">Demo seed previews and promotion results will appear here.</pre></div>
|
|
432
|
+
</section>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
363
435
|
<div class="grid" style="margin-top: 16px;">
|
|
364
436
|
<section class="card">
|
|
365
437
|
<h2>Review Queue</h2>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
2
|
import {
|
|
3
3
|
FallbackLlmClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4722S3A6.js";
|
|
5
5
|
import {
|
|
6
6
|
listJsonFiles
|
|
7
7
|
} from "./chunk-DEIBZP3O.js";
|
|
@@ -233,4 +233,4 @@ export {
|
|
|
233
233
|
runCalibrationIfEnabled,
|
|
234
234
|
synthesizeCalibrationRules
|
|
235
235
|
};
|
|
236
|
-
//# sourceMappingURL=calibration-
|
|
236
|
+
//# sourceMappingURL=calibration-WH4XEHIA.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
2
|
import {
|
|
3
3
|
FallbackLlmClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4722S3A6.js";
|
|
5
5
|
import {
|
|
6
6
|
readChainIndex,
|
|
7
7
|
resolveChainsDir
|
|
@@ -203,4 +203,4 @@ export {
|
|
|
203
203
|
deriveCausalPromotionCandidates,
|
|
204
204
|
synthesizeCausalPreferencesViaLlm
|
|
205
205
|
};
|
|
206
|
-
//# sourceMappingURL=causal-consolidation-
|
|
206
|
+
//# sourceMappingURL=causal-consolidation-62M3TOOX.js.map
|
|
@@ -260,7 +260,7 @@ var FallbackLlmClient = class {
|
|
|
260
260
|
const result = await this.tryModel(model, messages, options);
|
|
261
261
|
if (result) {
|
|
262
262
|
if (isFallback) {
|
|
263
|
-
log.
|
|
263
|
+
log.debug(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);
|
|
264
264
|
}
|
|
265
265
|
return {
|
|
266
266
|
content: result.content,
|
|
@@ -531,4 +531,4 @@ export {
|
|
|
531
531
|
buildChatCompletionTokenLimit,
|
|
532
532
|
FallbackLlmClient
|
|
533
533
|
};
|
|
534
|
-
//# sourceMappingURL=chunk-
|
|
534
|
+
//# sourceMappingURL=chunk-4722S3A6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/json-extract.ts","../src/openai-chat-compat.ts","../src/resolve-provider-secret.ts","../src/fallback-llm.ts"],"sourcesContent":["/**\n * Utilities for extracting JSON payloads from LLM outputs.\n *\n * We see common failure modes:\n * - \"Here's an example: {..}\\nHere's the real answer: {..}\" (multiple JSON blocks)\n * - fenced ```json blocks\n * - leading/trailing prose around JSON\n *\n * These helpers attempt multiple candidates and let callers validate with schemas.\n */\n\nexport function stripCodeFences(text: string): string {\n return text.replace(/```(?:json)?\\s*([\\s\\S]*?)```/gi, (_m, inner) => String(inner).trim());\n}\n\nexport function extractJsonCandidates(text: string): string[] {\n const trimmed = text.trim();\n const cleaned = stripCodeFences(trimmed);\n const candidates: string[] = [];\n\n if (cleaned.length > 0) candidates.push(cleaned);\n candidates.push(...scanBalancedJsonBlocks(cleaned));\n\n // Legacy regex fallback (single object)\n const objMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (objMatch) candidates.push(objMatch[0]);\n\n const seen = new Set<string>();\n return candidates\n .map((c) => c.trim())\n .filter((c) => c.length > 0)\n .filter((c) => {\n if (seen.has(c)) return false;\n seen.add(c);\n return true;\n });\n}\n\nfunction scanBalancedJsonBlocks(text: string): string[] {\n const out: string[] = [];\n const opens = new Set([\"{\", \"[\"]);\n const closes: Record<string, string> = { \"{\": \"}\", \"[\": \"]\" };\n\n for (let i = 0; i < text.length; i++) {\n const start = text[i];\n if (!opens.has(start)) continue;\n\n const expectedClose = closes[start];\n let depth = 0;\n let inString = false;\n let escape = false;\n\n for (let j = i; j < text.length; j++) {\n const ch = text[j];\n\n if (inString) {\n if (escape) {\n escape = false;\n } else if (ch === \"\\\\\") {\n escape = true;\n } else if (ch === \"\\\"\") {\n inString = false;\n }\n continue;\n }\n\n if (ch === \"\\\"\") {\n inString = true;\n continue;\n }\n\n if (ch === start) depth++;\n if (ch === expectedClose) depth--;\n\n if (depth === 0) {\n out.push(text.slice(i, j + 1).trim());\n i = j;\n break;\n }\n }\n }\n\n return out;\n}\n\n","function normalizedModel(model: string): string {\n return model.trim().toLowerCase();\n}\n\nfunction matchesModelFamily(normalized: string, familyPattern: RegExp): boolean {\n return familyPattern.test(normalized);\n}\n\nexport function shouldAssumeOpenAiChatCompletions(baseUrl?: string): boolean {\n if (!baseUrl) return true;\n try {\n return new URL(baseUrl).hostname.toLowerCase() === \"api.openai.com\";\n } catch {\n return false;\n }\n}\n\nexport function usesMaxCompletionTokens(model: string, options?: { assumeOpenAI?: boolean }): boolean {\n const normalized = normalizedModel(model);\n if (options?.assumeOpenAI !== true) return false;\n if (matchesModelFamily(normalized, /^gpt-5(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4o(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4\\.1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o3(?:$|[-.])/)) return true;\n return matchesModelFamily(normalized, /^o4-mini(?:$|[-.])/);\n}\n\nexport function buildChatCompletionTokenLimit(\n model: string,\n maxTokens: number,\n options?: { assumeOpenAI?: boolean },\n): { max_tokens: number } | { max_completion_tokens: number } {\n const safeMaxTokens = Math.max(0, Math.floor(maxTokens));\n if (usesMaxCompletionTokens(model, options)) {\n return { max_completion_tokens: safeMaxTokens };\n }\n return { max_tokens: safeMaxTokens };\n}\n","import { log } from \"./logger.js\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\n/**\n * Resolve a provider API key using OpenClaw's own auth resolution system.\n *\n * This module delegates to the gateway's `resolveApiKeyForProvider()` function,\n * which handles all secret reference formats (SecretRef objects, auth profiles,\n * \"secretref-managed\" markers, environment variables, etc.) using the same\n * codepath the gateway uses for its own agent sessions.\n *\n * For plain-text API keys, a fast path returns them directly without\n * involving the gateway auth system.\n *\n * Results are cached per provider for the gateway process lifetime.\n */\n\ntype ResolveApiKeyFn = (params: {\n provider: string;\n cfg?: unknown;\n agentDir?: string;\n}) => Promise<{ apiKey?: string; source?: string; mode?: string } | null>;\n\nlet _resolveApiKeyForProvider: ResolveApiKeyFn | null = null;\nlet _resolverLoaded = false;\nlet _resolverNextRetryAt = 0;\nconst RESOLVER_RETRY_BACKOFF_MS = 60_000; // 1 minute between retries after first failure\nconst resolvedCache = new Map<string, string | undefined>();\n\n/**\n * Lazily load the gateway's resolveApiKeyForProvider function.\n * Returns null if not available (e.g., running outside the gateway process).\n */\nasync function getGatewayResolver(): Promise<ResolveApiKeyFn | null> {\n if (_resolverLoaded) {\n return _resolveApiKeyForProvider;\n }\n // Backoff: don't re-scan filesystem on every call when module wasn't found.\n // After a failure, wait RESOLVER_RETRY_BACKOFF_MS before trying again.\n if (_resolverNextRetryAt > 0 && Date.now() < _resolverNextRetryAt) {\n return null;\n }\n\n try {\n // The gateway bundles this in a runtime chunk — import it dynamically.\n // This import path is stable across gateway versions since it's a named runtime export.\n const candidates = [\n // Try glob-matching the runtime module name (hash varies per build)\n ...await findRuntimeModules(),\n ];\n\n const { pathToFileURL } = await import(\"node:url\");\n for (const candidate of candidates) {\n try {\n // Convert native path to file:// URL for cross-platform ESM import compatibility\n const importUrl = pathToFileURL(candidate).href;\n const mod = await import(importUrl);\n if (typeof mod.resolveApiKeyForProvider === \"function\") {\n _resolveApiKeyForProvider = mod.resolveApiKeyForProvider;\n _resolverLoaded = true;\n log.debug(\"loaded gateway resolveApiKeyForProvider from runtime module\");\n return _resolveApiKeyForProvider;\n }\n } catch {\n // Try next candidate\n }\n }\n } catch {\n // Silent\n }\n\n // Backoff before retrying — avoid repeated fs scanning.\n // Retries after RESOLVER_RETRY_BACKOFF_MS so the resolver can\n // recover if the gateway restarts or the module becomes available.\n _resolverNextRetryAt = Date.now() + RESOLVER_RETRY_BACKOFF_MS;\n log.debug(`gateway resolveApiKeyForProvider not available — will retry after ${RESOLVER_RETRY_BACKOFF_MS / 1000}s`);\n return null;\n}\n\n/**\n * Find the gateway's model-auth runtime module by scanning the dist directory.\n * Uses require.resolve to find the openclaw package regardless of install method.\n */\nasync function findRuntimeModules(): Promise<string[]> {\n const { readdirSync } = await import(\"node:fs\");\n const { createRequire } = await import(\"node:module\");\n const candidates: string[] = [];\n\n // Discover the openclaw dist directory from the installed package,\n // regardless of how it was installed (Homebrew, npm global, local, etc.)\n const distDirs: string[] = [];\n\n try {\n // require.resolve finds the package from the current process context\n const req = createRequire(import.meta.url);\n const openclawMain = req.resolve(\"openclaw\");\n const openclawDist = path.join(path.dirname(openclawMain), \"..\", \"dist\");\n if (openclawDist) distDirs.push(path.resolve(openclawDist));\n } catch {\n // openclaw not resolvable from plugin context — try alternate paths\n }\n\n // Fallback: infer from the running process (gateway runs from its own dist/)\n // Use fs.realpathSync to resolve symlinks (e.g., /usr/local/bin/openclaw → actual path)\n try {\n const { realpathSync } = await import(\"node:fs\");\n const mainScript = process.argv[1];\n if (mainScript) {\n const realScript = realpathSync(mainScript);\n if (realScript.includes(\"openclaw\")) {\n const distDir = path.dirname(realScript);\n if (!distDirs.includes(distDir)) distDirs.push(distDir);\n }\n }\n } catch {\n // Silent\n }\n\n for (const dir of distDirs) {\n try {\n const files = readdirSync(dir);\n for (const f of files) {\n if (f.startsWith(\"runtime-model-auth.runtime-\") && f.endsWith(\".js\")) {\n candidates.push(path.join(dir, f));\n }\n }\n } catch {\n // Directory doesn't exist — skip\n }\n }\n\n return candidates;\n}\n\n/**\n * Resolve a provider API key from various OpenClaw formats.\n *\n * Resolution order:\n * 1. Plain-text string → returned immediately\n * 2. Gateway's resolveApiKeyForProvider → handles all secret ref formats\n * 3. Environment variable fallback (PROVIDER_NAME_API_KEY)\n * 4. undefined → provider is skipped in the fallback chain\n */\nexport async function resolveProviderApiKey(\n providerId: string,\n apiKeyValue: unknown,\n gatewayConfig?: unknown,\n agentDir?: string,\n): Promise<string | undefined> {\n // Check cache first\n const cacheKey = `provider:${providerId}`;\n if (resolvedCache.has(cacheKey)) {\n return resolvedCache.get(cacheKey);\n }\n\n let resolved: string | undefined;\n\n // Fast path: plain-text string that looks like an actual API key\n if (typeof apiKeyValue === \"string\" && apiKeyValue.trim().length > 0) {\n // Skip known non-API-key markers used by the gateway for auth modes\n // that don't use bearer tokens (OAuth, local endpoints, GCP credentials)\n if (\n apiKeyValue === \"secretref-managed\" ||\n apiKeyValue.endsWith(\"-oauth\") ||\n apiKeyValue.endsWith(\"-local\") ||\n apiKeyValue === \"lm-studio\" ||\n apiKeyValue.startsWith(\"gcp-\")\n ) {\n // Fall through to gateway resolver / env var fallback\n } else {\n resolved = apiKeyValue;\n resolvedCache.set(cacheKey, resolved);\n return resolved;\n }\n }\n\n // The API key is either a SecretRef object, \"secretref-managed\", or empty.\n // Try the gateway's own auth resolution system first.\n const resolver = await getGatewayResolver();\n if (resolver) {\n try {\n const resolvedAgentDir = agentDir ?? path.join(os.homedir(), \".openclaw\", \"agents\", \"main\", \"agent\");\n const auth = await resolver({ provider: providerId, cfg: gatewayConfig, agentDir: resolvedAgentDir });\n if (auth?.apiKey) {\n resolved = auth.apiKey;\n log.debug(`resolved API key for provider \"${providerId}\" via gateway auth (source: ${auth.source ?? \"unknown\"}, mode: ${auth.mode ?? \"unknown\"})`);\n resolvedCache.set(cacheKey, resolved);\n return resolved;\n }\n } catch (err) {\n log.debug(\n `gateway auth resolution failed for provider \"${providerId}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // Environment variable fallback\n resolved = resolveFromEnv(providerId);\n if (resolved) {\n log.debug(`resolved API key for provider \"${providerId}\" from environment variable`);\n } else {\n log.debug(`could not resolve API key for provider \"${providerId}\" — skipping`);\n }\n\n // Only cache successful resolutions — failures are retried on next call\n // so providers can recover after transient issues (e.g., 1Password agent restart)\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n return resolved;\n}\n\n/**\n * Try to resolve an API key from environment variables.\n */\nfunction resolveFromEnv(providerId: string): string | undefined {\n const normalized = providerId.toUpperCase().replace(/[^A-Z0-9]/g, \"_\");\n const candidates = [\n `${normalized}_API_KEY`,\n `${normalized}_TOKEN`,\n ];\n for (const envVar of candidates) {\n const value = process.env[envVar];\n if (value && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\n/**\n * Clear the resolution cache (useful for testing or key rotation).\n */\nexport function clearSecretCache(): void {\n resolvedCache.clear();\n _resolveApiKeyForProvider = null;\n _resolverLoaded = false;\n _resolverNextRetryAt = 0;\n}\n","import { log } from \"./logger.js\";\nimport type { GatewayConfig, ModelProviderConfig, AgentPersona } from \"./types.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport {\n buildChatCompletionTokenLimit,\n shouldAssumeOpenAiChatCompletions,\n} from \"./openai-chat-compat.js\";\nimport { resolveProviderApiKey } from \"./resolve-provider-secret.js\";\n\nexport interface FallbackLlmOptions {\n temperature?: number;\n maxTokens?: number;\n timeoutMs?: number;\n /** Override which agent persona's model chain to use (by ID from agents.list[]). */\n agentId?: string;\n}\n\nexport interface FallbackLlmResponse {\n content: string;\n modelUsed: string;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n}\n\ninterface ModelRef {\n providerId: string;\n modelId: string;\n providerConfig: ModelProviderConfig;\n modelString: string;\n}\n\n/**\n * Generic fallback LLM client that uses the gateway's default AI configuration\n * and walks through the full fallback chain (primary + fallbacks).\n * Supports OpenAI and Anthropic API formats.\n */\nexport class FallbackLlmClient {\n private gatewayConfig: GatewayConfig | undefined;\n\n constructor(gatewayConfig?: GatewayConfig) {\n this.gatewayConfig = gatewayConfig;\n }\n\n /**\n * Check if fallback is available (gateway config has at least one model).\n */\n isAvailable(agentId?: string): boolean {\n const models = this.getModelChain(agentId);\n return models.length > 0;\n }\n\n /**\n * Make a chat completion request using the gateway's default AI chain.\n * Tries primary first, then each fallback in order.\n * When agentId is provided, uses that agent persona's model chain instead of defaults.\n */\n async chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions = {},\n ): Promise<FallbackLlmResponse | null> {\n const models = this.getModelChain(options.agentId);\n if (models.length === 0) {\n log.warn(\"fallback LLM: no models configured in gateway\");\n return null;\n }\n\n const runChain = async (): Promise<FallbackLlmResponse | null> => {\n // Try each model in the chain\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isFallback = i > 0;\n\n try {\n const result = await this.tryModel(model, messages, options);\n if (result) {\n if (isFallback) {\n log.debug(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);\n }\n return {\n content: result.content,\n modelUsed: model.modelString,\n usage: result.usage,\n };\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);\n // Continue to next model in chain\n }\n }\n\n log.warn(`fallback LLM: all ${models.length} models in chain failed`);\n return null;\n };\n\n if (typeof options.timeoutMs === \"number\") {\n if (options.timeoutMs <= 0) {\n log.warn(\"fallback LLM: timed out before request started\");\n return null;\n }\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runChain(),\n new Promise<null>((resolve) => {\n timeoutHandle = setTimeout(() => {\n log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);\n resolve(null);\n }, options.timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n }\n\n return await runChain();\n }\n\n /**\n * Make a request with structured output (Zod schema).\n * Returns parsed JSON or null on failure.\n */\n async parseWithSchema<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<T | null> {\n const detailed = await this.parseWithSchemaDetailed(messages, schema, options);\n return detailed?.result ?? null;\n }\n\n /**\n * Like parseWithSchema but also returns the model that was used,\n * so callers can emit accurate trace events.\n */\n async parseWithSchemaDetailed<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<{ result: T; modelUsed: string } | null> {\n const response = await this.chatCompletion(messages, options);\n if (!response?.content) return null;\n\n try {\n const candidates = extractJsonCandidates(response.content);\n for (const c of candidates) {\n try {\n const parsed = JSON.parse(c);\n return { result: schema.parse(parsed), modelUsed: response.modelUsed };\n } catch {\n // keep trying other candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"fallback LLM: failed to parse structured output:\", err);\n return null;\n }\n }\n\n /**\n * Get the full model chain from gateway config.\n * Returns array of models in order: [primary, fallback1, fallback2, ...]\n *\n * When agentId is provided, looks up the matching entry in agents.list[]\n * and uses that persona's model chain. Falls back to agents.defaults.model\n * if agentId is not found or not provided.\n */\n private getModelChain(agentId?: string): ModelRef[] {\n const chain: ModelRef[] = [];\n const providers = this.gatewayConfig?.models?.providers;\n\n if (!providers) return chain;\n\n // Resolve the model config: agent persona chain or global defaults\n let modelConfig: { primary?: string; fallbacks?: string[] } | undefined;\n\n if (agentId) {\n const persona = this.gatewayConfig?.agents?.list?.find(\n (a) => a.id === agentId,\n );\n if (persona?.model) {\n modelConfig = persona.model;\n log.debug(`fallback LLM: using agent persona \"${agentId}\" model chain`);\n } else {\n log.warn(\n `fallback LLM: agent persona \"${agentId}\" not found or has no model config, falling back to defaults`,\n );\n }\n }\n\n if (!modelConfig) {\n modelConfig = this.gatewayConfig?.agents?.defaults?.model;\n }\n\n // Build list of model strings: primary + fallbacks\n const modelStrings: string[] = [];\n\n if (modelConfig?.primary) {\n modelStrings.push(modelConfig.primary);\n }\n\n if (Array.isArray(modelConfig?.fallbacks)) {\n for (const fb of modelConfig.fallbacks) {\n if (typeof fb === \"string\" && !modelStrings.includes(fb)) {\n modelStrings.push(fb);\n }\n }\n }\n\n // Parse each model string and look up provider config\n for (const modelString of modelStrings) {\n const modelRef = this.parseModelString(modelString, providers);\n if (modelRef) {\n chain.push(modelRef);\n }\n }\n\n return chain;\n }\n\n /**\n * Parse a \"provider/model\" string and look up its config.\n */\n private parseModelString(\n modelString: string,\n providers: Record<string, ModelProviderConfig>,\n ): ModelRef | null {\n // Parse \"provider/model\" format (e.g., \"openai/gpt-5.2\", \"anthropic/claude-opus-4-6\")\n const parts = modelString.split(\"/\");\n if (parts.length < 2) {\n log.warn(`fallback LLM: invalid model format: ${modelString}`);\n return null;\n }\n\n const providerId = parts[0];\n const modelId = parts.slice(1).join(\"/\"); // Handle cases like \"openai/gpt-5.2-turbo\"\n\n const providerConfig = providers[providerId];\n if (!providerConfig) {\n log.warn(`fallback LLM: provider not found: ${providerId}`);\n return null;\n }\n\n return { providerId, modelId, providerConfig, modelString };\n }\n\n /**\n * Resolve the API key for a provider, handling OpenClaw secret ref formats.\n * Results are cached per provider so exec calls only happen once.\n */\n private async resolveApiKey(\n providerId: string,\n providerConfig: ModelProviderConfig,\n ): Promise<string | undefined> {\n return resolveProviderApiKey(providerId, providerConfig.apiKey, this.gatewayConfig);\n }\n\n /**\n * Try to call a single model.\n */\n private async tryModel(\n model: ModelRef,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n // Resolve the API key from secret refs before making the call.\n // If the raw key looks like an unresolved secret ref and resolution fails,\n // skip this provider entirely so the chain falls through to the next.\n const rawKey = model.providerConfig.apiKey;\n const needsResolution = rawKey === \"secretref-managed\"\n || (typeof rawKey === \"object\" && rawKey !== null);\n const resolvedApiKey = await this.resolveApiKey(model.providerId, model.providerConfig);\n\n if (needsResolution && !resolvedApiKey) {\n throw new Error(`API key for provider \"${model.providerId}\" could not be resolved from secret ref`);\n }\n\n const configWithResolvedKey = resolvedApiKey\n ? { ...model.providerConfig, apiKey: resolvedApiKey }\n : model.providerConfig;\n\n switch (model.providerConfig.api) {\n case \"anthropic-messages\":\n return await this.callAnthropic(configWithResolvedKey, model.modelId, messages, options);\n case \"openai-completions\":\n default:\n return await this.callOpenAI(\n configWithResolvedKey,\n model.modelId,\n messages,\n options,\n shouldAssumeOpenAiChatCompletions(model.providerConfig.baseUrl),\n );\n }\n }\n\n /**\n * Call OpenAI-compatible API.\n */\n private async callOpenAI(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n assumeOpenAI: boolean,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/chat/completions`\n : `${base}/v1/chat/completions`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n\n // Handle auth — apiKey is already resolved to a string by tryModel()\n if (config.apiKey && typeof config.apiKey === \"string\") {\n if (config.authHeader !== false) {\n headers[\"Authorization\"] = `Bearer ${config.apiKey}`;\n }\n }\n\n const body = {\n model: modelId,\n messages,\n temperature: options.temperature ?? 0.3,\n ...buildChatCompletionTokenLimit(modelId, options.maxTokens ?? 4096, {\n assumeOpenAI,\n }),\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error(\"Empty response from OpenAI API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Call Anthropic Messages API.\n */\n private async callAnthropic(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const url = `${config.baseUrl.replace(/\\/$/, \"\")}/messages`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n ...config.headers,\n };\n\n // Handle auth - Anthropic uses x-api-key header (apiKey resolved by tryModel)\n if (config.apiKey && typeof config.apiKey === \"string\") {\n headers[\"x-api-key\"] = config.apiKey;\n }\n\n // Extract system message (Anthropic handles it separately)\n const systemMessage = messages.find((m) => m.role === \"system\")?.content;\n const nonSystemMessages = messages.filter((m) => m.role !== \"system\");\n\n // Convert messages to Anthropic format\n const anthropicMessages = nonSystemMessages.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const body: Record<string, unknown> = {\n model: modelId,\n messages: anthropicMessages,\n max_tokens: options.maxTokens ?? 4096,\n temperature: options.temperature ?? 0.3,\n };\n\n if (systemMessage) {\n body.system = systemMessage;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n content: Array<{\n type: string;\n text: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const content = data.content?.[0]?.text;\n if (!content) {\n throw new Error(\"Empty response from Anthropic API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: (data.usage.input_tokens ?? 0) + (data.usage.output_tokens ?? 0),\n }\n : undefined,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -830,7 +830,7 @@ var CompoundingEngine = class {
|
|
|
830
830
|
let promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary, mistakes.registry, rubrics) : [];
|
|
831
831
|
if (this.config.cmcConsolidationEnabled) {
|
|
832
832
|
try {
|
|
833
|
-
const { deriveCausalPromotionCandidates } = await import("./causal-consolidation-
|
|
833
|
+
const { deriveCausalPromotionCandidates } = await import("./causal-consolidation-62M3TOOX.js");
|
|
834
834
|
const causalCandidates = await deriveCausalPromotionCandidates({
|
|
835
835
|
memoryDir: this.config.memoryDir,
|
|
836
836
|
causalTrajectoryStoreDir: this.config.causalTrajectoryStoreDir,
|
|
@@ -851,7 +851,7 @@ var CompoundingEngine = class {
|
|
|
851
851
|
}
|
|
852
852
|
if (this.config.calibrationEnabled) {
|
|
853
853
|
try {
|
|
854
|
-
const { runCalibrationConsolidation } = await import("./calibration-
|
|
854
|
+
const { runCalibrationConsolidation } = await import("./calibration-WH4XEHIA.js");
|
|
855
855
|
const calRules = await runCalibrationConsolidation({
|
|
856
856
|
memoryDir: this.config.memoryDir,
|
|
857
857
|
gatewayConfig: this.config.gatewayConfig,
|
|
@@ -1839,4 +1839,4 @@ export {
|
|
|
1839
1839
|
defaultTierMigrationCycleBudget,
|
|
1840
1840
|
CompoundingEngine
|
|
1841
1841
|
};
|
|
1842
|
-
//# sourceMappingURL=chunk-
|
|
1842
|
+
//# sourceMappingURL=chunk-PIQWOQEX.js.map
|