agenr 2.1.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/LICENSE +21 -661
- package/README.md +48 -25
- package/dist/adapters/openclaw/index.d.ts +1 -1
- package/dist/adapters/openclaw/index.js +533 -2260
- package/dist/adapters/skeln/index.d.ts +1951 -0
- package/dist/adapters/skeln/index.js +3964 -0
- package/dist/chunk-E2DHUFZK.js +2660 -0
- package/dist/{chunk-DGV6D6Q3.js → chunk-EEEL53X4.js} +3547 -4479
- package/dist/chunk-GELCEVFA.js +14 -0
- package/dist/chunk-JSVQILB3.js +1207 -0
- package/dist/{chunk-7TDALVPY.js → chunk-NNO2V4GH.js} +5139 -4635
- package/dist/{chunk-MJIB6J5S.js → chunk-NOIZQRQV.js} +6 -4
- package/dist/{chunk-IMQIJPIP.js → chunk-V5CDMHRN.js} +48 -60
- package/dist/claim-slot-policy-CdrW_1l4.d.ts +13 -0
- package/dist/cli.js +19 -17
- package/dist/core/recall/index.d.ts +5 -14
- package/dist/core/recall/index.js +1 -1
- package/dist/internal-eval-server.js +5 -4
- package/dist/internal-recall-eval-server.js +5 -4
- package/dist/{ports-Nj5nd2Ri.d.ts → ports-CpzWESmZ.d.ts} +208 -2
- package/package.json +8 -6
- /package/dist/{chunk-6T5RXGIR.js → chunk-5LADPJ4C.js} +0 -0
|
@@ -1,60 +1,247 @@
|
|
|
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-DGV6D6Q3.js";
|
|
6
|
+
readOpenClawSessionsStore
|
|
7
|
+
} from "../../chunk-JSVQILB3.js";
|
|
11
8
|
import {
|
|
12
9
|
BEFORE_TURN_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
13
10
|
BEFORE_TURN_DEBUG_ARTIFACT_MAX_TOP_K,
|
|
14
11
|
RECALL_DEBUG_ARTIFACT_DEFAULT_TOP_K,
|
|
15
|
-
RECALL_DEBUG_ARTIFACT_MAX_TOP_K
|
|
12
|
+
RECALL_DEBUG_ARTIFACT_MAX_TOP_K
|
|
13
|
+
} from "../../chunk-GELCEVFA.js";
|
|
14
|
+
import {
|
|
15
|
+
EPISODE_SUMMARY_TIMEOUT_MS,
|
|
16
|
+
FETCH_TOOL_PARAMETERS,
|
|
17
|
+
RECALL_TOOL_PARAMETERS,
|
|
18
|
+
STORE_TOOL_PARAMETERS,
|
|
19
|
+
UPDATE_TOOL_PARAMETERS,
|
|
20
|
+
buildClaimExtractionRuntime,
|
|
21
|
+
buildRecallToolServices,
|
|
22
|
+
composeHostPluginServices,
|
|
23
|
+
createDeadlineAwareEpisodeSummaryLlm,
|
|
24
|
+
createSessionStartTracker,
|
|
25
|
+
embedEpisodeSummaryWithinBudget,
|
|
26
|
+
extractRecentTurnsFromMessages,
|
|
27
|
+
formatAgenrSessionStartRecall,
|
|
28
|
+
formatUnifiedRecallResults,
|
|
29
|
+
normalizeOptionalBoolean,
|
|
30
|
+
normalizeOptionalPositiveInteger,
|
|
31
|
+
normalizePluginInjectionMemoryPolicyConfig,
|
|
32
|
+
normalizePromptText,
|
|
33
|
+
parseFetchToolParams,
|
|
34
|
+
parseRecallToolParams,
|
|
35
|
+
parseStoreToolParams,
|
|
36
|
+
parseUpdateToolParams,
|
|
37
|
+
resolveBeforeTurnPolicy,
|
|
38
|
+
resolveSessionIdentityKey,
|
|
39
|
+
resolveSessionStartPolicy,
|
|
40
|
+
runFetchMemoryTool,
|
|
41
|
+
runRecallMemoryTool,
|
|
42
|
+
runSessionStart,
|
|
43
|
+
runStoreMemoryTool,
|
|
44
|
+
runUpdateMemoryTool,
|
|
45
|
+
writeBoundedSingleTranscriptEpisode
|
|
46
|
+
} from "../../chunk-E2DHUFZK.js";
|
|
47
|
+
import {
|
|
48
|
+
asRecord,
|
|
49
|
+
buildEntryMemoryResolverPorts,
|
|
50
|
+
createSingleTranscriptDiscoveryPort,
|
|
51
|
+
formatErrorMessage,
|
|
52
|
+
formatTargetSelector,
|
|
53
|
+
readBooleanParam,
|
|
54
|
+
resolveTargetEntry,
|
|
55
|
+
sanitizeFetchToolParams,
|
|
56
|
+
sanitizeUpdateToolParams
|
|
57
|
+
} from "../../chunk-EEEL53X4.js";
|
|
58
|
+
import {
|
|
16
59
|
containsAgenrMemoryContext,
|
|
17
60
|
formatAgenrBeforeTurnRecall,
|
|
18
61
|
runBeforeTurn,
|
|
19
|
-
stripAgenrMemoryContext
|
|
20
|
-
|
|
21
|
-
} from "../../chunk-IMQIJPIP.js";
|
|
62
|
+
stripAgenrMemoryContext
|
|
63
|
+
} from "../../chunk-V5CDMHRN.js";
|
|
22
64
|
import {
|
|
23
65
|
EMBEDDING_DIMENSIONS,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
createOpenAICrossEncoder,
|
|
32
|
-
createRecallAdapter,
|
|
33
|
-
mapEntryRow,
|
|
34
|
-
normalizeManualClaimKeyUpdate,
|
|
35
|
-
projectClaimCentricRecallEntry,
|
|
36
|
-
readConfig,
|
|
37
|
-
resolveClaimExtractionConfig,
|
|
38
|
-
resolveConfigPath,
|
|
39
|
-
resolveCrossEncoderApiKey,
|
|
40
|
-
resolveDbPath,
|
|
41
|
-
resolveEmbeddingApiKey,
|
|
42
|
-
resolveEmbeddingModel,
|
|
43
|
-
resolveModel,
|
|
44
|
-
runUnifiedRecall,
|
|
45
|
-
validateTemporalValidityRange
|
|
46
|
-
} from "../../chunk-7TDALVPY.js";
|
|
66
|
+
buildRecallToolDetails,
|
|
67
|
+
formatRecallToolSummary,
|
|
68
|
+
formatUnifiedRecallLogSummary,
|
|
69
|
+
sanitizeRecallToolParams,
|
|
70
|
+
sanitizeStoreToolParams,
|
|
71
|
+
truncate
|
|
72
|
+
} from "../../chunk-NNO2V4GH.js";
|
|
47
73
|
import {
|
|
48
|
-
recall,
|
|
49
74
|
resolveClaimSlotPolicy
|
|
50
|
-
} from "../../chunk-
|
|
75
|
+
} from "../../chunk-5LADPJ4C.js";
|
|
51
76
|
|
|
52
77
|
// src/adapters/openclaw/index.ts
|
|
53
78
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
54
79
|
|
|
80
|
+
// src/adapters/openclaw/tools/shared.ts
|
|
81
|
+
import { failedTextResult, readNumberParam, readStringArrayParam, readStringParam, textResult } from "openclaw/plugin-sdk/agent-runtime";
|
|
82
|
+
var OPENCLAW_PARAM_READER = {
|
|
83
|
+
readString: readStringParam,
|
|
84
|
+
readNumber: readNumberParam,
|
|
85
|
+
readStringArray: readStringArrayParam
|
|
86
|
+
};
|
|
87
|
+
function toOpenClawToolResult(outcome) {
|
|
88
|
+
if (outcome.failed) {
|
|
89
|
+
return failedTextResult(outcome.text, {
|
|
90
|
+
...outcome.details,
|
|
91
|
+
status: "failed"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return textResult(outcome.text, outcome.details);
|
|
95
|
+
}
|
|
96
|
+
async function resolveTargetEntry2(services, params, options = {}) {
|
|
97
|
+
return resolveTargetEntry(buildEntryMemoryResolverPorts(services), params, options);
|
|
98
|
+
}
|
|
99
|
+
function logToolCall(logger, toolName, ctx, summary, sanitizedParams) {
|
|
100
|
+
logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} ${summary}`);
|
|
101
|
+
logger.info(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} params=${JSON.stringify(sanitizedParams)}`);
|
|
102
|
+
}
|
|
103
|
+
function logToolFailure(logger, toolName, ctx, error) {
|
|
104
|
+
logger.warn(`[agenr] tool=${toolName} ${formatToolSessionContext(ctx)} failed: ${formatErrorMessage(error)}`);
|
|
105
|
+
}
|
|
106
|
+
function sanitizeRetireToolParams(params) {
|
|
107
|
+
return {
|
|
108
|
+
...params.id ? { id: params.id } : {},
|
|
109
|
+
...params.subject ? { subject: params.subject } : {},
|
|
110
|
+
...params.reason !== void 0 ? { reasonLength: params.reason.length } : {}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function sanitizeTraceToolParams(params) {
|
|
114
|
+
return {
|
|
115
|
+
...params.id ? { id: params.id } : {},
|
|
116
|
+
...params.subject ? { subject: params.subject } : {},
|
|
117
|
+
...params.last !== void 0 ? { last: params.last } : {}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function formatTrace(entry, supersededBy, supersedes, claimFamily, recallEvents) {
|
|
121
|
+
const slotPolicy = entry.claim_key ? claimFamily ? {
|
|
122
|
+
policy: claimFamily.slotPolicy ?? resolveClaimSlotPolicy(claimFamily.claimKey).policy,
|
|
123
|
+
reason: claimFamily.slotPolicyReason ?? resolveClaimSlotPolicy(claimFamily.claimKey).reason
|
|
124
|
+
} : resolveClaimSlotPolicy(entry.claim_key) : void 0;
|
|
125
|
+
const lines = [
|
|
126
|
+
`Trace for ${entry.id} | ${entry.subject}`,
|
|
127
|
+
`type=${entry.type} expiry=${entry.expiry} importance=${entry.importance} retired=${entry.retired}`,
|
|
128
|
+
`content=${truncate(entry.content, 220)}`
|
|
129
|
+
];
|
|
130
|
+
if (supersededBy) {
|
|
131
|
+
lines.push(`superseded_by=${supersededBy.id} | ${supersededBy.subject}`);
|
|
132
|
+
}
|
|
133
|
+
if (supersedes.length > 0) {
|
|
134
|
+
lines.push(`supersedes=${supersedes.map((item) => `${item.id} (${item.subject})`).join(", ")}`);
|
|
135
|
+
}
|
|
136
|
+
if (entry.claim_key) {
|
|
137
|
+
lines.push(`claim_key=${entry.claim_key}`);
|
|
138
|
+
if (slotPolicy) {
|
|
139
|
+
lines.push(`slot_policy=${slotPolicy.policy}`);
|
|
140
|
+
lines.push(`slot_policy_reason=${slotPolicy.reason}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (claimFamily && claimFamily.entries.length > 0) {
|
|
144
|
+
lines.push(
|
|
145
|
+
`claim_family=${claimFamily.claimKey} | slot_policy=${slotPolicy?.policy ?? "exclusive"} | ${claimFamily.entries.map((item) => `${item.id}:${describeTraceEntryState(item)}:${formatClaimLifecycleLabel(item)}`).join(", ")}`
|
|
146
|
+
);
|
|
147
|
+
if (slotPolicy) {
|
|
148
|
+
lines.push(`claim_family_policy_reason=${slotPolicy.reason}`);
|
|
149
|
+
}
|
|
150
|
+
const transitionSummary = summarizeTraceClaimFamilyTransition(claimFamily.entries);
|
|
151
|
+
if (transitionSummary) {
|
|
152
|
+
lines.push(`transition=${transitionSummary}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (entry.valid_from || entry.valid_to) {
|
|
156
|
+
lines.push(`validity=${entry.valid_from ?? "?"} -> ${entry.valid_to ?? "ongoing"}`);
|
|
157
|
+
}
|
|
158
|
+
if (entry.supersession_kind) {
|
|
159
|
+
lines.push(`supersession_kind=${entry.supersession_kind}${entry.supersession_reason ? ` reason=${truncate(entry.supersession_reason, 120)}` : ""}`);
|
|
160
|
+
}
|
|
161
|
+
if (recallEvents.length > 0) {
|
|
162
|
+
lines.push(
|
|
163
|
+
`recent_recalls=${recallEvents.map((event) => `${event.recalledAt}${event.query ? ` query=${event.query}` : ""}${event.sessionKey ? ` session=${event.sessionKey}` : ""}`).join(" ; ")}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return lines.join("\n");
|
|
167
|
+
}
|
|
168
|
+
function toolFailureResult(error) {
|
|
169
|
+
return failedTextResult(formatErrorMessage(error), {
|
|
170
|
+
status: "failed"
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function formatToolSessionContext(ctx) {
|
|
174
|
+
const normalizedSessionId = ctx.sessionId?.trim();
|
|
175
|
+
const normalizedSessionKey = ctx.sessionKey?.trim();
|
|
176
|
+
if (normalizedSessionId && normalizedSessionKey) {
|
|
177
|
+
return `session=${normalizedSessionId} key=${normalizedSessionKey}`;
|
|
178
|
+
}
|
|
179
|
+
if (normalizedSessionId) {
|
|
180
|
+
return `session=${normalizedSessionId}`;
|
|
181
|
+
}
|
|
182
|
+
if (normalizedSessionKey) {
|
|
183
|
+
return `key=${normalizedSessionKey}`;
|
|
184
|
+
}
|
|
185
|
+
return "session=unknown";
|
|
186
|
+
}
|
|
187
|
+
function describeTraceEntryState(entry) {
|
|
188
|
+
if (entry.superseded_by) {
|
|
189
|
+
return "superseded";
|
|
190
|
+
}
|
|
191
|
+
if (entry.retired || entry.valid_to) {
|
|
192
|
+
return "historical";
|
|
193
|
+
}
|
|
194
|
+
return "current";
|
|
195
|
+
}
|
|
196
|
+
function formatClaimLifecycleLabel(entry) {
|
|
197
|
+
if (!entry.claim_key) {
|
|
198
|
+
return "no-key";
|
|
199
|
+
}
|
|
200
|
+
return entry.claim_key_status ?? "legacy";
|
|
201
|
+
}
|
|
202
|
+
function summarizeTraceClaimFamilyTransition(entries) {
|
|
203
|
+
const current = entries.find((entry) => !entry.retired && !entry.superseded_by);
|
|
204
|
+
const prior = [...entries].reverse().find((entry) => entry.id !== current?.id && (entry.superseded_by !== void 0 || entry.retired || entry.valid_to !== void 0));
|
|
205
|
+
if (current && prior) {
|
|
206
|
+
return `${prior.id} -> ${current.id}`;
|
|
207
|
+
}
|
|
208
|
+
if (prior) {
|
|
209
|
+
return `${prior.id} is historical with no current sibling in the traced family`;
|
|
210
|
+
}
|
|
211
|
+
if (current) {
|
|
212
|
+
return `${current.id} is the only current sibling in the traced family`;
|
|
213
|
+
}
|
|
214
|
+
return void 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/adapters/openclaw/tools/fetch.ts
|
|
218
|
+
function createAgenrFetchTool(ctx, servicesPromise, logger) {
|
|
219
|
+
return {
|
|
220
|
+
name: "agenr_fetch",
|
|
221
|
+
label: "Agenr Fetch",
|
|
222
|
+
description: "Fetch the full body and metadata for one durable memory entry by id or subject.",
|
|
223
|
+
parameters: FETCH_TOOL_PARAMETERS,
|
|
224
|
+
async execute(_toolCallId, rawParams) {
|
|
225
|
+
try {
|
|
226
|
+
const params = parseFetchToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
227
|
+
logToolCall(logger, "agenr_fetch", ctx, `target=${formatTargetSelector(params.id, params.subject)}`, sanitizeFetchToolParams(params));
|
|
228
|
+
const services = await servicesPromise;
|
|
229
|
+
return toOpenClawToolResult(
|
|
230
|
+
await runFetchMemoryTool(params, services, {
|
|
231
|
+
extraDetails: { sessionKey: ctx.sessionKey }
|
|
232
|
+
})
|
|
233
|
+
);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
logToolFailure(logger, "agenr_fetch", ctx, error);
|
|
236
|
+
return toolFailureResult(error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
55
242
|
// src/adapters/openclaw/tools/recall.ts
|
|
243
|
+
import { textResult as textResult2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
56
244
|
import { randomUUID } from "crypto";
|
|
57
|
-
import { readNumberParam, readStringArrayParam, readStringParam as readStringParam2, textResult } from "openclaw/plugin-sdk/agent-runtime";
|
|
58
245
|
|
|
59
246
|
// src/adapters/openclaw/debug/sink.ts
|
|
60
247
|
import { appendFile, mkdir } from "fs/promises";
|
|
@@ -277,906 +464,158 @@ function buildTopCandidates(result, reasonsByEntryId, topK) {
|
|
|
277
464
|
});
|
|
278
465
|
}
|
|
279
466
|
|
|
280
|
-
// src/adapters/openclaw/tools/
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
467
|
+
// src/adapters/openclaw/tools/recall.ts
|
|
468
|
+
function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
469
|
+
return {
|
|
470
|
+
name: "agenr_recall",
|
|
471
|
+
label: "Agenr Recall",
|
|
472
|
+
description: "Retrieve knowledge from agenr long-term memory. Use mode=auto for the normal path, including historical-state questions like what was the previous approach or what changed from X to Y and procedural questions like how to do something or what steps to follow; use mode=entries for exact facts and decisions; use mode=episodes for time-bounded 'what happened' questions; use mode=procedures for canonical methods and checklists. Time periods are parsed from the query text. Session-start recall is already handled automatically.",
|
|
473
|
+
parameters: RECALL_TOOL_PARAMETERS,
|
|
474
|
+
async execute(_toolCallId, rawParams) {
|
|
475
|
+
try {
|
|
476
|
+
const params = parseRecallToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
477
|
+
const sanitizedParams = sanitizeRecallToolParams(params);
|
|
478
|
+
logToolCall(logger, "agenr_recall", ctx, formatRecallToolSummary(params), sanitizedParams);
|
|
479
|
+
const services = await servicesPromise;
|
|
480
|
+
void services.debugSink.emit({
|
|
481
|
+
type: "tool_call",
|
|
482
|
+
tool: "agenr_recall",
|
|
483
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
484
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
485
|
+
params: sanitizedParams
|
|
486
|
+
});
|
|
487
|
+
const result = await runRecallMemoryTool(params, buildRecallToolServices(services), {
|
|
488
|
+
sessionKey: ctx.sessionKey,
|
|
489
|
+
slotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies,
|
|
490
|
+
debugLog: (message) => {
|
|
491
|
+
logger.debug?.(message);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
logger.info(
|
|
495
|
+
`[agenr] tool=agenr_recall session=${ctx.sessionId ?? "unknown"} key=${ctx.sessionKey ?? "unknown"} result: ${formatUnifiedRecallLogSummary(result)}`
|
|
496
|
+
);
|
|
497
|
+
emitRecallDebugArtifacts(services, ctx, params.query, result);
|
|
498
|
+
return textResult2(formatUnifiedRecallResults(result), buildRecallToolDetails(result));
|
|
499
|
+
} catch (error) {
|
|
500
|
+
logToolFailure(logger, "agenr_recall", ctx, error);
|
|
501
|
+
await emitRecallError(servicesPromise, ctx, error);
|
|
502
|
+
return toolFailureResult(error);
|
|
503
|
+
}
|
|
308
504
|
}
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
const entry = await services.memory.findEntryBySubject(subject ?? "");
|
|
312
|
-
if (!entry) {
|
|
313
|
-
throw new Error(`No agenr entry found for subject "${subject}".`);
|
|
314
|
-
}
|
|
315
|
-
return entry;
|
|
316
|
-
}
|
|
317
|
-
function readBooleanParam(params, key) {
|
|
318
|
-
const value = params[key];
|
|
319
|
-
if (value === void 0) {
|
|
320
|
-
return void 0;
|
|
321
|
-
}
|
|
322
|
-
if (typeof value === "boolean") {
|
|
323
|
-
return value;
|
|
324
|
-
}
|
|
325
|
-
throw new Error(`${key} must be a boolean.`);
|
|
326
|
-
}
|
|
327
|
-
function parseEntryTypes(values) {
|
|
328
|
-
return normalizeStringArray(values).map((value) => parseEntryType(value));
|
|
329
|
-
}
|
|
330
|
-
function parseRecallMode(value) {
|
|
331
|
-
if (value === void 0) {
|
|
332
|
-
return void 0;
|
|
333
|
-
}
|
|
334
|
-
if (value === "auto" || value === "entries" || value === "episodes" || value === "procedures") {
|
|
335
|
-
return value;
|
|
336
|
-
}
|
|
337
|
-
throw new Error(`Unsupported recall mode "${value}".`);
|
|
505
|
+
};
|
|
338
506
|
}
|
|
339
|
-
function
|
|
340
|
-
if (
|
|
341
|
-
return
|
|
507
|
+
function emitRecallDebugArtifacts(services, ctx, query, result) {
|
|
508
|
+
if (!services.debugSink.enabled) {
|
|
509
|
+
return;
|
|
342
510
|
}
|
|
343
|
-
|
|
511
|
+
const sessionIdPayload = ctx.sessionId ? { sessionId: ctx.sessionId } : {};
|
|
512
|
+
const sessionKeyPayload = ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {};
|
|
513
|
+
void services.debugSink.emit({
|
|
514
|
+
type: "tool_result",
|
|
515
|
+
tool: "agenr_recall",
|
|
516
|
+
...sessionIdPayload,
|
|
517
|
+
...sessionKeyPayload,
|
|
518
|
+
summary: {
|
|
519
|
+
count: result.count,
|
|
520
|
+
routing: {
|
|
521
|
+
requested: result.routing.requested,
|
|
522
|
+
detectedIntent: result.routing.detectedIntent,
|
|
523
|
+
queried: [...result.routing.queried],
|
|
524
|
+
reason: result.routing.reason
|
|
525
|
+
},
|
|
526
|
+
selectedEntryIds: result.entries.map((entry) => entry.entry.id),
|
|
527
|
+
episodeIds: result.episodes.map((episode) => episode.episode.id),
|
|
528
|
+
selectedProcedureKey: result.procedure?.procedure_key ?? null,
|
|
529
|
+
notices: [...result.notices],
|
|
530
|
+
procedureNotices: [...result.procedureNotices]
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
void services.debugSink.emit({
|
|
534
|
+
type: "unified_recall",
|
|
535
|
+
...sessionIdPayload,
|
|
536
|
+
...sessionKeyPayload,
|
|
537
|
+
debug: buildLiveRecallDebugArtifact({
|
|
538
|
+
caseId: `live-${randomUUID()}`,
|
|
539
|
+
query,
|
|
540
|
+
result,
|
|
541
|
+
eventLevel: services.debugSink.eventLevel,
|
|
542
|
+
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
543
|
+
})
|
|
544
|
+
});
|
|
344
545
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
546
|
+
async function emitRecallError(servicesPromise, ctx, error) {
|
|
547
|
+
try {
|
|
548
|
+
const services = await servicesPromise;
|
|
549
|
+
if (services.debugSink.enabled) {
|
|
550
|
+
void services.debugSink.emit({
|
|
551
|
+
type: "error",
|
|
552
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
553
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
554
|
+
scope: "agenr_recall",
|
|
555
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
} catch {
|
|
351
559
|
}
|
|
352
|
-
throw new Error(`Unsupported expiry "${value}".`);
|
|
353
560
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
561
|
+
|
|
562
|
+
// src/adapters/openclaw/tools/retire.ts
|
|
563
|
+
import { failedTextResult as failedTextResult2, readStringParam as readStringParam2, textResult as textResult3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
564
|
+
var RETIRE_TOOL_PARAMETERS = {
|
|
565
|
+
type: "object",
|
|
566
|
+
additionalProperties: false,
|
|
567
|
+
properties: {
|
|
568
|
+
id: {
|
|
569
|
+
type: "string",
|
|
570
|
+
description: "Entry id to retire. Provide exactly one of id or subject."
|
|
571
|
+
},
|
|
572
|
+
subject: {
|
|
573
|
+
type: "string",
|
|
574
|
+
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."
|
|
575
|
+
},
|
|
576
|
+
reason: {
|
|
577
|
+
type: "string",
|
|
578
|
+
description: "Optional retirement reason so later trace output explains why this memory was removed."
|
|
579
|
+
}
|
|
357
580
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
function buildSessionSourceFile(ctx) {
|
|
361
|
-
const target = ctx.sessionKey ?? ctx.sessionId ?? ctx.agentId ?? "unknown";
|
|
362
|
-
return `openclaw-session:${target}`;
|
|
363
|
-
}
|
|
364
|
-
function buildToolCallClaimSupport(ctx, toolName, observedAt) {
|
|
581
|
+
};
|
|
582
|
+
function createAgenrRetireTool(ctx, servicesPromise, logger) {
|
|
365
583
|
return {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
...params.tags.length > 0 ? { tags: params.tags } : {},
|
|
398
|
-
contentLength: params.content.length,
|
|
399
|
-
...params.sourceContext !== void 0 ? { sourceContextLength: params.sourceContext.length } : {},
|
|
400
|
-
...params.supersedes !== void 0 ? { hasSupersedes: true } : {},
|
|
401
|
-
...params.claimKey !== void 0 ? { hasClaimKey: true } : {},
|
|
402
|
-
...params.validFrom !== void 0 ? { hasValidFrom: true } : {},
|
|
403
|
-
...params.validTo !== void 0 ? { hasValidTo: true } : {}
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
function formatRecallToolSummary(params) {
|
|
407
|
-
const parts = [`query=${JSON.stringify(truncate(params.query, 80))}`];
|
|
408
|
-
if (params.mode) {
|
|
409
|
-
parts.push(`mode=${params.mode}`);
|
|
410
|
-
}
|
|
411
|
-
if (params.limit !== void 0 && params.limit !== DEFAULT_RECALL_LIMIT) {
|
|
412
|
-
parts.push(`limit=${params.limit}`);
|
|
413
|
-
}
|
|
414
|
-
if (params.types.length > 0) {
|
|
415
|
-
parts.push(`types=${JSON.stringify(params.types)}`);
|
|
416
|
-
}
|
|
417
|
-
if (params.tags.length > 0) {
|
|
418
|
-
parts.push(`tags=${JSON.stringify(params.tags)}`);
|
|
419
|
-
}
|
|
420
|
-
if (params.asOf) {
|
|
421
|
-
parts.push(`as_of=${JSON.stringify(params.asOf)}`);
|
|
422
|
-
}
|
|
423
|
-
return parts.join(" ");
|
|
424
|
-
}
|
|
425
|
-
function sanitizeRecallToolParams(params) {
|
|
426
|
-
return {
|
|
427
|
-
query: params.query,
|
|
428
|
-
...params.mode ? { mode: params.mode } : {},
|
|
429
|
-
...params.limit !== void 0 ? { limit: params.limit } : {},
|
|
430
|
-
...params.threshold !== void 0 ? { threshold: params.threshold } : {},
|
|
431
|
-
...params.types.length > 0 ? { types: params.types } : {},
|
|
432
|
-
...params.tags.length > 0 ? { tags: params.tags } : {},
|
|
433
|
-
...params.asOf ? { asOf: params.asOf } : {}
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function sanitizeRetireToolParams(params) {
|
|
437
|
-
return {
|
|
438
|
-
...params.id ? { id: params.id } : {},
|
|
439
|
-
...params.subject ? { subject: params.subject } : {},
|
|
440
|
-
...params.reason !== void 0 ? { reasonLength: params.reason.length } : {}
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
function sanitizeUpdateToolParams(params) {
|
|
444
|
-
return {
|
|
445
|
-
...params.id ? { id: params.id } : {},
|
|
446
|
-
...params.subject ? { subject: params.subject } : {},
|
|
447
|
-
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
448
|
-
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
449
|
-
...params.claimKey !== void 0 ? { hasClaimKey: true } : {},
|
|
450
|
-
...params.validFrom !== void 0 ? { hasValidFrom: true } : {},
|
|
451
|
-
...params.validTo !== void 0 ? { hasValidTo: true } : {}
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
function sanitizeTraceToolParams(params) {
|
|
455
|
-
return {
|
|
456
|
-
...params.id ? { id: params.id } : {},
|
|
457
|
-
...params.subject ? { subject: params.subject } : {},
|
|
458
|
-
...params.last !== void 0 ? { last: params.last } : {}
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
function formatUnifiedRecallResults(result) {
|
|
462
|
-
const lines = [
|
|
463
|
-
"Recall Route",
|
|
464
|
-
`requested=${result.routing.requested} detected=${result.routing.detectedIntent} queried=${result.routing.queried.join(", ") || "none"}`,
|
|
465
|
-
result.routing.reason,
|
|
466
|
-
""
|
|
467
|
-
];
|
|
468
|
-
if (result.timeWindow) {
|
|
469
|
-
lines.push("Resolved Time Window");
|
|
470
|
-
lines.push(`${result.timeWindow.start} -> ${result.timeWindow.end} (${result.timeWindow.timezone}) from ${JSON.stringify(result.timeWindow.resolvedFrom)}`);
|
|
471
|
-
lines.push("");
|
|
472
|
-
}
|
|
473
|
-
if (result.asOf) {
|
|
474
|
-
lines.push("As Of");
|
|
475
|
-
lines.push(result.asOf);
|
|
476
|
-
lines.push("");
|
|
477
|
-
}
|
|
478
|
-
if (result.routing.queried.includes("procedures") || result.procedure || result.procedureCandidates.length > 0 || result.procedureNotices.length > 0) {
|
|
479
|
-
appendProcedureMatches(lines, result);
|
|
480
|
-
lines.push("");
|
|
481
|
-
}
|
|
482
|
-
const renderEntriesFirst = result.routing.detectedIntent === "historical_state";
|
|
483
|
-
if (renderEntriesFirst) {
|
|
484
|
-
appendEntryMatches(lines, result);
|
|
485
|
-
lines.push("");
|
|
486
|
-
appendClaimTransitions(lines, result);
|
|
487
|
-
lines.push("");
|
|
488
|
-
appendEpisodeMatches(lines, result);
|
|
489
|
-
} else {
|
|
490
|
-
appendEpisodeMatches(lines, result);
|
|
491
|
-
lines.push("");
|
|
492
|
-
appendEntryMatches(lines, result);
|
|
493
|
-
lines.push("");
|
|
494
|
-
appendClaimTransitions(lines, result);
|
|
495
|
-
}
|
|
496
|
-
if (result.notices.length > 0) {
|
|
497
|
-
lines.push("");
|
|
498
|
-
lines.push("Notices");
|
|
499
|
-
for (const notice of result.notices) {
|
|
500
|
-
lines.push(`- ${notice}`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return lines.join("\n");
|
|
504
|
-
}
|
|
505
|
-
function appendProcedureMatches(lines, result) {
|
|
506
|
-
lines.push("Procedure Matches");
|
|
507
|
-
if (!result.procedure && result.procedureCandidates.length === 0) {
|
|
508
|
-
lines.push("None.");
|
|
509
|
-
} else {
|
|
510
|
-
if (result.procedure) {
|
|
511
|
-
appendCanonicalProcedure(lines, result.procedure, result.procedureCandidates);
|
|
512
|
-
} else {
|
|
513
|
-
lines.push("Canonical procedure: none.");
|
|
514
|
-
}
|
|
515
|
-
const additionalCandidates = result.procedureCandidates.filter((candidate) => candidate.procedure.id !== result.procedure?.id);
|
|
516
|
-
if (additionalCandidates.length > 0) {
|
|
517
|
-
lines.push("Other Candidates");
|
|
518
|
-
for (const [index, candidate] of additionalCandidates.entries()) {
|
|
519
|
-
lines.push(
|
|
520
|
-
`${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)}`
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (result.procedureNotices.length > 0) {
|
|
526
|
-
lines.push("Procedure Notices");
|
|
527
|
-
for (const notice of result.procedureNotices) {
|
|
528
|
-
lines.push(`- ${notice}`);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
function appendEntryMatches(lines, result) {
|
|
533
|
-
lines.push("Entry Matches");
|
|
534
|
-
if (result.projectedEntries.length === 0) {
|
|
535
|
-
lines.push("None.");
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
for (const [familyIndex, family] of result.entryFamilies.entries()) {
|
|
539
|
-
lines.push(
|
|
540
|
-
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}`
|
|
541
|
-
);
|
|
542
|
-
for (const [entryIndex, entry] of family.entries.entries()) {
|
|
543
|
-
lines.push(
|
|
544
|
-
` ${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)}`
|
|
545
|
-
);
|
|
546
|
-
lines.push(` ${truncate(entry.recall.entry.content, 220)}`);
|
|
547
|
-
lines.push(` freshness=${entry.freshness.label}`);
|
|
548
|
-
const provenance = formatProjectedEntryProvenance(entry);
|
|
549
|
-
if (provenance) {
|
|
550
|
-
lines.push(` provenance=${provenance}`);
|
|
551
|
-
}
|
|
552
|
-
lines.push(` why_surfaced=${entry.whySurfaced.summary}`);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
function appendEpisodeMatches(lines, result) {
|
|
557
|
-
lines.push("Episode Matches");
|
|
558
|
-
if (result.episodes.length === 0) {
|
|
559
|
-
lines.push("None.");
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
for (const [index, episode] of result.episodes.entries()) {
|
|
563
|
-
lines.push(
|
|
564
|
-
`${index + 1}. ${episode.episode.id} | ${episode.episode.source} | ${episode.episode.startedAt} -> ${episode.episode.endedAt ?? episode.episode.startedAt} | score ${episode.score.toFixed(2)}`
|
|
565
|
-
);
|
|
566
|
-
lines.push(` ${index < 3 ? episode.episode.summary.trim() : truncate(episode.episode.summary.trim(), 220)}`);
|
|
567
|
-
lines.push(` why_matched=${describeEpisodeMatch(episode)}`);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
function appendCanonicalProcedure(lines, procedure, candidates) {
|
|
571
|
-
const leadCandidate = candidates.find((candidate) => candidate.procedure.id === procedure.id);
|
|
572
|
-
lines.push(
|
|
573
|
-
leadCandidate ? `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title} | score ${leadCandidate.score.toFixed(2)}` : `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title}`
|
|
574
|
-
);
|
|
575
|
-
lines.push(` goal=${procedure.goal}`);
|
|
576
|
-
appendLabeledList(lines, "when_to_use", procedure.when_to_use);
|
|
577
|
-
appendLabeledList(lines, "when_not_to_use", procedure.when_not_to_use);
|
|
578
|
-
appendLabeledList(lines, "prerequisites", procedure.prerequisites);
|
|
579
|
-
lines.push(" steps");
|
|
580
|
-
for (const [index, step] of procedure.steps.entries()) {
|
|
581
|
-
lines.push(` ${index + 1}. [${step.kind}] ${step.instruction}`);
|
|
582
|
-
const stepDetails = formatProcedureStepDetails(step);
|
|
583
|
-
if (stepDetails.length > 0) {
|
|
584
|
-
for (const detail of stepDetails) {
|
|
585
|
-
lines.push(` ${detail}`);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
appendLabeledList(lines, "verification", procedure.verification);
|
|
590
|
-
appendLabeledList(lines, "failure_modes", procedure.failure_modes);
|
|
591
|
-
lines.push(" sources");
|
|
592
|
-
for (const source of procedure.sources) {
|
|
593
|
-
lines.push(` - ${formatProcedureSource(source)}`);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
function appendClaimTransitions(lines, result) {
|
|
597
|
-
lines.push("Claim Transitions");
|
|
598
|
-
if (result.claimTransitions.length === 0) {
|
|
599
|
-
lines.push("None.");
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
for (const [index, transition] of result.claimTransitions.entries()) {
|
|
603
|
-
lines.push(
|
|
604
|
-
`${index + 1}. family=${transition.claimKey ?? transition.familyKey} | slot_policy=${transition.slotPolicy}${transition.currentEntryId ? ` | current=${transition.currentEntryId}` : ""}${transition.priorEntryId ? ` | prior=${transition.priorEntryId}` : ""}`
|
|
605
|
-
);
|
|
606
|
-
lines.push(` ${transition.summary}`);
|
|
607
|
-
if (transition.episodeContext) {
|
|
608
|
-
lines.push(
|
|
609
|
-
` episode=${transition.episodeContext.episodeId} | ${transition.episodeContext.startedAt} -> ${transition.episodeContext.endedAt ?? transition.episodeContext.startedAt}`
|
|
610
|
-
);
|
|
611
|
-
lines.push(` ${truncate(transition.episodeContext.summary.trim(), 220)}`);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
function formatUnifiedRecallLogSummary(result) {
|
|
616
|
-
const procedureCount = result.procedureCandidates.length;
|
|
617
|
-
const procedureSummary = result.procedure ? ` [procedure: ${JSON.stringify(truncate(result.procedure.title, 80))}]` : "";
|
|
618
|
-
const entrySubjects = result.entries.map((entry) => entry.entry.subject.trim()).filter((subject) => subject.length > 0);
|
|
619
|
-
const displayed = entrySubjects.slice(0, RESULT_SUBJECT_LOG_LIMIT).map((subject) => JSON.stringify(truncate(subject, 80)));
|
|
620
|
-
const remaining = entrySubjects.length - RESULT_SUBJECT_LOG_LIMIT;
|
|
621
|
-
const suffix = displayed.length === 0 ? "" : ` [entry subjects: ${displayed.join(", ")}${remaining > 0 ? `, ... and ${remaining} more` : ""}]`;
|
|
622
|
-
const entryEpisodeSummary = `${result.episodes.length} episode${result.episodes.length === 1 ? "" : "s"}, ${result.entries.length} entr${result.entries.length === 1 ? "y" : "ies"}`;
|
|
623
|
-
if (procedureCount === 0 && !result.procedure) {
|
|
624
|
-
return `${entryEpisodeSummary}${suffix}`;
|
|
625
|
-
}
|
|
626
|
-
return `${procedureCount} procedure candidate${procedureCount === 1 ? "" : "s"}, ${entryEpisodeSummary}${procedureSummary}${suffix}`;
|
|
627
|
-
}
|
|
628
|
-
function appendLabeledList(lines, label, values) {
|
|
629
|
-
lines.push(` ${label}`);
|
|
630
|
-
if (values.length === 0) {
|
|
631
|
-
lines.push(" - none");
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
for (const value of values) {
|
|
635
|
-
lines.push(` - ${value}`);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
function formatProcedureStepDetails(step) {
|
|
639
|
-
switch (step.kind) {
|
|
640
|
-
case "run_command":
|
|
641
|
-
return [`command=${step.command}`];
|
|
642
|
-
case "read_reference":
|
|
643
|
-
return [`ref=${formatProcedureSource(step.ref)}`];
|
|
644
|
-
case "inspect_state":
|
|
645
|
-
return [step.target ? `target=${step.target}` : void 0, step.query ? `query=${step.query}` : void 0].filter(
|
|
646
|
-
(value) => value !== void 0
|
|
647
|
-
);
|
|
648
|
-
case "edit_file":
|
|
649
|
-
return [`path=${step.path}`, `edit=${step.edit}`];
|
|
650
|
-
case "ask_user":
|
|
651
|
-
return [`prompt=${step.prompt}`];
|
|
652
|
-
case "invoke_tool":
|
|
653
|
-
return [step.tool ? `tool=${step.tool}` : void 0, step.arguments ? `arguments=${JSON.stringify(step.arguments)}` : void 0].filter(
|
|
654
|
-
(value) => value !== void 0
|
|
655
|
-
);
|
|
656
|
-
case "verify":
|
|
657
|
-
return step.checks.map((check) => `check=${check}`);
|
|
658
|
-
default:
|
|
659
|
-
return [];
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
function formatProcedureSource(source) {
|
|
663
|
-
const parts = [source.kind, source.label, source.path, source.locator].filter((value) => Boolean(value && value.length > 0));
|
|
664
|
-
return parts.join(" | ");
|
|
665
|
-
}
|
|
666
|
-
function formatTrace(entry, supersededBy, supersedes, claimFamily, recallEvents) {
|
|
667
|
-
const slotPolicy = entry.claim_key ? claimFamily ? {
|
|
668
|
-
policy: claimFamily.slotPolicy ?? resolveClaimSlotPolicy(claimFamily.claimKey).policy,
|
|
669
|
-
reason: claimFamily.slotPolicyReason ?? resolveClaimSlotPolicy(claimFamily.claimKey).reason
|
|
670
|
-
} : resolveClaimSlotPolicy(entry.claim_key) : void 0;
|
|
671
|
-
const lines = [
|
|
672
|
-
`Trace for ${entry.id} | ${entry.subject}`,
|
|
673
|
-
`type=${entry.type} expiry=${entry.expiry} importance=${entry.importance} retired=${entry.retired}`,
|
|
674
|
-
`content=${truncate(entry.content, 220)}`
|
|
675
|
-
];
|
|
676
|
-
if (supersededBy) {
|
|
677
|
-
lines.push(`superseded_by=${supersededBy.id} | ${supersededBy.subject}`);
|
|
678
|
-
}
|
|
679
|
-
if (supersedes.length > 0) {
|
|
680
|
-
lines.push(`supersedes=${supersedes.map((item) => `${item.id} (${item.subject})`).join(", ")}`);
|
|
681
|
-
}
|
|
682
|
-
if (entry.claim_key) {
|
|
683
|
-
lines.push(`claim_key=${entry.claim_key}`);
|
|
684
|
-
if (slotPolicy) {
|
|
685
|
-
lines.push(`slot_policy=${slotPolicy.policy}`);
|
|
686
|
-
lines.push(`slot_policy_reason=${slotPolicy.reason}`);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
if (claimFamily && claimFamily.entries.length > 0) {
|
|
690
|
-
lines.push(
|
|
691
|
-
`claim_family=${claimFamily.claimKey} | slot_policy=${slotPolicy?.policy ?? "exclusive"} | ${claimFamily.entries.map((item) => `${item.id}:${describeTraceEntryState(item)}:${formatClaimLifecycleLabel(item)}`).join(", ")}`
|
|
692
|
-
);
|
|
693
|
-
if (slotPolicy) {
|
|
694
|
-
lines.push(`claim_family_policy_reason=${slotPolicy.reason}`);
|
|
695
|
-
}
|
|
696
|
-
const transitionSummary = summarizeTraceClaimFamilyTransition(claimFamily.entries);
|
|
697
|
-
if (transitionSummary) {
|
|
698
|
-
lines.push(`transition=${transitionSummary}`);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
if (entry.valid_from || entry.valid_to) {
|
|
702
|
-
lines.push(`validity=${entry.valid_from ?? "?"} -> ${entry.valid_to ?? "ongoing"}`);
|
|
703
|
-
}
|
|
704
|
-
if (entry.supersession_kind) {
|
|
705
|
-
lines.push(`supersession_kind=${entry.supersession_kind}${entry.supersession_reason ? ` reason=${truncate(entry.supersession_reason, 120)}` : ""}`);
|
|
706
|
-
}
|
|
707
|
-
if (recallEvents.length > 0) {
|
|
708
|
-
lines.push(
|
|
709
|
-
`recent_recalls=${recallEvents.map((event) => `${event.recalledAt}${event.query ? ` query=${event.query}` : ""}${event.sessionKey ? ` session=${event.sessionKey}` : ""}`).join(" ; ")}`
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
return lines.join("\n");
|
|
713
|
-
}
|
|
714
|
-
function truncate(value, maxChars) {
|
|
715
|
-
if (value.length <= maxChars) {
|
|
716
|
-
return value;
|
|
717
|
-
}
|
|
718
|
-
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
719
|
-
}
|
|
720
|
-
function toolFailureResult(error) {
|
|
721
|
-
return failedTextResult(formatErrorMessage(error), {
|
|
722
|
-
status: "failed"
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
function formatErrorMessage(error) {
|
|
726
|
-
if (error instanceof Error) {
|
|
727
|
-
return error.message;
|
|
728
|
-
}
|
|
729
|
-
return String(error);
|
|
730
|
-
}
|
|
731
|
-
function asRecord(value) {
|
|
732
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
733
|
-
return value;
|
|
734
|
-
}
|
|
735
|
-
throw new Error("Tool parameters must be an object.");
|
|
736
|
-
}
|
|
737
|
-
function formatToolSessionContext(ctx) {
|
|
738
|
-
const normalizedSessionId = ctx.sessionId?.trim();
|
|
739
|
-
const normalizedSessionKey = ctx.sessionKey?.trim();
|
|
740
|
-
if (normalizedSessionId && normalizedSessionKey) {
|
|
741
|
-
return `session=${normalizedSessionId} key=${normalizedSessionKey}`;
|
|
742
|
-
}
|
|
743
|
-
if (normalizedSessionId) {
|
|
744
|
-
return `session=${normalizedSessionId}`;
|
|
745
|
-
}
|
|
746
|
-
if (normalizedSessionKey) {
|
|
747
|
-
return `key=${normalizedSessionKey}`;
|
|
748
|
-
}
|
|
749
|
-
return "session=unknown";
|
|
750
|
-
}
|
|
751
|
-
function describeEpisodeMatch(result) {
|
|
752
|
-
if (result.scores.semantic > 0 && result.scores.temporal > 0) {
|
|
753
|
-
return "Semantic match within the resolved time window.";
|
|
754
|
-
}
|
|
755
|
-
if (result.scores.semantic > 0) {
|
|
756
|
-
return "Semantic match to the episode summary.";
|
|
757
|
-
}
|
|
758
|
-
if (result.scores.temporal > 0) {
|
|
759
|
-
return "Session overlaps the resolved time window.";
|
|
760
|
-
}
|
|
761
|
-
return "Matched episodic recall ranking.";
|
|
762
|
-
}
|
|
763
|
-
function formatClaimStatus(status) {
|
|
764
|
-
return status === "no_key" ? "no-key" : status;
|
|
765
|
-
}
|
|
766
|
-
function formatProjectedEntryProvenance(entry) {
|
|
767
|
-
const parts = [
|
|
768
|
-
entry.provenance.supersededById ? `superseded_by=${entry.provenance.supersededById}` : void 0,
|
|
769
|
-
entry.provenance.supersessionKind ? `kind=${entry.provenance.supersessionKind}` : void 0,
|
|
770
|
-
entry.provenance.supersessionReason ? `reason=${truncate(entry.provenance.supersessionReason, 120)}` : void 0,
|
|
771
|
-
entry.provenance.supportSourceKind ? `support=${entry.provenance.supportSourceKind}` : void 0,
|
|
772
|
-
entry.provenance.supportMode ? `support_mode=${entry.provenance.supportMode}` : void 0,
|
|
773
|
-
entry.provenance.supportObservedAt ? `observed=${entry.provenance.supportObservedAt}` : void 0,
|
|
774
|
-
entry.provenance.supportLocator ? `locator=${truncate(entry.provenance.supportLocator, 120)}` : void 0
|
|
775
|
-
].filter((value) => value !== void 0);
|
|
776
|
-
return parts.join(" | ");
|
|
777
|
-
}
|
|
778
|
-
function describeTraceEntryState(entry) {
|
|
779
|
-
if (entry.superseded_by) {
|
|
780
|
-
return "superseded";
|
|
781
|
-
}
|
|
782
|
-
if (entry.retired || entry.valid_to) {
|
|
783
|
-
return "historical";
|
|
784
|
-
}
|
|
785
|
-
return "current";
|
|
786
|
-
}
|
|
787
|
-
function formatClaimLifecycleLabel(entry) {
|
|
788
|
-
if (!entry.claim_key) {
|
|
789
|
-
return "no-key";
|
|
790
|
-
}
|
|
791
|
-
return entry.claim_key_status ?? "legacy";
|
|
792
|
-
}
|
|
793
|
-
function summarizeTraceClaimFamilyTransition(entries) {
|
|
794
|
-
const current = entries.find((entry) => !entry.retired && !entry.superseded_by);
|
|
795
|
-
const prior = [...entries].reverse().find((entry) => entry.id !== current?.id && (entry.superseded_by !== void 0 || entry.retired || entry.valid_to !== void 0));
|
|
796
|
-
if (current && prior) {
|
|
797
|
-
return `${prior.id} -> ${current.id}`;
|
|
798
|
-
}
|
|
799
|
-
if (prior) {
|
|
800
|
-
return `${prior.id} is historical with no current sibling in the traced family`;
|
|
801
|
-
}
|
|
802
|
-
if (current) {
|
|
803
|
-
return `${current.id} is the only current sibling in the traced family`;
|
|
804
|
-
}
|
|
805
|
-
return void 0;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// src/adapters/openclaw/tools/recall.ts
|
|
809
|
-
var RECALL_TOOL_PARAMETERS = {
|
|
810
|
-
type: "object",
|
|
811
|
-
additionalProperties: false,
|
|
812
|
-
properties: {
|
|
813
|
-
query: {
|
|
814
|
-
type: "string",
|
|
815
|
-
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'."
|
|
816
|
-
},
|
|
817
|
-
mode: {
|
|
818
|
-
type: "string",
|
|
819
|
-
enum: [...RECALL_MODES],
|
|
820
|
-
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."
|
|
821
|
-
},
|
|
822
|
-
limit: {
|
|
823
|
-
type: "integer",
|
|
824
|
-
minimum: 1,
|
|
825
|
-
maximum: 10,
|
|
826
|
-
description: "Maximum results to return. Lower this when you want a tighter shortlist."
|
|
827
|
-
},
|
|
828
|
-
threshold: {
|
|
829
|
-
type: "number",
|
|
830
|
-
minimum: 0,
|
|
831
|
-
maximum: 1,
|
|
832
|
-
description: "Minimum final score from 0 to 1. Raise this when you want fewer, higher-confidence matches."
|
|
833
|
-
},
|
|
834
|
-
types: {
|
|
835
|
-
type: "array",
|
|
836
|
-
items: {
|
|
837
|
-
type: "string",
|
|
838
|
-
enum: [...ENTRY_TYPES]
|
|
839
|
-
},
|
|
840
|
-
description: "Optional knowledge types to filter by, such as decision, preference, lesson, fact, milestone, or relationship."
|
|
841
|
-
},
|
|
842
|
-
tags: {
|
|
843
|
-
type: "array",
|
|
844
|
-
items: { type: "string" },
|
|
845
|
-
description: "Optional tags to filter by once you already know the relevant entity, system, or theme."
|
|
846
|
-
},
|
|
847
|
-
asOf: {
|
|
848
|
-
type: "string",
|
|
849
|
-
description: "Optional reference time for current-vs-prior resolution. Supports ISO timestamps and the same natural-language date phrases used elsewhere in recall."
|
|
850
|
-
}
|
|
851
|
-
},
|
|
852
|
-
required: ["query"]
|
|
853
|
-
};
|
|
854
|
-
function createAgenrRecallTool(ctx, servicesPromise, logger) {
|
|
855
|
-
return {
|
|
856
|
-
name: "agenr_recall",
|
|
857
|
-
label: "Agenr Recall",
|
|
858
|
-
description: "Retrieve knowledge from agenr long-term memory. Use mode=auto for the normal path, including historical-state questions like what was the previous approach or what changed from X to Y and procedural questions like how to do something or what steps to follow; use mode=entries for exact facts and decisions; use mode=episodes for time-bounded 'what happened' questions; use mode=procedures for canonical methods and checklists. Time periods are parsed from the query text. Session-start recall is already handled automatically.",
|
|
859
|
-
parameters: RECALL_TOOL_PARAMETERS,
|
|
860
|
-
async execute(_toolCallId, rawParams) {
|
|
861
|
-
try {
|
|
862
|
-
const params = asRecord(rawParams);
|
|
863
|
-
const query = readStringParam2(params, "query", { required: true, label: "query" });
|
|
864
|
-
const mode = parseRecallMode(readStringParam2(params, "mode"));
|
|
865
|
-
const limit = readNumberParam(params, "limit", { integer: true, strict: true });
|
|
866
|
-
const threshold = readNumberParam(params, "threshold", { strict: true });
|
|
867
|
-
const services = await servicesPromise;
|
|
868
|
-
const types = parseEntryTypes(readStringArrayParam(params, "types"));
|
|
869
|
-
const tags = normalizeStringArray(readStringArrayParam(params, "tags"));
|
|
870
|
-
const asOf = readStringParam2(params, "asOf");
|
|
871
|
-
const request = {
|
|
872
|
-
text: query,
|
|
873
|
-
...mode ? { mode } : {},
|
|
874
|
-
...limit !== void 0 ? { limit } : {},
|
|
875
|
-
...threshold !== void 0 ? { threshold } : {},
|
|
876
|
-
...types.length > 0 ? { types } : {},
|
|
877
|
-
...tags.length > 0 ? { tags } : {},
|
|
878
|
-
...asOf ? { asOf } : {},
|
|
879
|
-
sessionKey: ctx.sessionKey
|
|
880
|
-
};
|
|
881
|
-
const sanitizedParams = sanitizeRecallToolParams({
|
|
882
|
-
query,
|
|
883
|
-
mode,
|
|
884
|
-
limit,
|
|
885
|
-
threshold,
|
|
886
|
-
types,
|
|
887
|
-
tags,
|
|
888
|
-
...asOf ? { asOf } : {}
|
|
889
|
-
});
|
|
890
|
-
logToolCall(
|
|
891
|
-
logger,
|
|
892
|
-
"agenr_recall",
|
|
893
|
-
ctx,
|
|
894
|
-
formatRecallToolSummary({
|
|
895
|
-
query,
|
|
896
|
-
mode,
|
|
897
|
-
limit,
|
|
898
|
-
types,
|
|
899
|
-
tags,
|
|
900
|
-
...asOf ? { asOf } : {}
|
|
901
|
-
}),
|
|
902
|
-
sanitizedParams
|
|
903
|
-
);
|
|
904
|
-
void services.debugSink.emit({
|
|
905
|
-
type: "tool_call",
|
|
906
|
-
tool: "agenr_recall",
|
|
907
|
-
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
908
|
-
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
909
|
-
params: sanitizedParams
|
|
910
|
-
});
|
|
911
|
-
const result = await runUnifiedRecall(request, {
|
|
912
|
-
database: services.episodes,
|
|
913
|
-
procedures: services.procedures,
|
|
914
|
-
recall: services.recall,
|
|
915
|
-
embeddingAvailable: services.embeddingStatus.available,
|
|
916
|
-
embeddingError: services.embeddingStatus.error,
|
|
917
|
-
claimSlotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies,
|
|
918
|
-
debugLog: (message) => {
|
|
919
|
-
logger.debug?.(message);
|
|
920
|
-
},
|
|
921
|
-
embedQuery: services.embeddingStatus.available ? async (text) => {
|
|
922
|
-
const vectors = await services.embedding.embed([text]);
|
|
923
|
-
return vectors[0] ?? [];
|
|
924
|
-
} : void 0,
|
|
925
|
-
recallOptions: {
|
|
926
|
-
slotPolicyConfig: services.pluginConfig.memoryPolicy?.slotPolicies
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
logger.info(
|
|
930
|
-
`[agenr] tool=agenr_recall session=${ctx.sessionId ?? "unknown"} key=${ctx.sessionKey ?? "unknown"} result: ${formatUnifiedRecallLogSummary(result)}`
|
|
931
|
-
);
|
|
932
|
-
if (services.debugSink.enabled) {
|
|
933
|
-
const sessionIdPayload = ctx.sessionId ? { sessionId: ctx.sessionId } : {};
|
|
934
|
-
const sessionKeyPayload = ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {};
|
|
935
|
-
void services.debugSink.emit({
|
|
936
|
-
type: "tool_result",
|
|
937
|
-
tool: "agenr_recall",
|
|
938
|
-
...sessionIdPayload,
|
|
939
|
-
...sessionKeyPayload,
|
|
940
|
-
summary: {
|
|
941
|
-
count: result.count,
|
|
942
|
-
routing: {
|
|
943
|
-
requested: result.routing.requested,
|
|
944
|
-
detectedIntent: result.routing.detectedIntent,
|
|
945
|
-
queried: [...result.routing.queried],
|
|
946
|
-
reason: result.routing.reason
|
|
947
|
-
},
|
|
948
|
-
selectedEntryIds: result.entries.map((entry) => entry.entry.id),
|
|
949
|
-
episodeIds: result.episodes.map((episode) => episode.episode.id),
|
|
950
|
-
selectedProcedureKey: result.procedure?.procedure_key ?? null,
|
|
951
|
-
notices: [...result.notices],
|
|
952
|
-
procedureNotices: [...result.procedureNotices]
|
|
953
|
-
}
|
|
954
|
-
});
|
|
955
|
-
void services.debugSink.emit({
|
|
956
|
-
type: "unified_recall",
|
|
957
|
-
...sessionIdPayload,
|
|
958
|
-
...sessionKeyPayload,
|
|
959
|
-
debug: buildLiveRecallDebugArtifact({
|
|
960
|
-
caseId: `live-${randomUUID()}`,
|
|
961
|
-
query,
|
|
962
|
-
result,
|
|
963
|
-
eventLevel: services.debugSink.eventLevel,
|
|
964
|
-
maxTopCandidates: services.debugSink.maxTopCandidates
|
|
965
|
-
})
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
return textResult(formatUnifiedRecallResults(result), {
|
|
969
|
-
status: "ok",
|
|
970
|
-
count: result.count,
|
|
971
|
-
routing: {
|
|
972
|
-
requested: result.routing.requested,
|
|
973
|
-
detectedIntent: result.routing.detectedIntent,
|
|
974
|
-
queried: result.routing.queried,
|
|
975
|
-
reason: result.routing.reason
|
|
976
|
-
},
|
|
977
|
-
...result.asOf ? { asOf: result.asOf } : {},
|
|
978
|
-
...result.timeWindow ? { timeWindow: result.timeWindow } : {},
|
|
979
|
-
...result.procedure ? {
|
|
980
|
-
procedure: {
|
|
981
|
-
id: result.procedure.id,
|
|
982
|
-
procedureKey: result.procedure.procedure_key,
|
|
983
|
-
title: result.procedure.title,
|
|
984
|
-
goal: result.procedure.goal
|
|
985
|
-
}
|
|
986
|
-
} : {},
|
|
987
|
-
procedures: result.procedureCandidates.map((candidate) => ({
|
|
988
|
-
id: candidate.procedure.id,
|
|
989
|
-
procedureKey: candidate.procedure.procedure_key,
|
|
990
|
-
title: candidate.procedure.title,
|
|
991
|
-
goal: candidate.procedure.goal,
|
|
992
|
-
score: candidate.score,
|
|
993
|
-
lexicalScore: candidate.scores.lexical,
|
|
994
|
-
vectorScore: candidate.scores.vector
|
|
995
|
-
})),
|
|
996
|
-
procedureNotices: result.procedureNotices,
|
|
997
|
-
episodes: result.episodes.map((episode) => ({
|
|
998
|
-
id: episode.episode.id,
|
|
999
|
-
source: episode.episode.source,
|
|
1000
|
-
sourceId: episode.episode.sourceId,
|
|
1001
|
-
startedAt: episode.episode.startedAt,
|
|
1002
|
-
endedAt: episode.episode.endedAt,
|
|
1003
|
-
tags: episode.episode.tags,
|
|
1004
|
-
score: episode.score,
|
|
1005
|
-
activityLevel: episode.episode.activityLevel,
|
|
1006
|
-
summary: episode.episode.summary,
|
|
1007
|
-
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."
|
|
1008
|
-
})),
|
|
1009
|
-
entries: result.entries.map((entry) => ({
|
|
1010
|
-
id: entry.entry.id,
|
|
1011
|
-
subject: entry.entry.subject,
|
|
1012
|
-
type: entry.entry.type,
|
|
1013
|
-
expiry: entry.entry.expiry,
|
|
1014
|
-
importance: entry.entry.importance,
|
|
1015
|
-
score: entry.score,
|
|
1016
|
-
tags: entry.entry.tags,
|
|
1017
|
-
content: entry.entry.content
|
|
1018
|
-
})),
|
|
1019
|
-
projectedEntries: result.projectedEntries.map((entry) => ({
|
|
1020
|
-
id: entry.entryId,
|
|
1021
|
-
familyKey: entry.familyKey,
|
|
1022
|
-
claimKey: entry.claimKey,
|
|
1023
|
-
slotPolicy: entry.slotPolicy,
|
|
1024
|
-
memoryState: entry.memoryState,
|
|
1025
|
-
claimStatus: entry.claimStatus,
|
|
1026
|
-
freshness: entry.freshness,
|
|
1027
|
-
provenance: entry.provenance,
|
|
1028
|
-
whySurfaced: entry.whySurfaced
|
|
1029
|
-
})),
|
|
1030
|
-
entryFamilies: result.entryFamilies.map((family) => ({
|
|
1031
|
-
familyKey: family.familyKey,
|
|
1032
|
-
claimKey: family.claimKey,
|
|
1033
|
-
slotPolicy: family.slotPolicy,
|
|
1034
|
-
subject: family.subject,
|
|
1035
|
-
primaryEntryId: family.primary.entryId,
|
|
1036
|
-
entries: family.entries.map((entry) => ({
|
|
1037
|
-
id: entry.entryId,
|
|
1038
|
-
memoryState: entry.memoryState,
|
|
1039
|
-
claimStatus: entry.claimStatus
|
|
1040
|
-
}))
|
|
1041
|
-
})),
|
|
1042
|
-
claimTransitions: result.claimTransitions,
|
|
1043
|
-
notices: result.notices
|
|
1044
|
-
});
|
|
1045
|
-
} catch (error) {
|
|
1046
|
-
logToolFailure(logger, "agenr_recall", ctx, error);
|
|
1047
|
-
try {
|
|
1048
|
-
const services = await servicesPromise;
|
|
1049
|
-
if (services.debugSink.enabled) {
|
|
1050
|
-
void services.debugSink.emit({
|
|
1051
|
-
type: "error",
|
|
1052
|
-
...ctx.sessionId ? { sessionId: ctx.sessionId } : {},
|
|
1053
|
-
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
1054
|
-
scope: "agenr_recall",
|
|
1055
|
-
error: { message: error instanceof Error ? error.message : String(error) }
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
} catch {
|
|
1059
|
-
}
|
|
1060
|
-
return toolFailureResult(error);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
// src/adapters/openclaw/tools/retire.ts
|
|
1067
|
-
import { failedTextResult as failedTextResult2, readStringParam as readStringParam3, textResult as textResult2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1068
|
-
var RETIRE_TOOL_PARAMETERS = {
|
|
1069
|
-
type: "object",
|
|
1070
|
-
additionalProperties: false,
|
|
1071
|
-
properties: {
|
|
1072
|
-
id: {
|
|
1073
|
-
type: "string",
|
|
1074
|
-
description: "Entry id to retire. Provide exactly one of id or subject."
|
|
1075
|
-
},
|
|
1076
|
-
subject: {
|
|
1077
|
-
type: "string",
|
|
1078
|
-
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."
|
|
1079
|
-
},
|
|
1080
|
-
reason: {
|
|
1081
|
-
type: "string",
|
|
1082
|
-
description: "Optional retirement reason so later trace output explains why this memory was removed."
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1086
|
-
function createAgenrRetireTool(ctx, servicesPromise, logger) {
|
|
1087
|
-
return {
|
|
1088
|
-
name: "agenr_retire",
|
|
1089
|
-
label: "Agenr Retire",
|
|
1090
|
-
description: "Mark a memory entry as retired (soft delete). Retired entries are excluded from all recall.",
|
|
1091
|
-
parameters: RETIRE_TOOL_PARAMETERS,
|
|
1092
|
-
async execute(_toolCallId, rawParams) {
|
|
1093
|
-
try {
|
|
1094
|
-
const params = asRecord(rawParams);
|
|
1095
|
-
const id = readStringParam3(params, "id");
|
|
1096
|
-
const subject = readStringParam3(params, "subject");
|
|
1097
|
-
const reason = readStringParam3(params, "reason");
|
|
1098
|
-
logToolCall(logger, "agenr_retire", ctx, `target=${formatTargetSelector(id, subject)}`, sanitizeRetireToolParams({ id, subject, reason }));
|
|
1099
|
-
const services = await servicesPromise;
|
|
1100
|
-
const entry = await resolveTargetEntry(services, params);
|
|
1101
|
-
const retired = await services.entries.retireEntry(entry.id, reason);
|
|
1102
|
-
if (!retired) {
|
|
1103
|
-
return failedTextResult2(`Entry ${entry.id} is not active, so it could not be retired.`, {
|
|
1104
|
-
status: "failed",
|
|
1105
|
-
entryId: entry.id
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
return textResult2(`Retired "${entry.subject}".`, {
|
|
1109
|
-
status: "retired",
|
|
1110
|
-
entryId: entry.id,
|
|
1111
|
-
subject: entry.subject,
|
|
1112
|
-
sessionKey: ctx.sessionKey
|
|
1113
|
-
});
|
|
1114
|
-
} catch (error) {
|
|
1115
|
-
logToolFailure(logger, "agenr_retire", ctx, error);
|
|
1116
|
-
return toolFailureResult(error);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
584
|
+
name: "agenr_retire",
|
|
585
|
+
label: "Agenr Retire",
|
|
586
|
+
description: "Mark a memory entry as retired (soft delete). Retired entries are excluded from all recall.",
|
|
587
|
+
parameters: RETIRE_TOOL_PARAMETERS,
|
|
588
|
+
async execute(_toolCallId, rawParams) {
|
|
589
|
+
try {
|
|
590
|
+
const params = asRecord(rawParams);
|
|
591
|
+
const id = readStringParam2(params, "id");
|
|
592
|
+
const subject = readStringParam2(params, "subject");
|
|
593
|
+
const reason = readStringParam2(params, "reason");
|
|
594
|
+
logToolCall(logger, "agenr_retire", ctx, `target=${formatTargetSelector(id, subject)}`, sanitizeRetireToolParams({ id, subject, reason }));
|
|
595
|
+
const services = await servicesPromise;
|
|
596
|
+
const entry = await resolveTargetEntry2(services, params);
|
|
597
|
+
const retired = await services.entries.retireEntry(entry.id, reason);
|
|
598
|
+
if (!retired) {
|
|
599
|
+
return failedTextResult2(`Entry ${entry.id} is not active, so it could not be retired.`, {
|
|
600
|
+
status: "failed",
|
|
601
|
+
entryId: entry.id
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
return textResult3(`Retired "${entry.subject}".`, {
|
|
605
|
+
status: "retired",
|
|
606
|
+
entryId: entry.id,
|
|
607
|
+
subject: entry.subject,
|
|
608
|
+
sessionKey: ctx.sessionKey
|
|
609
|
+
});
|
|
610
|
+
} catch (error) {
|
|
611
|
+
logToolFailure(logger, "agenr_retire", ctx, error);
|
|
612
|
+
return toolFailureResult(error);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
1119
615
|
};
|
|
1120
616
|
}
|
|
1121
617
|
|
|
1122
618
|
// src/adapters/openclaw/tools/store.ts
|
|
1123
|
-
import { failedTextResult as failedTextResult3, readNumberParam as readNumberParam2, readStringArrayParam as readStringArrayParam2, readStringParam as readStringParam4, textResult as textResult3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1124
|
-
var STORE_TOOL_PARAMETERS = {
|
|
1125
|
-
type: "object",
|
|
1126
|
-
additionalProperties: false,
|
|
1127
|
-
properties: {
|
|
1128
|
-
type: {
|
|
1129
|
-
type: "string",
|
|
1130
|
-
enum: [...ENTRY_TYPES],
|
|
1131
|
-
description: ENTRY_TYPE_DESCRIPTION
|
|
1132
|
-
},
|
|
1133
|
-
subject: {
|
|
1134
|
-
type: "string",
|
|
1135
|
-
description: "Short subject line future recall can match. Name the durable takeaway, person, system, rule, relationship, or milestone directly."
|
|
1136
|
-
},
|
|
1137
|
-
content: {
|
|
1138
|
-
type: "string",
|
|
1139
|
-
description: "What a fresh session should remember. Store the durable takeaway, not the activity log, canonical record, or transient progress snapshot."
|
|
1140
|
-
},
|
|
1141
|
-
importance: {
|
|
1142
|
-
type: "integer",
|
|
1143
|
-
minimum: 1,
|
|
1144
|
-
maximum: 10,
|
|
1145
|
-
description: "Importance from 1 to 10. Use 7 for normal durable memory, 9 for critical constraints, and 10 only rarely."
|
|
1146
|
-
},
|
|
1147
|
-
expiry: {
|
|
1148
|
-
type: "string",
|
|
1149
|
-
enum: ["core", "permanent", "temporary"],
|
|
1150
|
-
description: EXPIRY_DESCRIPTION
|
|
1151
|
-
},
|
|
1152
|
-
tags: {
|
|
1153
|
-
type: "array",
|
|
1154
|
-
items: { type: "string" },
|
|
1155
|
-
description: "Optional tags for entities, systems, teams, or themes that should improve later recall."
|
|
1156
|
-
},
|
|
1157
|
-
sourceContext: {
|
|
1158
|
-
type: "string",
|
|
1159
|
-
description: "Optional provenance note explaining why this memory was stored or what situation produced it."
|
|
1160
|
-
},
|
|
1161
|
-
supersedes: {
|
|
1162
|
-
type: "string",
|
|
1163
|
-
description: "ID of an entry this replaces. The old entry will be marked as superseded."
|
|
1164
|
-
},
|
|
1165
|
-
claimKey: {
|
|
1166
|
-
type: "string",
|
|
1167
|
-
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.'
|
|
1168
|
-
},
|
|
1169
|
-
validFrom: {
|
|
1170
|
-
type: "string",
|
|
1171
|
-
description: "ISO 8601 timestamp for when this fact became true in the world."
|
|
1172
|
-
},
|
|
1173
|
-
validTo: {
|
|
1174
|
-
type: "string",
|
|
1175
|
-
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
1176
|
-
}
|
|
1177
|
-
},
|
|
1178
|
-
required: ["type", "subject", "content"]
|
|
1179
|
-
};
|
|
1180
619
|
function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
1181
620
|
return {
|
|
1182
621
|
name: "agenr_store",
|
|
@@ -1185,95 +624,17 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
|
1185
624
|
parameters: STORE_TOOL_PARAMETERS,
|
|
1186
625
|
async execute(_toolCallId, rawParams) {
|
|
1187
626
|
try {
|
|
1188
|
-
const params =
|
|
1189
|
-
|
|
1190
|
-
const subject = readStringParam4(params, "subject", { required: true, label: "subject" });
|
|
1191
|
-
const content = readStringParam4(params, "content", { required: true, label: "content" });
|
|
1192
|
-
const importance = readNumberParam2(params, "importance", { integer: true, strict: true });
|
|
1193
|
-
const expiry = parseExpiry(readStringParam4(params, "expiry"));
|
|
1194
|
-
const tags = normalizeStringArray(readStringArrayParam2(params, "tags"));
|
|
1195
|
-
const sourceContext = readStringParam4(params, "sourceContext");
|
|
1196
|
-
const supersedes = readStringParam4(params, "supersedes");
|
|
1197
|
-
const claimKey = readStringParam4(params, "claimKey", { trim: false });
|
|
1198
|
-
const validFrom = readStringParam4(params, "validFrom");
|
|
1199
|
-
const validTo = readStringParam4(params, "validTo");
|
|
1200
|
-
const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1201
|
-
logToolCall(
|
|
1202
|
-
logger,
|
|
1203
|
-
"agenr_store",
|
|
1204
|
-
ctx,
|
|
1205
|
-
`store 1 entry subject=${JSON.stringify(subject)} type=${type}`,
|
|
1206
|
-
sanitizeStoreToolParams({
|
|
1207
|
-
type,
|
|
1208
|
-
subject,
|
|
1209
|
-
content,
|
|
1210
|
-
importance,
|
|
1211
|
-
expiry,
|
|
1212
|
-
tags,
|
|
1213
|
-
sourceContext,
|
|
1214
|
-
supersedes,
|
|
1215
|
-
claimKey,
|
|
1216
|
-
validFrom,
|
|
1217
|
-
validTo
|
|
1218
|
-
})
|
|
1219
|
-
);
|
|
627
|
+
const params = parseStoreToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
628
|
+
logToolCall(logger, "agenr_store", ctx, `store 1 entry subject=${JSON.stringify(params.subject)} type=${params.type}`, sanitizeStoreToolParams(params));
|
|
1220
629
|
const services = await servicesPromise;
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
content,
|
|
1227
|
-
...importance !== void 0 ? { importance } : {},
|
|
1228
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
1229
|
-
...tags.length > 0 ? { tags } : {},
|
|
1230
|
-
...supersedes ? { supersedes } : {},
|
|
1231
|
-
...claimKey ? {
|
|
1232
|
-
claim_key: claimKey,
|
|
1233
|
-
claim_key_raw: claimKey,
|
|
1234
|
-
...buildToolCallClaimSupport(ctx, "agenr_store", claimSupportObservedAt)
|
|
1235
|
-
} : {},
|
|
1236
|
-
...validFrom ? { valid_from: validFrom } : {},
|
|
1237
|
-
...validTo ? { valid_to: validTo } : {},
|
|
1238
|
-
source_file: buildSessionSourceFile(ctx),
|
|
1239
|
-
source_context: sourceContext ?? "Stored via agenr_store from OpenClaw."
|
|
1240
|
-
}
|
|
1241
|
-
],
|
|
1242
|
-
services.entries,
|
|
1243
|
-
services.embedding,
|
|
1244
|
-
{
|
|
1245
|
-
...services.claimExtraction ? {
|
|
1246
|
-
claimExtraction: {
|
|
1247
|
-
llm: services.claimExtraction.llm,
|
|
1248
|
-
db: services.entries,
|
|
1249
|
-
config: services.claimExtraction.config
|
|
1250
|
-
}
|
|
1251
|
-
} : {},
|
|
630
|
+
return toOpenClawToolResult(
|
|
631
|
+
await runStoreMemoryTool(params, services, {
|
|
632
|
+
session: ctx,
|
|
633
|
+
sourcePrefix: "openclaw-session",
|
|
634
|
+
defaultSourceContext: "Stored via agenr_store from OpenClaw.",
|
|
1252
635
|
onWarning: (warning) => logger.warn(`[agenr] tool=agenr_store session=${ctx.sessionId ?? "unknown"} warning: ${warning}`)
|
|
1253
|
-
}
|
|
636
|
+
})
|
|
1254
637
|
);
|
|
1255
|
-
const storedEntry = await services.memory.findEntryBySubject(subject);
|
|
1256
|
-
if (result.stored > 0) {
|
|
1257
|
-
return textResult3(`Stored "${subject}".`, {
|
|
1258
|
-
status: "stored",
|
|
1259
|
-
subject,
|
|
1260
|
-
entryId: storedEntry?.id,
|
|
1261
|
-
result
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
|
-
if (result.skipped > 0) {
|
|
1265
|
-
return textResult3(`Skipped "${subject}" because an active duplicate already exists.`, {
|
|
1266
|
-
status: "skipped",
|
|
1267
|
-
subject,
|
|
1268
|
-
entryId: storedEntry?.id,
|
|
1269
|
-
result
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
return failedTextResult3(`Rejected "${subject}". Check the supplied type, content, and metadata.`, {
|
|
1273
|
-
status: "failed",
|
|
1274
|
-
subject,
|
|
1275
|
-
result
|
|
1276
|
-
});
|
|
1277
638
|
} catch (error) {
|
|
1278
639
|
logToolFailure(logger, "agenr_store", ctx, error);
|
|
1279
640
|
return toolFailureResult(error);
|
|
@@ -1283,7 +644,7 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
|
|
|
1283
644
|
}
|
|
1284
645
|
|
|
1285
646
|
// src/adapters/openclaw/tools/trace.ts
|
|
1286
|
-
import { failedTextResult as
|
|
647
|
+
import { failedTextResult as failedTextResult3, readStringParam as readStringParam3, textResult as textResult4 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1287
648
|
var TRACE_TOOL_PARAMETERS = {
|
|
1288
649
|
type: "object",
|
|
1289
650
|
additionalProperties: false,
|
|
@@ -1311,74 +672,39 @@ function createAgenrTraceTool(ctx, servicesPromise, logger) {
|
|
|
1311
672
|
async execute(_toolCallId, rawParams) {
|
|
1312
673
|
try {
|
|
1313
674
|
const params = asRecord(rawParams);
|
|
1314
|
-
const id =
|
|
1315
|
-
const subject =
|
|
675
|
+
const id = readStringParam3(params, "id");
|
|
676
|
+
const subject = readStringParam3(params, "subject");
|
|
1316
677
|
const last = readBooleanParam(params, "last");
|
|
1317
678
|
logToolCall(logger, "agenr_trace", ctx, `target=${formatTargetSelector(id, subject, last)}`, sanitizeTraceToolParams({ id, subject, last }));
|
|
1318
679
|
const services = await servicesPromise;
|
|
1319
|
-
const entry = await
|
|
680
|
+
const entry = await resolveTargetEntry2(services, params, { allowLast: true });
|
|
1320
681
|
const trace = await services.memory.getEntryTrace(entry.id);
|
|
1321
682
|
if (!trace) {
|
|
1322
|
-
return
|
|
683
|
+
return failedTextResult3(`Entry ${entry.id} was not found for tracing.`, {
|
|
1323
684
|
status: "failed",
|
|
1324
685
|
entryId: entry.id
|
|
1325
686
|
});
|
|
1326
687
|
}
|
|
1327
688
|
return textResult4(formatTrace(trace.entry, trace.supersededBy, trace.supersedes, trace.claimFamily, trace.recallEvents), {
|
|
1328
689
|
status: "ok",
|
|
1329
|
-
sessionKey: ctx.sessionKey,
|
|
1330
|
-
trace: {
|
|
1331
|
-
entry: trace.entry,
|
|
1332
|
-
supersededBy: trace.supersededBy,
|
|
1333
|
-
supersedes: trace.supersedes,
|
|
1334
|
-
claimFamily: trace.claimFamily,
|
|
1335
|
-
recallEvents: trace.recallEvents
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
logToolFailure(logger, "agenr_trace", ctx, error);
|
|
1340
|
-
return toolFailureResult(error);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
// src/adapters/openclaw/tools/update.ts
|
|
1347
|
-
import { failedTextResult as failedTextResult5, readNumberParam as readNumberParam3, readStringParam as readStringParam6, textResult as textResult5 } from "openclaw/plugin-sdk/agent-runtime";
|
|
1348
|
-
var UPDATE_TOOL_PARAMETERS = {
|
|
1349
|
-
type: "object",
|
|
1350
|
-
additionalProperties: false,
|
|
1351
|
-
properties: {
|
|
1352
|
-
id: {
|
|
1353
|
-
type: "string",
|
|
1354
|
-
description: "Entry id to update. Provide exactly one of id or subject."
|
|
1355
|
-
},
|
|
1356
|
-
subject: {
|
|
1357
|
-
type: "string",
|
|
1358
|
-
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."
|
|
1359
|
-
},
|
|
1360
|
-
importance: {
|
|
1361
|
-
type: "integer",
|
|
1362
|
-
description: "New importance from 1 to 10. Use 7 for normal durable memory and reserve 9 to 10 for rare critical entries."
|
|
1363
|
-
},
|
|
1364
|
-
expiry: {
|
|
1365
|
-
type: "string",
|
|
1366
|
-
description: UPDATE_EXPIRY_DESCRIPTION
|
|
1367
|
-
},
|
|
1368
|
-
claimKey: {
|
|
1369
|
-
type: "string",
|
|
1370
|
-
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.'
|
|
1371
|
-
},
|
|
1372
|
-
validFrom: {
|
|
1373
|
-
type: "string",
|
|
1374
|
-
description: "ISO 8601 timestamp for when this fact became true."
|
|
1375
|
-
},
|
|
1376
|
-
validTo: {
|
|
1377
|
-
type: "string",
|
|
1378
|
-
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
690
|
+
sessionKey: ctx.sessionKey,
|
|
691
|
+
trace: {
|
|
692
|
+
entry: trace.entry,
|
|
693
|
+
supersededBy: trace.supersededBy,
|
|
694
|
+
supersedes: trace.supersedes,
|
|
695
|
+
claimFamily: trace.claimFamily,
|
|
696
|
+
recallEvents: trace.recallEvents
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
} catch (error) {
|
|
700
|
+
logToolFailure(logger, "agenr_trace", ctx, error);
|
|
701
|
+
return toolFailureResult(error);
|
|
702
|
+
}
|
|
1379
703
|
}
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/adapters/openclaw/tools/update.ts
|
|
1382
708
|
function createAgenrUpdateTool(ctx, servicesPromise, logger) {
|
|
1383
709
|
return {
|
|
1384
710
|
name: "agenr_update",
|
|
@@ -1387,72 +713,30 @@ function createAgenrUpdateTool(ctx, servicesPromise, logger) {
|
|
|
1387
713
|
parameters: UPDATE_TOOL_PARAMETERS,
|
|
1388
714
|
async execute(_toolCallId, rawParams) {
|
|
1389
715
|
try {
|
|
1390
|
-
const params =
|
|
1391
|
-
const id = readStringParam6(params, "id");
|
|
1392
|
-
const subject = readStringParam6(params, "subject");
|
|
1393
|
-
const importance = readNumberParam3(params, "importance", { integer: true, strict: true });
|
|
1394
|
-
const expiry = parseExpiry(readStringParam6(params, "expiry"));
|
|
1395
|
-
const claimKeyInput = readStringParam6(params, "claimKey", { trim: false });
|
|
1396
|
-
const validFrom = readStringParam6(params, "validFrom");
|
|
1397
|
-
const validTo = readStringParam6(params, "validTo");
|
|
1398
|
-
const claimSupportObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1399
|
-
const claimSupport = claimKeyInput === void 0 ? void 0 : buildToolCallClaimSupport(ctx, "agenr_update", claimSupportObservedAt);
|
|
1400
|
-
const normalizedClaimKeyUpdate = claimKeyInput === void 0 ? void 0 : (() => {
|
|
1401
|
-
try {
|
|
1402
|
-
return normalizeManualClaimKeyUpdate({
|
|
1403
|
-
claimKey: claimKeyInput,
|
|
1404
|
-
rawClaimKey: claimKeyInput,
|
|
1405
|
-
supportSourceKind: claimSupport?.claim_support_source_kind,
|
|
1406
|
-
supportLocator: claimSupport?.claim_support_locator,
|
|
1407
|
-
supportObservedAt: claimSupport?.claim_support_observed_at,
|
|
1408
|
-
supportMode: claimSupport?.claim_support_mode
|
|
1409
|
-
});
|
|
1410
|
-
} catch {
|
|
1411
|
-
throw new Error("claimKey must use canonical entity/attribute format.");
|
|
1412
|
-
}
|
|
1413
|
-
})();
|
|
716
|
+
const params = parseUpdateToolParams(rawParams, OPENCLAW_PARAM_READER);
|
|
1414
717
|
logToolCall(
|
|
1415
718
|
logger,
|
|
1416
719
|
"agenr_update",
|
|
1417
720
|
ctx,
|
|
1418
|
-
`target=${formatTargetSelector(id, subject)}${importance !== void 0 ? ` importance=${importance}` : ""}${expiry !== void 0 ? ` expiry=${expiry}` : ""}`,
|
|
1419
|
-
sanitizeUpdateToolParams({
|
|
721
|
+
`target=${formatTargetSelector(params.id, params.subject)}${params.importance !== void 0 ? ` importance=${params.importance}` : ""}${params.expiry !== void 0 ? ` expiry=${params.expiry}` : ""}`,
|
|
722
|
+
sanitizeUpdateToolParams({
|
|
723
|
+
id: params.id,
|
|
724
|
+
subject: params.subject,
|
|
725
|
+
importance: params.importance,
|
|
726
|
+
expiry: params.expiry,
|
|
727
|
+
claimKey: params.claimKeyInput,
|
|
728
|
+
validFrom: params.validFrom,
|
|
729
|
+
validTo: params.validTo
|
|
730
|
+
})
|
|
1420
731
|
);
|
|
1421
732
|
const services = await servicesPromise;
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
const normalizedValidFrom = validFrom !== void 0 ? mergedValidity.value.validFrom : void 0;
|
|
1431
|
-
const normalizedValidTo = validTo !== void 0 ? mergedValidity.value.validTo : void 0;
|
|
1432
|
-
const updated = await services.entries.updateEntry(entry.id, {
|
|
1433
|
-
...importance !== void 0 ? { importance } : {},
|
|
1434
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
1435
|
-
...normalizedClaimKeyUpdate?.updateFields ?? {},
|
|
1436
|
-
...validFrom !== void 0 ? { valid_from: normalizedValidFrom } : {},
|
|
1437
|
-
...validTo !== void 0 ? { valid_to: normalizedValidTo } : {}
|
|
1438
|
-
});
|
|
1439
|
-
if (!updated) {
|
|
1440
|
-
return failedTextResult5(`Entry ${entry.id} is not active, so it could not be updated.`, {
|
|
1441
|
-
status: "failed",
|
|
1442
|
-
entryId: entry.id
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
return textResult5(`Updated "${entry.subject}".`, {
|
|
1446
|
-
status: "updated",
|
|
1447
|
-
entryId: entry.id,
|
|
1448
|
-
subject: entry.subject,
|
|
1449
|
-
sessionKey: ctx.sessionKey,
|
|
1450
|
-
...importance !== void 0 ? { importance } : {},
|
|
1451
|
-
...expiry !== void 0 ? { expiry } : {},
|
|
1452
|
-
...normalizedClaimKeyUpdate !== void 0 ? { claimKey: normalizedClaimKeyUpdate.claimKey } : {},
|
|
1453
|
-
...validFrom !== void 0 ? { validFrom: normalizedValidFrom } : {},
|
|
1454
|
-
...validTo !== void 0 ? { validTo: normalizedValidTo } : {}
|
|
1455
|
-
});
|
|
733
|
+
return toOpenClawToolResult(
|
|
734
|
+
await runUpdateMemoryTool(params, services, {
|
|
735
|
+
session: ctx,
|
|
736
|
+
sourcePrefix: "openclaw-session",
|
|
737
|
+
successDetails: { sessionKey: ctx.sessionKey }
|
|
738
|
+
})
|
|
739
|
+
);
|
|
1456
740
|
} catch (error) {
|
|
1457
741
|
logToolFailure(logger, "agenr_update", ctx, error);
|
|
1458
742
|
return toolFailureResult(error);
|
|
@@ -1465,6 +749,7 @@ function createAgenrUpdateTool(ctx, servicesPromise, logger) {
|
|
|
1465
749
|
function registerAgenrOpenClawTools(api, servicesPromise, logger) {
|
|
1466
750
|
api.registerTool((ctx) => createAgenrStoreTool(ctx, servicesPromise, logger), { names: ["agenr_store"] });
|
|
1467
751
|
api.registerTool((ctx) => createAgenrRecallTool(ctx, servicesPromise, logger), { names: ["agenr_recall"] });
|
|
752
|
+
api.registerTool((ctx) => createAgenrFetchTool(ctx, servicesPromise, logger), { names: ["agenr_fetch"] });
|
|
1468
753
|
api.registerTool((ctx) => createAgenrRetireTool(ctx, servicesPromise, logger), { names: ["agenr_retire"] });
|
|
1469
754
|
api.registerTool((ctx) => createAgenrUpdateTool(ctx, servicesPromise, logger), { names: ["agenr_update"] });
|
|
1470
755
|
api.registerTool((ctx) => createAgenrTraceTool(ctx, servicesPromise, logger), { names: ["agenr_trace"] });
|
|
@@ -1474,11 +759,11 @@ function registerAgenrOpenClawTools(api, servicesPromise, logger) {
|
|
|
1474
759
|
var openclaw_plugin_default = {
|
|
1475
760
|
id: "agenr",
|
|
1476
761
|
name: "agenr",
|
|
1477
|
-
version: "
|
|
762
|
+
version: "3.1.0",
|
|
1478
763
|
description: "agenr memory plugin for OpenClaw",
|
|
1479
764
|
kind: "memory",
|
|
1480
765
|
contracts: {
|
|
1481
|
-
tools: ["agenr_store", "agenr_recall", "agenr_retire", "agenr_update", "agenr_trace"]
|
|
766
|
+
tools: ["agenr_store", "agenr_recall", "agenr_fetch", "agenr_retire", "agenr_update", "agenr_trace"]
|
|
1482
767
|
},
|
|
1483
768
|
uiHints: {
|
|
1484
769
|
dbPath: {
|
|
@@ -1603,6 +888,14 @@ var openclaw_plugin_default = {
|
|
|
1603
888
|
additionalProperties: false,
|
|
1604
889
|
description: "Optional session-start overrides for prompt-time memory injection behavior.",
|
|
1605
890
|
properties: {
|
|
891
|
+
enabled: {
|
|
892
|
+
type: "boolean",
|
|
893
|
+
description: "Enable or disable all session-start memory injection. Defaults to true."
|
|
894
|
+
},
|
|
895
|
+
coreMemory: {
|
|
896
|
+
type: "boolean",
|
|
897
|
+
description: "Enable or disable always-on Core Memory injection at session start. Defaults to true."
|
|
898
|
+
},
|
|
1606
899
|
relevantDurableMemory: {
|
|
1607
900
|
type: "boolean",
|
|
1608
901
|
description: "Enable or disable artifact-grounded Relevant Durable Memory injection at session start. Defaults to true."
|
|
@@ -1722,7 +1015,7 @@ function normalizeAgenrOpenClawPluginConfig(value) {
|
|
|
1722
1015
|
if (!storeNudgeResult.ok) {
|
|
1723
1016
|
errors.push(...storeNudgeResult.errors);
|
|
1724
1017
|
}
|
|
1725
|
-
const memoryPolicyResult =
|
|
1018
|
+
const memoryPolicyResult = normalizePluginInjectionMemoryPolicyConfig(value.memoryPolicy);
|
|
1726
1019
|
if (!memoryPolicyResult.ok) {
|
|
1727
1020
|
errors.push(...memoryPolicyResult.errors);
|
|
1728
1021
|
}
|
|
@@ -1882,193 +1175,11 @@ function normalizeStoreNudgeConfig(value) {
|
|
|
1882
1175
|
value: Object.keys(normalizedValue).length > 0 ? resolveStoreNudgeConfig(normalizedValue) : void 0
|
|
1883
1176
|
};
|
|
1884
1177
|
}
|
|
1885
|
-
function normalizeMemoryPolicyConfig(value) {
|
|
1886
|
-
if (value === void 0) {
|
|
1887
|
-
return { ok: true, value: void 0 };
|
|
1888
|
-
}
|
|
1889
|
-
if (!isRecord(value)) {
|
|
1890
|
-
return { ok: false, errors: ["memoryPolicy must be an object when provided"] };
|
|
1891
|
-
}
|
|
1892
|
-
const errors = [];
|
|
1893
|
-
const slotPoliciesResult = normalizeClaimSlotPolicyConfig(value.slotPolicies);
|
|
1894
|
-
if (!slotPoliciesResult.ok) {
|
|
1895
|
-
errors.push(...slotPoliciesResult.errors);
|
|
1896
|
-
}
|
|
1897
|
-
const sessionStartResult = normalizeSessionStartMemoryPolicyConfig(value.sessionStart);
|
|
1898
|
-
if (!sessionStartResult.ok) {
|
|
1899
|
-
errors.push(...sessionStartResult.errors);
|
|
1900
|
-
}
|
|
1901
|
-
const beforeTurnResult = normalizeBeforeTurnMemoryPolicyConfig(value.beforeTurn);
|
|
1902
|
-
if (!beforeTurnResult.ok) {
|
|
1903
|
-
errors.push(...beforeTurnResult.errors);
|
|
1904
|
-
}
|
|
1905
|
-
const allowedKeys = /* @__PURE__ */ new Set(["slotPolicies", "sessionStart", "beforeTurn"]);
|
|
1906
|
-
for (const key of Object.keys(value)) {
|
|
1907
|
-
if (!allowedKeys.has(key)) {
|
|
1908
|
-
errors.push(`unknown config field: memoryPolicy.${key}`);
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
if (errors.length > 0) {
|
|
1912
|
-
return { ok: false, errors };
|
|
1913
|
-
}
|
|
1914
|
-
return {
|
|
1915
|
-
ok: true,
|
|
1916
|
-
value: slotPoliciesResult.ok && slotPoliciesResult.value || sessionStartResult.ok && sessionStartResult.value || beforeTurnResult.ok && beforeTurnResult.value ? {
|
|
1917
|
-
...slotPoliciesResult.ok && slotPoliciesResult.value ? { slotPolicies: slotPoliciesResult.value } : {},
|
|
1918
|
-
...sessionStartResult.ok && sessionStartResult.value ? { sessionStart: sessionStartResult.value } : {},
|
|
1919
|
-
...beforeTurnResult.ok && beforeTurnResult.value ? { beforeTurn: beforeTurnResult.value } : {}
|
|
1920
|
-
} : void 0
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
function normalizeSessionStartMemoryPolicyConfig(value) {
|
|
1924
|
-
if (value === void 0) {
|
|
1925
|
-
return { ok: true, value: void 0 };
|
|
1926
|
-
}
|
|
1927
|
-
if (!isRecord(value)) {
|
|
1928
|
-
return { ok: false, errors: ["memoryPolicy.sessionStart must be an object when provided"] };
|
|
1929
|
-
}
|
|
1930
|
-
const errors = [];
|
|
1931
|
-
const relevantDurableMemory = normalizeOptionalBoolean(value.relevantDurableMemory, "memoryPolicy.sessionStart.relevantDurableMemory", errors);
|
|
1932
|
-
const allowedKeys = /* @__PURE__ */ new Set(["relevantDurableMemory"]);
|
|
1933
|
-
for (const key of Object.keys(value)) {
|
|
1934
|
-
if (!allowedKeys.has(key)) {
|
|
1935
|
-
errors.push(`unknown config field: memoryPolicy.sessionStart.${key}`);
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
if (errors.length > 0) {
|
|
1939
|
-
return { ok: false, errors };
|
|
1940
|
-
}
|
|
1941
|
-
return {
|
|
1942
|
-
ok: true,
|
|
1943
|
-
value: relevantDurableMemory !== void 0 ? { relevantDurableMemory } : void 0
|
|
1944
|
-
};
|
|
1945
|
-
}
|
|
1946
|
-
function normalizeBeforeTurnMemoryPolicyConfig(value) {
|
|
1947
|
-
if (value === void 0) {
|
|
1948
|
-
return { ok: true, value: void 0 };
|
|
1949
|
-
}
|
|
1950
|
-
if (!isRecord(value)) {
|
|
1951
|
-
return { ok: false, errors: ["memoryPolicy.beforeTurn must be an object when provided"] };
|
|
1952
|
-
}
|
|
1953
|
-
const errors = [];
|
|
1954
|
-
const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.beforeTurn.enabled", errors);
|
|
1955
|
-
const procedureSuggestion = normalizeOptionalBoolean(value.procedureSuggestion, "memoryPolicy.beforeTurn.procedureSuggestion", errors);
|
|
1956
|
-
const maxDurableEntries = normalizeOptionalPositiveInteger(value.maxDurableEntries, "memoryPolicy.beforeTurn.maxDurableEntries", errors);
|
|
1957
|
-
const recallThreshold = normalizeOptionalUnitInterval(value.recallThreshold, "memoryPolicy.beforeTurn.recallThreshold", errors);
|
|
1958
|
-
const highConfidenceRecallThreshold = normalizeOptionalUnitInterval(
|
|
1959
|
-
value.highConfidenceRecallThreshold,
|
|
1960
|
-
"memoryPolicy.beforeTurn.highConfidenceRecallThreshold",
|
|
1961
|
-
errors
|
|
1962
|
-
);
|
|
1963
|
-
const procedureThreshold = normalizeOptionalUnitInterval(value.procedureThreshold, "memoryPolicy.beforeTurn.procedureThreshold", errors);
|
|
1964
|
-
const allowedKeys = /* @__PURE__ */ new Set([
|
|
1965
|
-
"enabled",
|
|
1966
|
-
"procedureSuggestion",
|
|
1967
|
-
"maxDurableEntries",
|
|
1968
|
-
"recallThreshold",
|
|
1969
|
-
"highConfidenceRecallThreshold",
|
|
1970
|
-
"procedureThreshold"
|
|
1971
|
-
]);
|
|
1972
|
-
for (const key of Object.keys(value)) {
|
|
1973
|
-
if (!allowedKeys.has(key)) {
|
|
1974
|
-
errors.push(`unknown config field: memoryPolicy.beforeTurn.${key}`);
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
if (errors.length > 0) {
|
|
1978
|
-
return { ok: false, errors };
|
|
1979
|
-
}
|
|
1980
|
-
return {
|
|
1981
|
-
ok: true,
|
|
1982
|
-
value: enabled !== void 0 || procedureSuggestion !== void 0 || maxDurableEntries !== void 0 || recallThreshold !== void 0 || highConfidenceRecallThreshold !== void 0 || procedureThreshold !== void 0 ? {
|
|
1983
|
-
...enabled !== void 0 ? { enabled } : {},
|
|
1984
|
-
...procedureSuggestion !== void 0 ? { procedureSuggestion } : {},
|
|
1985
|
-
...maxDurableEntries !== void 0 ? { maxDurableEntries } : {},
|
|
1986
|
-
...recallThreshold !== void 0 ? { recallThreshold } : {},
|
|
1987
|
-
...highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold } : {},
|
|
1988
|
-
...procedureThreshold !== void 0 ? { procedureThreshold } : {}
|
|
1989
|
-
} : void 0
|
|
1990
|
-
};
|
|
1991
|
-
}
|
|
1992
|
-
function normalizeClaimSlotPolicyConfig(value) {
|
|
1993
|
-
if (value === void 0) {
|
|
1994
|
-
return { ok: true, value: void 0 };
|
|
1995
|
-
}
|
|
1996
|
-
if (!isRecord(value)) {
|
|
1997
|
-
return { ok: false, errors: ["memoryPolicy.slotPolicies must be an object when provided"] };
|
|
1998
|
-
}
|
|
1999
|
-
const errors = [];
|
|
2000
|
-
const attributeHeads = normalizeClaimSlotPolicyAttributeHeads(value.attributeHeads, errors);
|
|
2001
|
-
const allowedKeys = /* @__PURE__ */ new Set(["attributeHeads"]);
|
|
2002
|
-
for (const key of Object.keys(value)) {
|
|
2003
|
-
if (!allowedKeys.has(key)) {
|
|
2004
|
-
errors.push(`unknown config field: memoryPolicy.slotPolicies.${key}`);
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
if (errors.length > 0) {
|
|
2008
|
-
return { ok: false, errors };
|
|
2009
|
-
}
|
|
2010
|
-
return {
|
|
2011
|
-
ok: true,
|
|
2012
|
-
value: attributeHeads ? { attributeHeads } : void 0
|
|
2013
|
-
};
|
|
2014
|
-
}
|
|
2015
|
-
function normalizeClaimSlotPolicyAttributeHeads(value, errors) {
|
|
2016
|
-
if (value === void 0) {
|
|
2017
|
-
return void 0;
|
|
2018
|
-
}
|
|
2019
|
-
if (!isRecord(value)) {
|
|
2020
|
-
errors.push("memoryPolicy.slotPolicies.attributeHeads must be an object when provided");
|
|
2021
|
-
return void 0;
|
|
2022
|
-
}
|
|
2023
|
-
const normalized = {};
|
|
2024
|
-
for (const [rawKey, rawPolicy] of Object.entries(value)) {
|
|
2025
|
-
const attributeHead = rawKey.trim().toLowerCase();
|
|
2026
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/.test(attributeHead)) {
|
|
2027
|
-
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${rawKey} must use a canonical attribute-head label`);
|
|
2028
|
-
continue;
|
|
2029
|
-
}
|
|
2030
|
-
if (rawPolicy !== "exclusive" && rawPolicy !== "multivalued") {
|
|
2031
|
-
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${attributeHead} must be "exclusive" or "multivalued"`);
|
|
2032
|
-
continue;
|
|
2033
|
-
}
|
|
2034
|
-
normalized[attributeHead] = rawPolicy;
|
|
2035
|
-
}
|
|
2036
|
-
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
2037
|
-
}
|
|
2038
|
-
function normalizeOptionalBoolean(value, label, errors) {
|
|
2039
|
-
if (value === void 0) {
|
|
2040
|
-
return void 0;
|
|
2041
|
-
}
|
|
2042
|
-
if (typeof value !== "boolean") {
|
|
2043
|
-
errors.push(`${label} must be a boolean when provided`);
|
|
2044
|
-
return void 0;
|
|
2045
|
-
}
|
|
2046
|
-
return value;
|
|
2047
|
-
}
|
|
2048
|
-
function normalizeOptionalPositiveInteger(value, label, errors) {
|
|
2049
|
-
if (value === void 0) {
|
|
2050
|
-
return void 0;
|
|
2051
|
-
}
|
|
2052
|
-
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
2053
|
-
errors.push(`${label} must be a positive integer when provided`);
|
|
2054
|
-
return void 0;
|
|
2055
|
-
}
|
|
2056
|
-
return value;
|
|
2057
|
-
}
|
|
2058
|
-
function normalizeOptionalUnitInterval(value, label, errors) {
|
|
2059
|
-
if (value === void 0) {
|
|
2060
|
-
return void 0;
|
|
2061
|
-
}
|
|
2062
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
2063
|
-
errors.push(`${label} must be a number between 0 and 1 when provided`);
|
|
2064
|
-
return void 0;
|
|
2065
|
-
}
|
|
2066
|
-
return value;
|
|
2067
|
-
}
|
|
2068
1178
|
|
|
2069
1179
|
// src/adapters/openclaw/format/prompt-section.ts
|
|
2070
1180
|
var MEMORY_TOOL_NAMES = {
|
|
2071
1181
|
recall: "agenr_recall",
|
|
1182
|
+
fetch: "agenr_fetch",
|
|
2072
1183
|
store: "agenr_store",
|
|
2073
1184
|
update: "agenr_update",
|
|
2074
1185
|
retire: "agenr_retire",
|
|
@@ -2090,6 +1201,7 @@ function buildAgenrMemoryPromptSection({
|
|
|
2090
1201
|
"## Memory Recall",
|
|
2091
1202
|
"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.",
|
|
2092
1203
|
"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.",
|
|
1204
|
+
"agenr_recall returns truncated entry previews with ids, scores, and preview_truncated flags.",
|
|
2093
1205
|
"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.",
|
|
2094
1206
|
"One focused agenr_recall call with the right scope beats several broad ones.",
|
|
2095
1207
|
"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.",
|
|
@@ -2128,6 +1240,9 @@ function buildAgenrMemoryPromptSection({
|
|
|
2128
1240
|
if (availableTools.has(MEMORY_TOOL_NAMES.update) || availableTools.has(MEMORY_TOOL_NAMES.retire)) {
|
|
2129
1241
|
lines.push("When memory is contradicted by live evidence, fix it with agenr_update or agenr_retire instead of silently working around it.");
|
|
2130
1242
|
}
|
|
1243
|
+
if (availableTools.has(MEMORY_TOOL_NAMES.fetch)) {
|
|
1244
|
+
lines.push("Call agenr_fetch with id when preview_truncated=true or exact stored wording is required.");
|
|
1245
|
+
}
|
|
2131
1246
|
if (availableTools.has(MEMORY_TOOL_NAMES.trace)) {
|
|
2132
1247
|
lines.push("Use agenr_trace when provenance, recall history, or supersession matters.");
|
|
2133
1248
|
}
|
|
@@ -2163,59 +1278,7 @@ function formatErrorMessage2(error) {
|
|
|
2163
1278
|
return error instanceof Error ? error.message : String(error);
|
|
2164
1279
|
}
|
|
2165
1280
|
|
|
2166
|
-
// src/adapters/openclaw/session/identity.ts
|
|
2167
|
-
function resolveSessionIdentityKey(sessionId, sessionKey) {
|
|
2168
|
-
const normalizedSessionId = sessionId?.trim();
|
|
2169
|
-
if (normalizedSessionId) {
|
|
2170
|
-
return `session:${normalizedSessionId}`;
|
|
2171
|
-
}
|
|
2172
|
-
const normalizedSessionKey = sessionKey?.trim();
|
|
2173
|
-
if (normalizedSessionKey) {
|
|
2174
|
-
return `key:${normalizedSessionKey}`;
|
|
2175
|
-
}
|
|
2176
|
-
return void 0;
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
1281
|
// src/adapters/openclaw/session/state.ts
|
|
2180
|
-
function createSessionStartTracker() {
|
|
2181
|
-
const seenSessionIdentities = /* @__PURE__ */ new Set();
|
|
2182
|
-
const resumedFromBySessionId = /* @__PURE__ */ new Map();
|
|
2183
|
-
return {
|
|
2184
|
-
consume(sessionId, sessionKey) {
|
|
2185
|
-
const identityKey = resolveSessionIdentityKey(sessionId, sessionKey);
|
|
2186
|
-
if (!identityKey) {
|
|
2187
|
-
return {
|
|
2188
|
-
isFirst: true,
|
|
2189
|
-
activeCount: seenSessionIdentities.size
|
|
2190
|
-
};
|
|
2191
|
-
}
|
|
2192
|
-
if (seenSessionIdentities.has(identityKey)) {
|
|
2193
|
-
return {
|
|
2194
|
-
isFirst: false,
|
|
2195
|
-
activeCount: seenSessionIdentities.size
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
seenSessionIdentities.add(identityKey);
|
|
2199
|
-
return {
|
|
2200
|
-
isFirst: true,
|
|
2201
|
-
activeCount: seenSessionIdentities.size
|
|
2202
|
-
};
|
|
2203
|
-
},
|
|
2204
|
-
rememberSessionStart(sessionId, _sessionKey, resumedFrom) {
|
|
2205
|
-
const normalizedSessionId = sessionId?.trim();
|
|
2206
|
-
const normalizedResumedFrom = resumedFrom?.trim();
|
|
2207
|
-
if (!normalizedSessionId || !normalizedResumedFrom) {
|
|
2208
|
-
return;
|
|
2209
|
-
}
|
|
2210
|
-
resumedFromBySessionId.set(normalizedSessionId, normalizedResumedFrom);
|
|
2211
|
-
},
|
|
2212
|
-
getResumedFrom(sessionId) {
|
|
2213
|
-
const normalizedSessionId = sessionId?.trim();
|
|
2214
|
-
return normalizedSessionId ? resumedFromBySessionId.get(normalizedSessionId) : void 0;
|
|
2215
|
-
}
|
|
2216
|
-
};
|
|
2217
|
-
}
|
|
2218
|
-
var MAX_STORED_SUBJECTS = 5;
|
|
2219
1282
|
function createMidSessionTracker() {
|
|
2220
1283
|
const states = /* @__PURE__ */ new Map();
|
|
2221
1284
|
return {
|
|
@@ -2278,6 +1341,7 @@ function createMidSessionState() {
|
|
|
2278
1341
|
storedSubjects: []
|
|
2279
1342
|
};
|
|
2280
1343
|
}
|
|
1344
|
+
var MAX_STORED_SUBJECTS = 5;
|
|
2281
1345
|
|
|
2282
1346
|
// src/adapters/openclaw/hooks/after-tool-call.ts
|
|
2283
1347
|
var STORE_TOOL_NAME = "agenr_store";
|
|
@@ -2356,270 +1420,13 @@ function hasNonEmptyString(value) {
|
|
|
2356
1420
|
|
|
2357
1421
|
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
2358
1422
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2359
|
-
|
|
2360
|
-
// src/app/session-start/service.ts
|
|
2361
|
-
var DEFAULT_MAX_CORE_ENTRIES = 4;
|
|
2362
|
-
var DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES = 3;
|
|
2363
|
-
var DEFAULT_MAX_DURABLE_ENTRIES = 5;
|
|
2364
|
-
var DEFAULT_MAX_ARTIFACT_CHARS = 1200;
|
|
2365
|
-
async function runSessionStart(input, deps) {
|
|
2366
|
-
const policy = normalizePolicy(input.policy);
|
|
2367
|
-
const contextSections = buildContextSections(input);
|
|
2368
|
-
const coreEntries = await deps.repository.listCoreEntries(policy.maxCoreEntries);
|
|
2369
|
-
const coreItems = coreEntries.map((entry) => buildCorePatchItem(entry));
|
|
2370
|
-
const diagnostics = {
|
|
2371
|
-
coreCandidateCount: coreEntries.length,
|
|
2372
|
-
artifactRecallCandidateCount: 0,
|
|
2373
|
-
artifactRecallUsed: false,
|
|
2374
|
-
notices: []
|
|
2375
|
-
};
|
|
2376
|
-
const artifactRecallQuery = policy.enableArtifactRecall ? buildArtifactRecallQuery(contextSections, policy.maxArtifactChars) : void 0;
|
|
2377
|
-
if (!policy.enableArtifactRecall) {
|
|
2378
|
-
diagnostics.notices.push("Artifact-grounded durable recall disabled by session-start policy.");
|
|
2379
|
-
}
|
|
2380
|
-
const artifactRecallItems = artifactRecallQuery ? await runArtifactRecallSelection(artifactRecallQuery, input.sessionKey, policy, deps, diagnostics) : [];
|
|
2381
|
-
const durableMemory = assignRanks(mergeDurableMemory(coreItems, artifactRecallItems, policy.maxDurableEntries));
|
|
2382
|
-
return {
|
|
2383
|
-
contextSections,
|
|
2384
|
-
durableMemory,
|
|
2385
|
-
diagnostics
|
|
2386
|
-
};
|
|
2387
|
-
}
|
|
2388
|
-
function buildContextSections(input) {
|
|
2389
|
-
const sections = [];
|
|
2390
|
-
const continuitySummaryText = normalizeOptionalString(input.continuitySummaryText);
|
|
2391
|
-
if (continuitySummaryText) {
|
|
2392
|
-
sections.push({
|
|
2393
|
-
kind: "continuity_summary",
|
|
2394
|
-
title: "Previous session summary",
|
|
2395
|
-
content: continuitySummaryText
|
|
2396
|
-
});
|
|
2397
|
-
}
|
|
2398
|
-
const recentSessionText = normalizeOptionalString(input.recentSessionText);
|
|
2399
|
-
if (recentSessionText) {
|
|
2400
|
-
sections.push({
|
|
2401
|
-
kind: "recent_session",
|
|
2402
|
-
title: "Recent session",
|
|
2403
|
-
content: recentSessionText
|
|
2404
|
-
});
|
|
2405
|
-
}
|
|
2406
|
-
return sections;
|
|
2407
|
-
}
|
|
2408
|
-
async function runArtifactRecallSelection(query, sessionKey, policy, deps, diagnostics) {
|
|
2409
|
-
diagnostics.artifactRecallUsed = true;
|
|
2410
|
-
diagnostics.artifactRecallQuery = query;
|
|
2411
|
-
let artifactRecallTrace;
|
|
2412
|
-
try {
|
|
2413
|
-
const recalled = await recall(
|
|
2414
|
-
{
|
|
2415
|
-
text: query,
|
|
2416
|
-
limit: policy.maxArtifactRecallEntries,
|
|
2417
|
-
threshold: policy.recallThreshold,
|
|
2418
|
-
sessionKey
|
|
2419
|
-
},
|
|
2420
|
-
deps.recall,
|
|
2421
|
-
{
|
|
2422
|
-
trace: {
|
|
2423
|
-
reportSummary(summary) {
|
|
2424
|
-
artifactRecallTrace = summary;
|
|
2425
|
-
}
|
|
2426
|
-
},
|
|
2427
|
-
slotPolicyConfig: deps.slotPolicyConfig
|
|
2428
|
-
}
|
|
2429
|
-
);
|
|
2430
|
-
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
2431
|
-
diagnostics.artifactRecallCandidateCount = recalled.length;
|
|
2432
|
-
if (artifactRecallTrace?.degraded.notices.length) {
|
|
2433
|
-
diagnostics.notices.push(...artifactRecallTrace.degraded.notices);
|
|
2434
|
-
}
|
|
2435
|
-
return recalled.map((item) => buildArtifactRecallPatchItem(item, deps));
|
|
2436
|
-
} catch (error) {
|
|
2437
|
-
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
2438
|
-
diagnostics.notices.push(`Artifact-grounded durable recall failed: ${formatErrorMessage3(error)}`);
|
|
2439
|
-
return [];
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
function buildCorePatchItem(entry) {
|
|
2443
|
-
return {
|
|
2444
|
-
rank: 0,
|
|
2445
|
-
entry,
|
|
2446
|
-
sourceKind: "core",
|
|
2447
|
-
whySurfaced: {
|
|
2448
|
-
summary: `always-on core memory; importance ${entry.importance}`,
|
|
2449
|
-
reasons: ["always-on core memory", `importance ${entry.importance}`, `expiry ${entry.expiry}`]
|
|
2450
|
-
},
|
|
2451
|
-
memoryState: resolveMemoryState(entry),
|
|
2452
|
-
claimStatus: resolveClaimStatus(entry),
|
|
2453
|
-
freshnessLabel: buildFreshnessLabel(entry),
|
|
2454
|
-
...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
|
|
2455
|
-
};
|
|
2456
|
-
}
|
|
2457
|
-
function buildArtifactRecallPatchItem(recalled, deps) {
|
|
2458
|
-
const projected = projectClaimCentricRecallEntry(recalled, {
|
|
2459
|
-
slotPolicyConfig: deps.slotPolicyConfig
|
|
2460
|
-
});
|
|
2461
|
-
return {
|
|
2462
|
-
rank: 0,
|
|
2463
|
-
entry: recalled.entry,
|
|
2464
|
-
sourceKind: "artifact_recall",
|
|
2465
|
-
score: recalled.score,
|
|
2466
|
-
whySurfaced: projected.whySurfaced,
|
|
2467
|
-
memoryState: projected.memoryState,
|
|
2468
|
-
claimStatus: projected.claimStatus,
|
|
2469
|
-
freshnessLabel: projected.freshness.label,
|
|
2470
|
-
...formatProjectedProvenance(projected.provenance) ? { provenanceSummary: formatProjectedProvenance(projected.provenance) } : {}
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
function buildArtifactRecallQuery(sections, maxChars) {
|
|
2474
|
-
if (sections.length === 0 || maxChars <= 0) {
|
|
2475
|
-
return void 0;
|
|
2476
|
-
}
|
|
2477
|
-
let remaining = maxChars;
|
|
2478
|
-
const parts = [];
|
|
2479
|
-
for (const section of sections) {
|
|
2480
|
-
if (remaining <= 0) {
|
|
2481
|
-
break;
|
|
2482
|
-
}
|
|
2483
|
-
const normalizedContent = normalizeWhitespace(section.content);
|
|
2484
|
-
if (normalizedContent.length === 0) {
|
|
2485
|
-
continue;
|
|
2486
|
-
}
|
|
2487
|
-
const labeled = `${section.title}: ${normalizedContent}`;
|
|
2488
|
-
const truncated = truncate2(labeled, remaining);
|
|
2489
|
-
if (truncated.length === 0) {
|
|
2490
|
-
continue;
|
|
2491
|
-
}
|
|
2492
|
-
parts.push(truncated);
|
|
2493
|
-
remaining -= truncated.length;
|
|
2494
|
-
}
|
|
2495
|
-
const query = normalizeWhitespace(parts.join("\n"));
|
|
2496
|
-
return query.length > 0 ? query : void 0;
|
|
2497
|
-
}
|
|
2498
|
-
function mergeDurableMemory(coreItems, artifactRecallItems, maxDurableEntries) {
|
|
2499
|
-
const merged = [];
|
|
2500
|
-
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
2501
|
-
for (const item of [...coreItems, ...artifactRecallItems]) {
|
|
2502
|
-
if (seenEntryIds.has(item.entry.id)) {
|
|
2503
|
-
continue;
|
|
2504
|
-
}
|
|
2505
|
-
seenEntryIds.add(item.entry.id);
|
|
2506
|
-
merged.push(item);
|
|
2507
|
-
if (merged.length >= maxDurableEntries) {
|
|
2508
|
-
break;
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
return merged;
|
|
2512
|
-
}
|
|
2513
|
-
function assignRanks(items) {
|
|
2514
|
-
return items.map((item, index) => ({
|
|
2515
|
-
...item,
|
|
2516
|
-
rank: index + 1
|
|
2517
|
-
}));
|
|
2518
|
-
}
|
|
2519
|
-
function normalizePolicy(policy) {
|
|
2520
|
-
const maxCoreEntries = normalizeCount(policy?.maxCoreEntries, DEFAULT_MAX_CORE_ENTRIES);
|
|
2521
|
-
const maxArtifactRecallEntries = normalizeCount(policy?.maxArtifactRecallEntries, DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES);
|
|
2522
|
-
const maxDurableEntries = Math.max(maxCoreEntries, normalizeCount(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES));
|
|
2523
|
-
return {
|
|
2524
|
-
maxCoreEntries,
|
|
2525
|
-
enableArtifactRecall: policy?.enableArtifactRecall !== false,
|
|
2526
|
-
maxArtifactRecallEntries,
|
|
2527
|
-
maxDurableEntries,
|
|
2528
|
-
maxArtifactChars: normalizeCount(policy?.maxArtifactChars, DEFAULT_MAX_ARTIFACT_CHARS),
|
|
2529
|
-
recallThreshold: normalizeThreshold(policy?.recallThreshold)
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
function normalizeCount(value, fallback) {
|
|
2533
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2534
|
-
return fallback;
|
|
2535
|
-
}
|
|
2536
|
-
return Math.max(0, Math.trunc(value));
|
|
2537
|
-
}
|
|
2538
|
-
function normalizeThreshold(value) {
|
|
2539
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2540
|
-
return 0;
|
|
2541
|
-
}
|
|
2542
|
-
return Math.min(1, Math.max(0, value));
|
|
2543
|
-
}
|
|
2544
|
-
function resolveMemoryState(entry) {
|
|
2545
|
-
if (entry.superseded_by) {
|
|
2546
|
-
return "superseded";
|
|
2547
|
-
}
|
|
2548
|
-
if (entry.retired || entry.valid_to) {
|
|
2549
|
-
return "historical";
|
|
2550
|
-
}
|
|
2551
|
-
return "current";
|
|
2552
|
-
}
|
|
2553
|
-
function resolveClaimStatus(entry) {
|
|
2554
|
-
if (!normalizeOptionalString(entry.claim_key)) {
|
|
2555
|
-
return "no_key";
|
|
2556
|
-
}
|
|
2557
|
-
return entry.claim_key_status ?? "legacy";
|
|
2558
|
-
}
|
|
2559
|
-
function buildFreshnessLabel(entry) {
|
|
2560
|
-
const parts = [`created ${entry.created_at}`];
|
|
2561
|
-
const validFrom = normalizeOptionalString(entry.valid_from);
|
|
2562
|
-
const validTo = normalizeOptionalString(entry.valid_to);
|
|
2563
|
-
if (validFrom || validTo) {
|
|
2564
|
-
parts.push(`valid ${validFrom ?? "?"} -> ${validTo ?? "ongoing"}`);
|
|
2565
|
-
}
|
|
2566
|
-
return parts.join(" | ");
|
|
2567
|
-
}
|
|
2568
|
-
function buildProvenanceSummary(entry) {
|
|
2569
|
-
const parts = [
|
|
2570
|
-
entry.superseded_by ? `superseded_by=${entry.superseded_by}` : void 0,
|
|
2571
|
-
entry.supersession_kind ? `kind=${entry.supersession_kind}` : void 0,
|
|
2572
|
-
entry.supersession_reason ? `reason=${entry.supersession_reason}` : void 0,
|
|
2573
|
-
entry.claim_support_source_kind ? `support=${entry.claim_support_source_kind}` : void 0,
|
|
2574
|
-
entry.claim_support_mode ? `support_mode=${entry.claim_support_mode}` : void 0,
|
|
2575
|
-
entry.claim_support_observed_at ? `observed=${entry.claim_support_observed_at}` : void 0,
|
|
2576
|
-
entry.claim_support_locator ? `locator=${entry.claim_support_locator}` : void 0
|
|
2577
|
-
].filter((value) => value !== void 0);
|
|
2578
|
-
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
2579
|
-
}
|
|
2580
|
-
function formatProjectedProvenance(provenance) {
|
|
2581
|
-
const parts = [
|
|
2582
|
-
provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
|
|
2583
|
-
provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
|
|
2584
|
-
provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
|
|
2585
|
-
provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
|
|
2586
|
-
provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
|
|
2587
|
-
provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
|
|
2588
|
-
provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
|
|
2589
|
-
].filter((value) => value !== void 0);
|
|
2590
|
-
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
2591
|
-
}
|
|
2592
|
-
function normalizeOptionalString(value) {
|
|
2593
|
-
const normalized = value?.trim();
|
|
2594
|
-
return normalized && normalized.length > 0 ? normalized : void 0;
|
|
2595
|
-
}
|
|
2596
|
-
function normalizeWhitespace(value) {
|
|
2597
|
-
return value.replace(/\s+/g, " ").trim();
|
|
2598
|
-
}
|
|
2599
|
-
function truncate2(value, maxChars) {
|
|
2600
|
-
if (maxChars <= 0) {
|
|
2601
|
-
return "";
|
|
2602
|
-
}
|
|
2603
|
-
if (value.length <= maxChars) {
|
|
2604
|
-
return value;
|
|
2605
|
-
}
|
|
2606
|
-
return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
2607
|
-
}
|
|
2608
|
-
function formatErrorMessage3(error) {
|
|
2609
|
-
if (error instanceof Error) {
|
|
2610
|
-
return error.message;
|
|
2611
|
-
}
|
|
2612
|
-
return String(error);
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
2616
1423
|
import path5 from "path";
|
|
2617
1424
|
|
|
2618
1425
|
// src/adapters/openclaw/episode/episode-writer.ts
|
|
2619
1426
|
import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary2, resolveDefaultAgentId as resolveDefaultAgentId2 } from "openclaw/plugin-sdk/agent-runtime";
|
|
2620
1427
|
|
|
2621
1428
|
// src/adapters/openclaw/llm/openclaw-llm-client.ts
|
|
2622
|
-
import { completeSimple, getModel } from "@
|
|
1429
|
+
import { completeSimple, getModel } from "@earendil-works/pi-ai";
|
|
2623
1430
|
|
|
2624
1431
|
// src/adapters/openclaw/embedded-agent/task-runner.ts
|
|
2625
1432
|
import * as fs from "fs/promises";
|
|
@@ -2667,7 +1474,7 @@ async function createOpenClawLlmClient(openClaw, modelRef, label = "model overri
|
|
|
2667
1474
|
provider: execution.provider,
|
|
2668
1475
|
cfg: openClaw.config
|
|
2669
1476
|
});
|
|
2670
|
-
const apiKey =
|
|
1477
|
+
const apiKey = normalizeOptionalString(auth.apiKey);
|
|
2671
1478
|
if (!apiKey) {
|
|
2672
1479
|
throw new Error(`OpenClaw auth did not resolve an API-key-compatible credential for ${execution.provider} (source=${auth.source}, mode=${auth.mode}).`);
|
|
2673
1480
|
}
|
|
@@ -2712,7 +1519,7 @@ function stripCodeFence(text) {
|
|
|
2712
1519
|
const match = /^```(?:json)?\s*([\s\S]+?)\s*```$/iu.exec(trimmed);
|
|
2713
1520
|
return match?.[1]?.trim() ?? trimmed;
|
|
2714
1521
|
}
|
|
2715
|
-
function
|
|
1522
|
+
function normalizeOptionalString(value) {
|
|
2716
1523
|
const normalized = value?.trim();
|
|
2717
1524
|
return normalized ? normalized : void 0;
|
|
2718
1525
|
}
|
|
@@ -2721,19 +1528,6 @@ function normalizeOptionalString2(value) {
|
|
|
2721
1528
|
var OPENCLAW_EPISODE_GENERATOR_VERSION = "openclaw-episodic-summary-v1";
|
|
2722
1529
|
|
|
2723
1530
|
// src/adapters/openclaw/episode/episode-writer.ts
|
|
2724
|
-
var EPISODE_SUMMARY_TIMEOUT_MS = 45e3;
|
|
2725
|
-
var EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE = "Episode summary generation timed out.";
|
|
2726
|
-
var EPISODE_EMBEDDING_TIMEOUT = /* @__PURE__ */ Symbol("episode-embedding-timeout");
|
|
2727
|
-
var EPISODE_EMBEDDING_MIN_HEADROOM_MS = 5e3;
|
|
2728
|
-
var OpenClawEpisodeSummaryTimeoutError = class extends Error {
|
|
2729
|
-
/**
|
|
2730
|
-
* Creates a timeout error with a stable name for caller-side handling.
|
|
2731
|
-
*/
|
|
2732
|
-
constructor() {
|
|
2733
|
-
super(EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE);
|
|
2734
|
-
this.name = "OpenClawEpisodeSummaryTimeoutError";
|
|
2735
|
-
}
|
|
2736
|
-
};
|
|
2737
1531
|
async function writeOpenClawPredecessorEpisode(params) {
|
|
2738
1532
|
const sessionContext = formatSessionContext(params.ctx.sessionId, params.ctx.sessionKey);
|
|
2739
1533
|
const writeStartedAtMs = Date.now();
|
|
@@ -2743,78 +1537,70 @@ async function writeOpenClawPredecessorEpisode(params) {
|
|
|
2743
1537
|
}
|
|
2744
1538
|
const predecessor = params.predecessor;
|
|
2745
1539
|
params.logger.info(`[agenr] session-start predecessor episode write triggered for ${sessionContext} predecessor=${predecessor.sessionFile}`);
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
1540
|
+
const episodeModelRef = resolveOpenClawEpisodeModelRef(params.services.openClaw, params.ctx.agentId, params.services.pluginConfig.episodeModel);
|
|
1541
|
+
const episodeModel = episodeModelRef ?? "default";
|
|
1542
|
+
const summaryDeadlineMs = writeStartedAtMs + EPISODE_SUMMARY_TIMEOUT_MS;
|
|
1543
|
+
const llm = await createOpenClawLlmClient(params.services.openClaw, episodeModelRef, "episode model override");
|
|
1544
|
+
const summaryLlm = createDeadlineAwareEpisodeSummaryLlm(
|
|
1545
|
+
{
|
|
1546
|
+
complete: llm.complete.bind(llm),
|
|
1547
|
+
completeJson: llm.completeJson.bind(llm),
|
|
1548
|
+
metadata: {
|
|
1549
|
+
modelRef: episodeModel,
|
|
1550
|
+
pricing: {
|
|
1551
|
+
input: 0,
|
|
1552
|
+
output: 0,
|
|
1553
|
+
cacheRead: 0,
|
|
1554
|
+
cacheWrite: 0
|
|
1555
|
+
},
|
|
1556
|
+
usage: {
|
|
1557
|
+
calls: 0,
|
|
1558
|
+
inputTokens: 0,
|
|
1559
|
+
outputTokens: 0,
|
|
1560
|
+
cacheReadTokens: 0,
|
|
1561
|
+
cacheWriteTokens: 0,
|
|
1562
|
+
totalTokens: 0,
|
|
1563
|
+
totalCost: 0
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
},
|
|
1567
|
+
summaryDeadlineMs
|
|
1568
|
+
);
|
|
1569
|
+
await writeBoundedSingleTranscriptEpisode({
|
|
1570
|
+
filePath: predecessor.sessionFile,
|
|
1571
|
+
context: sessionContext,
|
|
1572
|
+
actionLabel: "session-start predecessor episode write",
|
|
1573
|
+
logger: params.logger,
|
|
1574
|
+
summaryDeadlineMs,
|
|
1575
|
+
fileField: "predecessor",
|
|
1576
|
+
shortCountField: "cleanedMessages",
|
|
1577
|
+
failureModelRef: episodeModel,
|
|
1578
|
+
unexpectedFailureLevel: "info",
|
|
1579
|
+
ports: {
|
|
1580
|
+
files: createSingleTranscriptDiscoveryPort(predecessor.sessionFile),
|
|
1581
|
+
transcript: openClawTranscriptParser,
|
|
1582
|
+
episodes: params.services.episodes,
|
|
1583
|
+
createSummaryLlm: () => summaryLlm,
|
|
1584
|
+
embedSummary: (summary) => embedEpisodeSummaryWithinBudget({
|
|
1585
|
+
summary,
|
|
1586
|
+
embedding: params.services.embedding,
|
|
1587
|
+
embeddingAvailable: params.services.embeddingStatus.available,
|
|
1588
|
+
deadlineMs: summaryDeadlineMs,
|
|
1589
|
+
logger: params.logger,
|
|
1590
|
+
logContext: `[agenr] session-start predecessor episode embedding skipped for ${sessionContext} predecessor=${predecessor.sessionFile}`
|
|
2788
1591
|
})
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
}
|
|
2800
|
-
if (ingestResult.session.action === "failed") {
|
|
2801
|
-
logFailedEpisodeIngest(sessionContext, predecessor.sessionFile, ingestResult.session.error, episodeModel, params.logger);
|
|
2802
|
-
return;
|
|
2803
|
-
}
|
|
2804
|
-
params.logger.info(
|
|
2805
|
-
`[agenr] session-start predecessor episode write ${ingestResult.session.action} for ${sessionContext} predecessor=${predecessor.sessionFile} episode=${ingestResult.session.episodeId}`
|
|
2806
|
-
);
|
|
2807
|
-
} catch (error) {
|
|
2808
|
-
if (error instanceof OpenClawEpisodeSummaryTimeoutError) {
|
|
2809
|
-
params.logger.info(
|
|
2810
|
-
`[agenr] session-start predecessor episode write timed_out for ${sessionContext} predecessor=${predecessor.sessionFile} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`
|
|
2811
|
-
);
|
|
2812
|
-
return;
|
|
1592
|
+
},
|
|
1593
|
+
ingestOptions: {
|
|
1594
|
+
genVersion: OPENCLAW_EPISODE_GENERATOR_VERSION,
|
|
1595
|
+
skipActiveSessionCheck: true,
|
|
1596
|
+
candidateOverrides: {
|
|
1597
|
+
sessionId: predecessor.sessionId,
|
|
1598
|
+
agentId: trimOptionalString(params.ctx.agentId) ?? null,
|
|
1599
|
+
surface: resolveSessionSurface(params.ctx) ?? null,
|
|
1600
|
+
metadataSource: "registry"
|
|
1601
|
+
}
|
|
2813
1602
|
}
|
|
2814
|
-
|
|
2815
|
-
`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessor.sessionFile} reason=${formatErrorMessage2(error)}`
|
|
2816
|
-
);
|
|
2817
|
-
}
|
|
1603
|
+
});
|
|
2818
1604
|
}
|
|
2819
1605
|
function resolveSessionSurface(ctx) {
|
|
2820
1606
|
const sessionKey = ctx.sessionKey?.trim() ?? "";
|
|
@@ -2827,152 +1613,6 @@ function resolveSessionSurface(ctx) {
|
|
|
2827
1613
|
}
|
|
2828
1614
|
return void 0;
|
|
2829
1615
|
}
|
|
2830
|
-
function createSingleTranscriptDiscoveryPort(filePath) {
|
|
2831
|
-
return {
|
|
2832
|
-
async discoverFiles(_targetPath) {
|
|
2833
|
-
return [filePath];
|
|
2834
|
-
}
|
|
2835
|
-
};
|
|
2836
|
-
}
|
|
2837
|
-
async function createOpenClawEpisodeSummaryLlm(params) {
|
|
2838
|
-
const llm = await createOpenClawLlmClient(params.openClaw, params.resolvedModelRef, "episode model override");
|
|
2839
|
-
const usage = createEmptyUsageStats();
|
|
2840
|
-
const completeWithTimeout = async (task) => {
|
|
2841
|
-
usage.calls += 1;
|
|
2842
|
-
const remainingMs = Math.max(0, params.deadlineMs - Date.now());
|
|
2843
|
-
return Promise.race([
|
|
2844
|
-
task,
|
|
2845
|
-
new Promise((_, reject) => {
|
|
2846
|
-
setTimeout(() => reject(new OpenClawEpisodeSummaryTimeoutError()), remainingMs);
|
|
2847
|
-
})
|
|
2848
|
-
]);
|
|
2849
|
-
};
|
|
2850
|
-
return {
|
|
2851
|
-
complete: async (systemPrompt, userMessage) => completeWithTimeout(llm.complete(systemPrompt, userMessage)),
|
|
2852
|
-
completeJson: async (systemPrompt, userMessage) => {
|
|
2853
|
-
return completeWithTimeout(llm.completeJson(systemPrompt, userMessage));
|
|
2854
|
-
},
|
|
2855
|
-
metadata: {
|
|
2856
|
-
modelRef: params.modelRef,
|
|
2857
|
-
pricing: {
|
|
2858
|
-
input: 0,
|
|
2859
|
-
output: 0,
|
|
2860
|
-
cacheRead: 0,
|
|
2861
|
-
cacheWrite: 0
|
|
2862
|
-
},
|
|
2863
|
-
usage
|
|
2864
|
-
}
|
|
2865
|
-
};
|
|
2866
|
-
}
|
|
2867
|
-
function createPredecessorEpisodeEmbeddingStrategy(params) {
|
|
2868
|
-
return async (summary) => maybeEmbedEpisodeSummary({
|
|
2869
|
-
summary,
|
|
2870
|
-
embedding: params.embedding,
|
|
2871
|
-
embeddingAvailable: params.embeddingAvailable,
|
|
2872
|
-
logger: params.logger,
|
|
2873
|
-
sessionContext: params.sessionContext,
|
|
2874
|
-
predecessorFile: params.predecessorFile,
|
|
2875
|
-
deadlineMs: params.deadlineMs
|
|
2876
|
-
});
|
|
2877
|
-
}
|
|
2878
|
-
function logSkippedEpisodeIngest(sessionContext, predecessorFile, skipped, logger) {
|
|
2879
|
-
if (skipped.reason === "skipped_exists") {
|
|
2880
|
-
logger.info(
|
|
2881
|
-
`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=already_exists episode=${skipped.existingEpisode?.id}`
|
|
2882
|
-
);
|
|
2883
|
-
return;
|
|
2884
|
-
}
|
|
2885
|
-
if (skipped.reason === "skipped_short") {
|
|
2886
|
-
logger.info(
|
|
2887
|
-
`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=too_short cleanedMessages=${skipped.messageCount}`
|
|
2888
|
-
);
|
|
2889
|
-
return;
|
|
2890
|
-
}
|
|
2891
|
-
logger.info(`[agenr] session-start predecessor episode write skipped for ${sessionContext} predecessor=${predecessorFile} reason=${skipped.reason}`);
|
|
2892
|
-
}
|
|
2893
|
-
function logFailedEpisodeIngest(sessionContext, predecessorFile, error, episodeModel, logger) {
|
|
2894
|
-
if (error === EPISODE_SUMMARY_TIMEOUT_ERROR_MESSAGE) {
|
|
2895
|
-
logger.info(
|
|
2896
|
-
`[agenr] session-start predecessor episode write timed_out for ${sessionContext} predecessor=${predecessorFile} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`
|
|
2897
|
-
);
|
|
2898
|
-
return;
|
|
2899
|
-
}
|
|
2900
|
-
if (error === "invalid_response") {
|
|
2901
|
-
logger.info(
|
|
2902
|
-
`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessorFile} reason=invalid_response model=${episodeModel}`
|
|
2903
|
-
);
|
|
2904
|
-
return;
|
|
2905
|
-
}
|
|
2906
|
-
logger.info(`[agenr] session-start predecessor episode write failed for ${sessionContext} predecessor=${predecessorFile} reason=${error ?? "unknown"}`);
|
|
2907
|
-
}
|
|
2908
|
-
async function maybeEmbedEpisodeSummary(params) {
|
|
2909
|
-
if (!params.embeddingAvailable) {
|
|
2910
|
-
params.logger.info(
|
|
2911
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=embedding_unavailable`
|
|
2912
|
-
);
|
|
2913
|
-
return void 0;
|
|
2914
|
-
}
|
|
2915
|
-
const remainingBudgetMs = params.deadlineMs - Date.now();
|
|
2916
|
-
if (remainingBudgetMs < EPISODE_EMBEDDING_MIN_HEADROOM_MS) {
|
|
2917
|
-
params.logger.info(
|
|
2918
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=budget_tight remainingMs=${Math.max(
|
|
2919
|
-
0,
|
|
2920
|
-
remainingBudgetMs
|
|
2921
|
-
)}`
|
|
2922
|
-
);
|
|
2923
|
-
return void 0;
|
|
2924
|
-
}
|
|
2925
|
-
try {
|
|
2926
|
-
const result = await awaitEmbeddingWithTimeout(params.embedding.embed([params.summary]), remainingBudgetMs);
|
|
2927
|
-
if (result === EPISODE_EMBEDDING_TIMEOUT) {
|
|
2928
|
-
params.logger.info(
|
|
2929
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=embedding_timeout budgetMs=${remainingBudgetMs}`
|
|
2930
|
-
);
|
|
2931
|
-
return void 0;
|
|
2932
|
-
}
|
|
2933
|
-
const vector = result[0]?.map((value) => Number.isFinite(value) ? value : 0);
|
|
2934
|
-
if (!vector || vector.length === 0) {
|
|
2935
|
-
params.logger.info(
|
|
2936
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=empty_embedding`
|
|
2937
|
-
);
|
|
2938
|
-
return void 0;
|
|
2939
|
-
}
|
|
2940
|
-
return vector;
|
|
2941
|
-
} catch (error) {
|
|
2942
|
-
params.logger.info(
|
|
2943
|
-
`[agenr] session-start predecessor episode embedding skipped for ${params.sessionContext} predecessor=${params.predecessorFile} reason=${formatErrorMessage2(error)}`
|
|
2944
|
-
);
|
|
2945
|
-
return void 0;
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
async function awaitEmbeddingWithTimeout(promise, timeoutMs) {
|
|
2949
|
-
return new Promise((resolve, reject) => {
|
|
2950
|
-
const timeout = setTimeout(() => {
|
|
2951
|
-
resolve(EPISODE_EMBEDDING_TIMEOUT);
|
|
2952
|
-
}, timeoutMs);
|
|
2953
|
-
promise.then(
|
|
2954
|
-
(value) => {
|
|
2955
|
-
clearTimeout(timeout);
|
|
2956
|
-
resolve(value);
|
|
2957
|
-
},
|
|
2958
|
-
(error) => {
|
|
2959
|
-
clearTimeout(timeout);
|
|
2960
|
-
reject(error);
|
|
2961
|
-
}
|
|
2962
|
-
);
|
|
2963
|
-
});
|
|
2964
|
-
}
|
|
2965
|
-
function createEmptyUsageStats() {
|
|
2966
|
-
return {
|
|
2967
|
-
calls: 0,
|
|
2968
|
-
inputTokens: 0,
|
|
2969
|
-
outputTokens: 0,
|
|
2970
|
-
cacheReadTokens: 0,
|
|
2971
|
-
cacheWriteTokens: 0,
|
|
2972
|
-
totalTokens: 0,
|
|
2973
|
-
totalCost: 0
|
|
2974
|
-
};
|
|
2975
|
-
}
|
|
2976
1616
|
function trimOptionalString(value) {
|
|
2977
1617
|
const trimmed = value?.trim();
|
|
2978
1618
|
return trimmed ? trimmed : void 0;
|
|
@@ -3030,80 +1670,6 @@ function truncateSubject(subject) {
|
|
|
3030
1670
|
return `${trimmedSubject.slice(0, MAX_SUBJECT_LENGTH - 3).trimEnd()}...`;
|
|
3031
1671
|
}
|
|
3032
1672
|
|
|
3033
|
-
// src/adapters/openclaw/format/recall-format.ts
|
|
3034
|
-
var MAX_CONTENT_CHARS = 220;
|
|
3035
|
-
function formatAgenrSessionStartRecall(patch) {
|
|
3036
|
-
if (patch.contextSections.length === 0 && patch.durableMemory.length === 0) {
|
|
3037
|
-
return "";
|
|
3038
|
-
}
|
|
3039
|
-
const lines = [];
|
|
3040
|
-
for (const section of patch.contextSections) {
|
|
3041
|
-
lines.push(`## ${section.title}`);
|
|
3042
|
-
lines.push(section.content);
|
|
3043
|
-
lines.push("");
|
|
3044
|
-
}
|
|
3045
|
-
const durableSections = buildSections(patch);
|
|
3046
|
-
if (durableSections.length > 0) {
|
|
3047
|
-
const recallLines = [
|
|
3048
|
-
"## Agenr Session Recall",
|
|
3049
|
-
"Use this as prior context. Confirm anything important if the current conversation conflicts with it.",
|
|
3050
|
-
""
|
|
3051
|
-
];
|
|
3052
|
-
for (const section of durableSections) {
|
|
3053
|
-
recallLines.push(`### ${section.title}`);
|
|
3054
|
-
for (const item of section.entries) {
|
|
3055
|
-
recallLines.push(formatEntryHeader(item));
|
|
3056
|
-
recallLines.push(...formatEntryBodyLines(item));
|
|
3057
|
-
}
|
|
3058
|
-
recallLines.push("");
|
|
3059
|
-
}
|
|
3060
|
-
lines.push(wrapAgenrMemoryContext(recallLines.join("\n").trim()));
|
|
3061
|
-
}
|
|
3062
|
-
return lines.join("\n").trim();
|
|
3063
|
-
}
|
|
3064
|
-
function buildSections(patch) {
|
|
3065
|
-
const sections = [];
|
|
3066
|
-
const coreEntries = patch.durableMemory.filter((item) => item.sourceKind === "core");
|
|
3067
|
-
if (coreEntries.length > 0) {
|
|
3068
|
-
sections.push({ title: "Core Memory", entries: coreEntries });
|
|
3069
|
-
}
|
|
3070
|
-
const artifactRecallEntries = patch.durableMemory.filter((item) => item.sourceKind === "artifact_recall");
|
|
3071
|
-
if (artifactRecallEntries.length > 0) {
|
|
3072
|
-
sections.push({ title: "Relevant Durable Memory", entries: artifactRecallEntries });
|
|
3073
|
-
}
|
|
3074
|
-
return sections;
|
|
3075
|
-
}
|
|
3076
|
-
function formatEntryHeader(item) {
|
|
3077
|
-
const metadata = [
|
|
3078
|
-
`rank ${item.rank}`,
|
|
3079
|
-
item.entry.id,
|
|
3080
|
-
item.entry.type,
|
|
3081
|
-
item.entry.expiry,
|
|
3082
|
-
`importance ${item.entry.importance}`,
|
|
3083
|
-
item.score !== void 0 ? `score ${item.score.toFixed(2)}` : void 0
|
|
3084
|
-
].filter((value) => value !== void 0);
|
|
3085
|
-
return `- [${metadata.join(" | ")}] ${item.entry.subject}`;
|
|
3086
|
-
}
|
|
3087
|
-
function formatEntryBodyLines(item) {
|
|
3088
|
-
const lines = [` ${truncate3(item.entry.content.trim(), MAX_CONTENT_CHARS)}`];
|
|
3089
|
-
lines.push(` why: ${item.whySurfaced.summary}`);
|
|
3090
|
-
const metadata = [
|
|
3091
|
-
item.entry.tags.length > 0 ? `tags: ${item.entry.tags.join(", ")}` : void 0,
|
|
3092
|
-
item.freshnessLabel ? `freshness: ${item.freshnessLabel}` : void 0,
|
|
3093
|
-
item.provenanceSummary ? `provenance: ${truncate3(item.provenanceSummary, MAX_CONTENT_CHARS)}` : void 0
|
|
3094
|
-
].filter((value) => value !== void 0);
|
|
3095
|
-
if (metadata.length > 0) {
|
|
3096
|
-
lines.push(` ${metadata.join(" | ")}`);
|
|
3097
|
-
}
|
|
3098
|
-
return lines;
|
|
3099
|
-
}
|
|
3100
|
-
function truncate3(value, maxChars) {
|
|
3101
|
-
if (value.length <= maxChars) {
|
|
3102
|
-
return value;
|
|
3103
|
-
}
|
|
3104
|
-
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
3105
|
-
}
|
|
3106
|
-
|
|
3107
1673
|
// src/adapters/openclaw/session/continuity/continuity-summary-generator.ts
|
|
3108
1674
|
import * as fs3 from "fs/promises";
|
|
3109
1675
|
import { resolveAgentEffectiveModelPrimary as resolveAgentEffectiveModelPrimary3, resolveDefaultAgentId as resolveDefaultAgentId3 } from "openclaw/plugin-sdk/agent-runtime";
|
|
@@ -3311,10 +1877,10 @@ async function generateAndWriteOpenClawContinuitySummary(params) {
|
|
|
3311
1877
|
durationMs
|
|
3312
1878
|
};
|
|
3313
1879
|
}
|
|
3314
|
-
debugLog2(params.logger, "continuity-summary", `continuity summary generation error for file=${sessionFile}: ${
|
|
1880
|
+
debugLog2(params.logger, "continuity-summary", `continuity summary generation error for file=${sessionFile}: ${formatErrorMessage3(error)}`);
|
|
3315
1881
|
return {
|
|
3316
1882
|
status: "failed",
|
|
3317
|
-
reason:
|
|
1883
|
+
reason: formatErrorMessage3(error),
|
|
3318
1884
|
continuitySummaryPath,
|
|
3319
1885
|
messageCount: cleanedMessages.length,
|
|
3320
1886
|
transcriptChars: normalizedTranscript.length,
|
|
@@ -3333,7 +1899,7 @@ function normalizeContinuitySummary(value) {
|
|
|
3333
1899
|
const trimmed = value.trim();
|
|
3334
1900
|
return trimmed.replace(/^# .+\n+/u, "").trim();
|
|
3335
1901
|
}
|
|
3336
|
-
function
|
|
1902
|
+
function formatErrorMessage3(error) {
|
|
3337
1903
|
return error instanceof Error ? error.message : String(error);
|
|
3338
1904
|
}
|
|
3339
1905
|
function resolveOpenClawSummaryModelRef(openClaw, agentId, modelOverride) {
|
|
@@ -3847,11 +2413,11 @@ async function renderRecentSessionSection(sessionFile, logger) {
|
|
|
3847
2413
|
logger.debug?.(`[agenr] before_prompt_build: recent session tail for file=${sessionFile}: messages=${tail.length} chars=${body.length}`);
|
|
3848
2414
|
return body;
|
|
3849
2415
|
} catch (error) {
|
|
3850
|
-
logger.debug?.(`[agenr] before_prompt_build: failed to build recent session tail for file=${sessionFile}: ${
|
|
2416
|
+
logger.debug?.(`[agenr] before_prompt_build: failed to build recent session tail for file=${sessionFile}: ${formatErrorMessage4(error)}`);
|
|
3851
2417
|
return "";
|
|
3852
2418
|
}
|
|
3853
2419
|
}
|
|
3854
|
-
function
|
|
2420
|
+
function formatErrorMessage4(error) {
|
|
3855
2421
|
return error instanceof Error ? error.message : String(error);
|
|
3856
2422
|
}
|
|
3857
2423
|
function capRecentSession(value, maxChars) {
|
|
@@ -4039,22 +2605,6 @@ async function awaitWithTimeout(promise, timeoutMs) {
|
|
|
4039
2605
|
}
|
|
4040
2606
|
|
|
4041
2607
|
// src/adapters/openclaw/hooks/before-prompt-build.ts
|
|
4042
|
-
var DEFAULT_SESSION_START_POLICY = {
|
|
4043
|
-
maxCoreEntries: 4,
|
|
4044
|
-
maxArtifactRecallEntries: 3,
|
|
4045
|
-
maxDurableEntries: 5,
|
|
4046
|
-
maxArtifactChars: 1200
|
|
4047
|
-
};
|
|
4048
|
-
var DEFAULT_BEFORE_TURN_POLICY = {
|
|
4049
|
-
maxDurableEntries: 1,
|
|
4050
|
-
maxHighConfidenceDurableEntries: 2,
|
|
4051
|
-
maxRecentTurns: 2,
|
|
4052
|
-
maxQueryChars: 450,
|
|
4053
|
-
maxProcedureCandidates: 3,
|
|
4054
|
-
recallThreshold: 0.6,
|
|
4055
|
-
highConfidenceRecallThreshold: 0.85,
|
|
4056
|
-
procedureThreshold: 0.72
|
|
4057
|
-
};
|
|
4058
2608
|
var NON_USER_TRIGGER_SET = /* @__PURE__ */ new Set(["heartbeat", "cron", "memory"]);
|
|
4059
2609
|
var DEFAULT_STORE_NUDGE_CONFIG = resolveStoreNudgeConfig(void 0);
|
|
4060
2610
|
var INLINE_METADATA_SENTINELS2 = [
|
|
@@ -4079,6 +2629,10 @@ async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
|
4079
2629
|
params.logger.info(`[agenr] session-start recall for ${sessionContext}`);
|
|
4080
2630
|
try {
|
|
4081
2631
|
const services = await params.servicesPromise;
|
|
2632
|
+
if (services.pluginConfig.memoryPolicy?.sessionStart?.enabled === false) {
|
|
2633
|
+
params.logger.info(`[agenr] session-start recall disabled by memoryPolicy for ${sessionContext}`);
|
|
2634
|
+
return await resolveNonFirstTurnResult(event, ctx, sessionContext, params);
|
|
2635
|
+
}
|
|
4082
2636
|
const continuity = await resolvePredecessorContinuity(ctx, params.tracker, services, params.logger);
|
|
4083
2637
|
emitContinuityEvent(services.debugSink, ctx, continuity);
|
|
4084
2638
|
void writeOpenClawPredecessorEpisode({
|
|
@@ -4092,7 +2646,7 @@ async function handleAgenrBeforePromptBuild(event, ctx, params) {
|
|
|
4092
2646
|
sessionKey: ctx.sessionKey,
|
|
4093
2647
|
continuitySummaryText: continuity.continuitySummaryContent,
|
|
4094
2648
|
recentSessionText: continuity.recentSessionContent,
|
|
4095
|
-
policy: resolveSessionStartPolicy(services)
|
|
2649
|
+
policy: resolveSessionStartPolicy(services.pluginConfig.memoryPolicy)
|
|
4096
2650
|
},
|
|
4097
2651
|
services.sessionStart
|
|
4098
2652
|
);
|
|
@@ -4186,7 +2740,12 @@ async function resolveBeforeTurnResult(event, ctx, sessionContext, params) {
|
|
|
4186
2740
|
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=disabled`);
|
|
4187
2741
|
return void 0;
|
|
4188
2742
|
}
|
|
4189
|
-
const currentTurnText = normalizePromptText(event.prompt
|
|
2743
|
+
const currentTurnText = normalizePromptText(event.prompt, {
|
|
2744
|
+
stripInlineMetadata: true,
|
|
2745
|
+
inlineMetadataSentinels: INLINE_METADATA_SENTINELS2,
|
|
2746
|
+
stripTimestampPrefix: true,
|
|
2747
|
+
stripUserPrefix: true
|
|
2748
|
+
});
|
|
4190
2749
|
if (!currentTurnText) {
|
|
4191
2750
|
params.logger.debug?.(`[agenr] before_prompt_build: before-turn skipped for ${sessionContext} reason=empty_prompt`);
|
|
4192
2751
|
return void 0;
|
|
@@ -4196,9 +2755,12 @@ async function resolveBeforeTurnResult(event, ctx, sessionContext, params) {
|
|
|
4196
2755
|
{
|
|
4197
2756
|
sessionKey: ctx.sessionKey,
|
|
4198
2757
|
currentTurnText,
|
|
4199
|
-
recentTurns:
|
|
2758
|
+
recentTurns: extractRecentTurnsFromMessages(
|
|
2759
|
+
event.messages.filter((message) => Boolean(message) && typeof message === "object"),
|
|
2760
|
+
{ stripMemoryCheck: true }
|
|
2761
|
+
),
|
|
4200
2762
|
trigger: ctx.trigger,
|
|
4201
|
-
policy: resolveBeforeTurnPolicy(services)
|
|
2763
|
+
policy: resolveBeforeTurnPolicy(services.pluginConfig.memoryPolicy)
|
|
4202
2764
|
},
|
|
4203
2765
|
services.beforeTurn
|
|
4204
2766
|
);
|
|
@@ -4356,124 +2918,6 @@ function truncateForLog(value, maxChars) {
|
|
|
4356
2918
|
}
|
|
4357
2919
|
return normalized.length <= maxChars ? normalized : `${normalized.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
4358
2920
|
}
|
|
4359
|
-
function resolveSessionStartPolicy(services) {
|
|
4360
|
-
return {
|
|
4361
|
-
...DEFAULT_SESSION_START_POLICY,
|
|
4362
|
-
enableArtifactRecall: services.pluginConfig.memoryPolicy?.sessionStart?.relevantDurableMemory !== false
|
|
4363
|
-
};
|
|
4364
|
-
}
|
|
4365
|
-
function resolveBeforeTurnPolicy(services) {
|
|
4366
|
-
return {
|
|
4367
|
-
...DEFAULT_BEFORE_TURN_POLICY,
|
|
4368
|
-
enableProcedureSuggestion: services.pluginConfig.memoryPolicy?.beforeTurn?.procedureSuggestion !== false,
|
|
4369
|
-
...services.pluginConfig.memoryPolicy?.beforeTurn?.maxDurableEntries !== void 0 ? { maxDurableEntries: services.pluginConfig.memoryPolicy.beforeTurn.maxDurableEntries } : {},
|
|
4370
|
-
...services.pluginConfig.memoryPolicy?.beforeTurn?.recallThreshold !== void 0 ? { recallThreshold: services.pluginConfig.memoryPolicy.beforeTurn.recallThreshold } : {},
|
|
4371
|
-
...services.pluginConfig.memoryPolicy?.beforeTurn?.highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold: services.pluginConfig.memoryPolicy.beforeTurn.highConfidenceRecallThreshold } : {},
|
|
4372
|
-
...services.pluginConfig.memoryPolicy?.beforeTurn?.procedureThreshold !== void 0 ? { procedureThreshold: services.pluginConfig.memoryPolicy.beforeTurn.procedureThreshold } : {}
|
|
4373
|
-
};
|
|
4374
|
-
}
|
|
4375
|
-
function extractRecentTurns(messages) {
|
|
4376
|
-
const turns = [];
|
|
4377
|
-
for (const message of messages) {
|
|
4378
|
-
if (!message || typeof message !== "object") {
|
|
4379
|
-
continue;
|
|
4380
|
-
}
|
|
4381
|
-
const typed = message;
|
|
4382
|
-
const role = typed.role === "user" || typed.role === "assistant" ? typed.role : void 0;
|
|
4383
|
-
if (!role) {
|
|
4384
|
-
continue;
|
|
4385
|
-
}
|
|
4386
|
-
const text = sanitizeRecentTurnText(extractMessageText(typed.content), role);
|
|
4387
|
-
if (!text) {
|
|
4388
|
-
continue;
|
|
4389
|
-
}
|
|
4390
|
-
turns.push({ role, text });
|
|
4391
|
-
}
|
|
4392
|
-
return turns;
|
|
4393
|
-
}
|
|
4394
|
-
function extractMessageText(content) {
|
|
4395
|
-
if (typeof content === "string") {
|
|
4396
|
-
return content;
|
|
4397
|
-
}
|
|
4398
|
-
if (!Array.isArray(content)) {
|
|
4399
|
-
return "";
|
|
4400
|
-
}
|
|
4401
|
-
const blocks = [];
|
|
4402
|
-
for (const block of content) {
|
|
4403
|
-
if (typeof block === "string") {
|
|
4404
|
-
blocks.push(block);
|
|
4405
|
-
continue;
|
|
4406
|
-
}
|
|
4407
|
-
if (!block || typeof block !== "object") {
|
|
4408
|
-
continue;
|
|
4409
|
-
}
|
|
4410
|
-
const typed = block;
|
|
4411
|
-
if (typeof typed.text === "string") {
|
|
4412
|
-
blocks.push(typed.text);
|
|
4413
|
-
continue;
|
|
4414
|
-
}
|
|
4415
|
-
const type = typeof typed.type === "string" ? typed.type.trim().toLowerCase() : "";
|
|
4416
|
-
if (typeof typed.content === "string" && (type === "text" || type === "input_text" || type === "output_text")) {
|
|
4417
|
-
blocks.push(typed.content);
|
|
4418
|
-
}
|
|
4419
|
-
}
|
|
4420
|
-
return blocks.join("\n");
|
|
4421
|
-
}
|
|
4422
|
-
function sanitizeRecentTurnText(text, role) {
|
|
4423
|
-
if (!text.trim()) {
|
|
4424
|
-
return "";
|
|
4425
|
-
}
|
|
4426
|
-
const wrapperDetected = containsAgenrMemoryContext(text) || text.includes("## Agenr Session Recall") || text.includes("## Agenr Before-Turn Recall") || text.includes("[MEMORY CHECK]");
|
|
4427
|
-
let cleaned = stripAgenrMemoryContext(text);
|
|
4428
|
-
const headings = [
|
|
4429
|
-
"## Previous session summary",
|
|
4430
|
-
"## Recent session",
|
|
4431
|
-
"## Agenr Session Recall",
|
|
4432
|
-
"### Core Memory",
|
|
4433
|
-
"### Relevant Durable Memory",
|
|
4434
|
-
"## Agenr Before-Turn Recall",
|
|
4435
|
-
"### Suggested Procedure"
|
|
4436
|
-
];
|
|
4437
|
-
for (const heading of headings) {
|
|
4438
|
-
cleaned = cleaned.split(heading).join(" ");
|
|
4439
|
-
}
|
|
4440
|
-
cleaned = cleaned.replace(/\[MEMORY CHECK\][^\n]*/gu, " ");
|
|
4441
|
-
cleaned = collapseWhitespace2(cleaned);
|
|
4442
|
-
if (!wrapperDetected) {
|
|
4443
|
-
return cleaned;
|
|
4444
|
-
}
|
|
4445
|
-
const segments = stripAgenrMemoryContext(text).split(/\n\s*\n/gu).map((segment) => collapseWhitespace2(segment)).filter((segment) => segment.length > 0);
|
|
4446
|
-
const fallbackSegment = segments.at(-1);
|
|
4447
|
-
if (fallbackSegment) {
|
|
4448
|
-
return role === "user" ? fallbackSegment : collapseWhitespace2(cleaned);
|
|
4449
|
-
}
|
|
4450
|
-
return cleaned;
|
|
4451
|
-
}
|
|
4452
|
-
function normalizePromptText(prompt) {
|
|
4453
|
-
let cleaned = stripAgenrMemoryContext(prompt);
|
|
4454
|
-
cleaned = stripInlineMetadata2(cleaned);
|
|
4455
|
-
cleaned = cleaned.replace(/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]\s*/u, "");
|
|
4456
|
-
cleaned = cleaned.replace(/^\s*U:\s*/u, "");
|
|
4457
|
-
cleaned = collapseWhitespace2(cleaned);
|
|
4458
|
-
return cleaned.length > 0 ? cleaned : void 0;
|
|
4459
|
-
}
|
|
4460
|
-
function stripInlineMetadata2(text) {
|
|
4461
|
-
let cleaned = text;
|
|
4462
|
-
for (const sentinel of INLINE_METADATA_SENTINELS2) {
|
|
4463
|
-
const escapedSentinel = escapeForRegExp2(sentinel);
|
|
4464
|
-
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:\`\`\`json\\s*)?\\{[\\s\\S]*?\\}(?:\\s*\`\`\`)?`, "gu"), " ");
|
|
4465
|
-
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
|
|
4466
|
-
]*`, "gu"), " ");
|
|
4467
|
-
}
|
|
4468
|
-
cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
|
|
4469
|
-
return cleaned;
|
|
4470
|
-
}
|
|
4471
|
-
function escapeForRegExp2(value) {
|
|
4472
|
-
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
4473
|
-
}
|
|
4474
|
-
function collapseWhitespace2(value) {
|
|
4475
|
-
return value.replace(/\s+/gu, " ").trim();
|
|
4476
|
-
}
|
|
4477
2921
|
|
|
4478
2922
|
// src/adapters/openclaw/memory/flush-plan.ts
|
|
4479
2923
|
function buildAgenrMemoryFlushPlan(_params, logger) {
|
|
@@ -4493,7 +2937,7 @@ function createAgenrMemoryRuntime(servicesPromise) {
|
|
|
4493
2937
|
backend: "builtin",
|
|
4494
2938
|
provider: "agenr",
|
|
4495
2939
|
model: services.embeddingStatus.model,
|
|
4496
|
-
dbPath: services.dbPath,
|
|
2940
|
+
dbPath: services.config.dbPath,
|
|
4497
2941
|
files: snapshot.sourceFiles,
|
|
4498
2942
|
chunks: snapshot.activeEntries,
|
|
4499
2943
|
vector: {
|
|
@@ -4552,127 +2996,28 @@ function createAgenrMemoryRuntime(servicesPromise) {
|
|
|
4552
2996
|
};
|
|
4553
2997
|
}
|
|
4554
2998
|
|
|
4555
|
-
// src/adapters/db/session-start-repository.ts
|
|
4556
|
-
function createSessionStartRepository(executor) {
|
|
4557
|
-
return {
|
|
4558
|
-
listCoreEntries: async (limit) => listCoreEntries(executor, limit)
|
|
4559
|
-
};
|
|
4560
|
-
}
|
|
4561
|
-
async function listCoreEntries(executor, limit) {
|
|
4562
|
-
if (limit <= 0) {
|
|
4563
|
-
return [];
|
|
4564
|
-
}
|
|
4565
|
-
const result = await executor.execute({
|
|
4566
|
-
sql: `
|
|
4567
|
-
SELECT
|
|
4568
|
-
${ENTRY_SELECT_COLUMNS}
|
|
4569
|
-
FROM entries
|
|
4570
|
-
WHERE ${buildActiveEntryClause()}
|
|
4571
|
-
AND expiry = 'core'
|
|
4572
|
-
ORDER BY importance DESC, created_at DESC
|
|
4573
|
-
LIMIT ?
|
|
4574
|
-
`,
|
|
4575
|
-
args: [limit]
|
|
4576
|
-
});
|
|
4577
|
-
return result.rows.map((row) => mapEntryRow(row));
|
|
4578
|
-
}
|
|
4579
|
-
|
|
4580
2999
|
// src/app/openclaw/runtime.ts
|
|
4581
3000
|
import path6 from "path";
|
|
4582
3001
|
async function createAgenrOpenClawServices(config, options) {
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
3002
|
+
const debugSink = createDebugSink(options.openClaw, config);
|
|
3003
|
+
return composeHostPluginServices({
|
|
3004
|
+
config,
|
|
3005
|
+
resolvePath: options.resolvePath,
|
|
3006
|
+
readSlotPolicies: (hostConfig) => hostConfig.memoryPolicy?.slotPolicies,
|
|
3007
|
+
resolveClaimExtraction: ({ agenrConfig, hostConfig }) => buildClaimExtractionRuntime(
|
|
3008
|
+
agenrConfig,
|
|
3009
|
+
() => createOpenClawLlmClient(options.openClaw, hostConfig.claimExtractionModel, "claim extraction model override")
|
|
3010
|
+
),
|
|
3011
|
+
onBeforeClose: () => debugSink.close(),
|
|
3012
|
+
extend: ({ resolvedConfig, agenrConfig, runtimeServices }) => ({
|
|
3013
|
+
...runtimeServices,
|
|
3014
|
+
openClaw: options.openClaw,
|
|
3015
|
+
config: resolvedConfig,
|
|
3016
|
+
pluginConfig: config,
|
|
3017
|
+
agenrConfig,
|
|
3018
|
+
debugSink
|
|
3019
|
+
})
|
|
4592
3020
|
});
|
|
4593
|
-
return {
|
|
4594
|
-
openClaw: options.openClaw,
|
|
4595
|
-
config: resolvedConfig,
|
|
4596
|
-
pluginConfig: config,
|
|
4597
|
-
agenrConfig,
|
|
4598
|
-
dbPath: resolvedConfig.dbPath,
|
|
4599
|
-
entries: runtimeServices.entries,
|
|
4600
|
-
episodes: runtimeServices.episodes,
|
|
4601
|
-
procedures: runtimeServices.procedures,
|
|
4602
|
-
memory: runtimeServices.memory,
|
|
4603
|
-
sessionStart: runtimeServices.sessionStart,
|
|
4604
|
-
beforeTurn: runtimeServices.beforeTurn,
|
|
4605
|
-
embedding: runtimeServices.embedding,
|
|
4606
|
-
recall: runtimeServices.recall,
|
|
4607
|
-
claimExtraction: runtimeServices.claimExtraction,
|
|
4608
|
-
embeddingStatus: toPublicEmbeddingStatus(embeddingStatus),
|
|
4609
|
-
debugSink: runtimeServices.debugSink,
|
|
4610
|
-
close: runtimeServices.close
|
|
4611
|
-
};
|
|
4612
|
-
}
|
|
4613
|
-
function resolveEmbeddingStatus(config) {
|
|
4614
|
-
const model = resolveEmbeddingModel(config);
|
|
4615
|
-
try {
|
|
4616
|
-
return {
|
|
4617
|
-
available: true,
|
|
4618
|
-
provider: "openai",
|
|
4619
|
-
requestedProvider: "openai",
|
|
4620
|
-
model,
|
|
4621
|
-
apiKey: resolveEmbeddingApiKey(config)
|
|
4622
|
-
};
|
|
4623
|
-
} catch (error) {
|
|
4624
|
-
return {
|
|
4625
|
-
available: false,
|
|
4626
|
-
provider: "unconfigured",
|
|
4627
|
-
requestedProvider: "openai",
|
|
4628
|
-
model,
|
|
4629
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4630
|
-
};
|
|
4631
|
-
}
|
|
4632
|
-
}
|
|
4633
|
-
async function createRuntimeServices(dbPath, config, embeddingStatus, openClawContext) {
|
|
4634
|
-
const database = await createDatabase(dbPath);
|
|
4635
|
-
const embedding = embeddingStatus.available ? createEmbeddingClient(requireApiKey(embeddingStatus), embeddingStatus.model) : createUnavailableEmbeddingPort(embeddingStatus.error ?? "Embeddings are unavailable.");
|
|
4636
|
-
const baseRecall = createRecallAdapter(database, embedding);
|
|
4637
|
-
const crossEncoder = resolveCrossEncoder(config);
|
|
4638
|
-
const recall2 = attachCrossEncoderPort(baseRecall, crossEncoder);
|
|
4639
|
-
const claimExtraction = await createClaimExtractionRuntime(config, openClawContext.openClaw, openClawContext.pluginConfig);
|
|
4640
|
-
const debugSink = createDebugSink(openClawContext.openClaw, openClawContext.pluginConfig);
|
|
4641
|
-
let closed = false;
|
|
4642
|
-
return {
|
|
4643
|
-
entries: database,
|
|
4644
|
-
episodes: database,
|
|
4645
|
-
procedures: database,
|
|
4646
|
-
memory: createOpenClawRepository(database, {
|
|
4647
|
-
claimSlotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
4648
|
-
}),
|
|
4649
|
-
sessionStart: {
|
|
4650
|
-
repository: createSessionStartRepository(database),
|
|
4651
|
-
recall: recall2,
|
|
4652
|
-
slotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
4653
|
-
},
|
|
4654
|
-
beforeTurn: {
|
|
4655
|
-
recall: recall2,
|
|
4656
|
-
procedures: database,
|
|
4657
|
-
embedQuery: embeddingStatus.available ? async (text) => {
|
|
4658
|
-
const vectors = await embedding.embed([text]);
|
|
4659
|
-
return vectors[0] ?? [];
|
|
4660
|
-
} : void 0,
|
|
4661
|
-
slotPolicyConfig: openClawContext.pluginConfig.memoryPolicy?.slotPolicies
|
|
4662
|
-
},
|
|
4663
|
-
embedding,
|
|
4664
|
-
recall: recall2,
|
|
4665
|
-
claimExtraction,
|
|
4666
|
-
debugSink,
|
|
4667
|
-
async close() {
|
|
4668
|
-
if (closed) {
|
|
4669
|
-
return;
|
|
4670
|
-
}
|
|
4671
|
-
closed = true;
|
|
4672
|
-
await debugSink.close();
|
|
4673
|
-
await database.close();
|
|
4674
|
-
}
|
|
4675
|
-
};
|
|
4676
3021
|
}
|
|
4677
3022
|
function createDebugSink(openClaw, pluginConfig) {
|
|
4678
3023
|
const resolved = resolveDebugConfig(pluginConfig.debug);
|
|
@@ -4696,78 +3041,6 @@ function ensureDebugLogPath(resolved, openClaw) {
|
|
|
4696
3041
|
return { ...resolved, enabled: false };
|
|
4697
3042
|
}
|
|
4698
3043
|
}
|
|
4699
|
-
function toPublicEmbeddingStatus(status) {
|
|
4700
|
-
return {
|
|
4701
|
-
available: status.available,
|
|
4702
|
-
provider: status.provider,
|
|
4703
|
-
requestedProvider: status.requestedProvider,
|
|
4704
|
-
model: status.model,
|
|
4705
|
-
...status.error ? { error: status.error } : {}
|
|
4706
|
-
};
|
|
4707
|
-
}
|
|
4708
|
-
function createUnavailableEmbeddingPort(errorMessage) {
|
|
4709
|
-
return {
|
|
4710
|
-
async embed() {
|
|
4711
|
-
throw new Error(errorMessage);
|
|
4712
|
-
}
|
|
4713
|
-
};
|
|
4714
|
-
}
|
|
4715
|
-
function resolveRuntimeConfig(config, resolvePath) {
|
|
4716
|
-
const dbPathOverride = resolveOptionalPath(config.dbPath, resolvePath);
|
|
4717
|
-
const configPathOverride = resolveOptionalPath(config.configPath, resolvePath);
|
|
4718
|
-
const configPath = resolveConfigPath({
|
|
4719
|
-
configPath: configPathOverride,
|
|
4720
|
-
dbPath: dbPathOverride
|
|
4721
|
-
});
|
|
4722
|
-
const agenrConfig = readConfig({
|
|
4723
|
-
configPath,
|
|
4724
|
-
dbPath: dbPathOverride
|
|
4725
|
-
});
|
|
4726
|
-
const dbPath = dbPathOverride ?? resolveDbPath(agenrConfig);
|
|
4727
|
-
return {
|
|
4728
|
-
resolvedConfig: {
|
|
4729
|
-
dbPath,
|
|
4730
|
-
configPath
|
|
4731
|
-
},
|
|
4732
|
-
agenrConfig
|
|
4733
|
-
};
|
|
4734
|
-
}
|
|
4735
|
-
function resolveOptionalPath(value, resolvePath) {
|
|
4736
|
-
const normalized = value?.trim();
|
|
4737
|
-
if (!normalized) {
|
|
4738
|
-
return void 0;
|
|
4739
|
-
}
|
|
4740
|
-
return resolvePath ? resolvePath(normalized) : normalized;
|
|
4741
|
-
}
|
|
4742
|
-
function requireApiKey(status) {
|
|
4743
|
-
if (!status.apiKey) {
|
|
4744
|
-
throw new Error("Embedding API key is unavailable.");
|
|
4745
|
-
}
|
|
4746
|
-
return status.apiKey;
|
|
4747
|
-
}
|
|
4748
|
-
function resolveCrossEncoder(config) {
|
|
4749
|
-
try {
|
|
4750
|
-
const apiKey = resolveCrossEncoderApiKey(config);
|
|
4751
|
-
const { modelId } = resolveModel(config, "cross_encoder");
|
|
4752
|
-
return createOpenAICrossEncoder({ apiKey, model: modelId });
|
|
4753
|
-
} catch {
|
|
4754
|
-
return void 0;
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
async function createClaimExtractionRuntime(config, openClaw, pluginConfig) {
|
|
4758
|
-
const claimExtractionConfig = resolveClaimExtractionConfig(config);
|
|
4759
|
-
if (!claimExtractionConfig.enabled) {
|
|
4760
|
-
return void 0;
|
|
4761
|
-
}
|
|
4762
|
-
try {
|
|
4763
|
-
return {
|
|
4764
|
-
llm: await createOpenClawLlmClient(openClaw, pluginConfig.claimExtractionModel, "claim extraction model override"),
|
|
4765
|
-
config: claimExtractionConfig
|
|
4766
|
-
};
|
|
4767
|
-
} catch {
|
|
4768
|
-
return void 0;
|
|
4769
|
-
}
|
|
4770
|
-
}
|
|
4771
3044
|
|
|
4772
3045
|
// src/adapters/openclaw/index.ts
|
|
4773
3046
|
var openclaw_default = definePluginEntry({
|