@agenr/agenr-plugin 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MEHOGUZE.js → chunk-6T5RXGIR.js} +989 -70
- package/dist/{chunk-Y2BC7RCE.js → chunk-7TDALVPY.js} +1434 -305
- package/dist/{chunk-XD3446YW.js → chunk-DGV6D6Q3.js} +2 -21
- package/dist/chunk-IMQIJPIP.js +886 -0
- package/dist/chunk-MJIB6J5S.js +3059 -0
- package/dist/index.js +1407 -78
- package/openclaw.plugin.json +86 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,34 +7,276 @@ import {
|
|
|
7
7
|
parseTuiSessionKey,
|
|
8
8
|
readOpenClawSessionsStore,
|
|
9
9
|
storeEntriesDetailed
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DGV6D6Q3.js";
|
|
11
|
+
import {
|
|
12
|
+
BEFORE_TURN_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
13
|
+
BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K,
|
|
14
|
+
RECALL_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
15
|
+
RECALL_DEBUG_ARTIFACT_MAX_TOP_K,
|
|
16
|
+
containsAgenrMemoryContext,
|
|
17
|
+
formatAgenrBeforeTurnRecall,
|
|
18
|
+
runBeforeTurn,
|
|
19
|
+
stripAgenrMemoryContext,
|
|
20
|
+
wrapAgenrMemoryContext
|
|
21
|
+
} from "./chunk-IMQIJPIP.js";
|
|
11
22
|
import {
|
|
12
23
|
EMBEDDING_DIMENSIONS,
|
|
24
|
+
ENTRY_SELECT_COLUMNS,
|
|
13
25
|
ENTRY_TYPES,
|
|
14
26
|
EXPIRY_LEVELS,
|
|
27
|
+
attachCrossEncoderPort,
|
|
28
|
+
buildActiveEntryClause,
|
|
15
29
|
createDatabase,
|
|
16
30
|
createEmbeddingClient,
|
|
31
|
+
createOpenAICrossEncoder,
|
|
17
32
|
createRecallAdapter,
|
|
33
|
+
mapEntryRow,
|
|
18
34
|
normalizeManualClaimKeyUpdate,
|
|
35
|
+
projectClaimCentricRecallEntry,
|
|
19
36
|
readConfig,
|
|
20
37
|
resolveClaimExtractionConfig,
|
|
21
38
|
resolveConfigPath,
|
|
39
|
+
resolveCrossEncoderApiKey,
|
|
22
40
|
resolveDbPath,
|
|
23
41
|
resolveEmbeddingApiKey,
|
|
24
42
|
resolveEmbeddingModel,
|
|
43
|
+
resolveModel,
|
|
25
44
|
runUnifiedRecall,
|
|
26
45
|
validateTemporalValidityRange
|
|
27
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-7TDALVPY.js";
|
|
28
47
|
import {
|
|
48
|
+
recall,
|
|
29
49
|
resolveClaimSlotPolicy
|
|
30
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-6T5RXGIR.js";
|
|
31
51
|
|
|
32
52
|
// src/adapters/openclaw/index.ts
|
|
33
53
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
34
54
|
|
|
35
55
|
// src/adapters/openclaw/tools/recall.ts
|
|
56
|
+
import { randomUUID } from "crypto";
|
|
36
57
|
import { readNumberParam, readStringArrayParam, readStringParam as readStringParam2, textResult } from "openclaw/plugin-sdk/agent-runtime";
|
|
37
58
|
|
|
59
|
+
// src/adapters/openclaw/debug/sink.ts
|
|
60
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
61
|
+
import path from "path";
|
|
62
|
+
var NOOP_SINK = {
|
|
63
|
+
enabled: false,
|
|
64
|
+
eventLevel: "basic",
|
|
65
|
+
maxTopCandidates: 0,
|
|
66
|
+
async emit() {
|
|
67
|
+
return;
|
|
68
|
+
},
|
|
69
|
+
async close() {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
function createNoopAgenrDebugSink() {
|
|
74
|
+
return NOOP_SINK;
|
|
75
|
+
}
|
|
76
|
+
function createAgenrDebugSink(config) {
|
|
77
|
+
if (!config.enabled || !config.logPath) {
|
|
78
|
+
return NOOP_SINK;
|
|
79
|
+
}
|
|
80
|
+
const basePath = config.logPath;
|
|
81
|
+
const perSessionFiles = config.perSessionFiles;
|
|
82
|
+
const eventLevel = config.eventLevel;
|
|
83
|
+
const maxTopCandidates = config.maxTopCandidates;
|
|
84
|
+
const directoriesEnsured = /* @__PURE__ */ new Set();
|
|
85
|
+
let writeChain = Promise.resolve();
|
|
86
|
+
let closed = false;
|
|
87
|
+
const ensureDirectoryOnce = async (filePath) => {
|
|
88
|
+
const directory = path.dirname(filePath);
|
|
89
|
+
if (directoriesEnsured.has(directory)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await mkdir(directory, { recursive: true });
|
|
93
|
+
directoriesEnsured.add(directory);
|
|
94
|
+
};
|
|
95
|
+
const resolveFilePath = (event) => {
|
|
96
|
+
if (!perSessionFiles) {
|
|
97
|
+
return basePath;
|
|
98
|
+
}
|
|
99
|
+
const sessionSuffix = sanitizeSessionSuffix(event.sessionId) ?? sanitizeSessionSuffix(event.sessionKey);
|
|
100
|
+
if (!sessionSuffix) {
|
|
101
|
+
return basePath;
|
|
102
|
+
}
|
|
103
|
+
const directory = path.dirname(basePath);
|
|
104
|
+
const extension = path.extname(basePath);
|
|
105
|
+
const baseName = path.basename(basePath, extension);
|
|
106
|
+
const suffixed = `${baseName}.${sessionSuffix}${extension || ".jsonl"}`;
|
|
107
|
+
return path.join(directory, suffixed);
|
|
108
|
+
};
|
|
109
|
+
const writeLine = async (filePath, line) => {
|
|
110
|
+
await ensureDirectoryOnce(filePath);
|
|
111
|
+
await appendFile(filePath, `${line}
|
|
112
|
+
`, "utf8");
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
enabled: true,
|
|
116
|
+
eventLevel,
|
|
117
|
+
maxTopCandidates,
|
|
118
|
+
logPath: basePath,
|
|
119
|
+
async emit(event) {
|
|
120
|
+
if (closed) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const filePath = resolveFilePath(event);
|
|
124
|
+
const line = formatEventLine(event);
|
|
125
|
+
writeChain = writeChain.then(async () => {
|
|
126
|
+
try {
|
|
127
|
+
await writeLine(filePath, line);
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
await writeChain;
|
|
132
|
+
},
|
|
133
|
+
async close() {
|
|
134
|
+
closed = true;
|
|
135
|
+
await writeChain;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function formatEventLine(event) {
|
|
140
|
+
const line = {
|
|
141
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
142
|
+
...event
|
|
143
|
+
};
|
|
144
|
+
return JSON.stringify(line);
|
|
145
|
+
}
|
|
146
|
+
function sanitizeSessionSuffix(value) {
|
|
147
|
+
if (typeof value !== "string") {
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
const trimmed = value.trim();
|
|
151
|
+
if (trimmed.length === 0) {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
const sanitized = trimmed.replace(/[^A-Za-z0-9._-]+/gu, "_");
|
|
155
|
+
if (sanitized.length === 0) {
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
return sanitized.slice(0, 120);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/adapters/openclaw/debug/build-before-turn-artifact.ts
|
|
162
|
+
function buildLiveBeforeTurnDebugArtifact(params) {
|
|
163
|
+
const { caseId, patch, currentTurnText, trigger, eventLevel, maxTopCandidates } = params;
|
|
164
|
+
const diagnostics = patch.diagnostics;
|
|
165
|
+
const includeCandidateBreakdown = eventLevel === "detailed";
|
|
166
|
+
const topK = includeCandidateBreakdown ? clampTopK(maxTopCandidates) : 0;
|
|
167
|
+
const durableTopCandidates = includeCandidateBreakdown ? buildDurableCandidates(patch, topK) : [];
|
|
168
|
+
const procedureTopCandidates = includeCandidateBreakdown ? buildProcedureCandidates(patch, topK) : [];
|
|
169
|
+
const normalizedTrigger = trigger?.trim() || "unspecified";
|
|
170
|
+
return {
|
|
171
|
+
schemaVersion: "before-turn-debug-artifact.v1",
|
|
172
|
+
caseId,
|
|
173
|
+
input: {
|
|
174
|
+
trigger: normalizedTrigger,
|
|
175
|
+
currentTurnText
|
|
176
|
+
},
|
|
177
|
+
...diagnostics.queryPolicy ? { queryPolicy: diagnostics.queryPolicy } : {},
|
|
178
|
+
...diagnostics.queryVariants.length > 0 ? { queryVariants: [...diagnostics.queryVariants] } : {},
|
|
179
|
+
...diagnostics.abstentionReasons.length > 0 ? { abstentionReasons: [...diagnostics.abstentionReasons] } : {},
|
|
180
|
+
selectedEntryIds: patch.durableMemory.map((item) => item.entry.id),
|
|
181
|
+
selectedProcedureKey: patch.procedure?.procedure.procedure_key ?? null,
|
|
182
|
+
...durableTopCandidates.length > 0 ? { durableRecallTopCandidates: durableTopCandidates } : {},
|
|
183
|
+
...procedureTopCandidates.length > 0 ? { procedureTopCandidates } : {}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function clampTopK(requested) {
|
|
187
|
+
if (!Number.isFinite(requested) || !Number.isInteger(requested)) {
|
|
188
|
+
return BEFORE_TURN_DEBUG_ARTIFACT_DEFAULT_TOP_K;
|
|
189
|
+
}
|
|
190
|
+
if (requested < 1) {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
if (requested > BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K) {
|
|
194
|
+
return BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K;
|
|
195
|
+
}
|
|
196
|
+
return requested;
|
|
197
|
+
}
|
|
198
|
+
function buildDurableCandidates(patch, topK) {
|
|
199
|
+
if (topK < 1) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
return patch.durableMemory.slice(0, topK).map((item) => {
|
|
203
|
+
const reasons = item.whySurfaced.reasons.length > 0 ? [...item.whySurfaced.reasons] : void 0;
|
|
204
|
+
return {
|
|
205
|
+
id: item.entry.id,
|
|
206
|
+
score: item.score,
|
|
207
|
+
...reasons ? { reasons } : {}
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function buildProcedureCandidates(patch, topK) {
|
|
212
|
+
if (!patch.procedure || topK < 1) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
const reasons = patch.procedure.whySurfaced.reasons.length > 0 ? [...patch.procedure.whySurfaced.reasons] : void 0;
|
|
216
|
+
return [
|
|
217
|
+
{
|
|
218
|
+
procedureKey: patch.procedure.procedure.procedure_key,
|
|
219
|
+
score: patch.procedure.score,
|
|
220
|
+
...reasons ? { reasons } : {}
|
|
221
|
+
}
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/adapters/openclaw/debug/build-recall-artifact.ts
|
|
226
|
+
function buildLiveRecallDebugArtifact(params) {
|
|
227
|
+
const { caseId, query, result, eventLevel, maxTopCandidates } = params;
|
|
228
|
+
const selectedEntryIds = result.entries.map((entry) => entry.entry.id);
|
|
229
|
+
const includeCandidateBreakdown = eventLevel === "detailed";
|
|
230
|
+
const topK = includeCandidateBreakdown ? clampTopK2(maxTopCandidates) : 0;
|
|
231
|
+
const reasonsByEntryId = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const projected of result.projectedEntries) {
|
|
233
|
+
if (projected.whySurfaced.reasons.length > 0) {
|
|
234
|
+
reasonsByEntryId.set(projected.entryId, [...projected.whySurfaced.reasons]);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const topCandidates = includeCandidateBreakdown ? buildTopCandidates(result, reasonsByEntryId, topK) : [];
|
|
238
|
+
return {
|
|
239
|
+
schemaVersion: "recall-debug-artifact.v1",
|
|
240
|
+
caseId,
|
|
241
|
+
request: {
|
|
242
|
+
recallPath: "unified",
|
|
243
|
+
query
|
|
244
|
+
},
|
|
245
|
+
routing: result.routing,
|
|
246
|
+
selectedEntryIds,
|
|
247
|
+
...topCandidates.length > 0 ? { topCandidates } : {}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function clampTopK2(requested) {
|
|
251
|
+
if (!Number.isFinite(requested) || !Number.isInteger(requested)) {
|
|
252
|
+
return RECALL_DEBUG_ARTIFACT_DEFAULT_TOP_K;
|
|
253
|
+
}
|
|
254
|
+
if (requested < 1) {
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
|
257
|
+
if (requested > RECALL_DEBUG_ARTIFACT_MAX_TOP_K) {
|
|
258
|
+
return RECALL_DEBUG_ARTIFACT_MAX_TOP_K;
|
|
259
|
+
}
|
|
260
|
+
return requested;
|
|
261
|
+
}
|
|
262
|
+
function buildTopCandidates(result, reasonsByEntryId, topK) {
|
|
263
|
+
if (topK < 1) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
return result.entries.slice(0, topK).map((entry) => {
|
|
267
|
+
const reasons = reasonsByEntryId.get(entry.entry.id);
|
|
268
|
+
return {
|
|
269
|
+
id: entry.entry.id,
|
|
270
|
+
score: entry.score,
|
|
271
|
+
lexicalScore: entry.scores.lexical,
|
|
272
|
+
vectorScore: entry.scores.vector,
|
|
273
|
+
recencyScore: entry.scores.recency,
|
|
274
|
+
importanceScore: entry.scores.importance,
|
|
275
|
+
...reasons && reasons.length > 0 ? { reasons } : {}
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
38
280
|
// src/adapters/openclaw/tools/shared.ts
|
|
39
281
|
import { failedTextResult, readStringParam } from "openclaw/plugin-sdk/agent-runtime";
|
|
40
282
|
var ENTRY_TYPE_DESCRIPTION = "Knowledge type to store. Use fact for durable truth about a person, system, place, or how something works. Use decision for a standing rule, constraint, policy, or chosen approach future sessions should follow - not a progress update or completed action. Use preference for what someone likes, wants, values, or wants avoided. Use lesson for a non-obvious takeaway learned from experience that should change future behavior. Use milestone for a rare one-time event with durable future significance - not ordinary execution progress. Use relationship for a meaningful durable connection between people, groups, or systems.";
|
|
@@ -636,6 +878,15 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
|
636
878
|
...asOf ? { asOf } : {},
|
|
637
879
|
sessionKey: ctx.sessionKey
|
|
638
880
|
};
|
|
881
|
+
const sanitizedParams = sanitizeRecallToolParams({
|
|
882
|
+
query,
|
|
883
|
+
mode,
|
|
884
|
+
limit,
|
|
885
|
+
threshold,
|
|
886
|
+
types,
|
|
887
|
+
tags,
|
|
888
|
+
...asOf ? { asOf } : {}
|
|
889
|
+
});
|
|
639
890
|
logToolCall(
|
|
640
891
|
logger,
|
|
641
892
|
"agenr_recall",
|
|
@@ -648,16 +899,15 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
|
648
899
|
tags,
|
|
649
900
|
...asOf ? { asOf } : {}
|
|
650
901
|
}),
|
|
651
|
-
|
|
652
|
-
query,
|
|
653
|
-
mode,
|
|
654
|
-
limit,
|
|
655
|
-
threshold,
|
|
656
|
-
types,
|
|
657
|
-
tags,
|
|
658
|
-
...asOf ? { asOf } : {}
|
|
659
|
-
})
|
|
902
|
+
sanitizedParams
|
|
660
903
|
);
|
|
904
|
+
void services.debugSink.emit({
|
|
905
|
+
type: "tool_call",
|
|
906
|
+
tool: "agenr_recall",
|
|
907
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
908
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
909
|
+
params: sanitizedParams
|
|
910
|
+
});
|
|
661
911
|
const result = await runUnifiedRecall(request, {
|
|
662
912
|
database: services.episodes,
|
|
663
913
|
procedures: services.procedures,
|
|
@@ -679,6 +929,42 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
|
679
929
|
logger.info(
|
|
680
930
|
`[agenr] tool=agenr_recall session=${ctx.sessionId ?? "unknown"} key=${ctx.sessionKey ?? "unknown"} result: ${formatUnifiedRecallLogSummary(result)}`
|
|
681
931
|
);
|
|
932
|
+
if (services.debugSink.enabled) {
|
|
933
|
+
const sessionIdPayload = ctx.sessionId ? { sessionId: ctx.sessionId } : {};
|
|
934
|
+
const sessionKeyPayload = ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {};
|
|
935
|
+
void services.debugSink.emit({
|
|
936
|
+
type: "tool_result",
|
|
937
|
+
tool: "agenr_recall",
|
|
938
|
+
...sessionIdPayload,
|
|
939
|
+
...sessionKeyPayload,
|
|
940
|
+
summary: {
|
|
941
|
+
count: result.count,
|
|
942
|
+
routing: {
|
|
943
|
+
requested: result.routing.requested,
|
|
944
|
+
detectedIntent: result.routing.detectedIntent,
|
|
945
|
+
queried: [...result.routing.queried],
|
|
946
|
+
reason: result.routing.reason
|
|
947
|
+
},
|
|
948
|
+
selectedEntryIds: result.entries.map((entry) => entry.entry.id),
|
|
949
|
+
episodeIds: result.episodes.map((episode) => episode.episode.id),
|
|
950
|
+
selectedProcedureKey: result.procedure?.procedure_key ?? null,
|
|
951
|
+
notices: [...result.notices],
|
|
952
|
+
procedureNotices: [...result.procedureNotices]
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
void services.debugSink.emit({
|
|
956
|
+
type: "unified_recall",
|
|
957
|
+
...sessionIdPayload,
|
|
958
|
+
...sessionKeyPayload,
|
|
959
|
+
debug: buildLiveRecallDebugArtifact({
|
|
960
|
+
caseId: `live-${randomUUID()}`,
|
|
961
|
+
query,
|
|
962
|
+
result,
|
|
963
|
+
eventLevel: services.debugSink.eventLevel,
|
|
964
|
+
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
965
|
+
})
|
|
966
|
+
});
|
|
967
|
+
}
|
|
682
968
|
return textResult(formatUnifiedRecallResults(result), {
|
|
683
969
|
status: "ok",
|
|
684
970
|
count: result.count,
|
|
@@ -758,6 +1044,19 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
|
758
1044
|
});
|
|
759
1045
|
} catch (error) {
|
|
760
1046
|
logToolFailure(logger, "agenr_recall", ctx, error);
|
|
1047
|
+
try {
|
|
1048
|
+
const services = await servicesPromise;
|
|
1049
|
+
if (services.debugSink.enabled) {
|
|
1050
|
+
void services.debugSink.emit({
|
|
1051
|
+
type: "error",
|
|
1052
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
1053
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
1054
|
+
scope: "agenr_recall",
|
|
1055
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
761
1060
|
return toolFailureResult(error);
|
|
762
1061
|
}
|
|
763
1062
|
}
|
|
@@ -1175,7 +1474,7 @@ function registerAgenrOpenClawTools(api, servicesPromise, logger) {
|
|
|
1175
1474
|
var openclaw_plugin_default = {
|
|
1176
1475
|
id: "agenr",
|
|
1177
1476
|
name: "agenr",
|
|
1178
|
-
version: "2.0
|
|
1477
|
+
version: "2.1.0",
|
|
1179
1478
|
description: "agenr memory plugin for OpenClaw",
|
|
1180
1479
|
kind: "memory",
|
|
1181
1480
|
contracts: {
|
|
@@ -1208,7 +1507,11 @@ var openclaw_plugin_default = {
|
|
|
1208
1507
|
},
|
|
1209
1508
|
memoryPolicy: {
|
|
1210
1509
|
label: "Memory policy",
|
|
1211
|
-
help: "Optional runtime overrides for claim-aware read behavior
|
|
1510
|
+
help: "Optional runtime overrides for claim-aware read behavior, session-start memory injection, and proactive before-turn surfacing."
|
|
1511
|
+
},
|
|
1512
|
+
debug: {
|
|
1513
|
+
label: "Debug log",
|
|
1514
|
+
help: "Optional opt-in JSONL debug sink that records recall, session-start, and before-turn decisions into a dedicated agenr log file separate from OpenClaw host logs."
|
|
1212
1515
|
}
|
|
1213
1516
|
},
|
|
1214
1517
|
configSchema: {
|
|
@@ -1259,11 +1562,91 @@ var openclaw_plugin_default = {
|
|
|
1259
1562
|
}
|
|
1260
1563
|
}
|
|
1261
1564
|
},
|
|
1565
|
+
debug: {
|
|
1566
|
+
type: "object",
|
|
1567
|
+
additionalProperties: false,
|
|
1568
|
+
description: "Optional opt-in JSONL debug sink for live OpenClaw runs. Writes agenr-only events to a dedicated log file rather than the shared host log.",
|
|
1569
|
+
properties: {
|
|
1570
|
+
enabled: {
|
|
1571
|
+
type: "boolean",
|
|
1572
|
+
description: "Enable or disable the agenr JSONL debug sink. Defaults to false."
|
|
1573
|
+
},
|
|
1574
|
+
logPath: {
|
|
1575
|
+
type: "string",
|
|
1576
|
+
minLength: 1,
|
|
1577
|
+
description: "Optional explicit log-file path. Defaults to agenr-debug.jsonl inside the OpenClaw state directory."
|
|
1578
|
+
},
|
|
1579
|
+
eventLevel: {
|
|
1580
|
+
type: "string",
|
|
1581
|
+
enum: ["basic", "detailed"],
|
|
1582
|
+
description: "Event detail level. Detailed enables bounded top-K candidate breakdowns for recall and before-turn events. Defaults to basic."
|
|
1583
|
+
},
|
|
1584
|
+
perSessionFiles: {
|
|
1585
|
+
type: "boolean",
|
|
1586
|
+
description: "Split one JSONL file per OpenClaw session id. Defaults to false."
|
|
1587
|
+
},
|
|
1588
|
+
maxTopCandidates: {
|
|
1589
|
+
type: "integer",
|
|
1590
|
+
minimum: 1,
|
|
1591
|
+
maximum: 25,
|
|
1592
|
+
description: "Cap for top-K candidate breakdowns included in detailed events. Defaults to 10."
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
},
|
|
1262
1596
|
memoryPolicy: {
|
|
1263
1597
|
type: "object",
|
|
1264
1598
|
additionalProperties: false,
|
|
1265
1599
|
description: "Optional runtime overrides for claim-aware read behavior exposed by the OpenClaw adapter.",
|
|
1266
1600
|
properties: {
|
|
1601
|
+
sessionStart: {
|
|
1602
|
+
type: "object",
|
|
1603
|
+
additionalProperties: false,
|
|
1604
|
+
description: "Optional session-start overrides for prompt-time memory injection behavior.",
|
|
1605
|
+
properties: {
|
|
1606
|
+
relevantDurableMemory: {
|
|
1607
|
+
type: "boolean",
|
|
1608
|
+
description: "Enable or disable artifact-grounded Relevant Durable Memory injection at session start. Defaults to true."
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
},
|
|
1612
|
+
beforeTurn: {
|
|
1613
|
+
type: "object",
|
|
1614
|
+
additionalProperties: false,
|
|
1615
|
+
description: "Optional before-turn overrides for proactive prompt-time memory injection behavior.",
|
|
1616
|
+
properties: {
|
|
1617
|
+
enabled: {
|
|
1618
|
+
type: "boolean",
|
|
1619
|
+
description: "Enable or disable the proactive before-turn memory patch. Defaults to true."
|
|
1620
|
+
},
|
|
1621
|
+
procedureSuggestion: {
|
|
1622
|
+
type: "boolean",
|
|
1623
|
+
description: "Enable or disable proactive high-confidence procedure suggestion inside the before-turn patch. Defaults to true."
|
|
1624
|
+
},
|
|
1625
|
+
maxDurableEntries: {
|
|
1626
|
+
type: "integer",
|
|
1627
|
+
minimum: 1,
|
|
1628
|
+
description: "Normal durable-item cap for before-turn recall. Defaults to 1 and only expands when all surfaced items are very high confidence."
|
|
1629
|
+
},
|
|
1630
|
+
recallThreshold: {
|
|
1631
|
+
type: "number",
|
|
1632
|
+
minimum: 0,
|
|
1633
|
+
maximum: 1,
|
|
1634
|
+
description: "Durable-recall score threshold required before an entry can surface during before-turn recall. Defaults to 0.6."
|
|
1635
|
+
},
|
|
1636
|
+
highConfidenceRecallThreshold: {
|
|
1637
|
+
type: "number",
|
|
1638
|
+
minimum: 0,
|
|
1639
|
+
maximum: 1,
|
|
1640
|
+
description: "Durable-recall score threshold required before before-turn recall can expand beyond the normal durable-item cap. Defaults to 0.85."
|
|
1641
|
+
},
|
|
1642
|
+
procedureThreshold: {
|
|
1643
|
+
type: "number",
|
|
1644
|
+
minimum: 0,
|
|
1645
|
+
maximum: 1,
|
|
1646
|
+
description: "Procedure-recall score threshold required before a proactive procedure can surface. Defaults to 0.72."
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1267
1650
|
slotPolicies: {
|
|
1268
1651
|
type: "object",
|
|
1269
1652
|
additionalProperties: false,
|
|
@@ -1292,6 +1675,10 @@ var openclaw_plugin_default = {
|
|
|
1292
1675
|
var manifest = openclaw_plugin_default;
|
|
1293
1676
|
var DEFAULT_STORE_NUDGE_THRESHOLD = 8;
|
|
1294
1677
|
var DEFAULT_STORE_NUDGE_MAX_PER_SESSION = 5;
|
|
1678
|
+
var DEFAULT_DEBUG_EVENT_LEVEL = "basic";
|
|
1679
|
+
var DEFAULT_DEBUG_PER_SESSION_FILES = false;
|
|
1680
|
+
var DEFAULT_DEBUG_MAX_TOP_CANDIDATES = 10;
|
|
1681
|
+
var MAX_DEBUG_MAX_TOP_CANDIDATES = 25;
|
|
1295
1682
|
function normalizeAgenrOpenClawPluginConfig(value) {
|
|
1296
1683
|
if (value === void 0) {
|
|
1297
1684
|
return { ok: true, value: {} };
|
|
@@ -1339,7 +1726,11 @@ function normalizeAgenrOpenClawPluginConfig(value) {
|
|
|
1339
1726
|
if (!memoryPolicyResult.ok) {
|
|
1340
1727
|
errors.push(...memoryPolicyResult.errors);
|
|
1341
1728
|
}
|
|
1342
|
-
const
|
|
1729
|
+
const debugResult = normalizeDebugConfig(value.debug);
|
|
1730
|
+
if (!debugResult.ok) {
|
|
1731
|
+
errors.push(...debugResult.errors);
|
|
1732
|
+
}
|
|
1733
|
+
const allowedKeys = /* @__PURE__ */ new Set(["dbPath", "configPath", "continuityModel", "episodeModel", "claimExtractionModel", "storeNudge", "memoryPolicy", "debug"]);
|
|
1343
1734
|
for (const key of Object.keys(value)) {
|
|
1344
1735
|
if (!allowedKeys.has(key)) {
|
|
1345
1736
|
errors.push(`unknown config field: ${key}`);
|
|
@@ -1357,7 +1748,8 @@ function normalizeAgenrOpenClawPluginConfig(value) {
|
|
|
1357
1748
|
...episodeModel ? { episodeModel } : {},
|
|
1358
1749
|
...claimExtractionModel ? { claimExtractionModel } : {},
|
|
1359
1750
|
...storeNudgeResult.ok && storeNudgeResult.value ? { storeNudge: storeNudgeResult.value } : {},
|
|
1360
|
-
...memoryPolicyResult.ok && memoryPolicyResult.value ? { memoryPolicy: memoryPolicyResult.value } : {}
|
|
1751
|
+
...memoryPolicyResult.ok && memoryPolicyResult.value ? { memoryPolicy: memoryPolicyResult.value } : {},
|
|
1752
|
+
...debugResult.ok && debugResult.value ? { debug: debugResult.value } : {}
|
|
1361
1753
|
}
|
|
1362
1754
|
};
|
|
1363
1755
|
}
|
|
@@ -1381,6 +1773,78 @@ function createAgenrOpenClawPluginConfigSchema() {
|
|
|
1381
1773
|
function isRecord(value) {
|
|
1382
1774
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1383
1775
|
}
|
|
1776
|
+
function resolveDebugConfig(value) {
|
|
1777
|
+
const logPath = value?.logPath?.trim();
|
|
1778
|
+
return {
|
|
1779
|
+
enabled: value?.enabled ?? false,
|
|
1780
|
+
...logPath ? { logPath } : {},
|
|
1781
|
+
eventLevel: value?.eventLevel ?? DEFAULT_DEBUG_EVENT_LEVEL,
|
|
1782
|
+
perSessionFiles: value?.perSessionFiles ?? DEFAULT_DEBUG_PER_SESSION_FILES,
|
|
1783
|
+
maxTopCandidates: value?.maxTopCandidates ?? DEFAULT_DEBUG_MAX_TOP_CANDIDATES
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
function normalizeDebugConfig(value) {
|
|
1787
|
+
if (value === void 0) {
|
|
1788
|
+
return { ok: true, value: void 0 };
|
|
1789
|
+
}
|
|
1790
|
+
if (!isRecord(value)) {
|
|
1791
|
+
return { ok: false, errors: ["debug must be an object when provided"] };
|
|
1792
|
+
}
|
|
1793
|
+
const errors = [];
|
|
1794
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "debug.enabled", errors);
|
|
1795
|
+
const logPathRaw = value.logPath;
|
|
1796
|
+
let logPath;
|
|
1797
|
+
if (logPathRaw !== void 0) {
|
|
1798
|
+
if (typeof logPathRaw !== "string" || logPathRaw.trim().length === 0) {
|
|
1799
|
+
errors.push("debug.logPath must be a non-empty string when provided");
|
|
1800
|
+
} else {
|
|
1801
|
+
logPath = logPathRaw.trim();
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
const eventLevel = normalizeOptionalDebugEventLevel(value.eventLevel, errors);
|
|
1805
|
+
const perSessionFiles = normalizeOptionalBoolean(value.perSessionFiles, "debug.perSessionFiles", errors);
|
|
1806
|
+
const maxTopCandidates = normalizeOptionalTopCandidateCap(value.maxTopCandidates, errors);
|
|
1807
|
+
const allowedKeys = /* @__PURE__ */ new Set(["enabled", "logPath", "eventLevel", "perSessionFiles", "maxTopCandidates"]);
|
|
1808
|
+
for (const key of Object.keys(value)) {
|
|
1809
|
+
if (!allowedKeys.has(key)) {
|
|
1810
|
+
errors.push(`unknown config field: debug.${key}`);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (errors.length > 0) {
|
|
1814
|
+
return { ok: false, errors };
|
|
1815
|
+
}
|
|
1816
|
+
const normalized = {
|
|
1817
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
1818
|
+
...logPath !== void 0 ? { logPath } : {},
|
|
1819
|
+
...eventLevel !== void 0 ? { eventLevel } : {},
|
|
1820
|
+
...perSessionFiles !== void 0 ? { perSessionFiles } : {},
|
|
1821
|
+
...maxTopCandidates !== void 0 ? { maxTopCandidates } : {}
|
|
1822
|
+
};
|
|
1823
|
+
return {
|
|
1824
|
+
ok: true,
|
|
1825
|
+
value: Object.keys(normalized).length > 0 ? normalized : void 0
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
function normalizeOptionalDebugEventLevel(value, errors) {
|
|
1829
|
+
if (value === void 0) {
|
|
1830
|
+
return void 0;
|
|
1831
|
+
}
|
|
1832
|
+
if (value === "basic" || value === "detailed") {
|
|
1833
|
+
return value;
|
|
1834
|
+
}
|
|
1835
|
+
errors.push('debug.eventLevel must be "basic" or "detailed" when provided');
|
|
1836
|
+
return void 0;
|
|
1837
|
+
}
|
|
1838
|
+
function normalizeOptionalTopCandidateCap(value, errors) {
|
|
1839
|
+
if (value === void 0) {
|
|
1840
|
+
return void 0;
|
|
1841
|
+
}
|
|
1842
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0 || value > MAX_DEBUG_MAX_TOP_CANDIDATES) {
|
|
1843
|
+
errors.push(`debug.maxTopCandidates must be an integer between 1 and ${MAX_DEBUG_MAX_TOP_CANDIDATES} when provided`);
|
|
1844
|
+
return void 0;
|
|
1845
|
+
}
|
|
1846
|
+
return value;
|
|
1847
|
+
}
|
|
1384
1848
|
function resolveStoreNudgeConfig(value) {
|
|
1385
1849
|
return {
|
|
1386
1850
|
enabled: value?.enabled ?? true,
|
|
@@ -1430,7 +1894,15 @@ function normalizeMemoryPolicyConfig(value) {
|
|
|
1430
1894
|
if (!slotPoliciesResult.ok) {
|
|
1431
1895
|
errors.push(...slotPoliciesResult.errors);
|
|
1432
1896
|
}
|
|
1433
|
-
const
|
|
1897
|
+
const sessionStartResult = normalizeSessionStartMemoryPolicyConfig(value.sessionStart);
|
|
1898
|
+
if (!sessionStartResult.ok) {
|
|
1899
|
+
errors.push(...sessionStartResult.errors);
|
|
1900
|
+
}
|
|
1901
|
+
const beforeTurnResult = normalizeBeforeTurnMemoryPolicyConfig(value.beforeTurn);
|
|
1902
|
+
if (!beforeTurnResult.ok) {
|
|
1903
|
+
errors.push(...beforeTurnResult.errors);
|
|
1904
|
+
}
|
|
1905
|
+
const allowedKeys = /* @__PURE__ */ new Set(["slotPolicies", "sessionStart", "beforeTurn"]);
|
|
1434
1906
|
for (const key of Object.keys(value)) {
|
|
1435
1907
|
if (!allowedKeys.has(key)) {
|
|
1436
1908
|
errors.push(`unknown config field: memoryPolicy.${key}`);
|
|
@@ -1441,8 +1913,79 @@ function normalizeMemoryPolicyConfig(value) {
|
|
|
1441
1913
|
}
|
|
1442
1914
|
return {
|
|
1443
1915
|
ok: true,
|
|
1444
|
-
value: slotPoliciesResult.ok && slotPoliciesResult.value ? {
|
|
1445
|
-
slotPolicies: slotPoliciesResult.value
|
|
1916
|
+
value: slotPoliciesResult.ok && slotPoliciesResult.value || sessionStartResult.ok && sessionStartResult.value || beforeTurnResult.ok && beforeTurnResult.value ? {
|
|
1917
|
+
...slotPoliciesResult.ok && slotPoliciesResult.value ? { slotPolicies: slotPoliciesResult.value } : {},
|
|
1918
|
+
...sessionStartResult.ok && sessionStartResult.value ? { sessionStart: sessionStartResult.value } : {},
|
|
1919
|
+
...beforeTurnResult.ok && beforeTurnResult.value ? { beforeTurn: beforeTurnResult.value } : {}
|
|
1920
|
+
} : void 0
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
function normalizeSessionStartMemoryPolicyConfig(value) {
|
|
1924
|
+
if (value === void 0) {
|
|
1925
|
+
return { ok: true, value: void 0 };
|
|
1926
|
+
}
|
|
1927
|
+
if (!isRecord(value)) {
|
|
1928
|
+
return { ok: false, errors: ["memoryPolicy.sessionStart must be an object when provided"] };
|
|
1929
|
+
}
|
|
1930
|
+
const errors = [];
|
|
1931
|
+
const relevantDurableMemory = normalizeOptionalBoolean(value.relevantDurableMemory, "memoryPolicy.sessionStart.relevantDurableMemory", errors);
|
|
1932
|
+
const allowedKeys = /* @__PURE__ */ new Set(["relevantDurableMemory"]);
|
|
1933
|
+
for (const key of Object.keys(value)) {
|
|
1934
|
+
if (!allowedKeys.has(key)) {
|
|
1935
|
+
errors.push(`unknown config field: memoryPolicy.sessionStart.${key}`);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
if (errors.length > 0) {
|
|
1939
|
+
return { ok: false, errors };
|
|
1940
|
+
}
|
|
1941
|
+
return {
|
|
1942
|
+
ok: true,
|
|
1943
|
+
value: relevantDurableMemory !== void 0 ? { relevantDurableMemory } : void 0
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
function normalizeBeforeTurnMemoryPolicyConfig(value) {
|
|
1947
|
+
if (value === void 0) {
|
|
1948
|
+
return { ok: true, value: void 0 };
|
|
1949
|
+
}
|
|
1950
|
+
if (!isRecord(value)) {
|
|
1951
|
+
return { ok: false, errors: ["memoryPolicy.beforeTurn must be an object when provided"] };
|
|
1952
|
+
}
|
|
1953
|
+
const errors = [];
|
|
1954
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.beforeTurn.enabled", errors);
|
|
1955
|
+
const procedureSuggestion = normalizeOptionalBoolean(value.procedureSuggestion, "memoryPolicy.beforeTurn.procedureSuggestion", errors);
|
|
1956
|
+
const maxDurableEntries = normalizeOptionalPositiveInteger(value.maxDurableEntries, "memoryPolicy.beforeTurn.maxDurableEntries", errors);
|
|
1957
|
+
const recallThreshold = normalizeOptionalUnitInterval(value.recallThreshold, "memoryPolicy.beforeTurn.recallThreshold", errors);
|
|
1958
|
+
const highConfidenceRecallThreshold = normalizeOptionalUnitInterval(
|
|
1959
|
+
value.highConfidenceRecallThreshold,
|
|
1960
|
+
"memoryPolicy.beforeTurn.highConfidenceRecallThreshold",
|
|
1961
|
+
errors
|
|
1962
|
+
);
|
|
1963
|
+
const procedureThreshold = normalizeOptionalUnitInterval(value.procedureThreshold, "memoryPolicy.beforeTurn.procedureThreshold", errors);
|
|
1964
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
1965
|
+
"enabled",
|
|
1966
|
+
"procedureSuggestion",
|
|
1967
|
+
"maxDurableEntries",
|
|
1968
|
+
"recallThreshold",
|
|
1969
|
+
"highConfidenceRecallThreshold",
|
|
1970
|
+
"procedureThreshold"
|
|
1971
|
+
]);
|
|
1972
|
+
for (const key of Object.keys(value)) {
|
|
1973
|
+
if (!allowedKeys.has(key)) {
|
|
1974
|
+
errors.push(`unknown config field: memoryPolicy.beforeTurn.${key}`);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
if (errors.length > 0) {
|
|
1978
|
+
return { ok: false, errors };
|
|
1979
|
+
}
|
|
1980
|
+
return {
|
|
1981
|
+
ok: true,
|
|
1982
|
+
value: enabled !== void 0 || procedureSuggestion !== void 0 || maxDurableEntries !== void 0 || recallThreshold !== void 0 || highConfidenceRecallThreshold !== void 0 || procedureThreshold !== void 0 ? {
|
|
1983
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
1984
|
+
...procedureSuggestion !== void 0 ? { procedureSuggestion } : {},
|
|
1985
|
+
...maxDurableEntries !== void 0 ? { maxDurableEntries } : {},
|
|
1986
|
+
...recallThreshold !== void 0 ? { recallThreshold } : {},
|
|
1987
|
+
...highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold } : {},
|
|
1988
|
+
...procedureThreshold !== void 0 ? { procedureThreshold } : {}
|
|
1446
1989
|
} : void 0
|
|
1447
1990
|
};
|
|
1448
1991
|
}
|
|
@@ -1512,6 +2055,16 @@ function normalizeOptionalPositiveInteger(value, label, errors) {
|
|
|
1512
2055
|
}
|
|
1513
2056
|
return value;
|
|
1514
2057
|
}
|
|
2058
|
+
function normalizeOptionalUnitInterval(value, label, errors) {
|
|
2059
|
+
if (value === void 0) {
|
|
2060
|
+
return void 0;
|
|
2061
|
+
}
|
|
2062
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
2063
|
+
errors.push(`${label} must be a number between 0 and 1 when provided`);
|
|
2064
|
+
return void 0;
|
|
2065
|
+
}
|
|
2066
|
+
return value;
|
|
2067
|
+
}
|
|
1515
2068
|
|
|
1516
2069
|
// src/adapters/openclaw/format/prompt-section.ts
|
|
1517
2070
|
var MEMORY_TOOL_NAMES = {
|
|
@@ -1535,10 +2088,11 @@ function buildAgenrMemoryPromptSection({
|
|
|
1535
2088
|
);
|
|
1536
2089
|
const lines = [
|
|
1537
2090
|
"## Memory Recall",
|
|
1538
|
-
"Before answering anything about prior work, decisions, preferences, people, dates, unfinished work, or past sessions, call agenr_recall first. Session-start recall is automatic; use agenr_recall mid-session when you need context you do not already have.",
|
|
2091
|
+
"Before answering anything about prior work, decisions, preferences, people, dates, unfinished work, or past sessions, call agenr_recall first. Session-start recall is automatic, and conservative before-turn recall may also appear as injected background context; use agenr_recall mid-session when you need context you do not already have.",
|
|
1539
2092
|
"agenr_recall supports exact fact recall plus historical and episodic recall behind one tool: use mode=entries for exact facts, decisions, thresholds, and versions; use mode=auto for prior-state questions like what was the previous approach, what did we use before, or what changed from X to Y; use mode=episodes when you explicitly want session narrative recall.",
|
|
1540
2093
|
"For temporal narrative questions, put the time phrase in the query itself: examples include yesterday, last week, this month, 2 weeks ago, or in March.",
|
|
1541
2094
|
"One focused agenr_recall call with the right scope beats several broad ones.",
|
|
2095
|
+
"When Agenr injects memory automatically, treat it as non-user background context and use it silently when relevant rather than forcing it into the reply.",
|
|
1542
2096
|
"Memory authority, strongest to weakest:",
|
|
1543
2097
|
"- Durable entries are the canonical record for verified facts, decisions, preferences, and lessons unless live evidence contradicts them.",
|
|
1544
2098
|
"- Episode recall explains what happened in completed sessions, but it is a narrative summary, not an exact log.",
|
|
@@ -1800,6 +2354,267 @@ function hasNonEmptyString(value) {
|
|
|
1800
2354
|
return readString(value) !== void 0;
|
|
1801
2355
|
}
|
|
1802
2356
|
|
|
2357
|
+
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
2358
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2359
|
+
|
|
2360
|
+
// src/app/session-start/service.ts
|
|
2361
|
+
var DEFAULT_MAX_CORE_ENTRIES = 4;
|
|
2362
|
+
var DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES = 3;
|
|
2363
|
+
var DEFAULT_MAX_DURABLE_ENTRIES = 5;
|
|
2364
|
+
var DEFAULT_MAX_ARTIFACT_CHARS = 1200;
|
|
2365
|
+
async function runSessionStart(input, deps) {
|
|
2366
|
+
const policy = normalizePolicy(input.policy);
|
|
2367
|
+
const contextSections = buildContextSections(input);
|
|
2368
|
+
const coreEntries = await deps.repository.listCoreEntries(policy.maxCoreEntries);
|
|
2369
|
+
const coreItems = coreEntries.map((entry) => buildCorePatchItem(entry));
|
|
2370
|
+
const diagnostics = {
|
|
2371
|
+
coreCandidateCount: coreEntries.length,
|
|
2372
|
+
artifactRecallCandidateCount: 0,
|
|
2373
|
+
artifactRecallUsed: false,
|
|
2374
|
+
notices: []
|
|
2375
|
+
};
|
|
2376
|
+
const artifactRecallQuery = policy.enableArtifactRecall ? buildArtifactRecallQuery(contextSections, policy.maxArtifactChars) : void 0;
|
|
2377
|
+
if (!policy.enableArtifactRecall) {
|
|
2378
|
+
diagnostics.notices.push("Artifact-grounded durable recall disabled by session-start policy.");
|
|
2379
|
+
}
|
|
2380
|
+
const artifactRecallItems = artifactRecallQuery ? await runArtifactRecallSelection(artifactRecallQuery, input.sessionKey, policy, deps, diagnostics) : [];
|
|
2381
|
+
const durableMemory = assignRanks(mergeDurableMemory(coreItems, artifactRecallItems, policy.maxDurableEntries));
|
|
2382
|
+
return {
|
|
2383
|
+
contextSections,
|
|
2384
|
+
durableMemory,
|
|
2385
|
+
diagnostics
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
function buildContextSections(input) {
|
|
2389
|
+
const sections = [];
|
|
2390
|
+
const continuitySummaryText = normalizeOptionalString(input.continuitySummaryText);
|
|
2391
|
+
if (continuitySummaryText) {
|
|
2392
|
+
sections.push({
|
|
2393
|
+
kind: "continuity_summary",
|
|
2394
|
+
title: "Previous session summary",
|
|
2395
|
+
content: continuitySummaryText
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
const recentSessionText = normalizeOptionalString(input.recentSessionText);
|
|
2399
|
+
if (recentSessionText) {
|
|
2400
|
+
sections.push({
|
|
2401
|
+
kind: "recent_session",
|
|
2402
|
+
title: "Recent session",
|
|
2403
|
+
content: recentSessionText
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
return sections;
|
|
2407
|
+
}
|
|
2408
|
+
async function runArtifactRecallSelection(query, sessionKey, policy, deps, diagnostics) {
|
|
2409
|
+
diagnostics.artifactRecallUsed = true;
|
|
2410
|
+
diagnostics.artifactRecallQuery = query;
|
|
2411
|
+
let artifactRecallTrace;
|
|
2412
|
+
try {
|
|
2413
|
+
const recalled = await recall(
|
|
2414
|
+
{
|
|
2415
|
+
text: query,
|
|
2416
|
+
limit: policy.maxArtifactRecallEntries,
|
|
2417
|
+
threshold: policy.recallThreshold,
|
|
2418
|
+
sessionKey
|
|
2419
|
+
},
|
|
2420
|
+
deps.recall,
|
|
2421
|
+
{
|
|
2422
|
+
trace: {
|
|
2423
|
+
reportSummary(summary) {
|
|
2424
|
+
artifactRecallTrace = summary;
|
|
2425
|
+
}
|
|
2426
|
+
},
|
|
2427
|
+
slotPolicyConfig: deps.slotPolicyConfig
|
|
2428
|
+
}
|
|
2429
|
+
);
|
|
2430
|
+
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
2431
|
+
diagnostics.artifactRecallCandidateCount = recalled.length;
|
|
2432
|
+
if (artifactRecallTrace?.degraded.notices.length) {
|
|
2433
|
+
diagnostics.notices.push(...artifactRecallTrace.degraded.notices);
|
|
2434
|
+
}
|
|
2435
|
+
return recalled.map((item) => buildArtifactRecallPatchItem(item, deps));
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
2438
|
+
diagnostics.notices.push(`Artifact-grounded durable recall failed: ${formatErrorMessage3(error)}`);
|
|
2439
|
+
return [];
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function buildCorePatchItem(entry) {
|
|
2443
|
+
return {
|
|
2444
|
+
rank: 0,
|
|
2445
|
+
entry,
|
|
2446
|
+
sourceKind: "core",
|
|
2447
|
+
whySurfaced: {
|
|
2448
|
+
summary: `always-on core memory; importance ${entry.importance}`,
|
|
2449
|
+
reasons: ["always-on core memory", `importance ${entry.importance}`, `expiry ${entry.expiry}`]
|
|
2450
|
+
},
|
|
2451
|
+
memoryState: resolveMemoryState(entry),
|
|
2452
|
+
claimStatus: resolveClaimStatus(entry),
|
|
2453
|
+
freshnessLabel: buildFreshnessLabel(entry),
|
|
2454
|
+
...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
function buildArtifactRecallPatchItem(recalled, deps) {
|
|
2458
|
+
const projected = projectClaimCentricRecallEntry(recalled, {
|
|
2459
|
+
slotPolicyConfig: deps.slotPolicyConfig
|
|
2460
|
+
});
|
|
2461
|
+
return {
|
|
2462
|
+
rank: 0,
|
|
2463
|
+
entry: recalled.entry,
|
|
2464
|
+
sourceKind: "artifact_recall",
|
|
2465
|
+
score: recalled.score,
|
|
2466
|
+
whySurfaced: projected.whySurfaced,
|
|
2467
|
+
memoryState: projected.memoryState,
|
|
2468
|
+
claimStatus: projected.claimStatus,
|
|
2469
|
+
freshnessLabel: projected.freshness.label,
|
|
2470
|
+
...formatProjectedProvenance(projected.provenance) ? { provenanceSummary: formatProjectedProvenance(projected.provenance) } : {}
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
function buildArtifactRecallQuery(sections, maxChars) {
|
|
2474
|
+
if (sections.length === 0 || maxChars <= 0) {
|
|
2475
|
+
return void 0;
|
|
2476
|
+
}
|
|
2477
|
+
let remaining = maxChars;
|
|
2478
|
+
const parts = [];
|
|
2479
|
+
for (const section of sections) {
|
|
2480
|
+
if (remaining <= 0) {
|
|
2481
|
+
break;
|
|
2482
|
+
}
|
|
2483
|
+
const normalizedContent = normalizeWhitespace(section.content);
|
|
2484
|
+
if (normalizedContent.length === 0) {
|
|
2485
|
+
continue;
|
|
2486
|
+
}
|
|
2487
|
+
const labeled = `${section.title}: ${normalizedContent}`;
|
|
2488
|
+
const truncated = truncate2(labeled, remaining);
|
|
2489
|
+
if (truncated.length === 0) {
|
|
2490
|
+
continue;
|
|
2491
|
+
}
|
|
2492
|
+
parts.push(truncated);
|
|
2493
|
+
remaining -= truncated.length;
|
|
2494
|
+
}
|
|
2495
|
+
const query = normalizeWhitespace(parts.join("\n"));
|
|
2496
|
+
return query.length > 0 ? query : void 0;
|
|
2497
|
+
}
|
|
2498
|
+
function mergeDurableMemory(coreItems, artifactRecallItems, maxDurableEntries) {
|
|
2499
|
+
const merged = [];
|
|
2500
|
+
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
2501
|
+
for (const item of [...coreItems, ...artifactRecallItems]) {
|
|
2502
|
+
if (seenEntryIds.has(item.entry.id)) {
|
|
2503
|
+
continue;
|
|
2504
|
+
}
|
|
2505
|
+
seenEntryIds.add(item.entry.id);
|
|
2506
|
+
merged.push(item);
|
|
2507
|
+
if (merged.length >= maxDurableEntries) {
|
|
2508
|
+
break;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
return merged;
|
|
2512
|
+
}
|
|
2513
|
+
function assignRanks(items) {
|
|
2514
|
+
return items.map((item, index) => ({
|
|
2515
|
+
...item,
|
|
2516
|
+
rank: index + 1
|
|
2517
|
+
}));
|
|
2518
|
+
}
|
|
2519
|
+
function normalizePolicy(policy) {
|
|
2520
|
+
const maxCoreEntries = normalizeCount(policy?.maxCoreEntries, DEFAULT_MAX_CORE_ENTRIES);
|
|
2521
|
+
const maxArtifactRecallEntries = normalizeCount(policy?.maxArtifactRecallEntries, DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES);
|
|
2522
|
+
const maxDurableEntries = Math.max(maxCoreEntries, normalizeCount(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES));
|
|
2523
|
+
return {
|
|
2524
|
+
maxCoreEntries,
|
|
2525
|
+
enableArtifactRecall: policy?.enableArtifactRecall !== false,
|
|
2526
|
+
maxArtifactRecallEntries,
|
|
2527
|
+
maxDurableEntries,
|
|
2528
|
+
maxArtifactChars: normalizeCount(policy?.maxArtifactChars, DEFAULT_MAX_ARTIFACT_CHARS),
|
|
2529
|
+
recallThreshold: normalizeThreshold(policy?.recallThreshold)
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
function normalizeCount(value, fallback) {
|
|
2533
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2534
|
+
return fallback;
|
|
2535
|
+
}
|
|
2536
|
+
return Math.max(0, Math.trunc(value));
|
|
2537
|
+
}
|
|
2538
|
+
function normalizeThreshold(value) {
|
|
2539
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2540
|
+
return 0;
|
|
2541
|
+
}
|
|
2542
|
+
return Math.min(1, Math.max(0, value));
|
|
2543
|
+
}
|
|
2544
|
+
function resolveMemoryState(entry) {
|
|
2545
|
+
if (entry.superseded_by) {
|
|
2546
|
+
return "superseded";
|
|
2547
|
+
}
|
|
2548
|
+
if (entry.retired || entry.valid_to) {
|
|
2549
|
+
return "historical";
|
|
2550
|
+
}
|
|
2551
|
+
return "current";
|
|
2552
|
+
}
|
|
2553
|
+
function resolveClaimStatus(entry) {
|
|
2554
|
+
if (!normalizeOptionalString(entry.claim_key)) {
|
|
2555
|
+
return "no_key";
|
|
2556
|
+
}
|
|
2557
|
+
return entry.claim_key_status ?? "legacy";
|
|
2558
|
+
}
|
|
2559
|
+
function buildFreshnessLabel(entry) {
|
|
2560
|
+
const parts = [`created ${entry.created_at}`];
|
|
2561
|
+
const validFrom = normalizeOptionalString(entry.valid_from);
|
|
2562
|
+
const validTo = normalizeOptionalString(entry.valid_to);
|
|
2563
|
+
if (validFrom || validTo) {
|
|
2564
|
+
parts.push(`valid ${validFrom ?? "?"} -> ${validTo ?? "ongoing"}`);
|
|
2565
|
+
}
|
|
2566
|
+
return parts.join(" | ");
|
|
2567
|
+
}
|
|
2568
|
+
function buildProvenanceSummary(entry) {
|
|
2569
|
+
const parts = [
|
|
2570
|
+
entry.superseded_by ? `superseded_by=${entry.superseded_by}` : void 0,
|
|
2571
|
+
entry.supersession_kind ? `kind=${entry.supersession_kind}` : void 0,
|
|
2572
|
+
entry.supersession_reason ? `reason=${entry.supersession_reason}` : void 0,
|
|
2573
|
+
entry.claim_support_source_kind ? `support=${entry.claim_support_source_kind}` : void 0,
|
|
2574
|
+
entry.claim_support_mode ? `support_mode=${entry.claim_support_mode}` : void 0,
|
|
2575
|
+
entry.claim_support_observed_at ? `observed=${entry.claim_support_observed_at}` : void 0,
|
|
2576
|
+
entry.claim_support_locator ? `locator=${entry.claim_support_locator}` : void 0
|
|
2577
|
+
].filter((value) => value !== void 0);
|
|
2578
|
+
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
2579
|
+
}
|
|
2580
|
+
function formatProjectedProvenance(provenance) {
|
|
2581
|
+
const parts = [
|
|
2582
|
+
provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
|
|
2583
|
+
provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
|
|
2584
|
+
provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
|
|
2585
|
+
provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
|
|
2586
|
+
provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
|
|
2587
|
+
provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
|
|
2588
|
+
provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
|
|
2589
|
+
].filter((value) => value !== void 0);
|
|
2590
|
+
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
2591
|
+
}
|
|
2592
|
+
function normalizeOptionalString(value) {
|
|
2593
|
+
const normalized = value?.trim();
|
|
2594
|
+
return normalized && normalized.length > 0 ? normalized : void 0;
|
|
2595
|
+
}
|
|
2596
|
+
function normalizeWhitespace(value) {
|
|
2597
|
+
return value.replace(/\s+/g, " ").trim();
|
|
2598
|
+
}
|
|
2599
|
+
function truncate2(value, maxChars) {
|
|
2600
|
+
if (maxChars <= 0) {
|
|
2601
|
+
return "";
|
|
2602
|
+
}
|
|
2603
|
+
if (value.length <= maxChars) {
|
|
2604
|
+
return value;
|
|
2605
|
+
}
|
|
2606
|
+
return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
2607
|
+
}
|
|
2608
|
+
function formatErrorMessage3(error) {
|
|
2609
|
+
if (error instanceof Error) {
|
|
2610
|
+
return error.message;
|
|
2611
|
+
}
|
|
2612
|
+
return String(error);
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
2616
|
+
import path5 from "path";
|
|
2617
|
+
|
|
1803
2618
|
// src/adapters/openclaw/episode/episode-writer.ts
|
|
1804
2619
|
import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary2, resolveDefaultAgentId as resolveDefaultAgentId2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1805
2620
|
|
|
@@ -1809,7 +2624,7 @@ import { completeSimple, getModel } from "@mariozechner/pi-ai";
|
|
|
1809
2624
|
// src/adapters/openclaw/embedded-agent/task-runner.ts
|
|
1810
2625
|
import * as fs from "fs/promises";
|
|
1811
2626
|
import os from "os";
|
|
1812
|
-
import
|
|
2627
|
+
import path2 from "path";
|
|
1813
2628
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER, parseModelRef, resolveAgentEffectiveModelPrimary, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
1814
2629
|
function resolveOpenClawEmbeddedAgentExecution(params) {
|
|
1815
2630
|
const agentId = params.requestedAgentId?.trim() || resolveDefaultAgentId(params.openClaw.config);
|
|
@@ -1852,7 +2667,7 @@ async function createOpenClawLlmClient(openClaw, modelRef, label = "model overri
|
|
|
1852
2667
|
provider: execution.provider,
|
|
1853
2668
|
cfg: openClaw.config
|
|
1854
2669
|
});
|
|
1855
|
-
const apiKey =
|
|
2670
|
+
const apiKey = normalizeOptionalString2(auth.apiKey);
|
|
1856
2671
|
if (!apiKey) {
|
|
1857
2672
|
throw new Error(`OpenClaw auth did not resolve an API-key-compatible credential for ${execution.provider} (source=${auth.source}, mode=${auth.mode}).`);
|
|
1858
2673
|
}
|
|
@@ -1897,7 +2712,7 @@ function stripCodeFence(text) {
|
|
|
1897
2712
|
const match = /^```(?:json)?\s*([\s\S]+?)\s*```$/iu.exec(trimmed);
|
|
1898
2713
|
return match?.[1]?.trim() ?? trimmed;
|
|
1899
2714
|
}
|
|
1900
|
-
function
|
|
2715
|
+
function normalizeOptionalString2(value) {
|
|
1901
2716
|
const normalized = value?.trim();
|
|
1902
2717
|
return normalized ? normalized : void 0;
|
|
1903
2718
|
}
|
|
@@ -2216,33 +3031,51 @@ function truncateSubject(subject) {
|
|
|
2216
3031
|
}
|
|
2217
3032
|
|
|
2218
3033
|
// src/adapters/openclaw/format/recall-format.ts
|
|
2219
|
-
var MAX_CONTENT_CHARS =
|
|
2220
|
-
function formatAgenrSessionStartRecall(
|
|
2221
|
-
|
|
2222
|
-
if (sections.length === 0) {
|
|
3034
|
+
var MAX_CONTENT_CHARS = 220;
|
|
3035
|
+
function formatAgenrSessionStartRecall(patch) {
|
|
3036
|
+
if (patch.contextSections.length === 0 && patch.durableMemory.length === 0) {
|
|
2223
3037
|
return "";
|
|
2224
3038
|
}
|
|
2225
|
-
const lines = [
|
|
2226
|
-
for (const section of
|
|
2227
|
-
lines.push(
|
|
2228
|
-
|
|
2229
|
-
lines.push(formatEntryHeader(item));
|
|
2230
|
-
lines.push(formatEntryBody(item.entry));
|
|
2231
|
-
}
|
|
3039
|
+
const lines = [];
|
|
3040
|
+
for (const section of patch.contextSections) {
|
|
3041
|
+
lines.push(`## ${section.title}`);
|
|
3042
|
+
lines.push(section.content);
|
|
2232
3043
|
lines.push("");
|
|
2233
3044
|
}
|
|
3045
|
+
const durableSections = buildSections(patch);
|
|
3046
|
+
if (durableSections.length > 0) {
|
|
3047
|
+
const recallLines = [
|
|
3048
|
+
"## Agenr Session Recall",
|
|
3049
|
+
"Use this as prior context. Confirm anything important if the current conversation conflicts with it.",
|
|
3050
|
+
""
|
|
3051
|
+
];
|
|
3052
|
+
for (const section of durableSections) {
|
|
3053
|
+
recallLines.push(`### ${section.title}`);
|
|
3054
|
+
for (const item of section.entries) {
|
|
3055
|
+
recallLines.push(formatEntryHeader(item));
|
|
3056
|
+
recallLines.push(...formatEntryBodyLines(item));
|
|
3057
|
+
}
|
|
3058
|
+
recallLines.push("");
|
|
3059
|
+
}
|
|
3060
|
+
lines.push(wrapAgenrMemoryContext(recallLines.join("\n").trim()));
|
|
3061
|
+
}
|
|
2234
3062
|
return lines.join("\n").trim();
|
|
2235
3063
|
}
|
|
2236
|
-
function buildSections(
|
|
3064
|
+
function buildSections(patch) {
|
|
2237
3065
|
const sections = [];
|
|
2238
|
-
const coreEntries =
|
|
3066
|
+
const coreEntries = patch.durableMemory.filter((item) => item.sourceKind === "core");
|
|
2239
3067
|
if (coreEntries.length > 0) {
|
|
2240
3068
|
sections.push({ title: "Core Memory", entries: coreEntries });
|
|
2241
3069
|
}
|
|
3070
|
+
const artifactRecallEntries = patch.durableMemory.filter((item) => item.sourceKind === "artifact_recall");
|
|
3071
|
+
if (artifactRecallEntries.length > 0) {
|
|
3072
|
+
sections.push({ title: "Relevant Durable Memory", entries: artifactRecallEntries });
|
|
3073
|
+
}
|
|
2242
3074
|
return sections;
|
|
2243
3075
|
}
|
|
2244
3076
|
function formatEntryHeader(item) {
|
|
2245
3077
|
const metadata = [
|
|
3078
|
+
`rank ${item.rank}`,
|
|
2246
3079
|
item.entry.id,
|
|
2247
3080
|
item.entry.type,
|
|
2248
3081
|
item.entry.expiry,
|
|
@@ -2251,18 +3084,20 @@ function formatEntryHeader(item) {
|
|
|
2251
3084
|
].filter((value) => value !== void 0);
|
|
2252
3085
|
return `- [${metadata.join(" | ")}] ${item.entry.subject}`;
|
|
2253
3086
|
}
|
|
2254
|
-
function
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
entry.
|
|
3087
|
+
function formatEntryBodyLines(item) {
|
|
3088
|
+
const lines = [` ${truncate3(item.entry.content.trim(), MAX_CONTENT_CHARS)}`];
|
|
3089
|
+
lines.push(` why: ${item.whySurfaced.summary}`);
|
|
3090
|
+
const metadata = [
|
|
3091
|
+
item.entry.tags.length > 0 ? `tags: ${item.entry.tags.join(", ")}` : void 0,
|
|
3092
|
+
item.freshnessLabel ? `freshness: ${item.freshnessLabel}` : void 0,
|
|
3093
|
+
item.provenanceSummary ? `provenance: ${truncate3(item.provenanceSummary, MAX_CONTENT_CHARS)}` : void 0
|
|
2259
3094
|
].filter((value) => value !== void 0);
|
|
2260
|
-
if (
|
|
2261
|
-
|
|
3095
|
+
if (metadata.length > 0) {
|
|
3096
|
+
lines.push(` ${metadata.join(" | ")}`);
|
|
2262
3097
|
}
|
|
2263
|
-
return
|
|
3098
|
+
return lines;
|
|
2264
3099
|
}
|
|
2265
|
-
function
|
|
3100
|
+
function truncate3(value, maxChars) {
|
|
2266
3101
|
if (value.length <= maxChars) {
|
|
2267
3102
|
return value;
|
|
2268
3103
|
}
|
|
@@ -2275,14 +3110,14 @@ import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary3
|
|
|
2275
3110
|
|
|
2276
3111
|
// src/adapters/openclaw/session/continuity/continuity-summary-reader.ts
|
|
2277
3112
|
import * as fs2 from "fs/promises";
|
|
2278
|
-
import
|
|
3113
|
+
import path3 from "path";
|
|
2279
3114
|
function resolveOpenClawContinuitySummaryPath(sessionFile, logger) {
|
|
2280
3115
|
const normalizedSessionFile = sessionFile.trim();
|
|
2281
3116
|
const sessionId = deriveOpenClawSessionIdFromFilePath(normalizedSessionFile, logger);
|
|
2282
3117
|
if (!sessionId) {
|
|
2283
3118
|
return void 0;
|
|
2284
3119
|
}
|
|
2285
|
-
const continuitySummaryPath =
|
|
3120
|
+
const continuitySummaryPath = path3.join(path3.dirname(normalizedSessionFile), `${sessionId}.continuity-summary.md`);
|
|
2286
3121
|
debugLog(logger, "continuity-summary-reader", `resolved continuity summary path for session=${sessionId}: ${continuitySummaryPath}`);
|
|
2287
3122
|
return continuitySummaryPath;
|
|
2288
3123
|
}
|
|
@@ -2476,10 +3311,10 @@ async function generateAndWriteOpenClawContinuitySummary(params) {
|
|
|
2476
3311
|
durationMs
|
|
2477
3312
|
};
|
|
2478
3313
|
}
|
|
2479
|
-
debugLog2(params.logger, "continuity-summary", `continuity summary generation error for file=${sessionFile}: ${
|
|
3314
|
+
debugLog2(params.logger, "continuity-summary", `continuity summary generation error for file=${sessionFile}: ${formatErrorMessage4(error)}`);
|
|
2480
3315
|
return {
|
|
2481
3316
|
status: "failed",
|
|
2482
|
-
reason:
|
|
3317
|
+
reason: formatErrorMessage4(error),
|
|
2483
3318
|
continuitySummaryPath,
|
|
2484
3319
|
messageCount: cleanedMessages.length,
|
|
2485
3320
|
transcriptChars: normalizedTranscript.length,
|
|
@@ -2498,7 +3333,7 @@ function normalizeContinuitySummary(value) {
|
|
|
2498
3333
|
const trimmed = value.trim();
|
|
2499
3334
|
return trimmed.replace(/^# .+\n+/u, "").trim();
|
|
2500
3335
|
}
|
|
2501
|
-
function
|
|
3336
|
+
function formatErrorMessage4(error) {
|
|
2502
3337
|
return error instanceof Error ? error.message : String(error);
|
|
2503
3338
|
}
|
|
2504
3339
|
function resolveOpenClawSummaryModelRef(openClaw, agentId, modelOverride) {
|
|
@@ -2538,7 +3373,7 @@ function trimToBoundary(value, fromStart) {
|
|
|
2538
3373
|
|
|
2539
3374
|
// src/adapters/openclaw/session/continuity/predecessor-resolver.ts
|
|
2540
3375
|
import * as fs4 from "fs/promises";
|
|
2541
|
-
import
|
|
3376
|
+
import path4 from "path";
|
|
2542
3377
|
|
|
2543
3378
|
// src/adapters/openclaw/session/session-key-parser.ts
|
|
2544
3379
|
var AGENT_SESSION_KEY_PATTERN = /^agent:([^:]+):(.+)$/i;
|
|
@@ -2761,15 +3596,15 @@ async function findResumedFromTranscriptCandidates(sessionsDir, sessionId, logge
|
|
|
2761
3596
|
}
|
|
2762
3597
|
const fileName = entry.name.trim();
|
|
2763
3598
|
if (fileName === prefix) {
|
|
2764
|
-
liveMatches.push(
|
|
3599
|
+
liveMatches.push(path4.join(sessionsDir, fileName));
|
|
2765
3600
|
continue;
|
|
2766
3601
|
}
|
|
2767
3602
|
if (fileName.startsWith(`${prefix}.reset.`)) {
|
|
2768
|
-
resetMatches.push(
|
|
3603
|
+
resetMatches.push(path4.join(sessionsDir, fileName));
|
|
2769
3604
|
continue;
|
|
2770
3605
|
}
|
|
2771
3606
|
if (fileName.startsWith(`${prefix}.deleted.`)) {
|
|
2772
|
-
deletedMatches.push(
|
|
3607
|
+
deletedMatches.push(path4.join(sessionsDir, fileName));
|
|
2773
3608
|
}
|
|
2774
3609
|
}
|
|
2775
3610
|
const ordered = [
|
|
@@ -2945,7 +3780,7 @@ function resolveOpenClawSessionsDirectory(ctx, parsedAgentId, resolveStateDir) {
|
|
|
2945
3780
|
if (!agentId) {
|
|
2946
3781
|
return void 0;
|
|
2947
3782
|
}
|
|
2948
|
-
return
|
|
3783
|
+
return path4.join(resolveStateDir(process.env), "agents", agentId, "sessions");
|
|
2949
3784
|
}
|
|
2950
3785
|
function isSameTuiLane(currentStableLane, candidateStableLane) {
|
|
2951
3786
|
if (!currentStableLane || !candidateStableLane) {
|
|
@@ -2957,7 +3792,7 @@ function isSameTuiLane(currentStableLane, candidateStableLane) {
|
|
|
2957
3792
|
return currentStableLane === candidateStableLane;
|
|
2958
3793
|
}
|
|
2959
3794
|
function compareArchivePathsDescending(left, right) {
|
|
2960
|
-
return
|
|
3795
|
+
return path4.basename(right).localeCompare(path4.basename(left));
|
|
2961
3796
|
}
|
|
2962
3797
|
function debugLog3(logger, subsystem, message) {
|
|
2963
3798
|
logger?.debug?.(`[agenr] ${subsystem}: ${message}`);
|
|
@@ -2983,19 +3818,40 @@ function formatSessionContext2(sessionId, sessionKey) {
|
|
|
2983
3818
|
// src/adapters/openclaw/session/continuity/recent-session.ts
|
|
2984
3819
|
var RECENT_SESSION_MESSAGE_LIMIT = 6;
|
|
2985
3820
|
var RECENT_SESSION_MAX_CHARS = 1800;
|
|
3821
|
+
var SESSION_START_SECTION_HEADINGS = [
|
|
3822
|
+
"## Previous session summary",
|
|
3823
|
+
"## Recent session",
|
|
3824
|
+
"## Agenr Session Recall",
|
|
3825
|
+
"### Core Memory",
|
|
3826
|
+
"### Relevant Durable Memory",
|
|
3827
|
+
"## Agenr Before-Turn Recall",
|
|
3828
|
+
"### Suggested Procedure"
|
|
3829
|
+
];
|
|
3830
|
+
var INLINE_METADATA_SENTINELS = [
|
|
3831
|
+
"Sender (untrusted metadata):",
|
|
3832
|
+
"Conversation info (untrusted metadata):",
|
|
3833
|
+
"Thread starter (untrusted, for context):",
|
|
3834
|
+
"Replied message (untrusted, for context):",
|
|
3835
|
+
"Forwarded message context (untrusted metadata):",
|
|
3836
|
+
"Chat history since last reply (untrusted, for context):"
|
|
3837
|
+
];
|
|
2986
3838
|
async function renderRecentSessionSection(sessionFile, logger) {
|
|
2987
3839
|
try {
|
|
2988
3840
|
const transcript = await openClawTranscriptParser.parseFile(sessionFile);
|
|
2989
|
-
const
|
|
2990
|
-
|
|
3841
|
+
const sanitizedMessages = transcript.messages.map((message) => ({
|
|
3842
|
+
prefix: message.role === "user" ? "U" : "A",
|
|
3843
|
+
text: sanitizeRecentSessionMessage(message.text, message.role)
|
|
3844
|
+
})).filter((message) => message.text.length > 0);
|
|
3845
|
+
const tail = sanitizedMessages.slice(-RECENT_SESSION_MESSAGE_LIMIT);
|
|
3846
|
+
const body = capRecentSession(tail.map((message) => `${message.prefix}: ${message.text}`).join("\n"), RECENT_SESSION_MAX_CHARS);
|
|
2991
3847
|
logger.debug?.(`[agenr] before_prompt_build: recent session tail for file=${sessionFile}: messages=${tail.length} chars=${body.length}`);
|
|
2992
3848
|
return body;
|
|
2993
3849
|
} catch (error) {
|
|
2994
|
-
logger.debug?.(`[agenr] before_prompt_build: failed to build recent session tail for file=${sessionFile}: ${
|
|
3850
|
+
logger.debug?.(`[agenr] before_prompt_build: failed to build recent session tail for file=${sessionFile}: ${formatErrorMessage5(error)}`);
|
|
2995
3851
|
return "";
|
|
2996
3852
|
}
|
|
2997
3853
|
}
|
|
2998
|
-
function
|
|
3854
|
+
function formatErrorMessage5(error) {
|
|
2999
3855
|
return error instanceof Error ? error.message : String(error);
|
|
3000
3856
|
}
|
|
3001
3857
|
function capRecentSession(value, maxChars) {
|
|
@@ -3005,6 +3861,53 @@ function capRecentSession(value, maxChars) {
|
|
|
3005
3861
|
const marker = "[...truncated earlier recent session...]\n";
|
|
3006
3862
|
return `${marker}${value.slice(-(maxChars - marker.length)).trimStart()}`;
|
|
3007
3863
|
}
|
|
3864
|
+
function sanitizeRecentSessionMessage(text, role) {
|
|
3865
|
+
const wrapperDetected = containsAgenrMemoryContext(text) || SESSION_START_SECTION_HEADINGS.some((heading) => text.includes(heading));
|
|
3866
|
+
let cleaned = stripAgenrMemoryContext(text);
|
|
3867
|
+
for (const heading of SESSION_START_SECTION_HEADINGS) {
|
|
3868
|
+
cleaned = cleaned.split(heading).join(" ");
|
|
3869
|
+
}
|
|
3870
|
+
cleaned = stripInlineMetadata(cleaned);
|
|
3871
|
+
cleaned = collapseWhitespace(cleaned);
|
|
3872
|
+
if (wrapperDetected) {
|
|
3873
|
+
cleaned = unwrapEmbeddedTranscriptTurn(cleaned, role);
|
|
3874
|
+
}
|
|
3875
|
+
return collapseWhitespace(cleaned);
|
|
3876
|
+
}
|
|
3877
|
+
function stripInlineMetadata(text) {
|
|
3878
|
+
let cleaned = text;
|
|
3879
|
+
for (const sentinel of INLINE_METADATA_SENTINELS) {
|
|
3880
|
+
const escapedSentinel = escapeForRegExp(sentinel);
|
|
3881
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:json\\s*)?\\{[\\s\\S]*?\\}`, "gu"), " ");
|
|
3882
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
|
|
3883
|
+
]*`, "gu"), " ");
|
|
3884
|
+
}
|
|
3885
|
+
cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
|
|
3886
|
+
return cleaned;
|
|
3887
|
+
}
|
|
3888
|
+
function unwrapEmbeddedTranscriptTurn(text, role) {
|
|
3889
|
+
if (role === "user") {
|
|
3890
|
+
const timestampMatches = [...text.matchAll(/\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]/gu)];
|
|
3891
|
+
if (timestampMatches.length > 1) {
|
|
3892
|
+
const lastTimestamp = timestampMatches.at(-1);
|
|
3893
|
+
if (lastTimestamp?.index !== void 0) {
|
|
3894
|
+
return text.slice(lastTimestamp.index).trim();
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
const marker = role === "user" ? "U:" : "A:";
|
|
3899
|
+
const lastMarkerIndex = text.lastIndexOf(marker);
|
|
3900
|
+
if (lastMarkerIndex < 0) {
|
|
3901
|
+
return text;
|
|
3902
|
+
}
|
|
3903
|
+
return text.slice(lastMarkerIndex + marker.length).trim();
|
|
3904
|
+
}
|
|
3905
|
+
function escapeForRegExp(value) {
|
|
3906
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
3907
|
+
}
|
|
3908
|
+
function collapseWhitespace(value) {
|
|
3909
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
3910
|
+
}
|
|
3008
3911
|
|
|
3009
3912
|
// src/adapters/openclaw/session/continuity/index.ts
|
|
3010
3913
|
var READ_TIME_CONTINUITY_SUMMARY_TIMEOUT_MS = 35e3;
|
|
@@ -3136,9 +4039,32 @@ async function awaitWithTimeout(promise, timeoutMs) {
|
|
|
3136
4039
|
}
|
|
3137
4040
|
|
|
3138
4041
|
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
3139
|
-
var
|
|
4042
|
+
var DEFAULT_SESSION_START_POLICY = {
|
|
4043
|
+
maxCoreEntries: 4,
|
|
4044
|
+
maxArtifactRecallEntries: 3,
|
|
4045
|
+
maxDurableEntries: 5,
|
|
4046
|
+
maxArtifactChars: 1200
|
|
4047
|
+
};
|
|
4048
|
+
var DEFAULT_BEFORE_TURN_POLICY = {
|
|
4049
|
+
maxDurableEntries: 1,
|
|
4050
|
+
maxHighConfidenceDurableEntries: 2,
|
|
4051
|
+
maxRecentTurns: 2,
|
|
4052
|
+
maxQueryChars: 450,
|
|
4053
|
+
maxProcedureCandidates: 3,
|
|
4054
|
+
recallThreshold: 0.6,
|
|
4055
|
+
highConfidenceRecallThreshold: 0.85,
|
|
4056
|
+
procedureThreshold: 0.72
|
|
4057
|
+
};
|
|
3140
4058
|
var NON_USER_TRIGGER_SET = /* @__PURE__ */ new Set(["heartbeat", "cron", "memory"]);
|
|
3141
4059
|
var DEFAULT_STORE_NUDGE_CONFIG = resolveStoreNudgeConfig(void 0);
|
|
4060
|
+
var INLINE_METADATA_SENTINELS2 = [
|
|
4061
|
+
"Sender (untrusted metadata):",
|
|
4062
|
+
"Conversation info (untrusted metadata):",
|
|
4063
|
+
"Thread starter (untrusted, for context):",
|
|
4064
|
+
"Replied message (untrusted, for context):",
|
|
4065
|
+
"Forwarded message context (untrusted metadata):",
|
|
4066
|
+
"Chat history since last reply (untrusted, for context):"
|
|
4067
|
+
];
|
|
3142
4068
|
async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
3143
4069
|
const sessionContext = formatSessionContext(ctx.sessionId, ctx.sessionKey);
|
|
3144
4070
|
const trackerState = params.tracker.consume(ctx.sessionId, ctx.sessionKey);
|
|
@@ -3146,7 +4072,7 @@ async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
|
3146
4072
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker duplicate blocked for ${sessionContext}`);
|
|
3147
4073
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker active count=${trackerState.activeCount}`);
|
|
3148
4074
|
params.logger.info(`[agenr] session-start recall skipped (already ran) for ${sessionContext}`);
|
|
3149
|
-
return
|
|
4075
|
+
return await resolveNonFirstTurnResult(event, ctx, sessionContext, params);
|
|
3150
4076
|
}
|
|
3151
4077
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker first start for ${sessionContext}`);
|
|
3152
4078
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker active count=${trackerState.activeCount}`);
|
|
@@ -3154,24 +4080,52 @@ async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
|
3154
4080
|
try {
|
|
3155
4081
|
const services = await params.servicesPromise;
|
|
3156
4082
|
const continuity = await resolvePredecessorContinuity(ctx, params.tracker, services, params.logger);
|
|
4083
|
+
emitContinuityEvent(services.debugSink, ctx, continuity);
|
|
3157
4084
|
void writeOpenClawPredecessorEpisode({
|
|
3158
4085
|
ctx,
|
|
3159
4086
|
predecessor: continuity.predecessor,
|
|
3160
4087
|
services,
|
|
3161
4088
|
logger: params.logger
|
|
3162
4089
|
});
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
const prependContext =
|
|
3173
|
-
|
|
3174
|
-
|
|
4090
|
+
const sessionStartPatch = await runSessionStart(
|
|
4091
|
+
{
|
|
4092
|
+
sessionKey: ctx.sessionKey,
|
|
4093
|
+
continuitySummaryText: continuity.continuitySummaryContent,
|
|
4094
|
+
recentSessionText: continuity.recentSessionContent,
|
|
4095
|
+
policy: resolveSessionStartPolicy(services)
|
|
4096
|
+
},
|
|
4097
|
+
services.sessionStart
|
|
4098
|
+
);
|
|
4099
|
+
const prependContext = formatAgenrSessionStartRecall(sessionStartPatch);
|
|
4100
|
+
if (services.debugSink.enabled) {
|
|
4101
|
+
void services.debugSink.emit({
|
|
4102
|
+
type: "session_start_recall",
|
|
4103
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
4104
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
4105
|
+
debug: {
|
|
4106
|
+
durableMemoryCount: sessionStartPatch.durableMemory.length,
|
|
4107
|
+
selectedEntryIds: sessionStartPatch.durableMemory.map((item) => item.entry.id),
|
|
4108
|
+
coreCandidateCount: sessionStartPatch.diagnostics.coreCandidateCount,
|
|
4109
|
+
artifactRecallCandidateCount: sessionStartPatch.diagnostics.artifactRecallCandidateCount,
|
|
4110
|
+
artifactRecallUsed: sessionStartPatch.diagnostics.artifactRecallUsed,
|
|
4111
|
+
notices: [...sessionStartPatch.diagnostics.notices]
|
|
4112
|
+
}
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
params.logger.info(
|
|
4116
|
+
`[agenr] session-start recall: ${sessionStartPatch.durableMemory.length} durable entries for ${sessionContext} (core_candidates=${sessionStartPatch.diagnostics.coreCandidateCount} artifact_candidates=${sessionStartPatch.diagnostics.artifactRecallCandidateCount})`
|
|
4117
|
+
);
|
|
4118
|
+
if (sessionStartPatch.diagnostics.artifactRecallUsed) {
|
|
4119
|
+
params.logger.debug?.(
|
|
4120
|
+
`[agenr] before_prompt_build: session-start artifact recall for ${sessionContext} query_length=${sessionStartPatch.diagnostics.artifactRecallQuery?.length ?? 0} notices=${sessionStartPatch.diagnostics.notices.length}`
|
|
4121
|
+
);
|
|
4122
|
+
}
|
|
4123
|
+
if (sessionStartPatch.diagnostics.notices.length > 0) {
|
|
4124
|
+
params.logger.info(`[agenr] session-start recall notices for ${sessionContext}: ${sessionStartPatch.diagnostics.notices.join(" | ")}`);
|
|
4125
|
+
}
|
|
4126
|
+
params.logger.debug?.(
|
|
4127
|
+
`[agenr] before_prompt_build: session-start durable entries for ${sessionContext}: ${formatEntryRefs(sessionStartPatch.durableMemory.map((item) => item.entry))}`
|
|
4128
|
+
);
|
|
3175
4129
|
params.logger.debug?.(`[agenr] before_prompt_build: session-start prependContext length for ${sessionContext}: ${prependContext.length} chars`);
|
|
3176
4130
|
if (prependContext.length === 0) {
|
|
3177
4131
|
params.logger.info(`[agenr] session-start recall: nothing to inject for ${sessionContext}`);
|
|
@@ -3180,6 +4134,131 @@ ${continuity.recentSessionContent}`,
|
|
|
3180
4134
|
return { prependContext };
|
|
3181
4135
|
} catch (error) {
|
|
3182
4136
|
params.logger.warn(`[agenr] session-start recall failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
|
|
4137
|
+
try {
|
|
4138
|
+
const services = await params.servicesPromise;
|
|
4139
|
+
if (services.debugSink.enabled) {
|
|
4140
|
+
void services.debugSink.emit({
|
|
4141
|
+
type: "error",
|
|
4142
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
4143
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
4144
|
+
scope: "session_start_recall",
|
|
4145
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
} catch {
|
|
4149
|
+
}
|
|
4150
|
+
return void 0;
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
function emitContinuityEvent(sink, ctx, continuity) {
|
|
4154
|
+
if (!sink.enabled) {
|
|
4155
|
+
return;
|
|
4156
|
+
}
|
|
4157
|
+
void sink.emit({
|
|
4158
|
+
type: "continuity_resolution",
|
|
4159
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
4160
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
4161
|
+
summary: {
|
|
4162
|
+
predecessorFound: Boolean(continuity.predecessor),
|
|
4163
|
+
...continuity.predecessor ? { predecessorFileBasename: path5.basename(continuity.predecessor.sessionFile) } : {},
|
|
4164
|
+
hasContinuitySummary: continuity.continuitySummaryContent.length > 0,
|
|
4165
|
+
hasRecentSession: continuity.recentSessionContent.length > 0,
|
|
4166
|
+
continuitySummaryChars: continuity.continuitySummaryContent.length,
|
|
4167
|
+
recentSessionChars: continuity.recentSessionContent.length
|
|
4168
|
+
}
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
4171
|
+
async function resolveNonFirstTurnResult(event, ctx, sessionContext, params) {
|
|
4172
|
+
const beforeTurnResult = await resolveBeforeTurnResult(event, ctx, sessionContext, params);
|
|
4173
|
+
if (beforeTurnResult) {
|
|
4174
|
+
return beforeTurnResult;
|
|
4175
|
+
}
|
|
4176
|
+
return resolveStoreNudgeResult(event, ctx, sessionContext, params);
|
|
4177
|
+
}
|
|
4178
|
+
async function resolveBeforeTurnResult(event, ctx, sessionContext, params) {
|
|
4179
|
+
const normalizedTrigger = ctx.trigger?.trim().toLowerCase();
|
|
4180
|
+
if (normalizedTrigger && NON_USER_TRIGGER_SET.has(normalizedTrigger)) {
|
|
4181
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=non_user_trigger trigger=${normalizedTrigger}`);
|
|
4182
|
+
return void 0;
|
|
4183
|
+
}
|
|
4184
|
+
const services = await params.servicesPromise;
|
|
4185
|
+
if (services.pluginConfig.memoryPolicy?.beforeTurn?.enabled === false) {
|
|
4186
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=disabled`);
|
|
4187
|
+
return void 0;
|
|
4188
|
+
}
|
|
4189
|
+
const currentTurnText = normalizePromptText(event.prompt);
|
|
4190
|
+
if (!currentTurnText) {
|
|
4191
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=empty_prompt`);
|
|
4192
|
+
return void 0;
|
|
4193
|
+
}
|
|
4194
|
+
try {
|
|
4195
|
+
const beforeTurnPatch = await runBeforeTurn(
|
|
4196
|
+
{
|
|
4197
|
+
sessionKey: ctx.sessionKey,
|
|
4198
|
+
currentTurnText,
|
|
4199
|
+
recentTurns: extractRecentTurns(event.messages),
|
|
4200
|
+
trigger: ctx.trigger,
|
|
4201
|
+
policy: resolveBeforeTurnPolicy(services)
|
|
4202
|
+
},
|
|
4203
|
+
services.beforeTurn
|
|
4204
|
+
);
|
|
4205
|
+
const prependContext = formatAgenrBeforeTurnRecall(beforeTurnPatch);
|
|
4206
|
+
if (services.debugSink.enabled) {
|
|
4207
|
+
void services.debugSink.emit({
|
|
4208
|
+
type: "before_turn_decision",
|
|
4209
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
4210
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
4211
|
+
debug: buildLiveBeforeTurnDebugArtifact({
|
|
4212
|
+
caseId: `live-${randomUUID2()}`,
|
|
4213
|
+
patch: beforeTurnPatch,
|
|
4214
|
+
currentTurnText,
|
|
4215
|
+
trigger: ctx.trigger,
|
|
4216
|
+
eventLevel: services.debugSink.eventLevel,
|
|
4217
|
+
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
4218
|
+
})
|
|
4219
|
+
});
|
|
4220
|
+
}
|
|
4221
|
+
params.logger.info(
|
|
4222
|
+
`[agenr] before-turn recall: ${beforeTurnPatch.durableMemory.length} durable entries for ${sessionContext} (durable_candidates=${beforeTurnPatch.diagnostics.durableRecallCandidateCount} procedure_candidates=${beforeTurnPatch.diagnostics.procedureCandidateCount})`
|
|
4223
|
+
);
|
|
4224
|
+
if (beforeTurnPatch.procedure) {
|
|
4225
|
+
params.logger.info(
|
|
4226
|
+
`[agenr] before-turn procedure suggestion for ${sessionContext}: ${beforeTurnPatch.procedure.procedure.procedure_key} score=${beforeTurnPatch.procedure.score.toFixed(2)}`
|
|
4227
|
+
);
|
|
4228
|
+
}
|
|
4229
|
+
if (beforeTurnPatch.diagnostics.notices.length > 0) {
|
|
4230
|
+
params.logger.info(`[agenr] before-turn recall notices for ${sessionContext}: ${beforeTurnPatch.diagnostics.notices.join(" | ")}`);
|
|
4231
|
+
}
|
|
4232
|
+
if (beforeTurnPatch.diagnostics.abstained) {
|
|
4233
|
+
params.logger.debug?.(
|
|
4234
|
+
`[agenr] before_prompt_build: before-turn abstained for ${sessionContext}: category=${beforeTurnPatch.diagnostics.suppressedTurnCategory ?? "none"} signals=${beforeTurnPatch.diagnostics.turnSignalLabels.join(",") || "none"} reasons=${beforeTurnPatch.diagnostics.abstentionReasons.join(" | ") || "none"}`
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4237
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn diagnostics for ${sessionContext}: ${formatBeforeTurnDiagnosticsForLog(beforeTurnPatch)}`);
|
|
4238
|
+
params.logger.debug?.(
|
|
4239
|
+
`[agenr] before_prompt_build: before-turn durable entries for ${sessionContext}: ${formatEntryRefs(
|
|
4240
|
+
beforeTurnPatch.durableMemory.map((item) => item.entry)
|
|
4241
|
+
)}`
|
|
4242
|
+
);
|
|
4243
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn prependContext length for ${sessionContext}: ${prependContext.length} chars`);
|
|
4244
|
+
if (prependContext.length === 0) {
|
|
4245
|
+
return void 0;
|
|
4246
|
+
}
|
|
4247
|
+
return { prependContext };
|
|
4248
|
+
} catch (error) {
|
|
4249
|
+
params.logger.warn(`[agenr] before-turn recall failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
|
|
4250
|
+
try {
|
|
4251
|
+
if (services.debugSink.enabled) {
|
|
4252
|
+
void services.debugSink.emit({
|
|
4253
|
+
type: "error",
|
|
4254
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
4255
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
4256
|
+
scope: "before_turn_decision",
|
|
4257
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
4258
|
+
});
|
|
4259
|
+
}
|
|
4260
|
+
} catch {
|
|
4261
|
+
}
|
|
3183
4262
|
return void 0;
|
|
3184
4263
|
}
|
|
3185
4264
|
}
|
|
@@ -3219,12 +4298,182 @@ function resolveStoreNudgeResult(_event, ctx, sessionContext, params) {
|
|
|
3219
4298
|
params.logger.info(`[agenr] store nudge injected for ${sessionContext} ordinal=${state.nudgeCount} turn=${state.turnCount} gap=${gapSinceSuccessfulStore}`);
|
|
3220
4299
|
return { prependContext };
|
|
3221
4300
|
}
|
|
3222
|
-
async function runAgenrSessionStartRecall(services) {
|
|
3223
|
-
return { core: await services.memory.listCoreEntries(CORE_ENTRY_LIMIT) };
|
|
3224
|
-
}
|
|
3225
4301
|
function formatEntryRefs(entries) {
|
|
3226
4302
|
return entries.length === 0 ? "none" : entries.map((entry) => `${entry.subject} [${entry.id}]`).join(", ");
|
|
3227
4303
|
}
|
|
4304
|
+
function formatBeforeTurnDiagnosticsForLog(patch) {
|
|
4305
|
+
return JSON.stringify({
|
|
4306
|
+
query: truncateForLog(patch.diagnostics.query, 160),
|
|
4307
|
+
queryPolicy: patch.diagnostics.queryPolicy,
|
|
4308
|
+
queryVariants: patch.diagnostics.queryVariants.map((variant) => ({
|
|
4309
|
+
kind: variant.kind,
|
|
4310
|
+
query: truncateForLog(variant.query, 120),
|
|
4311
|
+
candidateCount: variant.candidateCount,
|
|
4312
|
+
selected: variant.selected
|
|
4313
|
+
})),
|
|
4314
|
+
turnSignalLabels: patch.diagnostics.turnSignalLabels,
|
|
4315
|
+
suppressedTurnCategory: patch.diagnostics.suppressedTurnCategory,
|
|
4316
|
+
durableRecallCandidateCount: patch.diagnostics.durableRecallCandidateCount,
|
|
4317
|
+
procedureCandidateCount: patch.diagnostics.procedureCandidateCount,
|
|
4318
|
+
directness: patch.diagnostics.directness ? {
|
|
4319
|
+
queryKind: patch.diagnostics.directness.queryKind,
|
|
4320
|
+
entity: patch.diagnostics.directness.entity,
|
|
4321
|
+
decision: patch.diagnostics.directness.decision,
|
|
4322
|
+
winnerEntryId: patch.diagnostics.directness.winnerEntryId,
|
|
4323
|
+
runnerUpEntryId: patch.diagnostics.directness.runnerUpEntryId,
|
|
4324
|
+
winnerGap: patch.diagnostics.directness.winnerGap,
|
|
4325
|
+
reason: truncateForLog(patch.diagnostics.directness.reason, 180),
|
|
4326
|
+
candidates: patch.diagnostics.directness.candidates.map((candidate) => ({
|
|
4327
|
+
entryId: candidate.entryId,
|
|
4328
|
+
baseRank: candidate.baseRank,
|
|
4329
|
+
baseScore: candidate.baseScore,
|
|
4330
|
+
directnessDelta: candidate.directnessDelta,
|
|
4331
|
+
adjustedScore: candidate.adjustedScore,
|
|
4332
|
+
signals: candidate.signals
|
|
4333
|
+
}))
|
|
4334
|
+
} : void 0,
|
|
4335
|
+
abstained: patch.diagnostics.abstained,
|
|
4336
|
+
abstentionReasons: patch.diagnostics.abstentionReasons.map((reason) => truncateForLog(reason, 180)),
|
|
4337
|
+
notices: patch.diagnostics.notices.map((notice) => truncateForLog(notice, 180)),
|
|
4338
|
+
selectedEntries: patch.durableMemory.map((item) => ({
|
|
4339
|
+
id: item.entry.id,
|
|
4340
|
+
subject: truncateForLog(item.entry.subject, 80),
|
|
4341
|
+
score: Number(item.score.toFixed(3))
|
|
4342
|
+
})),
|
|
4343
|
+
procedure: patch.procedure ? {
|
|
4344
|
+
procedureKey: patch.procedure.procedure.procedure_key,
|
|
4345
|
+
score: Number(patch.procedure.score.toFixed(3))
|
|
4346
|
+
} : void 0
|
|
4347
|
+
});
|
|
4348
|
+
}
|
|
4349
|
+
function truncateForLog(value, maxChars) {
|
|
4350
|
+
if (typeof value !== "string") {
|
|
4351
|
+
return void 0;
|
|
4352
|
+
}
|
|
4353
|
+
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
4354
|
+
if (normalized.length === 0) {
|
|
4355
|
+
return void 0;
|
|
4356
|
+
}
|
|
4357
|
+
return normalized.length <= maxChars ? normalized : `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
4358
|
+
}
|
|
4359
|
+
function resolveSessionStartPolicy(services) {
|
|
4360
|
+
return {
|
|
4361
|
+
...DEFAULT_SESSION_START_POLICY,
|
|
4362
|
+
enableArtifactRecall: services.pluginConfig.memoryPolicy?.sessionStart?.relevantDurableMemory !== false
|
|
4363
|
+
};
|
|
4364
|
+
}
|
|
4365
|
+
function resolveBeforeTurnPolicy(services) {
|
|
4366
|
+
return {
|
|
4367
|
+
...DEFAULT_BEFORE_TURN_POLICY,
|
|
4368
|
+
enableProcedureSuggestion: services.pluginConfig.memoryPolicy?.beforeTurn?.procedureSuggestion !== false,
|
|
4369
|
+
...services.pluginConfig.memoryPolicy?.beforeTurn?.maxDurableEntries !== void 0 ? { maxDurableEntries: services.pluginConfig.memoryPolicy.beforeTurn.maxDurableEntries } : {},
|
|
4370
|
+
...services.pluginConfig.memoryPolicy?.beforeTurn?.recallThreshold !== void 0 ? { recallThreshold: services.pluginConfig.memoryPolicy.beforeTurn.recallThreshold } : {},
|
|
4371
|
+
...services.pluginConfig.memoryPolicy?.beforeTurn?.highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold: services.pluginConfig.memoryPolicy.beforeTurn.highConfidenceRecallThreshold } : {},
|
|
4372
|
+
...services.pluginConfig.memoryPolicy?.beforeTurn?.procedureThreshold !== void 0 ? { procedureThreshold: services.pluginConfig.memoryPolicy.beforeTurn.procedureThreshold } : {}
|
|
4373
|
+
};
|
|
4374
|
+
}
|
|
4375
|
+
function extractRecentTurns(messages) {
|
|
4376
|
+
const turns = [];
|
|
4377
|
+
for (const message of messages) {
|
|
4378
|
+
if (!message || typeof message !== "object") {
|
|
4379
|
+
continue;
|
|
4380
|
+
}
|
|
4381
|
+
const typed = message;
|
|
4382
|
+
const role = typed.role === "user" || typed.role === "assistant" ? typed.role : void 0;
|
|
4383
|
+
if (!role) {
|
|
4384
|
+
continue;
|
|
4385
|
+
}
|
|
4386
|
+
const text = sanitizeRecentTurnText(extractMessageText(typed.content), role);
|
|
4387
|
+
if (!text) {
|
|
4388
|
+
continue;
|
|
4389
|
+
}
|
|
4390
|
+
turns.push({ role, text });
|
|
4391
|
+
}
|
|
4392
|
+
return turns;
|
|
4393
|
+
}
|
|
4394
|
+
function extractMessageText(content) {
|
|
4395
|
+
if (typeof content === "string") {
|
|
4396
|
+
return content;
|
|
4397
|
+
}
|
|
4398
|
+
if (!Array.isArray(content)) {
|
|
4399
|
+
return "";
|
|
4400
|
+
}
|
|
4401
|
+
const blocks = [];
|
|
4402
|
+
for (const block of content) {
|
|
4403
|
+
if (typeof block === "string") {
|
|
4404
|
+
blocks.push(block);
|
|
4405
|
+
continue;
|
|
4406
|
+
}
|
|
4407
|
+
if (!block || typeof block !== "object") {
|
|
4408
|
+
continue;
|
|
4409
|
+
}
|
|
4410
|
+
const typed = block;
|
|
4411
|
+
if (typeof typed.text === "string") {
|
|
4412
|
+
blocks.push(typed.text);
|
|
4413
|
+
continue;
|
|
4414
|
+
}
|
|
4415
|
+
const type = typeof typed.type === "string" ? typed.type.trim().toLowerCase() : "";
|
|
4416
|
+
if (typeof typed.content === "string" && (type === "text" || type === "input_text" || type === "output_text")) {
|
|
4417
|
+
blocks.push(typed.content);
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
return blocks.join("\n");
|
|
4421
|
+
}
|
|
4422
|
+
function sanitizeRecentTurnText(text, role) {
|
|
4423
|
+
if (!text.trim()) {
|
|
4424
|
+
return "";
|
|
4425
|
+
}
|
|
4426
|
+
const wrapperDetected = containsAgenrMemoryContext(text) || text.includes("## Agenr Session Recall") || text.includes("## Agenr Before-Turn Recall") || text.includes("[MEMORY CHECK]");
|
|
4427
|
+
let cleaned = stripAgenrMemoryContext(text);
|
|
4428
|
+
const headings = [
|
|
4429
|
+
"## Previous session summary",
|
|
4430
|
+
"## Recent session",
|
|
4431
|
+
"## Agenr Session Recall",
|
|
4432
|
+
"### Core Memory",
|
|
4433
|
+
"### Relevant Durable Memory",
|
|
4434
|
+
"## Agenr Before-Turn Recall",
|
|
4435
|
+
"### Suggested Procedure"
|
|
4436
|
+
];
|
|
4437
|
+
for (const heading of headings) {
|
|
4438
|
+
cleaned = cleaned.split(heading).join(" ");
|
|
4439
|
+
}
|
|
4440
|
+
cleaned = cleaned.replace(/\[MEMORY CHECK\][^\n]*/gu, " ");
|
|
4441
|
+
cleaned = collapseWhitespace2(cleaned);
|
|
4442
|
+
if (!wrapperDetected) {
|
|
4443
|
+
return cleaned;
|
|
4444
|
+
}
|
|
4445
|
+
const segments = stripAgenrMemoryContext(text).split(/\n\s*\n/gu).map((segment) => collapseWhitespace2(segment)).filter((segment) => segment.length > 0);
|
|
4446
|
+
const fallbackSegment = segments.at(-1);
|
|
4447
|
+
if (fallbackSegment) {
|
|
4448
|
+
return role === "user" ? fallbackSegment : collapseWhitespace2(cleaned);
|
|
4449
|
+
}
|
|
4450
|
+
return cleaned;
|
|
4451
|
+
}
|
|
4452
|
+
function normalizePromptText(prompt) {
|
|
4453
|
+
let cleaned = stripAgenrMemoryContext(prompt);
|
|
4454
|
+
cleaned = stripInlineMetadata2(cleaned);
|
|
4455
|
+
cleaned = cleaned.replace(/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]\s*/u, "");
|
|
4456
|
+
cleaned = cleaned.replace(/^\s*U:\s*/u, "");
|
|
4457
|
+
cleaned = collapseWhitespace2(cleaned);
|
|
4458
|
+
return cleaned.length > 0 ? cleaned : void 0;
|
|
4459
|
+
}
|
|
4460
|
+
function stripInlineMetadata2(text) {
|
|
4461
|
+
let cleaned = text;
|
|
4462
|
+
for (const sentinel of INLINE_METADATA_SENTINELS2) {
|
|
4463
|
+
const escapedSentinel = escapeForRegExp2(sentinel);
|
|
4464
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:\`\`\`json\\s*)?\\{[\\s\\S]*?\\}(?:\\s*\`\`\`)?`, "gu"), " ");
|
|
4465
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
|
|
4466
|
+
]*`, "gu"), " ");
|
|
4467
|
+
}
|
|
4468
|
+
cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
|
|
4469
|
+
return cleaned;
|
|
4470
|
+
}
|
|
4471
|
+
function escapeForRegExp2(value) {
|
|
4472
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
4473
|
+
}
|
|
4474
|
+
function collapseWhitespace2(value) {
|
|
4475
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
4476
|
+
}
|
|
3228
4477
|
|
|
3229
4478
|
// src/adapters/openclaw/memory/flush-plan.ts
|
|
3230
4479
|
function buildAgenrMemoryFlushPlan(_params, logger) {
|
|
@@ -3303,7 +4552,33 @@ function createAgenrMemoryRuntime(servicesPromise) {
|
|
|
3303
4552
|
};
|
|
3304
4553
|
}
|
|
3305
4554
|
|
|
4555
|
+
// src/adapters/db/session-start-repository.ts
|
|
4556
|
+
function createSessionStartRepository(executor) {
|
|
4557
|
+
return {
|
|
4558
|
+
listCoreEntries: async (limit) => listCoreEntries(executor, limit)
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
async function listCoreEntries(executor, limit) {
|
|
4562
|
+
if (limit <= 0) {
|
|
4563
|
+
return [];
|
|
4564
|
+
}
|
|
4565
|
+
const result = await executor.execute({
|
|
4566
|
+
sql: `
|
|
4567
|
+
SELECT
|
|
4568
|
+
${ENTRY_SELECT_COLUMNS}
|
|
4569
|
+
FROM entries
|
|
4570
|
+
WHERE ${buildActiveEntryClause()}
|
|
4571
|
+
AND expiry = 'core'
|
|
4572
|
+
ORDER BY importance DESC, created_at DESC
|
|
4573
|
+
LIMIT ?
|
|
4574
|
+
`,
|
|
4575
|
+
args: [limit]
|
|
4576
|
+
});
|
|
4577
|
+
return result.rows.map((row) => mapEntryRow(row));
|
|
4578
|
+
}
|
|
4579
|
+
|
|
3306
4580
|
// src/app/openclaw/runtime.ts
|
|
4581
|
+
import path6 from "path";
|
|
3307
4582
|
async function createAgenrOpenClawServices(config, options) {
|
|
3308
4583
|
const { resolvedConfig, agenrConfig: loadedAgenrConfig } = resolveRuntimeConfig(config, options.resolvePath);
|
|
3309
4584
|
const agenrConfig = {
|
|
@@ -3325,10 +4600,13 @@ async function createAgenrOpenClawServices(config, options) {
|
|
|
3325
4600
|
episodes: runtimeServices.episodes,
|
|
3326
4601
|
procedures: runtimeServices.procedures,
|
|
3327
4602
|
memory: runtimeServices.memory,
|
|
4603
|
+
sessionStart: runtimeServices.sessionStart,
|
|
4604
|
+
beforeTurn: runtimeServices.beforeTurn,
|
|
3328
4605
|
embedding: runtimeServices.embedding,
|
|
3329
4606
|
recall: runtimeServices.recall,
|
|
3330
4607
|
claimExtraction: runtimeServices.claimExtraction,
|
|
3331
4608
|
embeddingStatus: toPublicEmbeddingStatus(embeddingStatus),
|
|
4609
|
+
debugSink: runtimeServices.debugSink,
|
|
3332
4610
|
close: runtimeServices.close
|
|
3333
4611
|
};
|
|
3334
4612
|
}
|
|
@@ -3355,7 +4633,11 @@ function resolveEmbeddingStatus(config) {
|
|
|
3355
4633
|
async function createRuntimeServices(dbPath, config, embeddingStatus, openClawContext) {
|
|
3356
4634
|
const database = await createDatabase(dbPath);
|
|
3357
4635
|
const embedding = embeddingStatus.available ? createEmbeddingClient(requireApiKey(embeddingStatus), embeddingStatus.model) : createUnavailableEmbeddingPort(embeddingStatus.error ?? "Embeddings are unavailable.");
|
|
4636
|
+
const baseRecall = createRecallAdapter(database, embedding);
|
|
4637
|
+
const crossEncoder = resolveCrossEncoder(config);
|
|
4638
|
+
const recall2 = attachCrossEncoderPort(baseRecall, crossEncoder);
|
|
3358
4639
|
const claimExtraction = await createClaimExtractionRuntime(config, openClawContext.openClaw, openClawContext.pluginConfig);
|
|
4640
|
+
const debugSink = createDebugSink(openClawContext.openClaw, openClawContext.pluginConfig);
|
|
3359
4641
|
let closed = false;
|
|
3360
4642
|
return {
|
|
3361
4643
|
entries: database,
|
|
@@ -3364,18 +4646,56 @@ async function createRuntimeServices(dbPath, config, embeddingStatus, openClawCo
|
|
|
3364
4646
|
memory: createOpenClawRepository(database, {
|
|
3365
4647
|
claimSlotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
3366
4648
|
}),
|
|
4649
|
+
sessionStart: {
|
|
4650
|
+
repository: createSessionStartRepository(database),
|
|
4651
|
+
recall: recall2,
|
|
4652
|
+
slotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
4653
|
+
},
|
|
4654
|
+
beforeTurn: {
|
|
4655
|
+
recall: recall2,
|
|
4656
|
+
procedures: database,
|
|
4657
|
+
embedQuery: embeddingStatus.available ? async (text) => {
|
|
4658
|
+
const vectors = await embedding.embed([text]);
|
|
4659
|
+
return vectors[0] ?? [];
|
|
4660
|
+
} : void 0,
|
|
4661
|
+
slotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
4662
|
+
},
|
|
3367
4663
|
embedding,
|
|
3368
|
-
recall:
|
|
4664
|
+
recall: recall2,
|
|
3369
4665
|
claimExtraction,
|
|
4666
|
+
debugSink,
|
|
3370
4667
|
async close() {
|
|
3371
4668
|
if (closed) {
|
|
3372
4669
|
return;
|
|
3373
4670
|
}
|
|
3374
4671
|
closed = true;
|
|
4672
|
+
await debugSink.close();
|
|
3375
4673
|
await database.close();
|
|
3376
4674
|
}
|
|
3377
4675
|
};
|
|
3378
4676
|
}
|
|
4677
|
+
function createDebugSink(openClaw, pluginConfig) {
|
|
4678
|
+
const resolved = resolveDebugConfig(pluginConfig.debug);
|
|
4679
|
+
if (!resolved.enabled) {
|
|
4680
|
+
return createNoopAgenrDebugSink();
|
|
4681
|
+
}
|
|
4682
|
+
const withLogPath = ensureDebugLogPath(resolved, openClaw);
|
|
4683
|
+
return createAgenrDebugSink(withLogPath);
|
|
4684
|
+
}
|
|
4685
|
+
function ensureDebugLogPath(resolved, openClaw) {
|
|
4686
|
+
if (resolved.logPath) {
|
|
4687
|
+
return resolved;
|
|
4688
|
+
}
|
|
4689
|
+
try {
|
|
4690
|
+
const stateDir = openClaw.runtime.state.resolveStateDir(process.env);
|
|
4691
|
+
return {
|
|
4692
|
+
...resolved,
|
|
4693
|
+
logPath: path6.join(stateDir, "agenr", "logs", "debug.jsonl")
|
|
4694
|
+
};
|
|
4695
|
+
} catch {
|
|
4696
|
+
return { ...resolved, enabled: false };
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
3379
4699
|
function toPublicEmbeddingStatus(status) {
|
|
3380
4700
|
return {
|
|
3381
4701
|
available: status.available,
|
|
@@ -3425,6 +4745,15 @@ function requireApiKey(status) {
|
|
|
3425
4745
|
}
|
|
3426
4746
|
return status.apiKey;
|
|
3427
4747
|
}
|
|
4748
|
+
function resolveCrossEncoder(config) {
|
|
4749
|
+
try {
|
|
4750
|
+
const apiKey = resolveCrossEncoderApiKey(config);
|
|
4751
|
+
const { modelId } = resolveModel(config, "cross_encoder");
|
|
4752
|
+
return createOpenAICrossEncoder({ apiKey, model: modelId });
|
|
4753
|
+
} catch {
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
3428
4757
|
async function createClaimExtractionRuntime(config, openClaw, pluginConfig) {
|
|
3429
4758
|
const claimExtractionConfig = resolveClaimExtractionConfig(config);
|
|
3430
4759
|
if (!claimExtractionConfig.enabled) {
|