@remnic/plugin-openclaw 1.0.34 → 1.0.35
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 +24 -9
- package/dist/{calibration-JD4AU7FB.js → calibration-Z5WWNV7U.js} +1 -1
- package/dist/{causal-consolidation-DSLFN64P.js → causal-consolidation-C64NNE4T.js} +3 -3
- package/dist/{chunk-NDZNURDM.js → chunk-CMKR6NDQ.js} +1 -1
- package/dist/{chunk-ZXLYEVOP.js → chunk-EYCLXMIV.js} +117 -1
- package/dist/{chunk-4UA6KMRO.js → chunk-JGIUTWZS.js} +1 -1
- package/dist/{chunk-7NMHI4IC.js → chunk-UTDLHBBV.js} +3 -3
- package/dist/{chunk-7NUFIRM3.js → chunk-VFULKFKI.js} +59 -8
- package/dist/{contradiction-scan-U3QKHWQN.js → contradiction-scan-A5NOTZPN.js} +2 -1
- package/dist/{engine-57HLTQBN.js → engine-47AKKYJ4.js} +2 -2
- package/dist/{fallback-llm-33SPYXQY.js → fallback-llm-45A755XP.js} +1 -1
- package/dist/index.js +12872 -474
- package/dist/{memory-governance-FEQCA35V.js → memory-governance-QS7Z425Y.js} +2 -2
- package/dist/{storage-R3V6ZFQT.js → storage-DDYQGLXA.js} +1 -1
- package/openclaw.plugin.json +137 -13
- package/package.json +13 -7
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @remnic/plugin-openclaw
|
|
2
2
|
|
|
3
|
-
OpenClaw plugin for Remnic memory. The package bundles the OpenClaw adapter plus the Remnic core runtime so it can run without a separate Remnic service; the adapter registers OpenClaw hooks/tools and delegates memory behavior to [`@remnic/core`](https://www.npmjs.com/package/@remnic/core).
|
|
3
|
+
OpenClaw plugin for Remnic memory and context. The package bundles the OpenClaw adapter plus the Remnic core runtime so it can run without a separate Remnic service; the adapter registers OpenClaw hooks/tools and delegates memory behavior to [`@remnic/core`](https://www.npmjs.com/package/@remnic/core).
|
|
4
4
|
|
|
5
|
-
Part of [Remnic](https://github.com/joshuaswarren/remnic),
|
|
5
|
+
Part of [Remnic](https://github.com/joshuaswarren/remnic), open-source memory and context for user-aware agents.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ openclaw plugins install clawhub:@remnic/plugin-openclaw
|
|
|
12
12
|
|
|
13
13
|
Or ask your OpenClaw agent:
|
|
14
14
|
|
|
15
|
-
> Install the @remnic/plugin-openclaw plugin and configure it as my memory system.
|
|
15
|
+
> Install the @remnic/plugin-openclaw ClawHub plugin and configure it as my memory system.
|
|
16
16
|
|
|
17
17
|
## Configure
|
|
18
18
|
|
|
@@ -86,7 +86,7 @@ This plugin hooks into the OpenClaw gateway lifecycle:
|
|
|
86
86
|
- **`agent_end`** -- buffers the conversation turn for extraction
|
|
87
87
|
- **`before_compaction`** / **`after_compaction`** -- saves checkpoints and triggers session reset on context compaction
|
|
88
88
|
- **`before_reset`** -- bounded flush of the in-flight buffer before OpenClaw discards a session
|
|
89
|
-
- **`
|
|
89
|
+
- **`api.registerCommand()`** -- exposes Remnic slash-command descriptors to the command palette
|
|
90
90
|
- **`session_start`** / **`session_end`** -- session lifecycle tracking
|
|
91
91
|
- **`before_tool_call`** / **`after_tool_call`** -- tool usage observation for analytics
|
|
92
92
|
- **`llm_output`** -- LLM token usage tracking
|
|
@@ -164,13 +164,28 @@ CI jobs that provision OpenClaw should use
|
|
|
164
164
|
`npm run check:openclaw-sdk-surface:required` or pass
|
|
165
165
|
`-- --require --package-root <path>` so a missing SDK fails instead of skipping.
|
|
166
166
|
|
|
167
|
-
Last compatibility sweep: May
|
|
167
|
+
Last compatibility sweep: May 16, 2026. The SDK surface check passed against
|
|
168
168
|
`openclaw@2026.5.3`, `openclaw@2026.5.3-1`, `openclaw@2026.5.4-beta.1`,
|
|
169
169
|
`openclaw@2026.5.4-beta.2`, `openclaw@2026.5.4-beta.3`,
|
|
170
|
-
`openclaw@2026.5.4`, `openclaw@2026.5.5`,
|
|
170
|
+
`openclaw@2026.5.4`, `openclaw@2026.5.5`, `openclaw@2026.5.6`, and
|
|
171
|
+
`openclaw@2026.5.16-beta.2`.
|
|
171
172
|
Keep the peer range broad unless an upstream release removes a runtime surface
|
|
172
173
|
Remnic actively uses.
|
|
173
174
|
|
|
175
|
+
OpenClaw 2026.5.16 package-entry discovery prefers explicit built runtime
|
|
176
|
+
entries for installed packages. The published Remnic adapter declares
|
|
177
|
+
`openclaw.runtimeExtensions: ["./dist/index.js"]` alongside the existing
|
|
178
|
+
extension entry, and its package install metadata advertises
|
|
179
|
+
`openclaw.install.clawhubSpec: "clawhub:@remnic/plugin-openclaw"`,
|
|
180
|
+
`openclaw.install.npmSpec: "@remnic/plugin-openclaw"`, and
|
|
181
|
+
`openclaw.install.defaultChoice: "clawhub"`. That keeps OpenClaw setup,
|
|
182
|
+
repair, and update flows ClawHub-first while preserving npm as the fallback
|
|
183
|
+
install surface and `>=2026.5.16-beta.1` as the minimum supported host range.
|
|
184
|
+
The manifest also declares `activation.onStartup: false`, leaves provider
|
|
185
|
+
ownership to the host, exposes the optional plugin-mode OpenAI key through
|
|
186
|
+
`providerAuthChoices`, and keeps `providerAuthEnvVars.openai` as compatibility
|
|
187
|
+
metadata for OpenClaw's pre-runtime env-var auth probes.
|
|
188
|
+
|
|
174
189
|
Native memory registrars are tracked separately in
|
|
175
190
|
[`docs/plugins/openclaw-native-memory-registrars.md`](../../docs/plugins/openclaw-native-memory-registrars.md).
|
|
176
191
|
That spike explains why Remnic currently uses `registerMemoryCapability()` as
|
|
@@ -254,7 +269,7 @@ private plugin state, transcript buffers, auth metadata, or artifact paths.
|
|
|
254
269
|
| `session_start` / `session_end` hooks | Supported | 2026.3.22 |
|
|
255
270
|
| `before_compaction` / `after_compaction` hooks | Supported | 2026.3.22 |
|
|
256
271
|
| `before_reset` hook | Supported | 2026.4.10 |
|
|
257
|
-
| `
|
|
272
|
+
| `api.registerCommand()` runtime discovery | Supported | 2026.5.16 |
|
|
258
273
|
| `api.resetSession()` (compaction reset) | Supported | 2026.3.22 |
|
|
259
274
|
| Checkpoint saves before compaction | Supported | 2026.3.22 |
|
|
260
275
|
|
|
@@ -285,8 +300,8 @@ registered as the `before_prompt_build` hook timeout on OpenClaw versions with
|
|
|
285
300
|
per-hook timeout support. Raise it if first-turn recall is timing out during
|
|
286
301
|
slow startup; older OpenClaw versions ignore the extra hook option safely.
|
|
287
302
|
|
|
288
|
-
Session-scoped recall controls are exposed through OpenClaw's
|
|
289
|
-
|
|
303
|
+
Session-scoped recall controls are exposed through OpenClaw's
|
|
304
|
+
`api.registerCommand()` surface when it is available:
|
|
290
305
|
|
|
291
306
|
- `remnic off` / `remnic on`
|
|
292
307
|
- `remnic status`
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
buildExtensionsBlockForConsolidation,
|
|
7
7
|
runPostConsolidationMaterialize
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CMKR6NDQ.js";
|
|
9
9
|
import "./chunk-WPINX4MF.js";
|
|
10
10
|
import {
|
|
11
11
|
isRecord
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
} from "./chunk-5LE4HTVL.js";
|
|
17
17
|
import {
|
|
18
18
|
FallbackLlmClient
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VFULKFKI.js";
|
|
20
20
|
import "./chunk-3A5ELHTT.js";
|
|
21
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-EYCLXMIV.js";
|
|
22
22
|
import "./chunk-6OJAU466.js";
|
|
23
23
|
import "./chunk-RKR6PTPA.js";
|
|
24
24
|
import {
|
|
@@ -1674,6 +1674,103 @@ function applyContinuityLoopReview(existing, input, nowIso) {
|
|
|
1674
1674
|
};
|
|
1675
1675
|
}
|
|
1676
1676
|
|
|
1677
|
+
// ../remnic-core/src/utils/iso-timestamp.ts
|
|
1678
|
+
var ISO_UTC_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
1679
|
+
var ISO_OFFSET_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
1680
|
+
var FLEXIBLE_ISO_TIMESTAMP_RE = /^(\d{4})-(\d{2})-(\d{2})(?:[Tt](\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?(?:[Zz]|([+-])(\d{2}):(\d{2}))?)?$/;
|
|
1681
|
+
function isoDaysInMonth(year, month) {
|
|
1682
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
1683
|
+
return [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1] ?? 0;
|
|
1684
|
+
}
|
|
1685
|
+
function validateDateComponents(isoString) {
|
|
1686
|
+
const match = isoString.match(
|
|
1687
|
+
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/
|
|
1688
|
+
);
|
|
1689
|
+
if (!match) return false;
|
|
1690
|
+
const [, yStr, mStr, dStr, hStr, minStr, sStr] = match;
|
|
1691
|
+
const y = Number(yStr);
|
|
1692
|
+
const m = Number(mStr);
|
|
1693
|
+
const d = Number(dStr);
|
|
1694
|
+
const h = Number(hStr);
|
|
1695
|
+
const min = Number(minStr);
|
|
1696
|
+
const s = Number(sStr);
|
|
1697
|
+
if (m < 1 || m > 12) return false;
|
|
1698
|
+
if (d < 1 || d > 31) return false;
|
|
1699
|
+
if (h > 23 || min > 59 || s > 59) return false;
|
|
1700
|
+
if (d > isoDaysInMonth(y, m)) return false;
|
|
1701
|
+
return true;
|
|
1702
|
+
}
|
|
1703
|
+
function validateOffset(isoString) {
|
|
1704
|
+
const offsetMatch = isoString.match(/([+-])(\d{2}):(\d{2})$/);
|
|
1705
|
+
if (!offsetMatch) return true;
|
|
1706
|
+
const oh = Number(offsetMatch[2]);
|
|
1707
|
+
const om = Number(offsetMatch[3]);
|
|
1708
|
+
if (oh > 14 || om > 59) return false;
|
|
1709
|
+
if (oh === 14 && om > 0) return false;
|
|
1710
|
+
return true;
|
|
1711
|
+
}
|
|
1712
|
+
function normalizeUtcForComparison(value) {
|
|
1713
|
+
const fracMatch = value.match(/\.(\d+)Z$/);
|
|
1714
|
+
if (fracMatch) {
|
|
1715
|
+
const ms = (fracMatch[1] + "000").slice(0, 3);
|
|
1716
|
+
return value.replace(/\.\d+Z$/, `.${ms}Z`);
|
|
1717
|
+
}
|
|
1718
|
+
return value.replace(/Z$/, ".000Z");
|
|
1719
|
+
}
|
|
1720
|
+
function parseIsoUtcTimestamp(value) {
|
|
1721
|
+
if (typeof value !== "string" || !ISO_UTC_TIMESTAMP_RE.test(value)) {
|
|
1722
|
+
return null;
|
|
1723
|
+
}
|
|
1724
|
+
const ts = Date.parse(value);
|
|
1725
|
+
if (!Number.isFinite(ts)) return null;
|
|
1726
|
+
if (!validateDateComponents(value)) return null;
|
|
1727
|
+
const roundTrip = new Date(ts).toISOString();
|
|
1728
|
+
if (roundTrip !== normalizeUtcForComparison(value)) return null;
|
|
1729
|
+
return ts;
|
|
1730
|
+
}
|
|
1731
|
+
function parseIsoOffsetTimestamp(value) {
|
|
1732
|
+
if (typeof value !== "string" || !ISO_OFFSET_TIMESTAMP_RE.test(value)) {
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
const ts = Date.parse(value);
|
|
1736
|
+
if (!Number.isFinite(ts)) return null;
|
|
1737
|
+
if (!validateDateComponents(value)) return null;
|
|
1738
|
+
if (!validateOffset(value)) return null;
|
|
1739
|
+
if (value.endsWith("Z")) {
|
|
1740
|
+
const roundTrip = new Date(ts).toISOString();
|
|
1741
|
+
if (roundTrip !== normalizeUtcForComparison(value)) return null;
|
|
1742
|
+
}
|
|
1743
|
+
return ts;
|
|
1744
|
+
}
|
|
1745
|
+
function parseFlexibleIsoTimestamp(value) {
|
|
1746
|
+
const match = typeof value === "string" ? value.match(FLEXIBLE_ISO_TIMESTAMP_RE) : null;
|
|
1747
|
+
if (!match) {
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1750
|
+
const year = Number(match[1]);
|
|
1751
|
+
const month = Number(match[2]);
|
|
1752
|
+
const day = Number(match[3]);
|
|
1753
|
+
const hour = match[4] === void 0 ? 0 : Number(match[4]);
|
|
1754
|
+
const minute = match[5] === void 0 ? 0 : Number(match[5]);
|
|
1755
|
+
const second = match[6] === void 0 ? 0 : Number(match[6]);
|
|
1756
|
+
const offsetHour = match[8] === void 0 ? void 0 : Number(match[8]);
|
|
1757
|
+
const offsetMinute = match[9] === void 0 ? void 0 : Number(match[9]);
|
|
1758
|
+
const hasTime = match[4] !== void 0;
|
|
1759
|
+
const hasOffset = offsetHour !== void 0 && offsetMinute !== void 0;
|
|
1760
|
+
const hasTimezone = /(?:[Zz]|[+-]\d{2}:\d{2})$/.test(value);
|
|
1761
|
+
if (month < 1 || month > 12 || day < 1 || day > isoDaysInMonth(year, month) || hasTime && !hasTimezone) {
|
|
1762
|
+
return null;
|
|
1763
|
+
}
|
|
1764
|
+
if (hasTime && (hour > 23 || minute > 59 || second > 59)) {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
if (hasOffset && (offsetMinute > 59 || offsetHour > 14 || offsetHour === 14 && offsetMinute > 0)) {
|
|
1768
|
+
return null;
|
|
1769
|
+
}
|
|
1770
|
+
const ts = Date.parse(value);
|
|
1771
|
+
return Number.isFinite(ts) ? ts : null;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1677
1774
|
// ../remnic-core/src/storage.ts
|
|
1678
1775
|
var ARTIFACT_SEARCH_STOPWORDS = /* @__PURE__ */ new Set([
|
|
1679
1776
|
"a",
|
|
@@ -1718,6 +1815,18 @@ function assertMemoryWorthCounter(field, value) {
|
|
|
1718
1815
|
throw new Error(`${field} must be >= 0, got ${value}`);
|
|
1719
1816
|
}
|
|
1720
1817
|
}
|
|
1818
|
+
function normalizeMemoryWriteTimestamp(field, value) {
|
|
1819
|
+
if (value === void 0) return void 0;
|
|
1820
|
+
if (typeof value !== "string") {
|
|
1821
|
+
throw new Error(`${field} must be an ISO timestamp string, got ${String(value)}`);
|
|
1822
|
+
}
|
|
1823
|
+
const trimmed = value.trim();
|
|
1824
|
+
const parsed = parseFlexibleIsoTimestamp(trimmed);
|
|
1825
|
+
if (parsed === null) {
|
|
1826
|
+
throw new Error(`${field} must be a valid ISO timestamp, got ${JSON.stringify(value)}`);
|
|
1827
|
+
}
|
|
1828
|
+
return new Date(parsed).toISOString();
|
|
1829
|
+
}
|
|
1721
1830
|
function isErrnoCode(error, code) {
|
|
1722
1831
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1723
1832
|
}
|
|
@@ -3729,6 +3838,7 @@ var StorageManager = class _StorageManager {
|
|
|
3729
3838
|
const id = `${category}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
3730
3839
|
const conf = options.confidence ?? 0.8;
|
|
3731
3840
|
const tier = confidenceTier(conf);
|
|
3841
|
+
const validAt = normalizeMemoryWriteTimestamp("validAt", options.validAt);
|
|
3732
3842
|
let expiresAt;
|
|
3733
3843
|
if (typeof options.expiresAt === "string" && options.expiresAt.length > 0) {
|
|
3734
3844
|
expiresAt = options.expiresAt;
|
|
@@ -3758,6 +3868,7 @@ var StorageManager = class _StorageManager {
|
|
|
3758
3868
|
sourceMemoryId: options.sourceMemoryId,
|
|
3759
3869
|
sourceTurnId: options.sourceTurnId,
|
|
3760
3870
|
memoryKind: options.memoryKind,
|
|
3871
|
+
valid_at: validAt,
|
|
3761
3872
|
structuredAttributes: options.structuredAttributes
|
|
3762
3873
|
};
|
|
3763
3874
|
if (options.status !== void 0) {
|
|
@@ -6413,6 +6524,7 @@ ${memory.content}
|
|
|
6413
6524
|
const id = `${parentId}-chunk-${chunkIndex}`;
|
|
6414
6525
|
const conf = options.confidence ?? 0.8;
|
|
6415
6526
|
const tier = confidenceTier(conf);
|
|
6527
|
+
const validAt = normalizeMemoryWriteTimestamp("validAt", options.validAt);
|
|
6416
6528
|
const fm = {
|
|
6417
6529
|
id,
|
|
6418
6530
|
category,
|
|
@@ -6430,7 +6542,8 @@ ${memory.content}
|
|
|
6430
6542
|
intentGoal: options.intentGoal,
|
|
6431
6543
|
intentActionType: options.intentActionType,
|
|
6432
6544
|
intentEntityTypes: options.intentEntityTypes,
|
|
6433
|
-
memoryKind: options.memoryKind
|
|
6545
|
+
memoryKind: options.memoryKind,
|
|
6546
|
+
valid_at: validAt
|
|
6434
6547
|
};
|
|
6435
6548
|
const sanitized = sanitizeMemoryContent(content);
|
|
6436
6549
|
if (!sanitized.clean) {
|
|
@@ -6728,6 +6841,9 @@ export {
|
|
|
6728
6841
|
normalizeProjectionPreview,
|
|
6729
6842
|
normalizeProjectionTags,
|
|
6730
6843
|
parseContinuityImprovementLoops,
|
|
6844
|
+
parseIsoUtcTimestamp,
|
|
6845
|
+
parseIsoOffsetTimestamp,
|
|
6846
|
+
parseFlexibleIsoTimestamp,
|
|
6731
6847
|
normalizeEntityName,
|
|
6732
6848
|
ContentHashIndex,
|
|
6733
6849
|
normalizeAttributePairs,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
StorageManager,
|
|
6
6
|
parseContinuityImprovementLoops,
|
|
7
7
|
sanitizeMemoryContent
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EYCLXMIV.js";
|
|
9
9
|
import {
|
|
10
10
|
log
|
|
11
11
|
} from "./chunk-UFU5GGGA.js";
|
|
@@ -842,7 +842,7 @@ var CompoundingEngine = class {
|
|
|
842
842
|
let promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary, mistakes.registry, rubrics) : [];
|
|
843
843
|
if (this.config.cmcConsolidationEnabled) {
|
|
844
844
|
try {
|
|
845
|
-
const { deriveCausalPromotionCandidates, materializeAfterCausalConsolidation } = await import("./causal-consolidation-
|
|
845
|
+
const { deriveCausalPromotionCandidates, materializeAfterCausalConsolidation } = await import("./causal-consolidation-C64NNE4T.js");
|
|
846
846
|
const causalCandidates = await deriveCausalPromotionCandidates({
|
|
847
847
|
memoryDir: this.config.memoryDir,
|
|
848
848
|
causalTrajectoryStoreDir: this.config.causalTrajectoryStoreDir,
|
|
@@ -875,7 +875,7 @@ var CompoundingEngine = class {
|
|
|
875
875
|
}
|
|
876
876
|
if (this.config.calibrationEnabled) {
|
|
877
877
|
try {
|
|
878
|
-
const { runCalibrationConsolidation } = await import("./calibration-
|
|
878
|
+
const { runCalibrationConsolidation } = await import("./calibration-Z5WWNV7U.js");
|
|
879
879
|
const calRules = await runCalibrationConsolidation({
|
|
880
880
|
memoryDir: this.config.memoryDir,
|
|
881
881
|
gatewayConfig: this.config.gatewayConfig,
|
|
@@ -66,6 +66,11 @@ var _resolverLoaded = false;
|
|
|
66
66
|
var _resolverNextRetryAt = 0;
|
|
67
67
|
var RESOLVER_RETRY_BACKOFF_MS = 6e4;
|
|
68
68
|
var resolvedCache = /* @__PURE__ */ new Map();
|
|
69
|
+
var NON_LITERAL_AUTH_MARKERS = /* @__PURE__ */ new Set([
|
|
70
|
+
"secretref-managed",
|
|
71
|
+
"lm-studio"
|
|
72
|
+
]);
|
|
73
|
+
var ENV_VAR_MARKER_RE = /^[A-Z][A-Z0-9_]*(?:_API_KEY|_ACCESS_TOKEN|_TOKEN|_SECRET|_CREDENTIALS|_CREDENTIALS_JSON)$/;
|
|
69
74
|
async function getGatewayResolver() {
|
|
70
75
|
if (_resolverLoaded) {
|
|
71
76
|
return _resolveCredentialForProvider;
|
|
@@ -178,6 +183,14 @@ function findExecutableOnPath(executableName, access, stat, executableMode) {
|
|
|
178
183
|
}
|
|
179
184
|
return void 0;
|
|
180
185
|
}
|
|
186
|
+
function isNonLiteralAuthMarker(value) {
|
|
187
|
+
return NON_LITERAL_AUTH_MARKERS.has(value) || value.endsWith("-oauth") || value.endsWith("-local") || value.startsWith("gcp-") || ENV_VAR_MARKER_RE.test(value);
|
|
188
|
+
}
|
|
189
|
+
function resolveFromNamedEnvVar(marker) {
|
|
190
|
+
if (!ENV_VAR_MARKER_RE.test(marker)) return void 0;
|
|
191
|
+
const value = readEnvVar(marker);
|
|
192
|
+
return value && value.trim().length > 0 ? value.trim() : void 0;
|
|
193
|
+
}
|
|
181
194
|
async function resolveProviderCredential(providerId, credentialValue, gatewayConfig, agentDir) {
|
|
182
195
|
const resolvedAgentDir = path.resolve(
|
|
183
196
|
agentDir ?? path.join(os.homedir(), ".openclaw", "agents", "main", "agent")
|
|
@@ -188,9 +201,16 @@ async function resolveProviderCredential(providerId, credentialValue, gatewayCon
|
|
|
188
201
|
}
|
|
189
202
|
let resolved;
|
|
190
203
|
if (typeof credentialValue === "string" && credentialValue.trim().length > 0) {
|
|
191
|
-
|
|
204
|
+
const trimmedCredentialValue = credentialValue.trim();
|
|
205
|
+
if (isNonLiteralAuthMarker(trimmedCredentialValue)) {
|
|
206
|
+
const markerEnvValue = resolveFromNamedEnvVar(trimmedCredentialValue);
|
|
207
|
+
if (markerEnvValue) {
|
|
208
|
+
resolved = markerEnvValue;
|
|
209
|
+
resolvedCache.set(cacheKey, resolved);
|
|
210
|
+
return resolved;
|
|
211
|
+
}
|
|
192
212
|
} else {
|
|
193
|
-
resolved =
|
|
213
|
+
resolved = trimmedCredentialValue;
|
|
194
214
|
resolvedCache.set(cacheKey, resolved);
|
|
195
215
|
return resolved;
|
|
196
216
|
}
|
|
@@ -307,7 +327,8 @@ function normalizeCodexCliFallbackConfig(config) {
|
|
|
307
327
|
}
|
|
308
328
|
function normalizeCodexCliFallbackOptions(options) {
|
|
309
329
|
return {
|
|
310
|
-
...options.timeoutMs !== void 0 ? { timeoutMs: normalizeCodexCliTimeoutMs(options.timeoutMs) } : {}
|
|
330
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: normalizeCodexCliTimeoutMs(options.timeoutMs) } : {},
|
|
331
|
+
...options.signal ? { signal: options.signal } : {}
|
|
311
332
|
};
|
|
312
333
|
}
|
|
313
334
|
function normalizeOptionalString(value, label) {
|
|
@@ -386,12 +407,15 @@ var FallbackLlmClient = class {
|
|
|
386
407
|
log.warn("fallback LLM: no models configured in gateway");
|
|
387
408
|
return null;
|
|
388
409
|
}
|
|
389
|
-
const runChain = async () => {
|
|
410
|
+
const runChain = async (runOptions) => {
|
|
390
411
|
for (let i = 0; i < models.length; i++) {
|
|
412
|
+
if (runOptions.signal?.aborted) {
|
|
413
|
+
throw abortReason(runOptions.signal);
|
|
414
|
+
}
|
|
391
415
|
const model = models[i];
|
|
392
416
|
const isFallback = i > 0;
|
|
393
417
|
try {
|
|
394
|
-
const result = await this.tryModel(model, messages,
|
|
418
|
+
const result = await this.tryModel(model, messages, runOptions);
|
|
395
419
|
if (result) {
|
|
396
420
|
if (isFallback) {
|
|
397
421
|
log.debug(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);
|
|
@@ -403,6 +427,9 @@ var FallbackLlmClient = class {
|
|
|
403
427
|
};
|
|
404
428
|
}
|
|
405
429
|
} catch (err) {
|
|
430
|
+
if (runOptions.signal?.aborted) {
|
|
431
|
+
throw abortReason(runOptions.signal);
|
|
432
|
+
}
|
|
406
433
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
407
434
|
log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);
|
|
408
435
|
}
|
|
@@ -416,21 +443,37 @@ var FallbackLlmClient = class {
|
|
|
416
443
|
return null;
|
|
417
444
|
}
|
|
418
445
|
let timeoutHandle;
|
|
446
|
+
const controller = new AbortController();
|
|
447
|
+
const onCallerAbort = () => {
|
|
448
|
+
controller.abort(abortReason(options.signal));
|
|
449
|
+
};
|
|
450
|
+
options.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
451
|
+
if (options.signal?.aborted) {
|
|
452
|
+
onCallerAbort();
|
|
453
|
+
}
|
|
454
|
+
const timedOptions = { ...options, signal: controller.signal };
|
|
455
|
+
const chain = runChain(timedOptions);
|
|
456
|
+
chain.catch(() => {
|
|
457
|
+
});
|
|
419
458
|
try {
|
|
420
459
|
return await Promise.race([
|
|
421
|
-
|
|
460
|
+
chain,
|
|
422
461
|
new Promise((resolve) => {
|
|
423
462
|
timeoutHandle = setTimeout(() => {
|
|
424
463
|
log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);
|
|
464
|
+
controller.abort(
|
|
465
|
+
new Error(`fallback LLM timed out after ${options.timeoutMs}ms`)
|
|
466
|
+
);
|
|
425
467
|
resolve(null);
|
|
426
468
|
}, options.timeoutMs);
|
|
427
469
|
})
|
|
428
470
|
]);
|
|
429
471
|
} finally {
|
|
430
472
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
473
|
+
options.signal?.removeEventListener("abort", onCallerAbort);
|
|
431
474
|
}
|
|
432
475
|
}
|
|
433
|
-
return await runChain();
|
|
476
|
+
return await runChain(options);
|
|
434
477
|
}
|
|
435
478
|
/**
|
|
436
479
|
* Make a request with structured output (Zod schema).
|
|
@@ -618,7 +661,7 @@ var FallbackLlmClient = class {
|
|
|
618
661
|
effectiveConfig,
|
|
619
662
|
model.modelId,
|
|
620
663
|
messages,
|
|
621
|
-
{ timeoutMs: options.timeoutMs }
|
|
664
|
+
{ timeoutMs: options.timeoutMs, signal: options.signal }
|
|
622
665
|
);
|
|
623
666
|
}
|
|
624
667
|
if (model.providerConfig.api === "ollama-chat") {
|
|
@@ -712,6 +755,7 @@ var FallbackLlmClient = class {
|
|
|
712
755
|
const response = await fetch(url, {
|
|
713
756
|
method: "POST",
|
|
714
757
|
headers,
|
|
758
|
+
signal: options.signal,
|
|
715
759
|
body: JSON.stringify(body)
|
|
716
760
|
});
|
|
717
761
|
if (!response.ok) {
|
|
@@ -750,6 +794,7 @@ var FallbackLlmClient = class {
|
|
|
750
794
|
const response = await fetch(url, {
|
|
751
795
|
method: "POST",
|
|
752
796
|
headers,
|
|
797
|
+
signal: options.signal,
|
|
753
798
|
body: JSON.stringify({
|
|
754
799
|
model: modelId,
|
|
755
800
|
messages,
|
|
@@ -816,6 +861,7 @@ var FallbackLlmClient = class {
|
|
|
816
861
|
const response = await fetch(url, {
|
|
817
862
|
method: "POST",
|
|
818
863
|
headers,
|
|
864
|
+
signal: options.signal,
|
|
819
865
|
body: JSON.stringify(body)
|
|
820
866
|
});
|
|
821
867
|
if (!response.ok) {
|
|
@@ -868,6 +914,7 @@ var FallbackLlmClient = class {
|
|
|
868
914
|
const response = await fetch(url, {
|
|
869
915
|
method: "POST",
|
|
870
916
|
headers,
|
|
917
|
+
signal: options.signal,
|
|
871
918
|
body: JSON.stringify(body)
|
|
872
919
|
});
|
|
873
920
|
if (!response.ok) {
|
|
@@ -889,6 +936,10 @@ var FallbackLlmClient = class {
|
|
|
889
936
|
};
|
|
890
937
|
}
|
|
891
938
|
};
|
|
939
|
+
function abortReason(signal) {
|
|
940
|
+
const reason = signal?.reason;
|
|
941
|
+
return reason instanceof Error ? reason : new Error("fallback LLM request aborted");
|
|
942
|
+
}
|
|
892
943
|
function normalizeRuntimePath(value) {
|
|
893
944
|
if (typeof value !== "string") return void 0;
|
|
894
945
|
const trimmed = value.trim();
|
|
@@ -135,7 +135,8 @@ async function callLlm(client, config, userMessage) {
|
|
|
135
135
|
if ("chatCompletion" in client && typeof client.chatCompletion === "function") {
|
|
136
136
|
const result = await client.chatCompletion(messages, {
|
|
137
137
|
temperature: 0.1,
|
|
138
|
-
maxTokens: 4096
|
|
138
|
+
maxTokens: 4096,
|
|
139
|
+
operation: "contradiction-judge"
|
|
139
140
|
});
|
|
140
141
|
return result?.content ?? "";
|
|
141
142
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CompoundingEngine,
|
|
3
3
|
defaultTierMigrationCycleBudget
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UTDLHBBV.js";
|
|
5
5
|
import "./chunk-EXDYWXMB.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-EYCLXMIV.js";
|
|
7
7
|
import "./chunk-6OJAU466.js";
|
|
8
8
|
import "./chunk-RKR6PTPA.js";
|
|
9
9
|
import "./chunk-UFU5GGGA.js";
|