agenr 2.0.1 → 3.0.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/CHANGELOG.md +64 -0
- package/dist/adapters/openclaw/index.d.ts +1 -1
- package/dist/adapters/openclaw/index.js +994 -1420
- package/dist/adapters/skeln/index.d.ts +1951 -0
- package/dist/adapters/skeln/index.js +3899 -0
- package/dist/chunk-575MUIW5.js +1039 -0
- package/dist/{chunk-MEHOGUZE.js → chunk-5LADPJ4C.js} +989 -70
- package/dist/{chunk-Y2BC7RCE.js → chunk-ELR2HSVC.js} +6008 -4618
- package/dist/chunk-GELCEVFA.js +14 -0
- package/dist/{chunk-XD3446YW.js → chunk-LAXNNWHM.js} +3397 -4527
- package/dist/chunk-MYZ2CWY6.js +2738 -0
- package/dist/chunk-P5SB75FK.js +3061 -0
- package/dist/chunk-TBFAARM5.js +1196 -0
- package/dist/claim-slot-policy-CdrW_1l4.d.ts +13 -0
- package/dist/cli.js +292 -613
- package/dist/core/recall/index.d.ts +538 -119
- package/dist/core/recall/index.js +37 -3
- package/dist/internal-eval-server.d.ts +1 -0
- package/dist/internal-eval-server.js +7 -0
- package/dist/internal-recall-eval-server.js +6 -1816
- package/dist/ports-CpzWESmZ.d.ts +925 -0
- package/package.json +9 -6
- package/dist/ports-D2NOK2gR.d.ts +0 -256
|
@@ -1,195 +1,335 @@
|
|
|
1
1
|
import {
|
|
2
2
|
OpenClawTranscriptParser,
|
|
3
|
-
createOpenClawRepository,
|
|
4
3
|
deriveOpenClawSessionIdFromFilePath,
|
|
5
|
-
ingestEpisodeTranscript,
|
|
6
4
|
openClawTranscriptParser,
|
|
7
5
|
parseTuiSessionKey,
|
|
8
|
-
readOpenClawSessionsStore
|
|
9
|
-
|
|
10
|
-
} from "../../chunk-XD3446YW.js";
|
|
6
|
+
readOpenClawSessionsStore
|
|
7
|
+
} from "../../chunk-TBFAARM5.js";
|
|
11
8
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
9
|
+
BEFORE_TURN_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
10
|
+
BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K,
|
|
11
|
+
RECALL_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
12
|
+
RECALL_DEBUG_ARTIFACT_MAX_TOP_K
|
|
13
|
+
} from "../../chunk-GELCEVFA.js";
|
|
14
|
+
import {
|
|
15
|
+
EPISODE_SUMMARY_TIMEOUT_MS,
|
|
16
|
+
RECALL_TOOL_PARAMETERS,
|
|
17
|
+
STORE_TOOL_PARAMETERS,
|
|
18
|
+
UPDATE_TOOL_PARAMETERS,
|
|
19
|
+
asRecord,
|
|
20
|
+
buildClaimExtractionRuntime,
|
|
21
|
+
buildRecallToolServices,
|
|
22
|
+
composeHostPluginServices,
|
|
23
|
+
createDeadlineAwareEpisodeSummaryLlm,
|
|
24
|
+
createSessionStartTracker,
|
|
25
|
+
embedEpisodeSummaryWithinBudget,
|
|
26
|
+
extractRecentTurnsFromMessages,
|
|
27
|
+
formatAgenrSessionStartRecall,
|
|
28
|
+
formatErrorMessage,
|
|
29
|
+
formatTargetSelector,
|
|
30
|
+
formatUnifiedRecallResults,
|
|
31
|
+
normalizeOptionalBoolean,
|
|
32
|
+
normalizeOptionalPositiveInteger,
|
|
33
|
+
normalizePluginInjectionMemoryPolicyConfig,
|
|
34
|
+
normalizePromptText,
|
|
35
|
+
parseRecallToolParams,
|
|
36
|
+
parseStoreToolParams,
|
|
37
|
+
parseUpdateToolParams,
|
|
38
|
+
readBooleanParam,
|
|
39
|
+
resolveBeforeTurnPolicy,
|
|
40
|
+
resolveSessionIdentityKey,
|
|
41
|
+
resolveSessionStartPolicy,
|
|
42
|
+
resolveTargetEntry,
|
|
43
|
+
runRecallMemoryTool,
|
|
44
|
+
runSessionStart,
|
|
45
|
+
runStoreMemoryTool,
|
|
46
|
+
runUpdateMemoryTool,
|
|
47
|
+
sanitizeUpdateToolParams,
|
|
48
|
+
writeBoundedSingleTranscriptEpisode
|
|
49
|
+
} from "../../chunk-MYZ2CWY6.js";
|
|
50
|
+
import {
|
|
51
|
+
createSingleTranscriptDiscoveryPort
|
|
52
|
+
} from "../../chunk-LAXNNWHM.js";
|
|
53
|
+
import {
|
|
54
|
+
buildRecallToolDetails,
|
|
55
|
+
containsAgenrMemoryContext,
|
|
56
|
+
formatAgenrBeforeTurnRecall,
|
|
57
|
+
formatRecallToolSummary,
|
|
58
|
+
formatUnifiedRecallLogSummary,
|
|
59
|
+
runBeforeTurn,
|
|
60
|
+
sanitizeRecallToolParams,
|
|
61
|
+
sanitizeStoreToolParams,
|
|
62
|
+
stripAgenrMemoryContext,
|
|
63
|
+
truncate
|
|
64
|
+
} from "../../chunk-575MUIW5.js";
|
|
65
|
+
import {
|
|
66
|
+
EMBEDDING_DIMENSIONS
|
|
67
|
+
} from "../../chunk-ELR2HSVC.js";
|
|
28
68
|
import {
|
|
29
69
|
resolveClaimSlotPolicy
|
|
30
|
-
} from "../../chunk-
|
|
70
|
+
} from "../../chunk-5LADPJ4C.js";
|
|
31
71
|
|
|
32
72
|
// src/adapters/openclaw/index.ts
|
|
33
73
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
34
74
|
|
|
35
75
|
// src/adapters/openclaw/tools/recall.ts
|
|
36
|
-
import {
|
|
76
|
+
import { textResult as textResult2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
77
|
+
import { randomUUID } from "crypto";
|
|
37
78
|
|
|
38
|
-
// src/adapters/openclaw/
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const selectorCount = (id ? 1 : 0) + (subject ? 1 : 0) + (last === true ? 1 : 0);
|
|
51
|
-
const selectorDescription = options.allowLast ? "id, subject, or last" : "id or subject";
|
|
52
|
-
if (selectorCount !== 1) {
|
|
53
|
-
throw new Error(`Provide exactly one target selector: ${selectorDescription}.`);
|
|
54
|
-
}
|
|
55
|
-
if (last) {
|
|
56
|
-
const entry2 = await services.memory.findMostRecentEntry();
|
|
57
|
-
if (!entry2) {
|
|
58
|
-
throw new Error("No agenr entries exist yet.");
|
|
59
|
-
}
|
|
60
|
-
return entry2;
|
|
61
|
-
}
|
|
62
|
-
if (id) {
|
|
63
|
-
const entry2 = await services.entries.getEntry(id) ?? (await services.memory.getEntryTrace(id))?.entry;
|
|
64
|
-
if (!entry2) {
|
|
65
|
-
throw new Error(`No agenr entry found for id ${id}.`);
|
|
66
|
-
}
|
|
67
|
-
return entry2;
|
|
68
|
-
}
|
|
69
|
-
const entry = await services.memory.findEntryBySubject(subject ?? "");
|
|
70
|
-
if (!entry) {
|
|
71
|
-
throw new Error(`No agenr entry found for subject "${subject}".`);
|
|
72
|
-
}
|
|
73
|
-
return entry;
|
|
74
|
-
}
|
|
75
|
-
function readBooleanParam(params, key) {
|
|
76
|
-
const value = params[key];
|
|
77
|
-
if (value === void 0) {
|
|
78
|
-
return void 0;
|
|
79
|
-
}
|
|
80
|
-
if (typeof value === "boolean") {
|
|
81
|
-
return value;
|
|
79
|
+
// src/adapters/openclaw/debug/sink.ts
|
|
80
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
81
|
+
import path from "path";
|
|
82
|
+
var NOOP_SINK = {
|
|
83
|
+
enabled: false,
|
|
84
|
+
eventLevel: "basic",
|
|
85
|
+
maxTopCandidates: 0,
|
|
86
|
+
async emit() {
|
|
87
|
+
return;
|
|
88
|
+
},
|
|
89
|
+
async close() {
|
|
90
|
+
return;
|
|
82
91
|
}
|
|
83
|
-
|
|
92
|
+
};
|
|
93
|
+
function createNoopAgenrDebugSink() {
|
|
94
|
+
return NOOP_SINK;
|
|
95
|
+
}
|
|
96
|
+
function createAgenrDebugSink(config) {
|
|
97
|
+
if (!config.enabled || !config.logPath) {
|
|
98
|
+
return NOOP_SINK;
|
|
99
|
+
}
|
|
100
|
+
const basePath = config.logPath;
|
|
101
|
+
const perSessionFiles = config.perSessionFiles;
|
|
102
|
+
const eventLevel = config.eventLevel;
|
|
103
|
+
const maxTopCandidates = config.maxTopCandidates;
|
|
104
|
+
const directoriesEnsured = /* @__PURE__ */ new Set();
|
|
105
|
+
let writeChain = Promise.resolve();
|
|
106
|
+
let closed = false;
|
|
107
|
+
const ensureDirectoryOnce = async (filePath) => {
|
|
108
|
+
const directory = path.dirname(filePath);
|
|
109
|
+
if (directoriesEnsured.has(directory)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
await mkdir(directory, { recursive: true });
|
|
113
|
+
directoriesEnsured.add(directory);
|
|
114
|
+
};
|
|
115
|
+
const resolveFilePath = (event) => {
|
|
116
|
+
if (!perSessionFiles) {
|
|
117
|
+
return basePath;
|
|
118
|
+
}
|
|
119
|
+
const sessionSuffix = sanitizeSessionSuffix(event.sessionId) ?? sanitizeSessionSuffix(event.sessionKey);
|
|
120
|
+
if (!sessionSuffix) {
|
|
121
|
+
return basePath;
|
|
122
|
+
}
|
|
123
|
+
const directory = path.dirname(basePath);
|
|
124
|
+
const extension = path.extname(basePath);
|
|
125
|
+
const baseName = path.basename(basePath, extension);
|
|
126
|
+
const suffixed = `${baseName}.${sessionSuffix}${extension || ".jsonl"}`;
|
|
127
|
+
return path.join(directory, suffixed);
|
|
128
|
+
};
|
|
129
|
+
const writeLine = async (filePath, line) => {
|
|
130
|
+
await ensureDirectoryOnce(filePath);
|
|
131
|
+
await appendFile(filePath, `${line}
|
|
132
|
+
`, "utf8");
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
enabled: true,
|
|
136
|
+
eventLevel,
|
|
137
|
+
maxTopCandidates,
|
|
138
|
+
logPath: basePath,
|
|
139
|
+
async emit(event) {
|
|
140
|
+
if (closed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const filePath = resolveFilePath(event);
|
|
144
|
+
const line = formatEventLine(event);
|
|
145
|
+
writeChain = writeChain.then(async () => {
|
|
146
|
+
try {
|
|
147
|
+
await writeLine(filePath, line);
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
await writeChain;
|
|
152
|
+
},
|
|
153
|
+
async close() {
|
|
154
|
+
closed = true;
|
|
155
|
+
await writeChain;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
84
158
|
}
|
|
85
|
-
function
|
|
86
|
-
|
|
159
|
+
function formatEventLine(event) {
|
|
160
|
+
const line = {
|
|
161
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
162
|
+
...event
|
|
163
|
+
};
|
|
164
|
+
return JSON.stringify(line);
|
|
87
165
|
}
|
|
88
|
-
function
|
|
89
|
-
if (value
|
|
166
|
+
function sanitizeSessionSuffix(value) {
|
|
167
|
+
if (typeof value !== "string") {
|
|
90
168
|
return void 0;
|
|
91
169
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
throw new Error(`Unsupported recall mode "${value}".`);
|
|
96
|
-
}
|
|
97
|
-
function parseEntryType(value) {
|
|
98
|
-
if (ENTRY_TYPES.includes(value)) {
|
|
99
|
-
return value;
|
|
100
|
-
}
|
|
101
|
-
throw new Error(`Unsupported entry type "${value}".`);
|
|
102
|
-
}
|
|
103
|
-
function parseExpiry(value) {
|
|
104
|
-
if (value === void 0) {
|
|
170
|
+
const trimmed = value.trim();
|
|
171
|
+
if (trimmed.length === 0) {
|
|
105
172
|
return void 0;
|
|
106
173
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw new Error(`Unsupported expiry "${value}".`);
|
|
111
|
-
}
|
|
112
|
-
function normalizeStringArray(values) {
|
|
113
|
-
if (!values) {
|
|
114
|
-
return [];
|
|
174
|
+
const sanitized = trimmed.replace(/[^A-Za-z0-9._-]+/gu, "_");
|
|
175
|
+
if (sanitized.length === 0) {
|
|
176
|
+
return void 0;
|
|
115
177
|
}
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
function buildSessionSourceFile(ctx) {
|
|
119
|
-
const target = ctx.sessionKey ?? ctx.sessionId ?? ctx.agentId ?? "unknown";
|
|
120
|
-
return `openclaw-session:${target}`;
|
|
178
|
+
return sanitized.slice(0, 120);
|
|
121
179
|
}
|
|
122
|
-
|
|
180
|
+
|
|
181
|
+
// src/adapters/openclaw/debug/build-before-turn-artifact.ts
|
|
182
|
+
function buildLiveBeforeTurnDebugArtifact(params) {
|
|
183
|
+
const { caseId, patch, currentTurnText, trigger, eventLevel, maxTopCandidates } = params;
|
|
184
|
+
const diagnostics = patch.diagnostics;
|
|
185
|
+
const includeCandidateBreakdown = eventLevel === "detailed";
|
|
186
|
+
const topK = includeCandidateBreakdown ? clampTopK(maxTopCandidates) : 0;
|
|
187
|
+
const durableTopCandidates = includeCandidateBreakdown ? buildDurableCandidates(patch, topK) : [];
|
|
188
|
+
const procedureTopCandidates = includeCandidateBreakdown ? buildProcedureCandidates(patch, topK) : [];
|
|
189
|
+
const normalizedTrigger = trigger?.trim() || "unspecified";
|
|
123
190
|
return {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
191
|
+
schemaVersion: "before-turn-debug-artifact.v1",
|
|
192
|
+
caseId,
|
|
193
|
+
input: {
|
|
194
|
+
trigger: normalizedTrigger,
|
|
195
|
+
currentTurnText
|
|
196
|
+
},
|
|
197
|
+
...diagnostics.queryPolicy ? { queryPolicy: diagnostics.queryPolicy } : {},
|
|
198
|
+
...diagnostics.queryVariants.length > 0 ? { queryVariants: [...diagnostics.queryVariants] } : {},
|
|
199
|
+
...diagnostics.abstentionReasons.length > 0 ? { abstentionReasons: [...diagnostics.abstentionReasons] } : {},
|
|
200
|
+
selectedEntryIds: patch.durableMemory.map((item) => item.entry.id),
|
|
201
|
+
selectedProcedureKey: patch.procedure?.procedure.procedure_key ?? null,
|
|
202
|
+
...durableTopCandidates.length > 0 ? { durableRecallTopCandidates: durableTopCandidates } : {},
|
|
203
|
+
...procedureTopCandidates.length > 0 ? { procedureTopCandidates } : {}
|
|
128
204
|
};
|
|
129
205
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
function formatTargetSelector(id, subject, last) {
|
|
138
|
-
if (last === true) {
|
|
139
|
-
return "last";
|
|
206
|
+
function clampTopK(requested) {
|
|
207
|
+
if (!Number.isFinite(requested) || !Number.isInteger(requested)) {
|
|
208
|
+
return BEFORE_TURN_DEBUG_ARTIFACT_DEFAULT_TOP_K;
|
|
209
|
+
}
|
|
210
|
+
if (requested < 1) {
|
|
211
|
+
return 0;
|
|
140
212
|
}
|
|
141
|
-
if (
|
|
142
|
-
return
|
|
213
|
+
if (requested > BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K) {
|
|
214
|
+
return BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K;
|
|
215
|
+
}
|
|
216
|
+
return requested;
|
|
217
|
+
}
|
|
218
|
+
function buildDurableCandidates(patch, topK) {
|
|
219
|
+
if (topK < 1) {
|
|
220
|
+
return [];
|
|
143
221
|
}
|
|
144
|
-
|
|
145
|
-
|
|
222
|
+
return patch.durableMemory.slice(0, topK).map((item) => {
|
|
223
|
+
const reasons = item.whySurfaced.reasons.length > 0 ? [...item.whySurfaced.reasons] : void 0;
|
|
224
|
+
return {
|
|
225
|
+
id: item.entry.id,
|
|
226
|
+
score: item.score,
|
|
227
|
+
...reasons ? { reasons } : {}
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function buildProcedureCandidates(patch, topK) {
|
|
232
|
+
if (!patch.procedure || topK < 1) {
|
|
233
|
+
return [];
|
|
146
234
|
}
|
|
147
|
-
|
|
235
|
+
const reasons = patch.procedure.whySurfaced.reasons.length > 0 ? [...patch.procedure.whySurfaced.reasons] : void 0;
|
|
236
|
+
return [
|
|
237
|
+
{
|
|
238
|
+
procedureKey: patch.procedure.procedure.procedure_key,
|
|
239
|
+
score: patch.procedure.score,
|
|
240
|
+
...reasons ? { reasons } : {}
|
|
241
|
+
}
|
|
242
|
+
];
|
|
148
243
|
}
|
|
149
|
-
|
|
244
|
+
|
|
245
|
+
// src/adapters/openclaw/debug/build-recall-artifact.ts
|
|
246
|
+
function buildLiveRecallDebugArtifact(params) {
|
|
247
|
+
const { caseId, query, result, eventLevel, maxTopCandidates } = params;
|
|
248
|
+
const selectedEntryIds = result.entries.map((entry) => entry.entry.id);
|
|
249
|
+
const includeCandidateBreakdown = eventLevel === "detailed";
|
|
250
|
+
const topK = includeCandidateBreakdown ? clampTopK2(maxTopCandidates) : 0;
|
|
251
|
+
const reasonsByEntryId = /* @__PURE__ */ new Map();
|
|
252
|
+
for (const projected of result.projectedEntries) {
|
|
253
|
+
if (projected.whySurfaced.reasons.length > 0) {
|
|
254
|
+
reasonsByEntryId.set(projected.entryId, [...projected.whySurfaced.reasons]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const topCandidates = includeCandidateBreakdown ? buildTopCandidates(result, reasonsByEntryId, topK) : [];
|
|
150
258
|
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
...
|
|
160
|
-
...params.validFrom !== void 0 ? { hasValidFrom: true } : {},
|
|
161
|
-
...params.validTo !== void 0 ? { hasValidTo: true } : {}
|
|
259
|
+
schemaVersion: "recall-debug-artifact.v1",
|
|
260
|
+
caseId,
|
|
261
|
+
request: {
|
|
262
|
+
recallPath: "unified",
|
|
263
|
+
query
|
|
264
|
+
},
|
|
265
|
+
routing: result.routing,
|
|
266
|
+
selectedEntryIds,
|
|
267
|
+
...topCandidates.length > 0 ? { topCandidates } : {}
|
|
162
268
|
};
|
|
163
269
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
parts.push(`mode=${params.mode}`);
|
|
270
|
+
function clampTopK2(requested) {
|
|
271
|
+
if (!Number.isFinite(requested) || !Number.isInteger(requested)) {
|
|
272
|
+
return RECALL_DEBUG_ARTIFACT_DEFAULT_TOP_K;
|
|
168
273
|
}
|
|
169
|
-
if (
|
|
170
|
-
|
|
274
|
+
if (requested < 1) {
|
|
275
|
+
return 0;
|
|
171
276
|
}
|
|
172
|
-
if (
|
|
173
|
-
|
|
277
|
+
if (requested > RECALL_DEBUG_ARTIFACT_MAX_TOP_K) {
|
|
278
|
+
return RECALL_DEBUG_ARTIFACT_MAX_TOP_K;
|
|
174
279
|
}
|
|
175
|
-
|
|
176
|
-
|
|
280
|
+
return requested;
|
|
281
|
+
}
|
|
282
|
+
function buildTopCandidates(result, reasonsByEntryId, topK) {
|
|
283
|
+
if (topK < 1) {
|
|
284
|
+
return [];
|
|
177
285
|
}
|
|
178
|
-
|
|
179
|
-
|
|
286
|
+
return result.entries.slice(0, topK).map((entry) => {
|
|
287
|
+
const reasons = reasonsByEntryId.get(entry.entry.id);
|
|
288
|
+
return {
|
|
289
|
+
id: entry.entry.id,
|
|
290
|
+
score: entry.score,
|
|
291
|
+
lexicalScore: entry.scores.lexical,
|
|
292
|
+
vectorScore: entry.scores.vector,
|
|
293
|
+
recencyScore: entry.scores.recency,
|
|
294
|
+
importanceScore: entry.scores.importance,
|
|
295
|
+
...reasons && reasons.length > 0 ? { reasons } : {}
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/adapters/openclaw/tools/shared.ts
|
|
301
|
+
import { failedTextResult, readNumberParam, readStringArrayParam, readStringParam, textResult } from "openclaw/plugin-sdk/agent-runtime";
|
|
302
|
+
var OPENCLAW_PARAM_READER = {
|
|
303
|
+
readString: readStringParam,
|
|
304
|
+
readNumber: readNumberParam,
|
|
305
|
+
readStringArray: readStringArrayParam
|
|
306
|
+
};
|
|
307
|
+
function toOpenClawToolResult(outcome) {
|
|
308
|
+
if (outcome.failed) {
|
|
309
|
+
return failedTextResult(outcome.text, {
|
|
310
|
+
...outcome.details,
|
|
311
|
+
status: "failed"
|
|
312
|
+
});
|
|
180
313
|
}
|
|
181
|
-
return
|
|
314
|
+
return textResult(outcome.text, outcome.details);
|
|
182
315
|
}
|
|
183
|
-
function
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
316
|
+
async function resolveTargetEntry2(services, params, options = {}) {
|
|
317
|
+
return resolveTargetEntry(
|
|
318
|
+
{
|
|
319
|
+
getEntryById: async (id) => await services.entries.getEntry(id) ?? (await services.memory.getEntryTrace(id))?.entry ?? null,
|
|
320
|
+
findEntryBySubject: async (subject) => services.memory.findEntryBySubject(subject),
|
|
321
|
+
findMostRecentEntry: async () => services.memory.findMostRecentEntry()
|
|
322
|
+
},
|
|
323
|
+
params,
|
|
324
|
+
options
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
function logToolCall(logger, toolName, ctx, summary, sanitizedParams) {
|
|
328
|
+
logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} ${summary}`);
|
|
329
|
+
logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} params=${JSON.stringify(sanitizedParams)}`);
|
|
330
|
+
}
|
|
331
|
+
function logToolFailure(logger, toolName, ctx, error) {
|
|
332
|
+
logger.warn(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} failed: ${formatErrorMessage(error)}`);
|
|
193
333
|
}
|
|
194
334
|
function sanitizeRetireToolParams(params) {
|
|
195
335
|
return {
|
|
@@ -198,17 +338,6 @@ function sanitizeRetireToolParams(params) {
|
|
|
198
338
|
...params.reason !== void 0 ? { reasonLength: params.reason.length } : {}
|
|
199
339
|
};
|
|
200
340
|
}
|
|
201
|
-
function sanitizeUpdateToolParams(params) {
|
|
202
|
-
return {
|
|
203
|
-
...params.id ? { id: params.id } : {},
|
|
204
|
-
...params.subject ? { subject: params.subject } : {},
|
|
205
|
-
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
206
|
-
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
207
|
-
...params.claimKey !== void 0 ? { hasClaimKey: true } : {},
|
|
208
|
-
...params.validFrom !== void 0 ? { hasValidFrom: true } : {},
|
|
209
|
-
...params.validTo !== void 0 ? { hasValidTo: true } : {}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
341
|
function sanitizeTraceToolParams(params) {
|
|
213
342
|
return {
|
|
214
343
|
...params.id ? { id: params.id } : {},
|
|
@@ -216,211 +345,6 @@ function sanitizeTraceToolParams(params) {
|
|
|
216
345
|
...params.last !== void 0 ? { last: params.last } : {}
|
|
217
346
|
};
|
|
218
347
|
}
|
|
219
|
-
function formatUnifiedRecallResults(result) {
|
|
220
|
-
const lines = [
|
|
221
|
-
"Recall Route",
|
|
222
|
-
`requested=${result.routing.requested} detected=${result.routing.detectedIntent} queried=${result.routing.queried.join(", ") || "none"}`,
|
|
223
|
-
result.routing.reason,
|
|
224
|
-
""
|
|
225
|
-
];
|
|
226
|
-
if (result.timeWindow) {
|
|
227
|
-
lines.push("Resolved Time Window");
|
|
228
|
-
lines.push(`${result.timeWindow.start} -> ${result.timeWindow.end} (${result.timeWindow.timezone}) from ${JSON.stringify(result.timeWindow.resolvedFrom)}`);
|
|
229
|
-
lines.push("");
|
|
230
|
-
}
|
|
231
|
-
if (result.asOf) {
|
|
232
|
-
lines.push("As Of");
|
|
233
|
-
lines.push(result.asOf);
|
|
234
|
-
lines.push("");
|
|
235
|
-
}
|
|
236
|
-
if (result.routing.queried.includes("procedures") || result.procedure || result.procedureCandidates.length > 0 || result.procedureNotices.length > 0) {
|
|
237
|
-
appendProcedureMatches(lines, result);
|
|
238
|
-
lines.push("");
|
|
239
|
-
}
|
|
240
|
-
const renderEntriesFirst = result.routing.detectedIntent === "historical_state";
|
|
241
|
-
if (renderEntriesFirst) {
|
|
242
|
-
appendEntryMatches(lines, result);
|
|
243
|
-
lines.push("");
|
|
244
|
-
appendClaimTransitions(lines, result);
|
|
245
|
-
lines.push("");
|
|
246
|
-
appendEpisodeMatches(lines, result);
|
|
247
|
-
} else {
|
|
248
|
-
appendEpisodeMatches(lines, result);
|
|
249
|
-
lines.push("");
|
|
250
|
-
appendEntryMatches(lines, result);
|
|
251
|
-
lines.push("");
|
|
252
|
-
appendClaimTransitions(lines, result);
|
|
253
|
-
}
|
|
254
|
-
if (result.notices.length > 0) {
|
|
255
|
-
lines.push("");
|
|
256
|
-
lines.push("Notices");
|
|
257
|
-
for (const notice of result.notices) {
|
|
258
|
-
lines.push(`- ${notice}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return lines.join("\n");
|
|
262
|
-
}
|
|
263
|
-
function appendProcedureMatches(lines, result) {
|
|
264
|
-
lines.push("Procedure Matches");
|
|
265
|
-
if (!result.procedure && result.procedureCandidates.length === 0) {
|
|
266
|
-
lines.push("None.");
|
|
267
|
-
} else {
|
|
268
|
-
if (result.procedure) {
|
|
269
|
-
appendCanonicalProcedure(lines, result.procedure, result.procedureCandidates);
|
|
270
|
-
} else {
|
|
271
|
-
lines.push("Canonical procedure: none.");
|
|
272
|
-
}
|
|
273
|
-
const additionalCandidates = result.procedureCandidates.filter((candidate) => candidate.procedure.id !== result.procedure?.id);
|
|
274
|
-
if (additionalCandidates.length > 0) {
|
|
275
|
-
lines.push("Other Candidates");
|
|
276
|
-
for (const [index, candidate] of additionalCandidates.entries()) {
|
|
277
|
-
lines.push(
|
|
278
|
-
`${index + 1}. ${candidate.procedure.procedure_key} | ${candidate.procedure.title} | score ${candidate.score.toFixed(2)} | lexical=${candidate.scores.lexical.toFixed(2)} | vector=${candidate.scores.vector.toFixed(2)}`
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
if (result.procedureNotices.length > 0) {
|
|
284
|
-
lines.push("Procedure Notices");
|
|
285
|
-
for (const notice of result.procedureNotices) {
|
|
286
|
-
lines.push(`- ${notice}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
function appendEntryMatches(lines, result) {
|
|
291
|
-
lines.push("Entry Matches");
|
|
292
|
-
if (result.projectedEntries.length === 0) {
|
|
293
|
-
lines.push("None.");
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
for (const [familyIndex, family] of result.entryFamilies.entries()) {
|
|
297
|
-
lines.push(
|
|
298
|
-
family.claimKey ? `Family ${familyIndex + 1}. claim_key=${family.claimKey} | slot_policy=${family.slotPolicy} | primary=${family.primary.entryId} | subject=${family.subject}` : `Standalone ${familyIndex + 1}. ${family.primary.entryId} | subject=${family.subject}`
|
|
299
|
-
);
|
|
300
|
-
for (const [entryIndex, entry] of family.entries.entries()) {
|
|
301
|
-
lines.push(
|
|
302
|
-
` ${entryIndex + 1}. ${entry.entryId} | ${entry.recall.entry.type} | ${entry.recall.entry.subject} | score ${entry.recall.score.toFixed(2)} | state=${entry.memoryState} | claim_status=${formatClaimStatus(entry.claimStatus)}`
|
|
303
|
-
);
|
|
304
|
-
lines.push(` ${truncate(entry.recall.entry.content, 220)}`);
|
|
305
|
-
lines.push(` freshness=${entry.freshness.label}`);
|
|
306
|
-
const provenance = formatProjectedEntryProvenance(entry);
|
|
307
|
-
if (provenance) {
|
|
308
|
-
lines.push(` provenance=${provenance}`);
|
|
309
|
-
}
|
|
310
|
-
lines.push(` why_surfaced=${entry.whySurfaced.summary}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function appendEpisodeMatches(lines, result) {
|
|
315
|
-
lines.push("Episode Matches");
|
|
316
|
-
if (result.episodes.length === 0) {
|
|
317
|
-
lines.push("None.");
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
for (const [index, episode] of result.episodes.entries()) {
|
|
321
|
-
lines.push(
|
|
322
|
-
`${index + 1}. ${episode.episode.id} | ${episode.episode.source} | ${episode.episode.startedAt} -> ${episode.episode.endedAt ?? episode.episode.startedAt} | score ${episode.score.toFixed(2)}`
|
|
323
|
-
);
|
|
324
|
-
lines.push(` ${index < 3 ? episode.episode.summary.trim() : truncate(episode.episode.summary.trim(), 220)}`);
|
|
325
|
-
lines.push(` why_matched=${describeEpisodeMatch(episode)}`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function appendCanonicalProcedure(lines, procedure, candidates) {
|
|
329
|
-
const leadCandidate = candidates.find((candidate) => candidate.procedure.id === procedure.id);
|
|
330
|
-
lines.push(
|
|
331
|
-
leadCandidate ? `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title} | score ${leadCandidate.score.toFixed(2)}` : `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title}`
|
|
332
|
-
);
|
|
333
|
-
lines.push(` goal=${procedure.goal}`);
|
|
334
|
-
appendLabeledList(lines, "when_to_use", procedure.when_to_use);
|
|
335
|
-
appendLabeledList(lines, "when_not_to_use", procedure.when_not_to_use);
|
|
336
|
-
appendLabeledList(lines, "prerequisites", procedure.prerequisites);
|
|
337
|
-
lines.push(" steps");
|
|
338
|
-
for (const [index, step] of procedure.steps.entries()) {
|
|
339
|
-
lines.push(` ${index + 1}. [${step.kind}] ${step.instruction}`);
|
|
340
|
-
const stepDetails = formatProcedureStepDetails(step);
|
|
341
|
-
if (stepDetails.length > 0) {
|
|
342
|
-
for (const detail of stepDetails) {
|
|
343
|
-
lines.push(` ${detail}`);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
appendLabeledList(lines, "verification", procedure.verification);
|
|
348
|
-
appendLabeledList(lines, "failure_modes", procedure.failure_modes);
|
|
349
|
-
lines.push(" sources");
|
|
350
|
-
for (const source of procedure.sources) {
|
|
351
|
-
lines.push(` - ${formatProcedureSource(source)}`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
function appendClaimTransitions(lines, result) {
|
|
355
|
-
lines.push("Claim Transitions");
|
|
356
|
-
if (result.claimTransitions.length === 0) {
|
|
357
|
-
lines.push("None.");
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
for (const [index, transition] of result.claimTransitions.entries()) {
|
|
361
|
-
lines.push(
|
|
362
|
-
`${index + 1}. family=${transition.claimKey ?? transition.familyKey} | slot_policy=${transition.slotPolicy}${transition.currentEntryId ? ` | current=${transition.currentEntryId}` : ""}${transition.priorEntryId ? ` | prior=${transition.priorEntryId}` : ""}`
|
|
363
|
-
);
|
|
364
|
-
lines.push(` ${transition.summary}`);
|
|
365
|
-
if (transition.episodeContext) {
|
|
366
|
-
lines.push(
|
|
367
|
-
` episode=${transition.episodeContext.episodeId} | ${transition.episodeContext.startedAt} -> ${transition.episodeContext.endedAt ?? transition.episodeContext.startedAt}`
|
|
368
|
-
);
|
|
369
|
-
lines.push(` ${truncate(transition.episodeContext.summary.trim(), 220)}`);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
function formatUnifiedRecallLogSummary(result) {
|
|
374
|
-
const procedureCount = result.procedureCandidates.length;
|
|
375
|
-
const procedureSummary = result.procedure ? ` [procedure: ${JSON.stringify(truncate(result.procedure.title, 80))}]` : "";
|
|
376
|
-
const entrySubjects = result.entries.map((entry) => entry.entry.subject.trim()).filter((subject) => subject.length > 0);
|
|
377
|
-
const displayed = entrySubjects.slice(0, RESULT_SUBJECT_LOG_LIMIT).map((subject) => JSON.stringify(truncate(subject, 80)));
|
|
378
|
-
const remaining = entrySubjects.length - RESULT_SUBJECT_LOG_LIMIT;
|
|
379
|
-
const suffix = displayed.length === 0 ? "" : ` [entry subjects: ${displayed.join(", ")}${remaining > 0 ? `, ... and ${remaining} more` : ""}]`;
|
|
380
|
-
const entryEpisodeSummary = `${result.episodes.length} episode${result.episodes.length === 1 ? "" : "s"}, ${result.entries.length} entr${result.entries.length === 1 ? "y" : "ies"}`;
|
|
381
|
-
if (procedureCount === 0 && !result.procedure) {
|
|
382
|
-
return `${entryEpisodeSummary}${suffix}`;
|
|
383
|
-
}
|
|
384
|
-
return `${procedureCount} procedure candidate${procedureCount === 1 ? "" : "s"}, ${entryEpisodeSummary}${procedureSummary}${suffix}`;
|
|
385
|
-
}
|
|
386
|
-
function appendLabeledList(lines, label, values) {
|
|
387
|
-
lines.push(` ${label}`);
|
|
388
|
-
if (values.length === 0) {
|
|
389
|
-
lines.push(" - none");
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
for (const value of values) {
|
|
393
|
-
lines.push(` - ${value}`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
function formatProcedureStepDetails(step) {
|
|
397
|
-
switch (step.kind) {
|
|
398
|
-
case "run_command":
|
|
399
|
-
return [`command=${step.command}`];
|
|
400
|
-
case "read_reference":
|
|
401
|
-
return [`ref=${formatProcedureSource(step.ref)}`];
|
|
402
|
-
case "inspect_state":
|
|
403
|
-
return [step.target ? `target=${step.target}` : void 0, step.query ? `query=${step.query}` : void 0].filter(
|
|
404
|
-
(value) => value !== void 0
|
|
405
|
-
);
|
|
406
|
-
case "edit_file":
|
|
407
|
-
return [`path=${step.path}`, `edit=${step.edit}`];
|
|
408
|
-
case "ask_user":
|
|
409
|
-
return [`prompt=${step.prompt}`];
|
|
410
|
-
case "invoke_tool":
|
|
411
|
-
return [step.tool ? `tool=${step.tool}` : void 0, step.arguments ? `arguments=${JSON.stringify(step.arguments)}` : void 0].filter(
|
|
412
|
-
(value) => value !== void 0
|
|
413
|
-
);
|
|
414
|
-
case "verify":
|
|
415
|
-
return step.checks.map((check) => `check=${check}`);
|
|
416
|
-
default:
|
|
417
|
-
return [];
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
function formatProcedureSource(source) {
|
|
421
|
-
const parts = [source.kind, source.label, source.path, source.locator].filter((value) => Boolean(value && value.length > 0));
|
|
422
|
-
return parts.join(" | ");
|
|
423
|
-
}
|
|
424
348
|
function formatTrace(entry, supersededBy, supersedes, claimFamily, recallEvents) {
|
|
425
349
|
const slotPolicy = entry.claim_key ? claimFamily ? {
|
|
426
350
|
policy: claimFamily.slotPolicy ?? resolveClaimSlotPolicy(claimFamily.claimKey).policy,
|
|
@@ -469,29 +393,11 @@ function formatTrace(entry, supersededBy, supersedes, claimFamily, recallEvents)
|
|
|
469
393
|
}
|
|
470
394
|
return lines.join("\n");
|
|
471
395
|
}
|
|
472
|
-
function truncate(value, maxChars) {
|
|
473
|
-
if (value.length <= maxChars) {
|
|
474
|
-
return value;
|
|
475
|
-
}
|
|
476
|
-
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
477
|
-
}
|
|
478
396
|
function toolFailureResult(error) {
|
|
479
397
|
return failedTextResult(formatErrorMessage(error), {
|
|
480
398
|
status: "failed"
|
|
481
399
|
});
|
|
482
400
|
}
|
|
483
|
-
function formatErrorMessage(error) {
|
|
484
|
-
if (error instanceof Error) {
|
|
485
|
-
return error.message;
|
|
486
|
-
}
|
|
487
|
-
return String(error);
|
|
488
|
-
}
|
|
489
|
-
function asRecord(value) {
|
|
490
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
491
|
-
return value;
|
|
492
|
-
}
|
|
493
|
-
throw new Error("Tool parameters must be an object.");
|
|
494
|
-
}
|
|
495
401
|
function formatToolSessionContext(ctx) {
|
|
496
402
|
const normalizedSessionId = ctx.sessionId?.trim();
|
|
497
403
|
const normalizedSessionKey = ctx.sessionKey?.trim();
|
|
@@ -506,33 +412,6 @@ function formatToolSessionContext(ctx) {
|
|
|
506
412
|
}
|
|
507
413
|
return "session=unknown";
|
|
508
414
|
}
|
|
509
|
-
function describeEpisodeMatch(result) {
|
|
510
|
-
if (result.scores.semantic > 0 && result.scores.temporal > 0) {
|
|
511
|
-
return "Semantic match within the resolved time window.";
|
|
512
|
-
}
|
|
513
|
-
if (result.scores.semantic > 0) {
|
|
514
|
-
return "Semantic match to the episode summary.";
|
|
515
|
-
}
|
|
516
|
-
if (result.scores.temporal > 0) {
|
|
517
|
-
return "Session overlaps the resolved time window.";
|
|
518
|
-
}
|
|
519
|
-
return "Matched episodic recall ranking.";
|
|
520
|
-
}
|
|
521
|
-
function formatClaimStatus(status) {
|
|
522
|
-
return status === "no_key" ? "no-key" : status;
|
|
523
|
-
}
|
|
524
|
-
function formatProjectedEntryProvenance(entry) {
|
|
525
|
-
const parts = [
|
|
526
|
-
entry.provenance.supersededById ? `superseded_by=${entry.provenance.supersededById}` : void 0,
|
|
527
|
-
entry.provenance.supersessionKind ? `kind=${entry.provenance.supersessionKind}` : void 0,
|
|
528
|
-
entry.provenance.supersessionReason ? `reason=${truncate(entry.provenance.supersessionReason, 120)}` : void 0,
|
|
529
|
-
entry.provenance.supportSourceKind ? `support=${entry.provenance.supportSourceKind}` : void 0,
|
|
530
|
-
entry.provenance.supportMode ? `support_mode=${entry.provenance.supportMode}` : void 0,
|
|
531
|
-
entry.provenance.supportObservedAt ? `observed=${entry.provenance.supportObservedAt}` : void 0,
|
|
532
|
-
entry.provenance.supportLocator ? `locator=${truncate(entry.provenance.supportLocator, 120)}` : void 0
|
|
533
|
-
].filter((value) => value !== void 0);
|
|
534
|
-
return parts.join(" | ");
|
|
535
|
-
}
|
|
536
415
|
function describeTraceEntryState(entry) {
|
|
537
416
|
if (entry.superseded_by) {
|
|
538
417
|
return "superseded";
|
|
@@ -564,51 +443,6 @@ function summarizeTraceClaimFamilyTransition(entries) {
|
|
|
564
443
|
}
|
|
565
444
|
|
|
566
445
|
// src/adapters/openclaw/tools/recall.ts
|
|
567
|
-
var RECALL_TOOL_PARAMETERS = {
|
|
568
|
-
type: "object",
|
|
569
|
-
additionalProperties: false,
|
|
570
|
-
properties: {
|
|
571
|
-
query: {
|
|
572
|
-
type: "string",
|
|
573
|
-
description: "What you need to remember. Use a focused natural-language query rather than a broad 'everything' search. Phrase prior-state asks directly, for example 'what was the previous approach' or 'what changed from X to Y'. Phrase procedural asks directly, for example 'how do I rotate credentials' or 'what steps should I follow'."
|
|
574
|
-
},
|
|
575
|
-
mode: {
|
|
576
|
-
type: "string",
|
|
577
|
-
enum: [...RECALL_MODES],
|
|
578
|
-
description: "Recall mode: auto routes between exact entry recall, historical-state recall, procedural recall, and episodes; entries forces semantic recall; episodes forces temporal or semantic session recall; procedures forces procedural recall."
|
|
579
|
-
},
|
|
580
|
-
limit: {
|
|
581
|
-
type: "integer",
|
|
582
|
-
minimum: 1,
|
|
583
|
-
maximum: 10,
|
|
584
|
-
description: "Maximum results to return. Lower this when you want a tighter shortlist."
|
|
585
|
-
},
|
|
586
|
-
threshold: {
|
|
587
|
-
type: "number",
|
|
588
|
-
minimum: 0,
|
|
589
|
-
maximum: 1,
|
|
590
|
-
description: "Minimum final score from 0 to 1. Raise this when you want fewer, higher-confidence matches."
|
|
591
|
-
},
|
|
592
|
-
types: {
|
|
593
|
-
type: "array",
|
|
594
|
-
items: {
|
|
595
|
-
type: "string",
|
|
596
|
-
enum: [...ENTRY_TYPES]
|
|
597
|
-
},
|
|
598
|
-
description: "Optional knowledge types to filter by, such as decision, preference, lesson, fact, milestone, or relationship."
|
|
599
|
-
},
|
|
600
|
-
tags: {
|
|
601
|
-
type: "array",
|
|
602
|
-
items: { type: "string" },
|
|
603
|
-
description: "Optional tags to filter by once you already know the relevant entity, system, or theme."
|
|
604
|
-
},
|
|
605
|
-
asOf: {
|
|
606
|
-
type: "string",
|
|
607
|
-
description: "Optional reference time for current-vs-prior resolution. Supports ISO timestamps and the same natural-language date phrases used elsewhere in recall."
|
|
608
|
-
}
|
|
609
|
-
},
|
|
610
|
-
required: ["query"]
|
|
611
|
-
};
|
|
612
446
|
function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
613
447
|
return {
|
|
614
448
|
name: "agenr_recall",
|
|
@@ -617,155 +451,94 @@ function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
|
617
451
|
parameters: RECALL_TOOL_PARAMETERS,
|
|
618
452
|
async execute(_toolCallId, rawParams) {
|
|
619
453
|
try {
|
|
620
|
-
const params =
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
const limit = readNumberParam(params, "limit", { integer: true, strict: true });
|
|
624
|
-
const threshold = readNumberParam(params, "threshold", { strict: true });
|
|
454
|
+
const params = parseRecallToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
455
|
+
const sanitizedParams = sanitizeRecallToolParams(params);
|
|
456
|
+
logToolCall(logger, "agenr_recall", ctx, formatRecallToolSummary(params), sanitizedParams);
|
|
625
457
|
const services = await servicesPromise;
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
...asOf ? { asOf } : {},
|
|
637
|
-
sessionKey: ctx.sessionKey
|
|
638
|
-
};
|
|
639
|
-
logToolCall(
|
|
640
|
-
logger,
|
|
641
|
-
"agenr_recall",
|
|
642
|
-
ctx,
|
|
643
|
-
formatRecallToolSummary({
|
|
644
|
-
query,
|
|
645
|
-
mode,
|
|
646
|
-
limit,
|
|
647
|
-
types,
|
|
648
|
-
tags,
|
|
649
|
-
...asOf ? { asOf } : {}
|
|
650
|
-
}),
|
|
651
|
-
sanitizeRecallToolParams({
|
|
652
|
-
query,
|
|
653
|
-
mode,
|
|
654
|
-
limit,
|
|
655
|
-
threshold,
|
|
656
|
-
types,
|
|
657
|
-
tags,
|
|
658
|
-
...asOf ? { asOf } : {}
|
|
659
|
-
})
|
|
660
|
-
);
|
|
661
|
-
const result = await runUnifiedRecall(request, {
|
|
662
|
-
database: services.episodes,
|
|
663
|
-
procedures: services.procedures,
|
|
664
|
-
recall: services.recall,
|
|
665
|
-
embeddingAvailable: services.embeddingStatus.available,
|
|
666
|
-
embeddingError: services.embeddingStatus.error,
|
|
667
|
-
claimSlotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies,
|
|
458
|
+
void services.debugSink.emit({
|
|
459
|
+
type: "tool_call",
|
|
460
|
+
tool: "agenr_recall",
|
|
461
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
462
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
463
|
+
params: sanitizedParams
|
|
464
|
+
});
|
|
465
|
+
const result = await runRecallMemoryTool(params, buildRecallToolServices(services), {
|
|
466
|
+
sessionKey: ctx.sessionKey,
|
|
467
|
+
slotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies,
|
|
668
468
|
debugLog: (message) => {
|
|
669
469
|
logger.debug?.(message);
|
|
670
|
-
},
|
|
671
|
-
embedQuery: services.embeddingStatus.available ? async (text) => {
|
|
672
|
-
const vectors = await services.embedding.embed([text]);
|
|
673
|
-
return vectors[0] ?? [];
|
|
674
|
-
} : void 0,
|
|
675
|
-
recallOptions: {
|
|
676
|
-
slotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies
|
|
677
470
|
}
|
|
678
471
|
});
|
|
679
472
|
logger.info(
|
|
680
473
|
`[agenr] tool=agenr_recall session=${ctx.sessionId ?? "unknown"} key=${ctx.sessionKey ?? "unknown"} result: ${formatUnifiedRecallLogSummary(result)}`
|
|
681
474
|
);
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
count: result.count,
|
|
685
|
-
routing: {
|
|
686
|
-
requested: result.routing.requested,
|
|
687
|
-
detectedIntent: result.routing.detectedIntent,
|
|
688
|
-
queried: result.routing.queried,
|
|
689
|
-
reason: result.routing.reason
|
|
690
|
-
},
|
|
691
|
-
...result.asOf ? { asOf: result.asOf } : {},
|
|
692
|
-
...result.timeWindow ? { timeWindow: result.timeWindow } : {},
|
|
693
|
-
...result.procedure ? {
|
|
694
|
-
procedure: {
|
|
695
|
-
id: result.procedure.id,
|
|
696
|
-
procedureKey: result.procedure.procedure_key,
|
|
697
|
-
title: result.procedure.title,
|
|
698
|
-
goal: result.procedure.goal
|
|
699
|
-
}
|
|
700
|
-
} : {},
|
|
701
|
-
procedures: result.procedureCandidates.map((candidate) => ({
|
|
702
|
-
id: candidate.procedure.id,
|
|
703
|
-
procedureKey: candidate.procedure.procedure_key,
|
|
704
|
-
title: candidate.procedure.title,
|
|
705
|
-
goal: candidate.procedure.goal,
|
|
706
|
-
score: candidate.score,
|
|
707
|
-
lexicalScore: candidate.scores.lexical,
|
|
708
|
-
vectorScore: candidate.scores.vector
|
|
709
|
-
})),
|
|
710
|
-
procedureNotices: result.procedureNotices,
|
|
711
|
-
episodes: result.episodes.map((episode) => ({
|
|
712
|
-
id: episode.episode.id,
|
|
713
|
-
source: episode.episode.source,
|
|
714
|
-
sourceId: episode.episode.sourceId,
|
|
715
|
-
startedAt: episode.episode.startedAt,
|
|
716
|
-
endedAt: episode.episode.endedAt,
|
|
717
|
-
tags: episode.episode.tags,
|
|
718
|
-
score: episode.score,
|
|
719
|
-
activityLevel: episode.episode.activityLevel,
|
|
720
|
-
summary: episode.episode.summary,
|
|
721
|
-
whyMatched: episode.scores.semantic > 0 && episode.scores.temporal > 0 ? "Semantic match within the resolved time window." : episode.scores.semantic > 0 ? "Semantic match to the episode summary." : episode.scores.temporal > 0 ? "Session overlaps the resolved time window." : "Matched episodic recall ranking."
|
|
722
|
-
})),
|
|
723
|
-
entries: result.entries.map((entry) => ({
|
|
724
|
-
id: entry.entry.id,
|
|
725
|
-
subject: entry.entry.subject,
|
|
726
|
-
type: entry.entry.type,
|
|
727
|
-
expiry: entry.entry.expiry,
|
|
728
|
-
importance: entry.entry.importance,
|
|
729
|
-
score: entry.score,
|
|
730
|
-
tags: entry.entry.tags,
|
|
731
|
-
content: entry.entry.content
|
|
732
|
-
})),
|
|
733
|
-
projectedEntries: result.projectedEntries.map((entry) => ({
|
|
734
|
-
id: entry.entryId,
|
|
735
|
-
familyKey: entry.familyKey,
|
|
736
|
-
claimKey: entry.claimKey,
|
|
737
|
-
slotPolicy: entry.slotPolicy,
|
|
738
|
-
memoryState: entry.memoryState,
|
|
739
|
-
claimStatus: entry.claimStatus,
|
|
740
|
-
freshness: entry.freshness,
|
|
741
|
-
provenance: entry.provenance,
|
|
742
|
-
whySurfaced: entry.whySurfaced
|
|
743
|
-
})),
|
|
744
|
-
entryFamilies: result.entryFamilies.map((family) => ({
|
|
745
|
-
familyKey: family.familyKey,
|
|
746
|
-
claimKey: family.claimKey,
|
|
747
|
-
slotPolicy: family.slotPolicy,
|
|
748
|
-
subject: family.subject,
|
|
749
|
-
primaryEntryId: family.primary.entryId,
|
|
750
|
-
entries: family.entries.map((entry) => ({
|
|
751
|
-
id: entry.entryId,
|
|
752
|
-
memoryState: entry.memoryState,
|
|
753
|
-
claimStatus: entry.claimStatus
|
|
754
|
-
}))
|
|
755
|
-
})),
|
|
756
|
-
claimTransitions: result.claimTransitions,
|
|
757
|
-
notices: result.notices
|
|
758
|
-
});
|
|
475
|
+
emitRecallDebugArtifacts(services, ctx, params.query, result);
|
|
476
|
+
return textResult2(formatUnifiedRecallResults(result), buildRecallToolDetails(result));
|
|
759
477
|
} catch (error) {
|
|
760
478
|
logToolFailure(logger, "agenr_recall", ctx, error);
|
|
479
|
+
await emitRecallError(servicesPromise, ctx, error);
|
|
761
480
|
return toolFailureResult(error);
|
|
762
481
|
}
|
|
763
482
|
}
|
|
764
483
|
};
|
|
765
484
|
}
|
|
485
|
+
function emitRecallDebugArtifacts(services, ctx, query, result) {
|
|
486
|
+
if (!services.debugSink.enabled) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const sessionIdPayload = ctx.sessionId ? { sessionId: ctx.sessionId } : {};
|
|
490
|
+
const sessionKeyPayload = ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {};
|
|
491
|
+
void services.debugSink.emit({
|
|
492
|
+
type: "tool_result",
|
|
493
|
+
tool: "agenr_recall",
|
|
494
|
+
...sessionIdPayload,
|
|
495
|
+
...sessionKeyPayload,
|
|
496
|
+
summary: {
|
|
497
|
+
count: result.count,
|
|
498
|
+
routing: {
|
|
499
|
+
requested: result.routing.requested,
|
|
500
|
+
detectedIntent: result.routing.detectedIntent,
|
|
501
|
+
queried: [...result.routing.queried],
|
|
502
|
+
reason: result.routing.reason
|
|
503
|
+
},
|
|
504
|
+
selectedEntryIds: result.entries.map((entry) => entry.entry.id),
|
|
505
|
+
episodeIds: result.episodes.map((episode) => episode.episode.id),
|
|
506
|
+
selectedProcedureKey: result.procedure?.procedure_key ?? null,
|
|
507
|
+
notices: [...result.notices],
|
|
508
|
+
procedureNotices: [...result.procedureNotices]
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
void services.debugSink.emit({
|
|
512
|
+
type: "unified_recall",
|
|
513
|
+
...sessionIdPayload,
|
|
514
|
+
...sessionKeyPayload,
|
|
515
|
+
debug: buildLiveRecallDebugArtifact({
|
|
516
|
+
caseId: `live-${randomUUID()}`,
|
|
517
|
+
query,
|
|
518
|
+
result,
|
|
519
|
+
eventLevel: services.debugSink.eventLevel,
|
|
520
|
+
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
521
|
+
})
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async function emitRecallError(servicesPromise, ctx, error) {
|
|
525
|
+
try {
|
|
526
|
+
const services = await servicesPromise;
|
|
527
|
+
if (services.debugSink.enabled) {
|
|
528
|
+
void services.debugSink.emit({
|
|
529
|
+
type: "error",
|
|
530
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
531
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
532
|
+
scope: "agenr_recall",
|
|
533
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
}
|
|
766
539
|
|
|
767
540
|
// src/adapters/openclaw/tools/retire.ts
|
|
768
|
-
import { failedTextResult as failedTextResult2, readStringParam as
|
|
541
|
+
import { failedTextResult as failedTextResult2, readStringParam as readStringParam2, textResult as textResult3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
769
542
|
var RETIRE_TOOL_PARAMETERS = {
|
|
770
543
|
type: "object",
|
|
771
544
|
additionalProperties: false,
|
|
@@ -793,12 +566,12 @@ function createAgenrRetireTool(ctx, servicesPromise, logger) {
|
|
|
793
566
|
async execute(_toolCallId, rawParams) {
|
|
794
567
|
try {
|
|
795
568
|
const params = asRecord(rawParams);
|
|
796
|
-
const id =
|
|
797
|
-
const subject =
|
|
798
|
-
const reason =
|
|
569
|
+
const id = readStringParam2(params, "id");
|
|
570
|
+
const subject = readStringParam2(params, "subject");
|
|
571
|
+
const reason = readStringParam2(params, "reason");
|
|
799
572
|
logToolCall(logger, "agenr_retire", ctx, `target=${formatTargetSelector(id, subject)}`, sanitizeRetireToolParams({ id, subject, reason }));
|
|
800
573
|
const services = await servicesPromise;
|
|
801
|
-
const entry = await
|
|
574
|
+
const entry = await resolveTargetEntry2(services, params);
|
|
802
575
|
const retired = await services.entries.retireEntry(entry.id, reason);
|
|
803
576
|
if (!retired) {
|
|
804
577
|
return failedTextResult2(`Entry ${entry.id} is not active, so it could not be retired.`, {
|
|
@@ -806,7 +579,7 @@ function createAgenrRetireTool(ctx, servicesPromise, logger) {
|
|
|
806
579
|
entryId: entry.id
|
|
807
580
|
});
|
|
808
581
|
}
|
|
809
|
-
return
|
|
582
|
+
return textResult3(`Retired "${entry.subject}".`, {
|
|
810
583
|
status: "retired",
|
|
811
584
|
entryId: entry.id,
|
|
812
585
|
subject: entry.subject,
|
|
@@ -821,63 +594,6 @@ function createAgenrRetireTool(ctx, servicesPromise, logger) {
|
|
|
821
594
|
}
|
|
822
595
|
|
|
823
596
|
// src/adapters/openclaw/tools/store.ts
|
|
824
|
-
import { failedTextResult as failedTextResult3, readNumberParam as readNumberParam2, readStringArrayParam as readStringArrayParam2, readStringParam as readStringParam4, textResult as textResult3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
825
|
-
var STORE_TOOL_PARAMETERS = {
|
|
826
|
-
type: "object",
|
|
827
|
-
additionalProperties: false,
|
|
828
|
-
properties: {
|
|
829
|
-
type: {
|
|
830
|
-
type: "string",
|
|
831
|
-
enum: [...ENTRY_TYPES],
|
|
832
|
-
description: ENTRY_TYPE_DESCRIPTION
|
|
833
|
-
},
|
|
834
|
-
subject: {
|
|
835
|
-
type: "string",
|
|
836
|
-
description: "Short subject line future recall can match. Name the durable takeaway, person, system, rule, relationship, or milestone directly."
|
|
837
|
-
},
|
|
838
|
-
content: {
|
|
839
|
-
type: "string",
|
|
840
|
-
description: "What a fresh session should remember. Store the durable takeaway, not the activity log, canonical record, or transient progress snapshot."
|
|
841
|
-
},
|
|
842
|
-
importance: {
|
|
843
|
-
type: "integer",
|
|
844
|
-
minimum: 1,
|
|
845
|
-
maximum: 10,
|
|
846
|
-
description: "Importance from 1 to 10. Use 7 for normal durable memory, 9 for critical constraints, and 10 only rarely."
|
|
847
|
-
},
|
|
848
|
-
expiry: {
|
|
849
|
-
type: "string",
|
|
850
|
-
enum: ["core", "permanent", "temporary"],
|
|
851
|
-
description: EXPIRY_DESCRIPTION
|
|
852
|
-
},
|
|
853
|
-
tags: {
|
|
854
|
-
type: "array",
|
|
855
|
-
items: { type: "string" },
|
|
856
|
-
description: "Optional tags for entities, systems, teams, or themes that should improve later recall."
|
|
857
|
-
},
|
|
858
|
-
sourceContext: {
|
|
859
|
-
type: "string",
|
|
860
|
-
description: "Optional provenance note explaining why this memory was stored or what situation produced it."
|
|
861
|
-
},
|
|
862
|
-
supersedes: {
|
|
863
|
-
type: "string",
|
|
864
|
-
description: "ID of an entry this replaces. The old entry will be marked as superseded."
|
|
865
|
-
},
|
|
866
|
-
claimKey: {
|
|
867
|
-
type: "string",
|
|
868
|
-
description: 'Slot key identifying the specific knowledge slot (entity/attribute format, e.g., "project_name/deploy_strategy" or "postgres/max_connections"). Entries with the same claim key are candidates for supersession.'
|
|
869
|
-
},
|
|
870
|
-
validFrom: {
|
|
871
|
-
type: "string",
|
|
872
|
-
description: "ISO 8601 timestamp for when this fact became true in the world."
|
|
873
|
-
},
|
|
874
|
-
validTo: {
|
|
875
|
-
type: "string",
|
|
876
|
-
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
877
|
-
}
|
|
878
|
-
},
|
|
879
|
-
required: ["type", "subject", "content"]
|
|
880
|
-
};
|
|
881
597
|
function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
882
598
|
return {
|
|
883
599
|
name: "agenr_store",
|
|
@@ -886,95 +602,17 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
|
886
602
|
parameters: STORE_TOOL_PARAMETERS,
|
|
887
603
|
async execute(_toolCallId, rawParams) {
|
|
888
604
|
try {
|
|
889
|
-
const params =
|
|
890
|
-
|
|
891
|
-
const subject = readStringParam4(params, "subject", { required: true, label: "subject" });
|
|
892
|
-
const content = readStringParam4(params, "content", { required: true, label: "content" });
|
|
893
|
-
const importance = readNumberParam2(params, "importance", { integer: true, strict: true });
|
|
894
|
-
const expiry = parseExpiry(readStringParam4(params, "expiry"));
|
|
895
|
-
const tags = normalizeStringArray(readStringArrayParam2(params, "tags"));
|
|
896
|
-
const sourceContext = readStringParam4(params, "sourceContext");
|
|
897
|
-
const supersedes = readStringParam4(params, "supersedes");
|
|
898
|
-
const claimKey = readStringParam4(params, "claimKey", { trim: false });
|
|
899
|
-
const validFrom = readStringParam4(params, "validFrom");
|
|
900
|
-
const validTo = readStringParam4(params, "validTo");
|
|
901
|
-
const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
902
|
-
logToolCall(
|
|
903
|
-
logger,
|
|
904
|
-
"agenr_store",
|
|
905
|
-
ctx,
|
|
906
|
-
`store 1 entry subject=${JSON.stringify(subject)} type=${type}`,
|
|
907
|
-
sanitizeStoreToolParams({
|
|
908
|
-
type,
|
|
909
|
-
subject,
|
|
910
|
-
content,
|
|
911
|
-
importance,
|
|
912
|
-
expiry,
|
|
913
|
-
tags,
|
|
914
|
-
sourceContext,
|
|
915
|
-
supersedes,
|
|
916
|
-
claimKey,
|
|
917
|
-
validFrom,
|
|
918
|
-
validTo
|
|
919
|
-
})
|
|
920
|
-
);
|
|
605
|
+
const params = parseStoreToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
606
|
+
logToolCall(logger, "agenr_store", ctx, `store 1 entry subject=${JSON.stringify(params.subject)} type=${params.type}`, sanitizeStoreToolParams(params));
|
|
921
607
|
const services = await servicesPromise;
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
content,
|
|
928
|
-
...importance !== void 0 ? { importance } : {},
|
|
929
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
930
|
-
...tags.length > 0 ? { tags } : {},
|
|
931
|
-
...supersedes ? { supersedes } : {},
|
|
932
|
-
...claimKey ? {
|
|
933
|
-
claim_key: claimKey,
|
|
934
|
-
claim_key_raw: claimKey,
|
|
935
|
-
...buildToolCallClaimSupport(ctx, "agenr_store", claimSupportObservedAt)
|
|
936
|
-
} : {},
|
|
937
|
-
...validFrom ? { valid_from: validFrom } : {},
|
|
938
|
-
...validTo ? { valid_to: validTo } : {},
|
|
939
|
-
source_file: buildSessionSourceFile(ctx),
|
|
940
|
-
source_context: sourceContext ?? "Stored via agenr_store from OpenClaw."
|
|
941
|
-
}
|
|
942
|
-
],
|
|
943
|
-
services.entries,
|
|
944
|
-
services.embedding,
|
|
945
|
-
{
|
|
946
|
-
...services.claimExtraction ? {
|
|
947
|
-
claimExtraction: {
|
|
948
|
-
llm: services.claimExtraction.llm,
|
|
949
|
-
db: services.entries,
|
|
950
|
-
config: services.claimExtraction.config
|
|
951
|
-
}
|
|
952
|
-
} : {},
|
|
608
|
+
return toOpenClawToolResult(
|
|
609
|
+
await runStoreMemoryTool(params, services, {
|
|
610
|
+
session: ctx,
|
|
611
|
+
sourcePrefix: "openclaw-session",
|
|
612
|
+
defaultSourceContext: "Stored via agenr_store from OpenClaw.",
|
|
953
613
|
onWarning: (warning) => logger.warn(`[agenr] tool=agenr_store session=${ctx.sessionId ?? "unknown"} warning: ${warning}`)
|
|
954
|
-
}
|
|
614
|
+
})
|
|
955
615
|
);
|
|
956
|
-
const storedEntry = await services.memory.findEntryBySubject(subject);
|
|
957
|
-
if (result.stored > 0) {
|
|
958
|
-
return textResult3(`Stored "${subject}".`, {
|
|
959
|
-
status: "stored",
|
|
960
|
-
subject,
|
|
961
|
-
entryId: storedEntry?.id,
|
|
962
|
-
result
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
if (result.skipped > 0) {
|
|
966
|
-
return textResult3(`Skipped "${subject}" because an active duplicate already exists.`, {
|
|
967
|
-
status: "skipped",
|
|
968
|
-
subject,
|
|
969
|
-
entryId: storedEntry?.id,
|
|
970
|
-
result
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
return failedTextResult3(`Rejected "${subject}". Check the supplied type, content, and metadata.`, {
|
|
974
|
-
status: "failed",
|
|
975
|
-
subject,
|
|
976
|
-
result
|
|
977
|
-
});
|
|
978
616
|
} catch (error) {
|
|
979
617
|
logToolFailure(logger, "agenr_store", ctx, error);
|
|
980
618
|
return toolFailureResult(error);
|
|
@@ -984,7 +622,7 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
|
984
622
|
}
|
|
985
623
|
|
|
986
624
|
// src/adapters/openclaw/tools/trace.ts
|
|
987
|
-
import { failedTextResult as
|
|
625
|
+
import { failedTextResult as failedTextResult3, readStringParam as readStringParam3, textResult as textResult4 } from "openclaw/plugin-sdk/agent-runtime";
|
|
988
626
|
var TRACE_TOOL_PARAMETERS = {
|
|
989
627
|
type: "object",
|
|
990
628
|
additionalProperties: false,
|
|
@@ -1012,15 +650,15 @@ function createAgenrTraceTool(ctx, servicesPromise, logger) {
|
|
|
1012
650
|
async execute(_toolCallId, rawParams) {
|
|
1013
651
|
try {
|
|
1014
652
|
const params = asRecord(rawParams);
|
|
1015
|
-
const id =
|
|
1016
|
-
const subject =
|
|
653
|
+
const id = readStringParam3(params, "id");
|
|
654
|
+
const subject = readStringParam3(params, "subject");
|
|
1017
655
|
const last = readBooleanParam(params, "last");
|
|
1018
656
|
logToolCall(logger, "agenr_trace", ctx, `target=${formatTargetSelector(id, subject, last)}`, sanitizeTraceToolParams({ id, subject, last }));
|
|
1019
657
|
const services = await servicesPromise;
|
|
1020
|
-
const entry = await
|
|
658
|
+
const entry = await resolveTargetEntry2(services, params, { allowLast: true });
|
|
1021
659
|
const trace = await services.memory.getEntryTrace(entry.id);
|
|
1022
660
|
if (!trace) {
|
|
1023
|
-
return
|
|
661
|
+
return failedTextResult3(`Entry ${entry.id} was not found for tracing.`, {
|
|
1024
662
|
status: "failed",
|
|
1025
663
|
entryId: entry.id
|
|
1026
664
|
});
|
|
@@ -1045,41 +683,6 @@ function createAgenrTraceTool(ctx, servicesPromise, logger) {
|
|
|
1045
683
|
}
|
|
1046
684
|
|
|
1047
685
|
// src/adapters/openclaw/tools/update.ts
|
|
1048
|
-
import { failedTextResult as failedTextResult5, readNumberParam as readNumberParam3, readStringParam as readStringParam6, textResult as textResult5 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1049
|
-
var UPDATE_TOOL_PARAMETERS = {
|
|
1050
|
-
type: "object",
|
|
1051
|
-
additionalProperties: false,
|
|
1052
|
-
properties: {
|
|
1053
|
-
id: {
|
|
1054
|
-
type: "string",
|
|
1055
|
-
description: "Entry id to update. Provide exactly one of id or subject."
|
|
1056
|
-
},
|
|
1057
|
-
subject: {
|
|
1058
|
-
type: "string",
|
|
1059
|
-
description: "Subject text to resolve when the id is unknown. The most recent exact or substring match wins. Provide exactly one of id or subject."
|
|
1060
|
-
},
|
|
1061
|
-
importance: {
|
|
1062
|
-
type: "integer",
|
|
1063
|
-
description: "New importance from 1 to 10. Use 7 for normal durable memory and reserve 9 to 10 for rare critical entries."
|
|
1064
|
-
},
|
|
1065
|
-
expiry: {
|
|
1066
|
-
type: "string",
|
|
1067
|
-
description: UPDATE_EXPIRY_DESCRIPTION
|
|
1068
|
-
},
|
|
1069
|
-
claimKey: {
|
|
1070
|
-
type: "string",
|
|
1071
|
-
description: 'Slot key identifying the specific knowledge slot (entity/attribute format, e.g., "project_name/deploy_strategy" or "postgres/max_connections"). Entries with the same claim key are candidates for supersession.'
|
|
1072
|
-
},
|
|
1073
|
-
validFrom: {
|
|
1074
|
-
type: "string",
|
|
1075
|
-
description: "ISO 8601 timestamp for when this fact became true."
|
|
1076
|
-
},
|
|
1077
|
-
validTo: {
|
|
1078
|
-
type: "string",
|
|
1079
|
-
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
686
|
function createAgenrUpdateTool(ctx, servicesPromise, logger) {
|
|
1084
687
|
return {
|
|
1085
688
|
name: "agenr_update",
|
|
@@ -1088,72 +691,30 @@ function createAgenrUpdateTool(ctx, servicesPromise, logger) {
|
|
|
1088
691
|
parameters: UPDATE_TOOL_PARAMETERS,
|
|
1089
692
|
async execute(_toolCallId, rawParams) {
|
|
1090
693
|
try {
|
|
1091
|
-
const params =
|
|
1092
|
-
const id = readStringParam6(params, "id");
|
|
1093
|
-
const subject = readStringParam6(params, "subject");
|
|
1094
|
-
const importance = readNumberParam3(params, "importance", { integer: true, strict: true });
|
|
1095
|
-
const expiry = parseExpiry(readStringParam6(params, "expiry"));
|
|
1096
|
-
const claimKeyInput = readStringParam6(params, "claimKey", { trim: false });
|
|
1097
|
-
const validFrom = readStringParam6(params, "validFrom");
|
|
1098
|
-
const validTo = readStringParam6(params, "validTo");
|
|
1099
|
-
const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1100
|
-
const claimSupport = claimKeyInput === void 0 ? void 0 : buildToolCallClaimSupport(ctx, "agenr_update", claimSupportObservedAt);
|
|
1101
|
-
const normalizedClaimKeyUpdate = claimKeyInput === void 0 ? void 0 : (() => {
|
|
1102
|
-
try {
|
|
1103
|
-
return normalizeManualClaimKeyUpdate({
|
|
1104
|
-
claimKey: claimKeyInput,
|
|
1105
|
-
rawClaimKey: claimKeyInput,
|
|
1106
|
-
supportSourceKind: claimSupport?.claim_support_source_kind,
|
|
1107
|
-
supportLocator: claimSupport?.claim_support_locator,
|
|
1108
|
-
supportObservedAt: claimSupport?.claim_support_observed_at,
|
|
1109
|
-
supportMode: claimSupport?.claim_support_mode
|
|
1110
|
-
});
|
|
1111
|
-
} catch {
|
|
1112
|
-
throw new Error("claimKey must use canonical entity/attribute format.");
|
|
1113
|
-
}
|
|
1114
|
-
})();
|
|
694
|
+
const params = parseUpdateToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
1115
695
|
logToolCall(
|
|
1116
696
|
logger,
|
|
1117
697
|
"agenr_update",
|
|
1118
698
|
ctx,
|
|
1119
|
-
`target=${formatTargetSelector(id, subject)}${importance !== void 0 ? ` importance=${importance}` : ""}${expiry !== void 0 ? ` expiry=${expiry}` : ""}`,
|
|
1120
|
-
sanitizeUpdateToolParams({
|
|
699
|
+
`target=${formatTargetSelector(params.id, params.subject)}${params.importance !== void 0 ? ` importance=${params.importance}` : ""}${params.expiry !== void 0 ? ` expiry=${params.expiry}` : ""}`,
|
|
700
|
+
sanitizeUpdateToolParams({
|
|
701
|
+
id: params.id,
|
|
702
|
+
subject: params.subject,
|
|
703
|
+
importance: params.importance,
|
|
704
|
+
expiry: params.expiry,
|
|
705
|
+
claimKey: params.claimKeyInput,
|
|
706
|
+
validFrom: params.validFrom,
|
|
707
|
+
validTo: params.validTo
|
|
708
|
+
})
|
|
1121
709
|
);
|
|
1122
710
|
const services = await servicesPromise;
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
const normalizedValidFrom = validFrom !== void 0 ? mergedValidity.value.validFrom : void 0;
|
|
1132
|
-
const normalizedValidTo = validTo !== void 0 ? mergedValidity.value.validTo : void 0;
|
|
1133
|
-
const updated = await services.entries.updateEntry(entry.id, {
|
|
1134
|
-
...importance !== void 0 ? { importance } : {},
|
|
1135
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
1136
|
-
...normalizedClaimKeyUpdate?.updateFields ?? {},
|
|
1137
|
-
...validFrom !== void 0 ? { valid_from: normalizedValidFrom } : {},
|
|
1138
|
-
...validTo !== void 0 ? { valid_to: normalizedValidTo } : {}
|
|
1139
|
-
});
|
|
1140
|
-
if (!updated) {
|
|
1141
|
-
return failedTextResult5(`Entry ${entry.id} is not active, so it could not be updated.`, {
|
|
1142
|
-
status: "failed",
|
|
1143
|
-
entryId: entry.id
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
return textResult5(`Updated "${entry.subject}".`, {
|
|
1147
|
-
status: "updated",
|
|
1148
|
-
entryId: entry.id,
|
|
1149
|
-
subject: entry.subject,
|
|
1150
|
-
sessionKey: ctx.sessionKey,
|
|
1151
|
-
...importance !== void 0 ? { importance } : {},
|
|
1152
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
1153
|
-
...normalizedClaimKeyUpdate !== void 0 ? { claimKey: normalizedClaimKeyUpdate.claimKey } : {},
|
|
1154
|
-
...validFrom !== void 0 ? { validFrom: normalizedValidFrom } : {},
|
|
1155
|
-
...validTo !== void 0 ? { validTo: normalizedValidTo } : {}
|
|
1156
|
-
});
|
|
711
|
+
return toOpenClawToolResult(
|
|
712
|
+
await runUpdateMemoryTool(params, services, {
|
|
713
|
+
session: ctx,
|
|
714
|
+
sourcePrefix: "openclaw-session",
|
|
715
|
+
successDetails: { sessionKey: ctx.sessionKey }
|
|
716
|
+
})
|
|
717
|
+
);
|
|
1157
718
|
} catch (error) {
|
|
1158
719
|
logToolFailure(logger, "agenr_update", ctx, error);
|
|
1159
720
|
return toolFailureResult(error);
|
|
@@ -1175,7 +736,7 @@ function registerAgenrOpenClawTools(api, servicesPromise, logger) {
|
|
|
1175
736
|
var openclaw_plugin_default = {
|
|
1176
737
|
id: "agenr",
|
|
1177
738
|
name: "agenr",
|
|
1178
|
-
version: "
|
|
739
|
+
version: "3.0.0",
|
|
1179
740
|
description: "agenr memory plugin for OpenClaw",
|
|
1180
741
|
kind: "memory",
|
|
1181
742
|
contracts: {
|
|
@@ -1208,7 +769,11 @@ var openclaw_plugin_default = {
|
|
|
1208
769
|
},
|
|
1209
770
|
memoryPolicy: {
|
|
1210
771
|
label: "Memory policy",
|
|
1211
|
-
help: "Optional runtime overrides for claim-aware read behavior
|
|
772
|
+
help: "Optional runtime overrides for claim-aware read behavior, session-start memory injection, and proactive before-turn surfacing."
|
|
773
|
+
},
|
|
774
|
+
debug: {
|
|
775
|
+
label: "Debug log",
|
|
776
|
+
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
777
|
}
|
|
1213
778
|
},
|
|
1214
779
|
configSchema: {
|
|
@@ -1259,11 +824,99 @@ var openclaw_plugin_default = {
|
|
|
1259
824
|
}
|
|
1260
825
|
}
|
|
1261
826
|
},
|
|
827
|
+
debug: {
|
|
828
|
+
type: "object",
|
|
829
|
+
additionalProperties: false,
|
|
830
|
+
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.",
|
|
831
|
+
properties: {
|
|
832
|
+
enabled: {
|
|
833
|
+
type: "boolean",
|
|
834
|
+
description: "Enable or disable the agenr JSONL debug sink. Defaults to false."
|
|
835
|
+
},
|
|
836
|
+
logPath: {
|
|
837
|
+
type: "string",
|
|
838
|
+
minLength: 1,
|
|
839
|
+
description: "Optional explicit log-file path. Defaults to agenr-debug.jsonl inside the OpenClaw state directory."
|
|
840
|
+
},
|
|
841
|
+
eventLevel: {
|
|
842
|
+
type: "string",
|
|
843
|
+
enum: ["basic", "detailed"],
|
|
844
|
+
description: "Event detail level. Detailed enables bounded top-K candidate breakdowns for recall and before-turn events. Defaults to basic."
|
|
845
|
+
},
|
|
846
|
+
perSessionFiles: {
|
|
847
|
+
type: "boolean",
|
|
848
|
+
description: "Split one JSONL file per OpenClaw session id. Defaults to false."
|
|
849
|
+
},
|
|
850
|
+
maxTopCandidates: {
|
|
851
|
+
type: "integer",
|
|
852
|
+
minimum: 1,
|
|
853
|
+
maximum: 25,
|
|
854
|
+
description: "Cap for top-K candidate breakdowns included in detailed events. Defaults to 10."
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
},
|
|
1262
858
|
memoryPolicy: {
|
|
1263
859
|
type: "object",
|
|
1264
860
|
additionalProperties: false,
|
|
1265
861
|
description: "Optional runtime overrides for claim-aware read behavior exposed by the OpenClaw adapter.",
|
|
1266
862
|
properties: {
|
|
863
|
+
sessionStart: {
|
|
864
|
+
type: "object",
|
|
865
|
+
additionalProperties: false,
|
|
866
|
+
description: "Optional session-start overrides for prompt-time memory injection behavior.",
|
|
867
|
+
properties: {
|
|
868
|
+
enabled: {
|
|
869
|
+
type: "boolean",
|
|
870
|
+
description: "Enable or disable all session-start memory injection. Defaults to true."
|
|
871
|
+
},
|
|
872
|
+
coreMemory: {
|
|
873
|
+
type: "boolean",
|
|
874
|
+
description: "Enable or disable always-on Core Memory injection at session start. Defaults to true."
|
|
875
|
+
},
|
|
876
|
+
relevantDurableMemory: {
|
|
877
|
+
type: "boolean",
|
|
878
|
+
description: "Enable or disable artifact-grounded Relevant Durable Memory injection at session start. Defaults to true."
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
beforeTurn: {
|
|
883
|
+
type: "object",
|
|
884
|
+
additionalProperties: false,
|
|
885
|
+
description: "Optional before-turn overrides for proactive prompt-time memory injection behavior.",
|
|
886
|
+
properties: {
|
|
887
|
+
enabled: {
|
|
888
|
+
type: "boolean",
|
|
889
|
+
description: "Enable or disable the proactive before-turn memory patch. Defaults to true."
|
|
890
|
+
},
|
|
891
|
+
procedureSuggestion: {
|
|
892
|
+
type: "boolean",
|
|
893
|
+
description: "Enable or disable proactive high-confidence procedure suggestion inside the before-turn patch. Defaults to true."
|
|
894
|
+
},
|
|
895
|
+
maxDurableEntries: {
|
|
896
|
+
type: "integer",
|
|
897
|
+
minimum: 1,
|
|
898
|
+
description: "Normal durable-item cap for before-turn recall. Defaults to 1 and only expands when all surfaced items are very high confidence."
|
|
899
|
+
},
|
|
900
|
+
recallThreshold: {
|
|
901
|
+
type: "number",
|
|
902
|
+
minimum: 0,
|
|
903
|
+
maximum: 1,
|
|
904
|
+
description: "Durable-recall score threshold required before an entry can surface during before-turn recall. Defaults to 0.6."
|
|
905
|
+
},
|
|
906
|
+
highConfidenceRecallThreshold: {
|
|
907
|
+
type: "number",
|
|
908
|
+
minimum: 0,
|
|
909
|
+
maximum: 1,
|
|
910
|
+
description: "Durable-recall score threshold required before before-turn recall can expand beyond the normal durable-item cap. Defaults to 0.85."
|
|
911
|
+
},
|
|
912
|
+
procedureThreshold: {
|
|
913
|
+
type: "number",
|
|
914
|
+
minimum: 0,
|
|
915
|
+
maximum: 1,
|
|
916
|
+
description: "Procedure-recall score threshold required before a proactive procedure can surface. Defaults to 0.72."
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
},
|
|
1267
920
|
slotPolicies: {
|
|
1268
921
|
type: "object",
|
|
1269
922
|
additionalProperties: false,
|
|
@@ -1292,6 +945,10 @@ var openclaw_plugin_default = {
|
|
|
1292
945
|
var manifest = openclaw_plugin_default;
|
|
1293
946
|
var DEFAULT_STORE_NUDGE_THRESHOLD = 8;
|
|
1294
947
|
var DEFAULT_STORE_NUDGE_MAX_PER_SESSION = 5;
|
|
948
|
+
var DEFAULT_DEBUG_EVENT_LEVEL = "basic";
|
|
949
|
+
var DEFAULT_DEBUG_PER_SESSION_FILES = false;
|
|
950
|
+
var DEFAULT_DEBUG_MAX_TOP_CANDIDATES = 10;
|
|
951
|
+
var MAX_DEBUG_MAX_TOP_CANDIDATES = 25;
|
|
1295
952
|
function normalizeAgenrOpenClawPluginConfig(value) {
|
|
1296
953
|
if (value === void 0) {
|
|
1297
954
|
return { ok: true, value: {} };
|
|
@@ -1335,11 +992,15 @@ function normalizeAgenrOpenClawPluginConfig(value) {
|
|
|
1335
992
|
if (!storeNudgeResult.ok) {
|
|
1336
993
|
errors.push(...storeNudgeResult.errors);
|
|
1337
994
|
}
|
|
1338
|
-
const memoryPolicyResult =
|
|
995
|
+
const memoryPolicyResult = normalizePluginInjectionMemoryPolicyConfig(value.memoryPolicy);
|
|
1339
996
|
if (!memoryPolicyResult.ok) {
|
|
1340
997
|
errors.push(...memoryPolicyResult.errors);
|
|
1341
998
|
}
|
|
1342
|
-
const
|
|
999
|
+
const debugResult = normalizeDebugConfig(value.debug);
|
|
1000
|
+
if (!debugResult.ok) {
|
|
1001
|
+
errors.push(...debugResult.errors);
|
|
1002
|
+
}
|
|
1003
|
+
const allowedKeys = /* @__PURE__ */ new Set(["dbPath", "configPath", "continuityModel", "episodeModel", "claimExtractionModel", "storeNudge", "memoryPolicy", "debug"]);
|
|
1343
1004
|
for (const key of Object.keys(value)) {
|
|
1344
1005
|
if (!allowedKeys.has(key)) {
|
|
1345
1006
|
errors.push(`unknown config field: ${key}`);
|
|
@@ -1357,7 +1018,8 @@ function normalizeAgenrOpenClawPluginConfig(value) {
|
|
|
1357
1018
|
...episodeModel ? { episodeModel } : {},
|
|
1358
1019
|
...claimExtractionModel ? { claimExtractionModel } : {},
|
|
1359
1020
|
...storeNudgeResult.ok && storeNudgeResult.value ? { storeNudge: storeNudgeResult.value } : {},
|
|
1360
|
-
...memoryPolicyResult.ok && memoryPolicyResult.value ? { memoryPolicy: memoryPolicyResult.value } : {}
|
|
1021
|
+
...memoryPolicyResult.ok && memoryPolicyResult.value ? { memoryPolicy: memoryPolicyResult.value } : {},
|
|
1022
|
+
...debugResult.ok && debugResult.value ? { debug: debugResult.value } : {}
|
|
1361
1023
|
}
|
|
1362
1024
|
};
|
|
1363
1025
|
}
|
|
@@ -1381,137 +1043,115 @@ function createAgenrOpenClawPluginConfigSchema() {
|
|
|
1381
1043
|
function isRecord(value) {
|
|
1382
1044
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1383
1045
|
}
|
|
1384
|
-
function
|
|
1046
|
+
function resolveDebugConfig(value) {
|
|
1047
|
+
const logPath = value?.logPath?.trim();
|
|
1385
1048
|
return {
|
|
1386
|
-
enabled: value?.enabled ??
|
|
1387
|
-
|
|
1388
|
-
|
|
1049
|
+
enabled: value?.enabled ?? false,
|
|
1050
|
+
...logPath ? { logPath } : {},
|
|
1051
|
+
eventLevel: value?.eventLevel ?? DEFAULT_DEBUG_EVENT_LEVEL,
|
|
1052
|
+
perSessionFiles: value?.perSessionFiles ?? DEFAULT_DEBUG_PER_SESSION_FILES,
|
|
1053
|
+
maxTopCandidates: value?.maxTopCandidates ?? DEFAULT_DEBUG_MAX_TOP_CANDIDATES
|
|
1389
1054
|
};
|
|
1390
1055
|
}
|
|
1391
|
-
function
|
|
1056
|
+
function normalizeDebugConfig(value) {
|
|
1392
1057
|
if (value === void 0) {
|
|
1393
1058
|
return { ok: true, value: void 0 };
|
|
1394
1059
|
}
|
|
1395
1060
|
if (!isRecord(value)) {
|
|
1396
|
-
return { ok: false, errors: ["
|
|
1061
|
+
return { ok: false, errors: ["debug must be an object when provided"] };
|
|
1397
1062
|
}
|
|
1398
1063
|
const errors = [];
|
|
1399
|
-
const enabled = normalizeOptionalBoolean(value.enabled, "
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1064
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "debug.enabled", errors);
|
|
1065
|
+
const logPathRaw = value.logPath;
|
|
1066
|
+
let logPath;
|
|
1067
|
+
if (logPathRaw !== void 0) {
|
|
1068
|
+
if (typeof logPathRaw !== "string" || logPathRaw.trim().length === 0) {
|
|
1069
|
+
errors.push("debug.logPath must be a non-empty string when provided");
|
|
1070
|
+
} else {
|
|
1071
|
+
logPath = logPathRaw.trim();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
const eventLevel = normalizeOptionalDebugEventLevel(value.eventLevel, errors);
|
|
1075
|
+
const perSessionFiles = normalizeOptionalBoolean(value.perSessionFiles, "debug.perSessionFiles", errors);
|
|
1076
|
+
const maxTopCandidates = normalizeOptionalTopCandidateCap(value.maxTopCandidates, errors);
|
|
1077
|
+
const allowedKeys = /* @__PURE__ */ new Set(["enabled", "logPath", "eventLevel", "perSessionFiles", "maxTopCandidates"]);
|
|
1403
1078
|
for (const key of Object.keys(value)) {
|
|
1404
1079
|
if (!allowedKeys.has(key)) {
|
|
1405
|
-
errors.push(`unknown config field:
|
|
1080
|
+
errors.push(`unknown config field: debug.${key}`);
|
|
1406
1081
|
}
|
|
1407
1082
|
}
|
|
1408
1083
|
if (errors.length > 0) {
|
|
1409
1084
|
return { ok: false, errors };
|
|
1410
1085
|
}
|
|
1411
|
-
const
|
|
1086
|
+
const normalized = {
|
|
1412
1087
|
...enabled !== void 0 ? { enabled } : {},
|
|
1413
|
-
...
|
|
1414
|
-
...
|
|
1088
|
+
...logPath !== void 0 ? { logPath } : {},
|
|
1089
|
+
...eventLevel !== void 0 ? { eventLevel } : {},
|
|
1090
|
+
...perSessionFiles !== void 0 ? { perSessionFiles } : {},
|
|
1091
|
+
...maxTopCandidates !== void 0 ? { maxTopCandidates } : {}
|
|
1415
1092
|
};
|
|
1416
1093
|
return {
|
|
1417
1094
|
ok: true,
|
|
1418
|
-
value: Object.keys(
|
|
1095
|
+
value: Object.keys(normalized).length > 0 ? normalized : void 0
|
|
1419
1096
|
};
|
|
1420
1097
|
}
|
|
1421
|
-
function
|
|
1098
|
+
function normalizeOptionalDebugEventLevel(value, errors) {
|
|
1422
1099
|
if (value === void 0) {
|
|
1423
|
-
return
|
|
1424
|
-
}
|
|
1425
|
-
if (!isRecord(value)) {
|
|
1426
|
-
return { ok: false, errors: ["memoryPolicy must be an object when provided"] };
|
|
1100
|
+
return void 0;
|
|
1427
1101
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
if (!slotPoliciesResult.ok) {
|
|
1431
|
-
errors.push(...slotPoliciesResult.errors);
|
|
1102
|
+
if (value === "basic" || value === "detailed") {
|
|
1103
|
+
return value;
|
|
1432
1104
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1105
|
+
errors.push('debug.eventLevel must be "basic" or "detailed" when provided');
|
|
1106
|
+
return void 0;
|
|
1107
|
+
}
|
|
1108
|
+
function normalizeOptionalTopCandidateCap(value, errors) {
|
|
1109
|
+
if (value === void 0) {
|
|
1110
|
+
return void 0;
|
|
1438
1111
|
}
|
|
1439
|
-
if (
|
|
1440
|
-
|
|
1112
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0 || value > MAX_DEBUG_MAX_TOP_CANDIDATES) {
|
|
1113
|
+
errors.push(`debug.maxTopCandidates must be an integer between 1 and ${MAX_DEBUG_MAX_TOP_CANDIDATES} when provided`);
|
|
1114
|
+
return void 0;
|
|
1441
1115
|
}
|
|
1116
|
+
return value;
|
|
1117
|
+
}
|
|
1118
|
+
function resolveStoreNudgeConfig(value) {
|
|
1442
1119
|
return {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
} : void 0
|
|
1120
|
+
enabled: value?.enabled ?? true,
|
|
1121
|
+
threshold: value?.threshold ?? DEFAULT_STORE_NUDGE_THRESHOLD,
|
|
1122
|
+
maxPerSession: value?.maxPerSession ?? DEFAULT_STORE_NUDGE_MAX_PER_SESSION
|
|
1447
1123
|
};
|
|
1448
1124
|
}
|
|
1449
|
-
function
|
|
1125
|
+
function normalizeStoreNudgeConfig(value) {
|
|
1450
1126
|
if (value === void 0) {
|
|
1451
1127
|
return { ok: true, value: void 0 };
|
|
1452
1128
|
}
|
|
1453
1129
|
if (!isRecord(value)) {
|
|
1454
|
-
return { ok: false, errors: ["
|
|
1130
|
+
return { ok: false, errors: ["storeNudge must be an object when provided"] };
|
|
1455
1131
|
}
|
|
1456
1132
|
const errors = [];
|
|
1457
|
-
const
|
|
1458
|
-
const
|
|
1133
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "storeNudge.enabled", errors);
|
|
1134
|
+
const threshold = normalizeOptionalPositiveInteger(value.threshold, "storeNudge.threshold", errors);
|
|
1135
|
+
const maxPerSession = normalizeOptionalPositiveInteger(value.maxPerSession, "storeNudge.maxPerSession", errors);
|
|
1136
|
+
const allowedKeys = /* @__PURE__ */ new Set(["enabled", "threshold", "maxPerSession"]);
|
|
1459
1137
|
for (const key of Object.keys(value)) {
|
|
1460
1138
|
if (!allowedKeys.has(key)) {
|
|
1461
|
-
errors.push(`unknown config field:
|
|
1139
|
+
errors.push(`unknown config field: storeNudge.${key}`);
|
|
1462
1140
|
}
|
|
1463
1141
|
}
|
|
1464
1142
|
if (errors.length > 0) {
|
|
1465
1143
|
return { ok: false, errors };
|
|
1466
1144
|
}
|
|
1145
|
+
const normalizedValue = {
|
|
1146
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
1147
|
+
...threshold !== void 0 ? { threshold } : {},
|
|
1148
|
+
...maxPerSession !== void 0 ? { maxPerSession } : {}
|
|
1149
|
+
};
|
|
1467
1150
|
return {
|
|
1468
1151
|
ok: true,
|
|
1469
|
-
value:
|
|
1152
|
+
value: Object.keys(normalizedValue).length > 0 ? resolveStoreNudgeConfig(normalizedValue) : void 0
|
|
1470
1153
|
};
|
|
1471
1154
|
}
|
|
1472
|
-
function normalizeClaimSlotPolicyAttributeHeads(value, errors) {
|
|
1473
|
-
if (value === void 0) {
|
|
1474
|
-
return void 0;
|
|
1475
|
-
}
|
|
1476
|
-
if (!isRecord(value)) {
|
|
1477
|
-
errors.push("memoryPolicy.slotPolicies.attributeHeads must be an object when provided");
|
|
1478
|
-
return void 0;
|
|
1479
|
-
}
|
|
1480
|
-
const normalized = {};
|
|
1481
|
-
for (const [rawKey, rawPolicy] of Object.entries(value)) {
|
|
1482
|
-
const attributeHead = rawKey.trim().toLowerCase();
|
|
1483
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/.test(attributeHead)) {
|
|
1484
|
-
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${rawKey} must use a canonical attribute-head label`);
|
|
1485
|
-
continue;
|
|
1486
|
-
}
|
|
1487
|
-
if (rawPolicy !== "exclusive" && rawPolicy !== "multivalued") {
|
|
1488
|
-
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${attributeHead} must be "exclusive" or "multivalued"`);
|
|
1489
|
-
continue;
|
|
1490
|
-
}
|
|
1491
|
-
normalized[attributeHead] = rawPolicy;
|
|
1492
|
-
}
|
|
1493
|
-
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
1494
|
-
}
|
|
1495
|
-
function normalizeOptionalBoolean(value, label, errors) {
|
|
1496
|
-
if (value === void 0) {
|
|
1497
|
-
return void 0;
|
|
1498
|
-
}
|
|
1499
|
-
if (typeof value !== "boolean") {
|
|
1500
|
-
errors.push(`${label} must be a boolean when provided`);
|
|
1501
|
-
return void 0;
|
|
1502
|
-
}
|
|
1503
|
-
return value;
|
|
1504
|
-
}
|
|
1505
|
-
function normalizeOptionalPositiveInteger(value, label, errors) {
|
|
1506
|
-
if (value === void 0) {
|
|
1507
|
-
return void 0;
|
|
1508
|
-
}
|
|
1509
|
-
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
1510
|
-
errors.push(`${label} must be a positive integer when provided`);
|
|
1511
|
-
return void 0;
|
|
1512
|
-
}
|
|
1513
|
-
return value;
|
|
1514
|
-
}
|
|
1515
1155
|
|
|
1516
1156
|
// src/adapters/openclaw/format/prompt-section.ts
|
|
1517
1157
|
var MEMORY_TOOL_NAMES = {
|
|
@@ -1535,10 +1175,11 @@ function buildAgenrMemoryPromptSection({
|
|
|
1535
1175
|
);
|
|
1536
1176
|
const lines = [
|
|
1537
1177
|
"## 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.",
|
|
1178
|
+
"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
1179
|
"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
1180
|
"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
1181
|
"One focused agenr_recall call with the right scope beats several broad ones.",
|
|
1182
|
+
"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
1183
|
"Memory authority, strongest to weakest:",
|
|
1543
1184
|
"- Durable entries are the canonical record for verified facts, decisions, preferences, and lessons unless live evidence contradicts them.",
|
|
1544
1185
|
"- Episode recall explains what happened in completed sessions, but it is a narrative summary, not an exact log.",
|
|
@@ -1606,62 +1247,10 @@ function formatSessionContext(sessionId, sessionKey) {
|
|
|
1606
1247
|
return "session=unknown";
|
|
1607
1248
|
}
|
|
1608
1249
|
function formatErrorMessage2(error) {
|
|
1609
|
-
return error instanceof Error ? error.message : String(error);
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
// src/adapters/openclaw/session/identity.ts
|
|
1613
|
-
function resolveSessionIdentityKey(sessionId, sessionKey) {
|
|
1614
|
-
const normalizedSessionId = sessionId?.trim();
|
|
1615
|
-
if (normalizedSessionId) {
|
|
1616
|
-
return `session:${normalizedSessionId}`;
|
|
1617
|
-
}
|
|
1618
|
-
const normalizedSessionKey = sessionKey?.trim();
|
|
1619
|
-
if (normalizedSessionKey) {
|
|
1620
|
-
return `key:${normalizedSessionKey}`;
|
|
1621
|
-
}
|
|
1622
|
-
return void 0;
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
// src/adapters/openclaw/session/state.ts
|
|
1626
|
-
function createSessionStartTracker() {
|
|
1627
|
-
const seenSessionIdentities = /* @__PURE__ */ new Set();
|
|
1628
|
-
const resumedFromBySessionId = /* @__PURE__ */ new Map();
|
|
1629
|
-
return {
|
|
1630
|
-
consume(sessionId, sessionKey) {
|
|
1631
|
-
const identityKey = resolveSessionIdentityKey(sessionId, sessionKey);
|
|
1632
|
-
if (!identityKey) {
|
|
1633
|
-
return {
|
|
1634
|
-
isFirst: true,
|
|
1635
|
-
activeCount: seenSessionIdentities.size
|
|
1636
|
-
};
|
|
1637
|
-
}
|
|
1638
|
-
if (seenSessionIdentities.has(identityKey)) {
|
|
1639
|
-
return {
|
|
1640
|
-
isFirst: false,
|
|
1641
|
-
activeCount: seenSessionIdentities.size
|
|
1642
|
-
};
|
|
1643
|
-
}
|
|
1644
|
-
seenSessionIdentities.add(identityKey);
|
|
1645
|
-
return {
|
|
1646
|
-
isFirst: true,
|
|
1647
|
-
activeCount: seenSessionIdentities.size
|
|
1648
|
-
};
|
|
1649
|
-
},
|
|
1650
|
-
rememberSessionStart(sessionId, _sessionKey, resumedFrom) {
|
|
1651
|
-
const normalizedSessionId = sessionId?.trim();
|
|
1652
|
-
const normalizedResumedFrom = resumedFrom?.trim();
|
|
1653
|
-
if (!normalizedSessionId || !normalizedResumedFrom) {
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
resumedFromBySessionId.set(normalizedSessionId, normalizedResumedFrom);
|
|
1657
|
-
},
|
|
1658
|
-
getResumedFrom(sessionId) {
|
|
1659
|
-
const normalizedSessionId = sessionId?.trim();
|
|
1660
|
-
return normalizedSessionId ? resumedFromBySessionId.get(normalizedSessionId) : void 0;
|
|
1661
|
-
}
|
|
1662
|
-
};
|
|
1250
|
+
return error instanceof Error ? error.message : String(error);
|
|
1663
1251
|
}
|
|
1664
|
-
|
|
1252
|
+
|
|
1253
|
+
// src/adapters/openclaw/session/state.ts
|
|
1665
1254
|
function createMidSessionTracker() {
|
|
1666
1255
|
const states = /* @__PURE__ */ new Map();
|
|
1667
1256
|
return {
|
|
@@ -1724,6 +1313,7 @@ function createMidSessionState() {
|
|
|
1724
1313
|
storedSubjects: []
|
|
1725
1314
|
};
|
|
1726
1315
|
}
|
|
1316
|
+
var MAX_STORED_SUBJECTS = 5;
|
|
1727
1317
|
|
|
1728
1318
|
// src/adapters/openclaw/hooks/after-tool-call.ts
|
|
1729
1319
|
var STORE_TOOL_NAME = "agenr_store";
|
|
@@ -1800,16 +1390,20 @@ function hasNonEmptyString(value) {
|
|
|
1800
1390
|
return readString(value) !== void 0;
|
|
1801
1391
|
}
|
|
1802
1392
|
|
|
1393
|
+
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
1394
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1395
|
+
import path5 from "path";
|
|
1396
|
+
|
|
1803
1397
|
// src/adapters/openclaw/episode/episode-writer.ts
|
|
1804
1398
|
import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary2, resolveDefaultAgentId as resolveDefaultAgentId2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1805
1399
|
|
|
1806
1400
|
// src/adapters/openclaw/llm/openclaw-llm-client.ts
|
|
1807
|
-
import { completeSimple, getModel } from "@
|
|
1401
|
+
import { completeSimple, getModel } from "@earendil-works/pi-ai";
|
|
1808
1402
|
|
|
1809
1403
|
// src/adapters/openclaw/embedded-agent/task-runner.ts
|
|
1810
1404
|
import * as fs from "fs/promises";
|
|
1811
1405
|
import os from "os";
|
|
1812
|
-
import
|
|
1406
|
+
import path2 from "path";
|
|
1813
1407
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER, parseModelRef, resolveAgentEffectiveModelPrimary, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
1814
1408
|
function resolveOpenClawEmbeddedAgentExecution(params) {
|
|
1815
1409
|
const agentId = params.requestedAgentId?.trim() || resolveDefaultAgentId(params.openClaw.config);
|
|
@@ -1906,19 +1500,6 @@ function normalizeOptionalString(value) {
|
|
|
1906
1500
|
var OPENCLAW_EPISODE_GENERATOR_VERSION = "openclaw-episodic-summary-v1";
|
|
1907
1501
|
|
|
1908
1502
|
// src/adapters/openclaw/episode/episode-writer.ts
|
|
1909
|
-
var EPISODE_SUMMARY_TIMEOUT_MS = 45e3;
|
|
1910
|
-
var EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE = "Episode summary generation timed out.";
|
|
1911
|
-
var EPISODE_EMBEDDING_TIMEOUT = /* @__PURE__ */ Symbol("episode-embedding-timeout");
|
|
1912
|
-
var EPISODE_EMBEDDING_MIN_HEADROOM_MS = 5e3;
|
|
1913
|
-
var OpenClawEpisodeSummaryTimeoutError = class extends Error {
|
|
1914
|
-
/**
|
|
1915
|
-
* Creates a timeout error with a stable name for caller-side handling.
|
|
1916
|
-
*/
|
|
1917
|
-
constructor() {
|
|
1918
|
-
super(EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE);
|
|
1919
|
-
this.name = "OpenClawEpisodeSummaryTimeoutError";
|
|
1920
|
-
}
|
|
1921
|
-
};
|
|
1922
1503
|
async function writeOpenClawPredecessorEpisode(params) {
|
|
1923
1504
|
const sessionContext = formatSessionContext(params.ctx.sessionId, params.ctx.sessionKey);
|
|
1924
1505
|
const writeStartedAtMs = Date.now();
|
|
@@ -1928,78 +1509,70 @@ async function writeOpenClawPredecessorEpisode(params) {
|
|
|
1928
1509
|
}
|
|
1929
1510
|
const predecessor = params.predecessor;
|
|
1930
1511
|
params.logger.info(`[agenr] session-start predecessor episode write triggered for ${sessionContext} predecessor=${predecessor.sessionFile}`);
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1512
|
+
const episodeModelRef = resolveOpenClawEpisodeModelRef(params.services.openClaw, params.ctx.agentId, params.services.pluginConfig.episodeModel);
|
|
1513
|
+
const episodeModel = episodeModelRef ?? "default";
|
|
1514
|
+
const summaryDeadlineMs = writeStartedAtMs + EPISODE_SUMMARY_TIMEOUT_MS;
|
|
1515
|
+
const llm = await createOpenClawLlmClient(params.services.openClaw, episodeModelRef, "episode model override");
|
|
1516
|
+
const summaryLlm = createDeadlineAwareEpisodeSummaryLlm(
|
|
1517
|
+
{
|
|
1518
|
+
complete: llm.complete.bind(llm),
|
|
1519
|
+
completeJson: llm.completeJson.bind(llm),
|
|
1520
|
+
metadata: {
|
|
1521
|
+
modelRef: episodeModel,
|
|
1522
|
+
pricing: {
|
|
1523
|
+
input: 0,
|
|
1524
|
+
output: 0,
|
|
1525
|
+
cacheRead: 0,
|
|
1526
|
+
cacheWrite: 0
|
|
1527
|
+
},
|
|
1528
|
+
usage: {
|
|
1529
|
+
calls: 0,
|
|
1530
|
+
inputTokens: 0,
|
|
1531
|
+
outputTokens: 0,
|
|
1532
|
+
cacheReadTokens: 0,
|
|
1533
|
+
cacheWriteTokens: 0,
|
|
1534
|
+
totalTokens: 0,
|
|
1535
|
+
totalCost: 0
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
},
|
|
1539
|
+
summaryDeadlineMs
|
|
1540
|
+
);
|
|
1541
|
+
await writeBoundedSingleTranscriptEpisode({
|
|
1542
|
+
filePath: predecessor.sessionFile,
|
|
1543
|
+
context: sessionContext,
|
|
1544
|
+
actionLabel: "session-start predecessor episode write",
|
|
1545
|
+
logger: params.logger,
|
|
1546
|
+
summaryDeadlineMs,
|
|
1547
|
+
fileField: "predecessor",
|
|
1548
|
+
shortCountField: "cleanedMessages",
|
|
1549
|
+
failureModelRef: episodeModel,
|
|
1550
|
+
unexpectedFailureLevel: "info",
|
|
1551
|
+
ports: {
|
|
1552
|
+
files: createSingleTranscriptDiscoveryPort(predecessor.sessionFile),
|
|
1553
|
+
transcript: openClawTranscriptParser,
|
|
1554
|
+
episodes: params.services.episodes,
|
|
1555
|
+
createSummaryLlm: () => summaryLlm,
|
|
1556
|
+
embedSummary: (summary) => embedEpisodeSummaryWithinBudget({
|
|
1557
|
+
summary,
|
|
1558
|
+
embedding: params.services.embedding,
|
|
1559
|
+
embeddingAvailable: params.services.embeddingStatus.available,
|
|
1560
|
+
deadlineMs: summaryDeadlineMs,
|
|
1561
|
+
logger: params.logger,
|
|
1562
|
+
logContext: `[agenr] session-start predecessor episode embedding skipped for ${sessionContext} predecessor=${predecessor.sessionFile}`
|
|
1973
1563
|
})
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
}
|
|
1985
|
-
if (ingestResult.session.action === "failed") {
|
|
1986
|
-
logFailedEpisodeIngest(sessionContext, predecessor.sessionFile, ingestResult.session.error, episodeModel, params.logger);
|
|
1987
|
-
return;
|
|
1988
|
-
}
|
|
1989
|
-
params.logger.info(
|
|
1990
|
-
`[agenr] session-start predecessor episode write ${ingestResult.session.action} for ${sessionContext} predecessor=${predecessor.sessionFile} episode=${ingestResult.session.episodeId}`
|
|
1991
|
-
);
|
|
1992
|
-
} catch (error) {
|
|
1993
|
-
if (error instanceof OpenClawEpisodeSummaryTimeoutError) {
|
|
1994
|
-
params.logger.info(
|
|
1995
|
-
`[agenr] session-start predecessor episode write timed_out for ${sessionContext} predecessor=${predecessor.sessionFile} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`
|
|
1996
|
-
);
|
|
1997
|
-
return;
|
|
1564
|
+
},
|
|
1565
|
+
ingestOptions: {
|
|
1566
|
+
genVersion: OPENCLAW_EPISODE_GENERATOR_VERSION,
|
|
1567
|
+
skipActiveSessionCheck: true,
|
|
1568
|
+
candidateOverrides: {
|
|
1569
|
+
sessionId: predecessor.sessionId,
|
|
1570
|
+
agentId: trimOptionalString(params.ctx.agentId) ?? null,
|
|
1571
|
+
surface: resolveSessionSurface(params.ctx) ?? null,
|
|
1572
|
+
metadataSource: "registry"
|
|
1573
|
+
}
|
|
1998
1574
|
}
|
|
1999
|
-
|
|
2000
|
-
`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessor.sessionFile} reason=${formatErrorMessage2(error)}`
|
|
2001
|
-
);
|
|
2002
|
-
}
|
|
1575
|
+
});
|
|
2003
1576
|
}
|
|
2004
1577
|
function resolveSessionSurface(ctx) {
|
|
2005
1578
|
const sessionKey = ctx.sessionKey?.trim() ?? "";
|
|
@@ -2012,152 +1585,6 @@ function resolveSessionSurface(ctx) {
|
|
|
2012
1585
|
}
|
|
2013
1586
|
return void 0;
|
|
2014
1587
|
}
|
|
2015
|
-
function createSingleTranscriptDiscoveryPort(filePath) {
|
|
2016
|
-
return {
|
|
2017
|
-
async discoverFiles(_targetPath) {
|
|
2018
|
-
return [filePath];
|
|
2019
|
-
}
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
async function createOpenClawEpisodeSummaryLlm(params) {
|
|
2023
|
-
const llm = await createOpenClawLlmClient(params.openClaw, params.resolvedModelRef, "episode model override");
|
|
2024
|
-
const usage = createEmptyUsageStats();
|
|
2025
|
-
const completeWithTimeout = async (task) => {
|
|
2026
|
-
usage.calls += 1;
|
|
2027
|
-
const remainingMs = Math.max(0, params.deadlineMs - Date.now());
|
|
2028
|
-
return Promise.race([
|
|
2029
|
-
task,
|
|
2030
|
-
new Promise((_, reject) => {
|
|
2031
|
-
setTimeout(() => reject(new OpenClawEpisodeSummaryTimeoutError()), remainingMs);
|
|
2032
|
-
})
|
|
2033
|
-
]);
|
|
2034
|
-
};
|
|
2035
|
-
return {
|
|
2036
|
-
complete: async (systemPrompt, userMessage) => completeWithTimeout(llm.complete(systemPrompt, userMessage)),
|
|
2037
|
-
completeJson: async (systemPrompt, userMessage) => {
|
|
2038
|
-
return completeWithTimeout(llm.completeJson(systemPrompt, userMessage));
|
|
2039
|
-
},
|
|
2040
|
-
metadata: {
|
|
2041
|
-
modelRef: params.modelRef,
|
|
2042
|
-
pricing: {
|
|
2043
|
-
input: 0,
|
|
2044
|
-
output: 0,
|
|
2045
|
-
cacheRead: 0,
|
|
2046
|
-
cacheWrite: 0
|
|
2047
|
-
},
|
|
2048
|
-
usage
|
|
2049
|
-
}
|
|
2050
|
-
};
|
|
2051
|
-
}
|
|
2052
|
-
function createPredecessorEpisodeEmbeddingStrategy(params) {
|
|
2053
|
-
return async (summary) => maybeEmbedEpisodeSummary({
|
|
2054
|
-
summary,
|
|
2055
|
-
embedding: params.embedding,
|
|
2056
|
-
embeddingAvailable: params.embeddingAvailable,
|
|
2057
|
-
logger: params.logger,
|
|
2058
|
-
sessionContext: params.sessionContext,
|
|
2059
|
-
predecessorFile: params.predecessorFile,
|
|
2060
|
-
deadlineMs: params.deadlineMs
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
function logSkippedEpisodeIngest(sessionContext, predecessorFile, skipped, logger) {
|
|
2064
|
-
if (skipped.reason === "skipped_exists") {
|
|
2065
|
-
logger.info(
|
|
2066
|
-
`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=already_exists episode=${skipped.existingEpisode?.id}`
|
|
2067
|
-
);
|
|
2068
|
-
return;
|
|
2069
|
-
}
|
|
2070
|
-
if (skipped.reason === "skipped_short") {
|
|
2071
|
-
logger.info(
|
|
2072
|
-
`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=too_short cleanedMessages=${skipped.messageCount}`
|
|
2073
|
-
);
|
|
2074
|
-
return;
|
|
2075
|
-
}
|
|
2076
|
-
logger.info(`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=${skipped.reason}`);
|
|
2077
|
-
}
|
|
2078
|
-
function logFailedEpisodeIngest(sessionContext, predecessorFile, error, episodeModel, logger) {
|
|
2079
|
-
if (error === EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE) {
|
|
2080
|
-
logger.info(
|
|
2081
|
-
`[agenr] session-start predecessor episode write timed_out for ${sessionContext} predecessor=${predecessorFile} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`
|
|
2082
|
-
);
|
|
2083
|
-
return;
|
|
2084
|
-
}
|
|
2085
|
-
if (error === "invalid_response") {
|
|
2086
|
-
logger.info(
|
|
2087
|
-
`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessorFile} reason=invalid_response model=${episodeModel}`
|
|
2088
|
-
);
|
|
2089
|
-
return;
|
|
2090
|
-
}
|
|
2091
|
-
logger.info(`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessorFile} reason=${error ?? "unknown"}`);
|
|
2092
|
-
}
|
|
2093
|
-
async function maybeEmbedEpisodeSummary(params) {
|
|
2094
|
-
if (!params.embeddingAvailable) {
|
|
2095
|
-
params.logger.info(
|
|
2096
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=embedding_unavailable`
|
|
2097
|
-
);
|
|
2098
|
-
return void 0;
|
|
2099
|
-
}
|
|
2100
|
-
const remainingBudgetMs = params.deadlineMs - Date.now();
|
|
2101
|
-
if (remainingBudgetMs < EPISODE_EMBEDDING_MIN_HEADROOM_MS) {
|
|
2102
|
-
params.logger.info(
|
|
2103
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=budget_tight remainingMs=${Math.max(
|
|
2104
|
-
0,
|
|
2105
|
-
remainingBudgetMs
|
|
2106
|
-
)}`
|
|
2107
|
-
);
|
|
2108
|
-
return void 0;
|
|
2109
|
-
}
|
|
2110
|
-
try {
|
|
2111
|
-
const result = await awaitEmbeddingWithTimeout(params.embedding.embed([params.summary]), remainingBudgetMs);
|
|
2112
|
-
if (result === EPISODE_EMBEDDING_TIMEOUT) {
|
|
2113
|
-
params.logger.info(
|
|
2114
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=embedding_timeout budgetMs=${remainingBudgetMs}`
|
|
2115
|
-
);
|
|
2116
|
-
return void 0;
|
|
2117
|
-
}
|
|
2118
|
-
const vector = result[0]?.map((value) => Number.isFinite(value) ? value : 0);
|
|
2119
|
-
if (!vector || vector.length === 0) {
|
|
2120
|
-
params.logger.info(
|
|
2121
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=empty_embedding`
|
|
2122
|
-
);
|
|
2123
|
-
return void 0;
|
|
2124
|
-
}
|
|
2125
|
-
return vector;
|
|
2126
|
-
} catch (error) {
|
|
2127
|
-
params.logger.info(
|
|
2128
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=${formatErrorMessage2(error)}`
|
|
2129
|
-
);
|
|
2130
|
-
return void 0;
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
2133
|
-
async function awaitEmbeddingWithTimeout(promise, timeoutMs) {
|
|
2134
|
-
return new Promise((resolve, reject) => {
|
|
2135
|
-
const timeout = setTimeout(() => {
|
|
2136
|
-
resolve(EPISODE_EMBEDDING_TIMEOUT);
|
|
2137
|
-
}, timeoutMs);
|
|
2138
|
-
promise.then(
|
|
2139
|
-
(value) => {
|
|
2140
|
-
clearTimeout(timeout);
|
|
2141
|
-
resolve(value);
|
|
2142
|
-
},
|
|
2143
|
-
(error) => {
|
|
2144
|
-
clearTimeout(timeout);
|
|
2145
|
-
reject(error);
|
|
2146
|
-
}
|
|
2147
|
-
);
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
function createEmptyUsageStats() {
|
|
2151
|
-
return {
|
|
2152
|
-
calls: 0,
|
|
2153
|
-
inputTokens: 0,
|
|
2154
|
-
outputTokens: 0,
|
|
2155
|
-
cacheReadTokens: 0,
|
|
2156
|
-
cacheWriteTokens: 0,
|
|
2157
|
-
totalTokens: 0,
|
|
2158
|
-
totalCost: 0
|
|
2159
|
-
};
|
|
2160
|
-
}
|
|
2161
1588
|
function trimOptionalString(value) {
|
|
2162
1589
|
const trimmed = value?.trim();
|
|
2163
1590
|
return trimmed ? trimmed : void 0;
|
|
@@ -2215,74 +1642,20 @@ function truncateSubject(subject) {
|
|
|
2215
1642
|
return `${trimmedSubject.slice(0, MAX_SUBJECT_LENGTH - 3).trimEnd()}...`;
|
|
2216
1643
|
}
|
|
2217
1644
|
|
|
2218
|
-
// src/adapters/openclaw/format/recall-format.ts
|
|
2219
|
-
var MAX_CONTENT_CHARS = 280;
|
|
2220
|
-
function formatAgenrSessionStartRecall(recall) {
|
|
2221
|
-
const sections = buildSections(recall);
|
|
2222
|
-
if (sections.length === 0) {
|
|
2223
|
-
return "";
|
|
2224
|
-
}
|
|
2225
|
-
const lines = ["## Agenr Session Recall", "Use this as prior context. Confirm anything important if the current conversation conflicts with it.", ""];
|
|
2226
|
-
for (const section of sections) {
|
|
2227
|
-
lines.push(`### ${section.title}`);
|
|
2228
|
-
for (const item of section.entries) {
|
|
2229
|
-
lines.push(formatEntryHeader(item));
|
|
2230
|
-
lines.push(formatEntryBody(item.entry));
|
|
2231
|
-
}
|
|
2232
|
-
lines.push("");
|
|
2233
|
-
}
|
|
2234
|
-
return lines.join("\n").trim();
|
|
2235
|
-
}
|
|
2236
|
-
function buildSections(recall) {
|
|
2237
|
-
const sections = [];
|
|
2238
|
-
const coreEntries = recall.core.map((entry) => ({ entry }));
|
|
2239
|
-
if (coreEntries.length > 0) {
|
|
2240
|
-
sections.push({ title: "Core Memory", entries: coreEntries });
|
|
2241
|
-
}
|
|
2242
|
-
return sections;
|
|
2243
|
-
}
|
|
2244
|
-
function formatEntryHeader(item) {
|
|
2245
|
-
const metadata = [
|
|
2246
|
-
item.entry.id,
|
|
2247
|
-
item.entry.type,
|
|
2248
|
-
item.entry.expiry,
|
|
2249
|
-
`importance ${item.entry.importance}`,
|
|
2250
|
-
item.score !== void 0 ? `score ${item.score.toFixed(2)}` : void 0
|
|
2251
|
-
].filter((value) => value !== void 0);
|
|
2252
|
-
return `- [${metadata.join(" | ")}] ${item.entry.subject}`;
|
|
2253
|
-
}
|
|
2254
|
-
function formatEntryBody(entry) {
|
|
2255
|
-
const content = truncate2(entry.content.trim(), MAX_CONTENT_CHARS);
|
|
2256
|
-
const extra = [
|
|
2257
|
-
entry.tags.length > 0 ? `tags: ${entry.tags.join(", ")}` : void 0,
|
|
2258
|
-
entry.created_at ? `created: ${entry.created_at.slice(0, 10)}` : void 0
|
|
2259
|
-
].filter((value) => value !== void 0);
|
|
2260
|
-
if (extra.length === 0) {
|
|
2261
|
-
return ` ${content}`;
|
|
2262
|
-
}
|
|
2263
|
-
return ` ${content} (${extra.join(" | ")})`;
|
|
2264
|
-
}
|
|
2265
|
-
function truncate2(value, maxChars) {
|
|
2266
|
-
if (value.length <= maxChars) {
|
|
2267
|
-
return value;
|
|
2268
|
-
}
|
|
2269
|
-
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
1645
|
// src/adapters/openclaw/session/continuity/continuity-summary-generator.ts
|
|
2273
1646
|
import * as fs3 from "fs/promises";
|
|
2274
1647
|
import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary3, resolveDefaultAgentId as resolveDefaultAgentId3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
2275
1648
|
|
|
2276
1649
|
// src/adapters/openclaw/session/continuity/continuity-summary-reader.ts
|
|
2277
1650
|
import * as fs2 from "fs/promises";
|
|
2278
|
-
import
|
|
1651
|
+
import path3 from "path";
|
|
2279
1652
|
function resolveOpenClawContinuitySummaryPath(sessionFile, logger) {
|
|
2280
1653
|
const normalizedSessionFile = sessionFile.trim();
|
|
2281
1654
|
const sessionId = deriveOpenClawSessionIdFromFilePath(normalizedSessionFile, logger);
|
|
2282
1655
|
if (!sessionId) {
|
|
2283
1656
|
return void 0;
|
|
2284
1657
|
}
|
|
2285
|
-
const continuitySummaryPath =
|
|
1658
|
+
const continuitySummaryPath = path3.join(path3.dirname(normalizedSessionFile), `${sessionId}.continuity-summary.md`);
|
|
2286
1659
|
debugLog(logger, "continuity-summary-reader", `resolved continuity summary path for session=${sessionId}: ${continuitySummaryPath}`);
|
|
2287
1660
|
return continuitySummaryPath;
|
|
2288
1661
|
}
|
|
@@ -2538,7 +1911,7 @@ function trimToBoundary(value, fromStart) {
|
|
|
2538
1911
|
|
|
2539
1912
|
// src/adapters/openclaw/session/continuity/predecessor-resolver.ts
|
|
2540
1913
|
import * as fs4 from "fs/promises";
|
|
2541
|
-
import
|
|
1914
|
+
import path4 from "path";
|
|
2542
1915
|
|
|
2543
1916
|
// src/adapters/openclaw/session/session-key-parser.ts
|
|
2544
1917
|
var AGENT_SESSION_KEY_PATTERN = /^agent:([^:]+):(.+)$/i;
|
|
@@ -2761,15 +2134,15 @@ async function findResumedFromTranscriptCandidates(sessionsDir, sessionId, logge
|
|
|
2761
2134
|
}
|
|
2762
2135
|
const fileName = entry.name.trim();
|
|
2763
2136
|
if (fileName === prefix) {
|
|
2764
|
-
liveMatches.push(
|
|
2137
|
+
liveMatches.push(path4.join(sessionsDir, fileName));
|
|
2765
2138
|
continue;
|
|
2766
2139
|
}
|
|
2767
2140
|
if (fileName.startsWith(`${prefix}.reset.`)) {
|
|
2768
|
-
resetMatches.push(
|
|
2141
|
+
resetMatches.push(path4.join(sessionsDir, fileName));
|
|
2769
2142
|
continue;
|
|
2770
2143
|
}
|
|
2771
2144
|
if (fileName.startsWith(`${prefix}.deleted.`)) {
|
|
2772
|
-
deletedMatches.push(
|
|
2145
|
+
deletedMatches.push(path4.join(sessionsDir, fileName));
|
|
2773
2146
|
}
|
|
2774
2147
|
}
|
|
2775
2148
|
const ordered = [
|
|
@@ -2945,7 +2318,7 @@ function resolveOpenClawSessionsDirectory(ctx, parsedAgentId, resolveStateDir) {
|
|
|
2945
2318
|
if (!agentId) {
|
|
2946
2319
|
return void 0;
|
|
2947
2320
|
}
|
|
2948
|
-
return
|
|
2321
|
+
return path4.join(resolveStateDir(process.env), "agents", agentId, "sessions");
|
|
2949
2322
|
}
|
|
2950
2323
|
function isSameTuiLane(currentStableLane, candidateStableLane) {
|
|
2951
2324
|
if (!currentStableLane || !candidateStableLane) {
|
|
@@ -2957,7 +2330,7 @@ function isSameTuiLane(currentStableLane, candidateStableLane) {
|
|
|
2957
2330
|
return currentStableLane === candidateStableLane;
|
|
2958
2331
|
}
|
|
2959
2332
|
function compareArchivePathsDescending(left, right) {
|
|
2960
|
-
return
|
|
2333
|
+
return path4.basename(right).localeCompare(path4.basename(left));
|
|
2961
2334
|
}
|
|
2962
2335
|
function debugLog3(logger, subsystem, message) {
|
|
2963
2336
|
logger?.debug?.(`[agenr] ${subsystem}: ${message}`);
|
|
@@ -2983,11 +2356,32 @@ function formatSessionContext2(sessionId, sessionKey) {
|
|
|
2983
2356
|
// src/adapters/openclaw/session/continuity/recent-session.ts
|
|
2984
2357
|
var RECENT_SESSION_MESSAGE_LIMIT = 6;
|
|
2985
2358
|
var RECENT_SESSION_MAX_CHARS = 1800;
|
|
2359
|
+
var SESSION_START_SECTION_HEADINGS = [
|
|
2360
|
+
"## Previous session summary",
|
|
2361
|
+
"## Recent session",
|
|
2362
|
+
"## Agenr Session Recall",
|
|
2363
|
+
"### Core Memory",
|
|
2364
|
+
"### Relevant Durable Memory",
|
|
2365
|
+
"## Agenr Before-Turn Recall",
|
|
2366
|
+
"### Suggested Procedure"
|
|
2367
|
+
];
|
|
2368
|
+
var INLINE_METADATA_SENTINELS = [
|
|
2369
|
+
"Sender (untrusted metadata):",
|
|
2370
|
+
"Conversation info (untrusted metadata):",
|
|
2371
|
+
"Thread starter (untrusted, for context):",
|
|
2372
|
+
"Replied message (untrusted, for context):",
|
|
2373
|
+
"Forwarded message context (untrusted metadata):",
|
|
2374
|
+
"Chat history since last reply (untrusted, for context):"
|
|
2375
|
+
];
|
|
2986
2376
|
async function renderRecentSessionSection(sessionFile, logger) {
|
|
2987
2377
|
try {
|
|
2988
2378
|
const transcript = await openClawTranscriptParser.parseFile(sessionFile);
|
|
2989
|
-
const
|
|
2990
|
-
|
|
2379
|
+
const sanitizedMessages = transcript.messages.map((message) => ({
|
|
2380
|
+
prefix: message.role === "user" ? "U" : "A",
|
|
2381
|
+
text: sanitizeRecentSessionMessage(message.text, message.role)
|
|
2382
|
+
})).filter((message) => message.text.length > 0);
|
|
2383
|
+
const tail = sanitizedMessages.slice(-RECENT_SESSION_MESSAGE_LIMIT);
|
|
2384
|
+
const body = capRecentSession(tail.map((message) => `${message.prefix}: ${message.text}`).join("\n"), RECENT_SESSION_MAX_CHARS);
|
|
2991
2385
|
logger.debug?.(`[agenr] before_prompt_build: recent session tail for file=${sessionFile}: messages=${tail.length} chars=${body.length}`);
|
|
2992
2386
|
return body;
|
|
2993
2387
|
} catch (error) {
|
|
@@ -3005,6 +2399,53 @@ function capRecentSession(value, maxChars) {
|
|
|
3005
2399
|
const marker = "[...truncated earlier recent session...]\n";
|
|
3006
2400
|
return `${marker}${value.slice(-(maxChars - marker.length)).trimStart()}`;
|
|
3007
2401
|
}
|
|
2402
|
+
function sanitizeRecentSessionMessage(text, role) {
|
|
2403
|
+
const wrapperDetected = containsAgenrMemoryContext(text) || SESSION_START_SECTION_HEADINGS.some((heading) => text.includes(heading));
|
|
2404
|
+
let cleaned = stripAgenrMemoryContext(text);
|
|
2405
|
+
for (const heading of SESSION_START_SECTION_HEADINGS) {
|
|
2406
|
+
cleaned = cleaned.split(heading).join(" ");
|
|
2407
|
+
}
|
|
2408
|
+
cleaned = stripInlineMetadata(cleaned);
|
|
2409
|
+
cleaned = collapseWhitespace(cleaned);
|
|
2410
|
+
if (wrapperDetected) {
|
|
2411
|
+
cleaned = unwrapEmbeddedTranscriptTurn(cleaned, role);
|
|
2412
|
+
}
|
|
2413
|
+
return collapseWhitespace(cleaned);
|
|
2414
|
+
}
|
|
2415
|
+
function stripInlineMetadata(text) {
|
|
2416
|
+
let cleaned = text;
|
|
2417
|
+
for (const sentinel of INLINE_METADATA_SENTINELS) {
|
|
2418
|
+
const escapedSentinel = escapeForRegExp(sentinel);
|
|
2419
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:json\\s*)?\\{[\\s\\S]*?\\}`, "gu"), " ");
|
|
2420
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
|
|
2421
|
+
]*`, "gu"), " ");
|
|
2422
|
+
}
|
|
2423
|
+
cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
|
|
2424
|
+
return cleaned;
|
|
2425
|
+
}
|
|
2426
|
+
function unwrapEmbeddedTranscriptTurn(text, role) {
|
|
2427
|
+
if (role === "user") {
|
|
2428
|
+
const timestampMatches = [...text.matchAll(/\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]/gu)];
|
|
2429
|
+
if (timestampMatches.length > 1) {
|
|
2430
|
+
const lastTimestamp = timestampMatches.at(-1);
|
|
2431
|
+
if (lastTimestamp?.index !== void 0) {
|
|
2432
|
+
return text.slice(lastTimestamp.index).trim();
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
const marker = role === "user" ? "U:" : "A:";
|
|
2437
|
+
const lastMarkerIndex = text.lastIndexOf(marker);
|
|
2438
|
+
if (lastMarkerIndex < 0) {
|
|
2439
|
+
return text;
|
|
2440
|
+
}
|
|
2441
|
+
return text.slice(lastMarkerIndex + marker.length).trim();
|
|
2442
|
+
}
|
|
2443
|
+
function escapeForRegExp(value) {
|
|
2444
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
2445
|
+
}
|
|
2446
|
+
function collapseWhitespace(value) {
|
|
2447
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
2448
|
+
}
|
|
3008
2449
|
|
|
3009
2450
|
// src/adapters/openclaw/session/continuity/index.ts
|
|
3010
2451
|
var READ_TIME_CONTINUITY_SUMMARY_TIMEOUT_MS = 35e3;
|
|
@@ -3136,9 +2577,16 @@ async function awaitWithTimeout(promise, timeoutMs) {
|
|
|
3136
2577
|
}
|
|
3137
2578
|
|
|
3138
2579
|
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
3139
|
-
var CORE_ENTRY_LIMIT = 4;
|
|
3140
2580
|
var NON_USER_TRIGGER_SET = /* @__PURE__ */ new Set(["heartbeat", "cron", "memory"]);
|
|
3141
2581
|
var DEFAULT_STORE_NUDGE_CONFIG = resolveStoreNudgeConfig(void 0);
|
|
2582
|
+
var INLINE_METADATA_SENTINELS2 = [
|
|
2583
|
+
"Sender (untrusted metadata):",
|
|
2584
|
+
"Conversation info (untrusted metadata):",
|
|
2585
|
+
"Thread starter (untrusted, for context):",
|
|
2586
|
+
"Replied message (untrusted, for context):",
|
|
2587
|
+
"Forwarded message context (untrusted metadata):",
|
|
2588
|
+
"Chat history since last reply (untrusted, for context):"
|
|
2589
|
+
];
|
|
3142
2590
|
async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
3143
2591
|
const sessionContext = formatSessionContext(ctx.sessionId, ctx.sessionKey);
|
|
3144
2592
|
const trackerState = params.tracker.consume(ctx.sessionId, ctx.sessionKey);
|
|
@@ -3146,32 +2594,64 @@ async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
|
3146
2594
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker duplicate blocked for ${sessionContext}`);
|
|
3147
2595
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker active count=${trackerState.activeCount}`);
|
|
3148
2596
|
params.logger.info(`[agenr] session-start recall skipped (already ran) for ${sessionContext}`);
|
|
3149
|
-
return
|
|
2597
|
+
return await resolveNonFirstTurnResult(event, ctx, sessionContext, params);
|
|
3150
2598
|
}
|
|
3151
2599
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker first start for ${sessionContext}`);
|
|
3152
2600
|
params.logger.debug?.(`[agenr] before_prompt_build: session tracker active count=${trackerState.activeCount}`);
|
|
3153
2601
|
params.logger.info(`[agenr] session-start recall for ${sessionContext}`);
|
|
3154
2602
|
try {
|
|
3155
2603
|
const services = await params.servicesPromise;
|
|
2604
|
+
if (services.pluginConfig.memoryPolicy?.sessionStart?.enabled === false) {
|
|
2605
|
+
params.logger.info(`[agenr] session-start recall disabled by memoryPolicy for ${sessionContext}`);
|
|
2606
|
+
return await resolveNonFirstTurnResult(event, ctx, sessionContext, params);
|
|
2607
|
+
}
|
|
3156
2608
|
const continuity = await resolvePredecessorContinuity(ctx, params.tracker, services, params.logger);
|
|
2609
|
+
emitContinuityEvent(services.debugSink, ctx, continuity);
|
|
3157
2610
|
void writeOpenClawPredecessorEpisode({
|
|
3158
2611
|
ctx,
|
|
3159
2612
|
predecessor: continuity.predecessor,
|
|
3160
2613
|
services,
|
|
3161
2614
|
logger: params.logger
|
|
3162
2615
|
});
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
const prependContext =
|
|
3173
|
-
|
|
3174
|
-
|
|
2616
|
+
const sessionStartPatch = await runSessionStart(
|
|
2617
|
+
{
|
|
2618
|
+
sessionKey: ctx.sessionKey,
|
|
2619
|
+
continuitySummaryText: continuity.continuitySummaryContent,
|
|
2620
|
+
recentSessionText: continuity.recentSessionContent,
|
|
2621
|
+
policy: resolveSessionStartPolicy(services.pluginConfig.memoryPolicy)
|
|
2622
|
+
},
|
|
2623
|
+
services.sessionStart
|
|
2624
|
+
);
|
|
2625
|
+
const prependContext = formatAgenrSessionStartRecall(sessionStartPatch);
|
|
2626
|
+
if (services.debugSink.enabled) {
|
|
2627
|
+
void services.debugSink.emit({
|
|
2628
|
+
type: "session_start_recall",
|
|
2629
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
2630
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
2631
|
+
debug: {
|
|
2632
|
+
durableMemoryCount: sessionStartPatch.durableMemory.length,
|
|
2633
|
+
selectedEntryIds: sessionStartPatch.durableMemory.map((item) => item.entry.id),
|
|
2634
|
+
coreCandidateCount: sessionStartPatch.diagnostics.coreCandidateCount,
|
|
2635
|
+
artifactRecallCandidateCount: sessionStartPatch.diagnostics.artifactRecallCandidateCount,
|
|
2636
|
+
artifactRecallUsed: sessionStartPatch.diagnostics.artifactRecallUsed,
|
|
2637
|
+
notices: [...sessionStartPatch.diagnostics.notices]
|
|
2638
|
+
}
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2641
|
+
params.logger.info(
|
|
2642
|
+
`[agenr] session-start recall: ${sessionStartPatch.durableMemory.length} durable entries for ${sessionContext} (core_candidates=${sessionStartPatch.diagnostics.coreCandidateCount} artifact_candidates=${sessionStartPatch.diagnostics.artifactRecallCandidateCount})`
|
|
2643
|
+
);
|
|
2644
|
+
if (sessionStartPatch.diagnostics.artifactRecallUsed) {
|
|
2645
|
+
params.logger.debug?.(
|
|
2646
|
+
`[agenr] before_prompt_build: session-start artifact recall for ${sessionContext} query_length=${sessionStartPatch.diagnostics.artifactRecallQuery?.length ?? 0} notices=${sessionStartPatch.diagnostics.notices.length}`
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
if (sessionStartPatch.diagnostics.notices.length > 0) {
|
|
2650
|
+
params.logger.info(`[agenr] session-start recall notices for ${sessionContext}: ${sessionStartPatch.diagnostics.notices.join(" | ")}`);
|
|
2651
|
+
}
|
|
2652
|
+
params.logger.debug?.(
|
|
2653
|
+
`[agenr] before_prompt_build: session-start durable entries for ${sessionContext}: ${formatEntryRefs(sessionStartPatch.durableMemory.map((item) => item.entry))}`
|
|
2654
|
+
);
|
|
3175
2655
|
params.logger.debug?.(`[agenr] before_prompt_build: session-start prependContext length for ${sessionContext}: ${prependContext.length} chars`);
|
|
3176
2656
|
if (prependContext.length === 0) {
|
|
3177
2657
|
params.logger.info(`[agenr] session-start recall: nothing to inject for ${sessionContext}`);
|
|
@@ -3180,6 +2660,139 @@ ${continuity.recentSessionContent}`,
|
|
|
3180
2660
|
return { prependContext };
|
|
3181
2661
|
} catch (error) {
|
|
3182
2662
|
params.logger.warn(`[agenr] session-start recall failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
|
|
2663
|
+
try {
|
|
2664
|
+
const services = await params.servicesPromise;
|
|
2665
|
+
if (services.debugSink.enabled) {
|
|
2666
|
+
void services.debugSink.emit({
|
|
2667
|
+
type: "error",
|
|
2668
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
2669
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
2670
|
+
scope: "session_start_recall",
|
|
2671
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
} catch {
|
|
2675
|
+
}
|
|
2676
|
+
return void 0;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
function emitContinuityEvent(sink, ctx, continuity) {
|
|
2680
|
+
if (!sink.enabled) {
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
void sink.emit({
|
|
2684
|
+
type: "continuity_resolution",
|
|
2685
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
2686
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
2687
|
+
summary: {
|
|
2688
|
+
predecessorFound: Boolean(continuity.predecessor),
|
|
2689
|
+
...continuity.predecessor ? { predecessorFileBasename: path5.basename(continuity.predecessor.sessionFile) } : {},
|
|
2690
|
+
hasContinuitySummary: continuity.continuitySummaryContent.length > 0,
|
|
2691
|
+
hasRecentSession: continuity.recentSessionContent.length > 0,
|
|
2692
|
+
continuitySummaryChars: continuity.continuitySummaryContent.length,
|
|
2693
|
+
recentSessionChars: continuity.recentSessionContent.length
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
async function resolveNonFirstTurnResult(event, ctx, sessionContext, params) {
|
|
2698
|
+
const beforeTurnResult = await resolveBeforeTurnResult(event, ctx, sessionContext, params);
|
|
2699
|
+
if (beforeTurnResult) {
|
|
2700
|
+
return beforeTurnResult;
|
|
2701
|
+
}
|
|
2702
|
+
return resolveStoreNudgeResult(event, ctx, sessionContext, params);
|
|
2703
|
+
}
|
|
2704
|
+
async function resolveBeforeTurnResult(event, ctx, sessionContext, params) {
|
|
2705
|
+
const normalizedTrigger = ctx.trigger?.trim().toLowerCase();
|
|
2706
|
+
if (normalizedTrigger && NON_USER_TRIGGER_SET.has(normalizedTrigger)) {
|
|
2707
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=non_user_trigger trigger=${normalizedTrigger}`);
|
|
2708
|
+
return void 0;
|
|
2709
|
+
}
|
|
2710
|
+
const services = await params.servicesPromise;
|
|
2711
|
+
if (services.pluginConfig.memoryPolicy?.beforeTurn?.enabled === false) {
|
|
2712
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=disabled`);
|
|
2713
|
+
return void 0;
|
|
2714
|
+
}
|
|
2715
|
+
const currentTurnText = normalizePromptText(event.prompt, {
|
|
2716
|
+
stripInlineMetadata: true,
|
|
2717
|
+
inlineMetadataSentinels: INLINE_METADATA_SENTINELS2,
|
|
2718
|
+
stripTimestampPrefix: true,
|
|
2719
|
+
stripUserPrefix: true
|
|
2720
|
+
});
|
|
2721
|
+
if (!currentTurnText) {
|
|
2722
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=empty_prompt`);
|
|
2723
|
+
return void 0;
|
|
2724
|
+
}
|
|
2725
|
+
try {
|
|
2726
|
+
const beforeTurnPatch = await runBeforeTurn(
|
|
2727
|
+
{
|
|
2728
|
+
sessionKey: ctx.sessionKey,
|
|
2729
|
+
currentTurnText,
|
|
2730
|
+
recentTurns: extractRecentTurnsFromMessages(
|
|
2731
|
+
event.messages.filter((message) => Boolean(message) && typeof message === "object"),
|
|
2732
|
+
{ stripMemoryCheck: true }
|
|
2733
|
+
),
|
|
2734
|
+
trigger: ctx.trigger,
|
|
2735
|
+
policy: resolveBeforeTurnPolicy(services.pluginConfig.memoryPolicy)
|
|
2736
|
+
},
|
|
2737
|
+
services.beforeTurn
|
|
2738
|
+
);
|
|
2739
|
+
const prependContext = formatAgenrBeforeTurnRecall(beforeTurnPatch);
|
|
2740
|
+
if (services.debugSink.enabled) {
|
|
2741
|
+
void services.debugSink.emit({
|
|
2742
|
+
type: "before_turn_decision",
|
|
2743
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
2744
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
2745
|
+
debug: buildLiveBeforeTurnDebugArtifact({
|
|
2746
|
+
caseId: `live-${randomUUID2()}`,
|
|
2747
|
+
patch: beforeTurnPatch,
|
|
2748
|
+
currentTurnText,
|
|
2749
|
+
trigger: ctx.trigger,
|
|
2750
|
+
eventLevel: services.debugSink.eventLevel,
|
|
2751
|
+
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
2752
|
+
})
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
params.logger.info(
|
|
2756
|
+
`[agenr] before-turn recall: ${beforeTurnPatch.durableMemory.length} durable entries for ${sessionContext} (durable_candidates=${beforeTurnPatch.diagnostics.durableRecallCandidateCount} procedure_candidates=${beforeTurnPatch.diagnostics.procedureCandidateCount})`
|
|
2757
|
+
);
|
|
2758
|
+
if (beforeTurnPatch.procedure) {
|
|
2759
|
+
params.logger.info(
|
|
2760
|
+
`[agenr] before-turn procedure suggestion for ${sessionContext}: ${beforeTurnPatch.procedure.procedure.procedure_key} score=${beforeTurnPatch.procedure.score.toFixed(2)}`
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
if (beforeTurnPatch.diagnostics.notices.length > 0) {
|
|
2764
|
+
params.logger.info(`[agenr] before-turn recall notices for ${sessionContext}: ${beforeTurnPatch.diagnostics.notices.join(" | ")}`);
|
|
2765
|
+
}
|
|
2766
|
+
if (beforeTurnPatch.diagnostics.abstained) {
|
|
2767
|
+
params.logger.debug?.(
|
|
2768
|
+
`[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"}`
|
|
2769
|
+
);
|
|
2770
|
+
}
|
|
2771
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn diagnostics for ${sessionContext}: ${formatBeforeTurnDiagnosticsForLog(beforeTurnPatch)}`);
|
|
2772
|
+
params.logger.debug?.(
|
|
2773
|
+
`[agenr] before_prompt_build: before-turn durable entries for ${sessionContext}: ${formatEntryRefs(
|
|
2774
|
+
beforeTurnPatch.durableMemory.map((item) => item.entry)
|
|
2775
|
+
)}`
|
|
2776
|
+
);
|
|
2777
|
+
params.logger.debug?.(`[agenr] before_prompt_build: before-turn prependContext length for ${sessionContext}: ${prependContext.length} chars`);
|
|
2778
|
+
if (prependContext.length === 0) {
|
|
2779
|
+
return void 0;
|
|
2780
|
+
}
|
|
2781
|
+
return { prependContext };
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
params.logger.warn(`[agenr] before-turn recall failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
|
|
2784
|
+
try {
|
|
2785
|
+
if (services.debugSink.enabled) {
|
|
2786
|
+
void services.debugSink.emit({
|
|
2787
|
+
type: "error",
|
|
2788
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
2789
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
2790
|
+
scope: "before_turn_decision",
|
|
2791
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
} catch {
|
|
2795
|
+
}
|
|
3183
2796
|
return void 0;
|
|
3184
2797
|
}
|
|
3185
2798
|
}
|
|
@@ -3219,12 +2832,64 @@ function resolveStoreNudgeResult(_event, ctx, sessionContext, params) {
|
|
|
3219
2832
|
params.logger.info(`[agenr] store nudge injected for ${sessionContext} ordinal=${state.nudgeCount} turn=${state.turnCount} gap=${gapSinceSuccessfulStore}`);
|
|
3220
2833
|
return { prependContext };
|
|
3221
2834
|
}
|
|
3222
|
-
async function runAgenrSessionStartRecall(services) {
|
|
3223
|
-
return { core: await services.memory.listCoreEntries(CORE_ENTRY_LIMIT) };
|
|
3224
|
-
}
|
|
3225
2835
|
function formatEntryRefs(entries) {
|
|
3226
2836
|
return entries.length === 0 ? "none" : entries.map((entry) => `${entry.subject} [${entry.id}]`).join(", ");
|
|
3227
2837
|
}
|
|
2838
|
+
function formatBeforeTurnDiagnosticsForLog(patch) {
|
|
2839
|
+
return JSON.stringify({
|
|
2840
|
+
query: truncateForLog(patch.diagnostics.query, 160),
|
|
2841
|
+
queryPolicy: patch.diagnostics.queryPolicy,
|
|
2842
|
+
queryVariants: patch.diagnostics.queryVariants.map((variant) => ({
|
|
2843
|
+
kind: variant.kind,
|
|
2844
|
+
query: truncateForLog(variant.query, 120),
|
|
2845
|
+
candidateCount: variant.candidateCount,
|
|
2846
|
+
selected: variant.selected
|
|
2847
|
+
})),
|
|
2848
|
+
turnSignalLabels: patch.diagnostics.turnSignalLabels,
|
|
2849
|
+
suppressedTurnCategory: patch.diagnostics.suppressedTurnCategory,
|
|
2850
|
+
durableRecallCandidateCount: patch.diagnostics.durableRecallCandidateCount,
|
|
2851
|
+
procedureCandidateCount: patch.diagnostics.procedureCandidateCount,
|
|
2852
|
+
directness: patch.diagnostics.directness ? {
|
|
2853
|
+
queryKind: patch.diagnostics.directness.queryKind,
|
|
2854
|
+
entity: patch.diagnostics.directness.entity,
|
|
2855
|
+
decision: patch.diagnostics.directness.decision,
|
|
2856
|
+
winnerEntryId: patch.diagnostics.directness.winnerEntryId,
|
|
2857
|
+
runnerUpEntryId: patch.diagnostics.directness.runnerUpEntryId,
|
|
2858
|
+
winnerGap: patch.diagnostics.directness.winnerGap,
|
|
2859
|
+
reason: truncateForLog(patch.diagnostics.directness.reason, 180),
|
|
2860
|
+
candidates: patch.diagnostics.directness.candidates.map((candidate) => ({
|
|
2861
|
+
entryId: candidate.entryId,
|
|
2862
|
+
baseRank: candidate.baseRank,
|
|
2863
|
+
baseScore: candidate.baseScore,
|
|
2864
|
+
directnessDelta: candidate.directnessDelta,
|
|
2865
|
+
adjustedScore: candidate.adjustedScore,
|
|
2866
|
+
signals: candidate.signals
|
|
2867
|
+
}))
|
|
2868
|
+
} : void 0,
|
|
2869
|
+
abstained: patch.diagnostics.abstained,
|
|
2870
|
+
abstentionReasons: patch.diagnostics.abstentionReasons.map((reason) => truncateForLog(reason, 180)),
|
|
2871
|
+
notices: patch.diagnostics.notices.map((notice) => truncateForLog(notice, 180)),
|
|
2872
|
+
selectedEntries: patch.durableMemory.map((item) => ({
|
|
2873
|
+
id: item.entry.id,
|
|
2874
|
+
subject: truncateForLog(item.entry.subject, 80),
|
|
2875
|
+
score: Number(item.score.toFixed(3))
|
|
2876
|
+
})),
|
|
2877
|
+
procedure: patch.procedure ? {
|
|
2878
|
+
procedureKey: patch.procedure.procedure.procedure_key,
|
|
2879
|
+
score: Number(patch.procedure.score.toFixed(3))
|
|
2880
|
+
} : void 0
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
function truncateForLog(value, maxChars) {
|
|
2884
|
+
if (typeof value !== "string") {
|
|
2885
|
+
return void 0;
|
|
2886
|
+
}
|
|
2887
|
+
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
2888
|
+
if (normalized.length === 0) {
|
|
2889
|
+
return void 0;
|
|
2890
|
+
}
|
|
2891
|
+
return normalized.length <= maxChars ? normalized : `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
2892
|
+
}
|
|
3228
2893
|
|
|
3229
2894
|
// src/adapters/openclaw/memory/flush-plan.ts
|
|
3230
2895
|
function buildAgenrMemoryFlushPlan(_params, logger) {
|
|
@@ -3244,7 +2909,7 @@ function createAgenrMemoryRuntime(servicesPromise) {
|
|
|
3244
2909
|
backend: "builtin",
|
|
3245
2910
|
provider: "agenr",
|
|
3246
2911
|
model: services.embeddingStatus.model,
|
|
3247
|
-
dbPath: services.dbPath,
|
|
2912
|
+
dbPath: services.config.dbPath,
|
|
3248
2913
|
files: snapshot.sourceFiles,
|
|
3249
2914
|
chunks: snapshot.activeEntries,
|
|
3250
2915
|
vector: {
|
|
@@ -3304,139 +2969,48 @@ function createAgenrMemoryRuntime(servicesPromise) {
|
|
|
3304
2969
|
}
|
|
3305
2970
|
|
|
3306
2971
|
// src/app/openclaw/runtime.ts
|
|
2972
|
+
import path6 from "path";
|
|
3307
2973
|
async function createAgenrOpenClawServices(config, options) {
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
procedures: runtimeServices.procedures,
|
|
3327
|
-
memory: runtimeServices.memory,
|
|
3328
|
-
embedding: runtimeServices.embedding,
|
|
3329
|
-
recall: runtimeServices.recall,
|
|
3330
|
-
claimExtraction: runtimeServices.claimExtraction,
|
|
3331
|
-
embeddingStatus: toPublicEmbeddingStatus(embeddingStatus),
|
|
3332
|
-
close: runtimeServices.close
|
|
3333
|
-
};
|
|
3334
|
-
}
|
|
3335
|
-
function resolveEmbeddingStatus(config) {
|
|
3336
|
-
const model = resolveEmbeddingModel(config);
|
|
3337
|
-
try {
|
|
3338
|
-
return {
|
|
3339
|
-
available: true,
|
|
3340
|
-
provider: "openai",
|
|
3341
|
-
requestedProvider: "openai",
|
|
3342
|
-
model,
|
|
3343
|
-
apiKey: resolveEmbeddingApiKey(config)
|
|
3344
|
-
};
|
|
3345
|
-
} catch (error) {
|
|
3346
|
-
return {
|
|
3347
|
-
available: false,
|
|
3348
|
-
provider: "unconfigured",
|
|
3349
|
-
requestedProvider: "openai",
|
|
3350
|
-
model,
|
|
3351
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3352
|
-
};
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
async function createRuntimeServices(dbPath, config, embeddingStatus, openClawContext) {
|
|
3356
|
-
const database = await createDatabase(dbPath);
|
|
3357
|
-
const embedding = embeddingStatus.available ? createEmbeddingClient(requireApiKey(embeddingStatus), embeddingStatus.model) : createUnavailableEmbeddingPort(embeddingStatus.error ?? "Embeddings are unavailable.");
|
|
3358
|
-
const claimExtraction = await createClaimExtractionRuntime(config, openClawContext.openClaw, openClawContext.pluginConfig);
|
|
3359
|
-
let closed = false;
|
|
3360
|
-
return {
|
|
3361
|
-
entries: database,
|
|
3362
|
-
episodes: database,
|
|
3363
|
-
procedures: database,
|
|
3364
|
-
memory: createOpenClawRepository(database, {
|
|
3365
|
-
claimSlotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
3366
|
-
}),
|
|
3367
|
-
embedding,
|
|
3368
|
-
recall: createRecallAdapter(database, embedding),
|
|
3369
|
-
claimExtraction,
|
|
3370
|
-
async close() {
|
|
3371
|
-
if (closed) {
|
|
3372
|
-
return;
|
|
3373
|
-
}
|
|
3374
|
-
closed = true;
|
|
3375
|
-
await database.close();
|
|
3376
|
-
}
|
|
3377
|
-
};
|
|
3378
|
-
}
|
|
3379
|
-
function toPublicEmbeddingStatus(status) {
|
|
3380
|
-
return {
|
|
3381
|
-
available: status.available,
|
|
3382
|
-
provider: status.provider,
|
|
3383
|
-
requestedProvider: status.requestedProvider,
|
|
3384
|
-
model: status.model,
|
|
3385
|
-
...status.error ? { error: status.error } : {}
|
|
3386
|
-
};
|
|
3387
|
-
}
|
|
3388
|
-
function createUnavailableEmbeddingPort(errorMessage) {
|
|
3389
|
-
return {
|
|
3390
|
-
async embed() {
|
|
3391
|
-
throw new Error(errorMessage);
|
|
3392
|
-
}
|
|
3393
|
-
};
|
|
3394
|
-
}
|
|
3395
|
-
function resolveRuntimeConfig(config, resolvePath) {
|
|
3396
|
-
const dbPathOverride = resolveOptionalPath(config.dbPath, resolvePath);
|
|
3397
|
-
const configPathOverride = resolveOptionalPath(config.configPath, resolvePath);
|
|
3398
|
-
const configPath = resolveConfigPath({
|
|
3399
|
-
configPath: configPathOverride,
|
|
3400
|
-
dbPath: dbPathOverride
|
|
2974
|
+
const debugSink = createDebugSink(options.openClaw, config);
|
|
2975
|
+
return composeHostPluginServices({
|
|
2976
|
+
config,
|
|
2977
|
+
resolvePath: options.resolvePath,
|
|
2978
|
+
readSlotPolicies: (hostConfig) => hostConfig.memoryPolicy?.slotPolicies,
|
|
2979
|
+
resolveClaimExtraction: ({ agenrConfig, hostConfig }) => buildClaimExtractionRuntime(
|
|
2980
|
+
agenrConfig,
|
|
2981
|
+
() => createOpenClawLlmClient(options.openClaw, hostConfig.claimExtractionModel, "claim extraction model override")
|
|
2982
|
+
),
|
|
2983
|
+
onBeforeClose: () => debugSink.close(),
|
|
2984
|
+
extend: ({ resolvedConfig, agenrConfig, runtimeServices }) => ({
|
|
2985
|
+
...runtimeServices,
|
|
2986
|
+
openClaw: options.openClaw,
|
|
2987
|
+
config: resolvedConfig,
|
|
2988
|
+
pluginConfig: config,
|
|
2989
|
+
agenrConfig,
|
|
2990
|
+
debugSink
|
|
2991
|
+
})
|
|
3401
2992
|
});
|
|
3402
|
-
const agenrConfig = readConfig({
|
|
3403
|
-
configPath,
|
|
3404
|
-
dbPath: dbPathOverride
|
|
3405
|
-
});
|
|
3406
|
-
const dbPath = dbPathOverride ?? resolveDbPath(agenrConfig);
|
|
3407
|
-
return {
|
|
3408
|
-
resolvedConfig: {
|
|
3409
|
-
dbPath,
|
|
3410
|
-
configPath
|
|
3411
|
-
},
|
|
3412
|
-
agenrConfig
|
|
3413
|
-
};
|
|
3414
|
-
}
|
|
3415
|
-
function resolveOptionalPath(value, resolvePath) {
|
|
3416
|
-
const normalized = value?.trim();
|
|
3417
|
-
if (!normalized) {
|
|
3418
|
-
return void 0;
|
|
3419
|
-
}
|
|
3420
|
-
return resolvePath ? resolvePath(normalized) : normalized;
|
|
3421
2993
|
}
|
|
3422
|
-
function
|
|
3423
|
-
|
|
3424
|
-
|
|
2994
|
+
function createDebugSink(openClaw, pluginConfig) {
|
|
2995
|
+
const resolved = resolveDebugConfig(pluginConfig.debug);
|
|
2996
|
+
if (!resolved.enabled) {
|
|
2997
|
+
return createNoopAgenrDebugSink();
|
|
3425
2998
|
}
|
|
3426
|
-
|
|
2999
|
+
const withLogPath = ensureDebugLogPath(resolved, openClaw);
|
|
3000
|
+
return createAgenrDebugSink(withLogPath);
|
|
3427
3001
|
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
return void 0;
|
|
3002
|
+
function ensureDebugLogPath(resolved, openClaw) {
|
|
3003
|
+
if (resolved.logPath) {
|
|
3004
|
+
return resolved;
|
|
3432
3005
|
}
|
|
3433
3006
|
try {
|
|
3007
|
+
const stateDir = openClaw.runtime.state.resolveStateDir(process.env);
|
|
3434
3008
|
return {
|
|
3435
|
-
|
|
3436
|
-
|
|
3009
|
+
...resolved,
|
|
3010
|
+
logPath: path6.join(stateDir, "agenr", "logs", "debug.jsonl")
|
|
3437
3011
|
};
|
|
3438
3012
|
} catch {
|
|
3439
|
-
return
|
|
3013
|
+
return { ...resolved, enabled: false };
|
|
3440
3014
|
}
|
|
3441
3015
|
}
|
|
3442
3016
|
|