agenr 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/adapters/openclaw/index.d.ts +1 -1
- package/dist/adapters/openclaw/index.js +294 -2049
- package/dist/adapters/skeln/index.d.ts +1951 -0
- package/dist/adapters/skeln/index.js +3899 -0
- package/dist/{chunk-IMQIJPIP.js → chunk-575MUIW5.js} +206 -53
- package/dist/{chunk-7TDALVPY.js → chunk-ELR2HSVC.js} +4920 -4659
- package/dist/chunk-GELCEVFA.js +14 -0
- package/dist/{chunk-DGV6D6Q3.js → chunk-LAXNNWHM.js} +3395 -4506
- package/dist/chunk-MYZ2CWY6.js +2738 -0
- package/dist/{chunk-MJIB6J5S.js → chunk-P5SB75FK.js} +6 -4
- package/dist/chunk-TBFAARM5.js +1196 -0
- 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 +7 -5
- /package/dist/{chunk-6T5RXGIR.js → chunk-5LADPJ4C.js} +0 -0
|
@@ -0,0 +1,2738 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMemoryRepository,
|
|
3
|
+
ingestEpisodeTranscript,
|
|
4
|
+
storeEntriesDetailed
|
|
5
|
+
} from "./chunk-LAXNNWHM.js";
|
|
6
|
+
import {
|
|
7
|
+
containsAgenrMemoryContext,
|
|
8
|
+
formatInjectionEntryBodyLines,
|
|
9
|
+
formatInjectionEntryHeader,
|
|
10
|
+
stripAgenrMemoryContext,
|
|
11
|
+
wrapAgenrMemoryContext
|
|
12
|
+
} from "./chunk-575MUIW5.js";
|
|
13
|
+
import {
|
|
14
|
+
ENTRY_SELECT_COLUMNS,
|
|
15
|
+
ENTRY_TYPES,
|
|
16
|
+
EXPIRY_LEVELS,
|
|
17
|
+
attachCrossEncoderPort,
|
|
18
|
+
buildActiveEntryClause,
|
|
19
|
+
createDatabase,
|
|
20
|
+
createEmbeddingClient,
|
|
21
|
+
createLlmClient,
|
|
22
|
+
createOpenAICrossEncoder,
|
|
23
|
+
createRecallAdapter,
|
|
24
|
+
mapEntryRow,
|
|
25
|
+
normalizeManualClaimKeyUpdate,
|
|
26
|
+
projectClaimCentricRecallEntry,
|
|
27
|
+
readConfig,
|
|
28
|
+
readNumber,
|
|
29
|
+
readOptionalString,
|
|
30
|
+
readRequiredString,
|
|
31
|
+
resolveClaimExtractionConfig,
|
|
32
|
+
resolveConfigPath,
|
|
33
|
+
resolveCrossEncoderApiKey,
|
|
34
|
+
resolveDbPath,
|
|
35
|
+
resolveEmbeddingApiKey,
|
|
36
|
+
resolveEmbeddingModel,
|
|
37
|
+
resolveLlmApiKey,
|
|
38
|
+
resolveModel,
|
|
39
|
+
runUnifiedRecall,
|
|
40
|
+
validateTemporalValidityRange
|
|
41
|
+
} from "./chunk-ELR2HSVC.js";
|
|
42
|
+
import {
|
|
43
|
+
recall
|
|
44
|
+
} from "./chunk-5LADPJ4C.js";
|
|
45
|
+
|
|
46
|
+
// src/adapters/shared/errors.ts
|
|
47
|
+
function formatErrorMessage(error) {
|
|
48
|
+
return error instanceof Error ? error.message : String(error);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/app/plugin-runtime/session-tracking.ts
|
|
52
|
+
function resolveSessionIdentityKey(sessionId, sessionKey) {
|
|
53
|
+
const normalizedSessionId = sessionId?.trim();
|
|
54
|
+
if (normalizedSessionId) {
|
|
55
|
+
return `session:${normalizedSessionId}`;
|
|
56
|
+
}
|
|
57
|
+
const normalizedSessionKey = sessionKey?.trim();
|
|
58
|
+
if (normalizedSessionKey) {
|
|
59
|
+
return `key:${normalizedSessionKey}`;
|
|
60
|
+
}
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
function createSessionStartTracker() {
|
|
64
|
+
const seenSessionIdentities = /* @__PURE__ */ new Set();
|
|
65
|
+
const resumedFromBySessionId = /* @__PURE__ */ new Map();
|
|
66
|
+
return {
|
|
67
|
+
consume(sessionId, sessionKey) {
|
|
68
|
+
const identityKey = resolveSessionIdentityKey(sessionId, sessionKey);
|
|
69
|
+
if (!identityKey) {
|
|
70
|
+
return {
|
|
71
|
+
isFirst: false,
|
|
72
|
+
activeCount: seenSessionIdentities.size
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (seenSessionIdentities.has(identityKey)) {
|
|
76
|
+
return {
|
|
77
|
+
isFirst: false,
|
|
78
|
+
activeCount: seenSessionIdentities.size
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
seenSessionIdentities.add(identityKey);
|
|
82
|
+
return {
|
|
83
|
+
isFirst: true,
|
|
84
|
+
activeCount: seenSessionIdentities.size
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
rememberSessionStart(sessionId, _sessionKey, resumedFrom) {
|
|
88
|
+
const normalizedSessionId = sessionId?.trim();
|
|
89
|
+
const normalizedResumedFrom = resumedFrom?.trim();
|
|
90
|
+
if (!normalizedSessionId || !normalizedResumedFrom) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
resumedFromBySessionId.set(normalizedSessionId, normalizedResumedFrom);
|
|
94
|
+
},
|
|
95
|
+
getResumedFrom(sessionId) {
|
|
96
|
+
const normalizedSessionId = sessionId?.trim();
|
|
97
|
+
return normalizedSessionId ? resumedFromBySessionId.get(normalizedSessionId) : void 0;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/adapters/shared/plugin-config-validators.ts
|
|
103
|
+
function normalizeOptionalBoolean(value, label, errors) {
|
|
104
|
+
if (value === void 0) {
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
if (typeof value !== "boolean") {
|
|
108
|
+
errors.push(`${label} must be a boolean when provided`);
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function normalizeOptionalPositiveInteger(value, label, errors) {
|
|
114
|
+
if (value === void 0) {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
118
|
+
errors.push(`${label} must be a positive integer when provided`);
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function normalizeOptionalUnitInterval(value, label, errors) {
|
|
124
|
+
if (value === void 0) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
128
|
+
errors.push(`${label} must be a number between 0 and 1 when provided`);
|
|
129
|
+
return void 0;
|
|
130
|
+
}
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/adapters/shared/plugin-memory-policy-config.ts
|
|
135
|
+
function normalizePluginInjectionMemoryPolicyConfig(value) {
|
|
136
|
+
if (value === void 0) {
|
|
137
|
+
return { ok: true, value: void 0 };
|
|
138
|
+
}
|
|
139
|
+
if (!isRecord(value)) {
|
|
140
|
+
return { ok: false, errors: ["memoryPolicy must be an object when provided"] };
|
|
141
|
+
}
|
|
142
|
+
const errors = [];
|
|
143
|
+
const slotPoliciesResult = normalizeClaimSlotPolicyConfig(value.slotPolicies);
|
|
144
|
+
if (!slotPoliciesResult.ok) {
|
|
145
|
+
errors.push(...slotPoliciesResult.errors);
|
|
146
|
+
}
|
|
147
|
+
const sessionStartResult = normalizeSessionStartMemoryPolicyConfig(value.sessionStart);
|
|
148
|
+
if (!sessionStartResult.ok) {
|
|
149
|
+
errors.push(...sessionStartResult.errors);
|
|
150
|
+
}
|
|
151
|
+
const beforeTurnResult = normalizeBeforeTurnMemoryPolicyConfig(value.beforeTurn);
|
|
152
|
+
if (!beforeTurnResult.ok) {
|
|
153
|
+
errors.push(...beforeTurnResult.errors);
|
|
154
|
+
}
|
|
155
|
+
const workingContextResult = normalizeWorkingContextMemoryPolicyConfig(value.workingContext);
|
|
156
|
+
if (!workingContextResult.ok) {
|
|
157
|
+
errors.push(...workingContextResult.errors);
|
|
158
|
+
}
|
|
159
|
+
const allowedKeys = /* @__PURE__ */ new Set(["slotPolicies", "sessionStart", "beforeTurn", "workingContext"]);
|
|
160
|
+
for (const key of Object.keys(value)) {
|
|
161
|
+
if (!allowedKeys.has(key)) {
|
|
162
|
+
errors.push(`unknown config field: memoryPolicy.${key}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (errors.length > 0) {
|
|
166
|
+
return { ok: false, errors };
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
value: slotPoliciesResult.ok && slotPoliciesResult.value || sessionStartResult.ok && sessionStartResult.value || beforeTurnResult.ok && beforeTurnResult.value || workingContextResult.ok && workingContextResult.value ? {
|
|
171
|
+
...slotPoliciesResult.ok && slotPoliciesResult.value ? { slotPolicies: slotPoliciesResult.value } : {},
|
|
172
|
+
...sessionStartResult.ok && sessionStartResult.value ? { sessionStart: sessionStartResult.value } : {},
|
|
173
|
+
...beforeTurnResult.ok && beforeTurnResult.value ? { beforeTurn: beforeTurnResult.value } : {},
|
|
174
|
+
...workingContextResult.ok && workingContextResult.value ? { workingContext: workingContextResult.value } : {}
|
|
175
|
+
} : void 0
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function normalizeWorkingContextMemoryPolicyConfig(value) {
|
|
179
|
+
if (value === void 0) {
|
|
180
|
+
return { ok: true, value: void 0 };
|
|
181
|
+
}
|
|
182
|
+
if (!isRecord(value)) {
|
|
183
|
+
return { ok: false, errors: ["memoryPolicy.workingContext must be an object when provided"] };
|
|
184
|
+
}
|
|
185
|
+
const errors = [];
|
|
186
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.workingContext.enabled", errors);
|
|
187
|
+
const allowedKeys = /* @__PURE__ */ new Set(["enabled"]);
|
|
188
|
+
for (const key of Object.keys(value)) {
|
|
189
|
+
if (!allowedKeys.has(key)) {
|
|
190
|
+
errors.push(`unknown config field: memoryPolicy.workingContext.${key}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (errors.length > 0) {
|
|
194
|
+
return { ok: false, errors };
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
ok: true,
|
|
198
|
+
value: enabled !== void 0 ? { enabled } : void 0
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function normalizeSessionStartMemoryPolicyConfig(value) {
|
|
202
|
+
if (value === void 0) {
|
|
203
|
+
return { ok: true, value: void 0 };
|
|
204
|
+
}
|
|
205
|
+
if (!isRecord(value)) {
|
|
206
|
+
return { ok: false, errors: ["memoryPolicy.sessionStart must be an object when provided"] };
|
|
207
|
+
}
|
|
208
|
+
const errors = [];
|
|
209
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.sessionStart.enabled", errors);
|
|
210
|
+
const coreMemory = normalizeOptionalBoolean(value.coreMemory, "memoryPolicy.sessionStart.coreMemory", errors);
|
|
211
|
+
const relevantDurableMemory = normalizeOptionalBoolean(value.relevantDurableMemory, "memoryPolicy.sessionStart.relevantDurableMemory", errors);
|
|
212
|
+
const allowedKeys = /* @__PURE__ */ new Set(["enabled", "coreMemory", "relevantDurableMemory"]);
|
|
213
|
+
for (const key of Object.keys(value)) {
|
|
214
|
+
if (!allowedKeys.has(key)) {
|
|
215
|
+
errors.push(`unknown config field: memoryPolicy.sessionStart.${key}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (errors.length > 0) {
|
|
219
|
+
return { ok: false, errors };
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
ok: true,
|
|
223
|
+
value: enabled !== void 0 || coreMemory !== void 0 || relevantDurableMemory !== void 0 ? {
|
|
224
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
225
|
+
...coreMemory !== void 0 ? { coreMemory } : {},
|
|
226
|
+
...relevantDurableMemory !== void 0 ? { relevantDurableMemory } : {}
|
|
227
|
+
} : void 0
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function normalizeBeforeTurnMemoryPolicyConfig(value) {
|
|
231
|
+
if (value === void 0) {
|
|
232
|
+
return { ok: true, value: void 0 };
|
|
233
|
+
}
|
|
234
|
+
if (!isRecord(value)) {
|
|
235
|
+
return { ok: false, errors: ["memoryPolicy.beforeTurn must be an object when provided"] };
|
|
236
|
+
}
|
|
237
|
+
const errors = [];
|
|
238
|
+
const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.beforeTurn.enabled", errors);
|
|
239
|
+
const procedureSuggestion = normalizeOptionalBoolean(value.procedureSuggestion, "memoryPolicy.beforeTurn.procedureSuggestion", errors);
|
|
240
|
+
const maxDurableEntries = normalizeOptionalPositiveInteger(value.maxDurableEntries, "memoryPolicy.beforeTurn.maxDurableEntries", errors);
|
|
241
|
+
const recallThreshold = normalizeOptionalUnitInterval(value.recallThreshold, "memoryPolicy.beforeTurn.recallThreshold", errors);
|
|
242
|
+
const highConfidenceRecallThreshold = normalizeOptionalUnitInterval(
|
|
243
|
+
value.highConfidenceRecallThreshold,
|
|
244
|
+
"memoryPolicy.beforeTurn.highConfidenceRecallThreshold",
|
|
245
|
+
errors
|
|
246
|
+
);
|
|
247
|
+
const procedureThreshold = normalizeOptionalUnitInterval(value.procedureThreshold, "memoryPolicy.beforeTurn.procedureThreshold", errors);
|
|
248
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
249
|
+
"enabled",
|
|
250
|
+
"procedureSuggestion",
|
|
251
|
+
"maxDurableEntries",
|
|
252
|
+
"recallThreshold",
|
|
253
|
+
"highConfidenceRecallThreshold",
|
|
254
|
+
"procedureThreshold"
|
|
255
|
+
]);
|
|
256
|
+
for (const key of Object.keys(value)) {
|
|
257
|
+
if (!allowedKeys.has(key)) {
|
|
258
|
+
errors.push(`unknown config field: memoryPolicy.beforeTurn.${key}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (errors.length > 0) {
|
|
262
|
+
return { ok: false, errors };
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
value: enabled !== void 0 || procedureSuggestion !== void 0 || maxDurableEntries !== void 0 || recallThreshold !== void 0 || highConfidenceRecallThreshold !== void 0 || procedureThreshold !== void 0 ? {
|
|
267
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
268
|
+
...procedureSuggestion !== void 0 ? { procedureSuggestion } : {},
|
|
269
|
+
...maxDurableEntries !== void 0 ? { maxDurableEntries } : {},
|
|
270
|
+
...recallThreshold !== void 0 ? { recallThreshold } : {},
|
|
271
|
+
...highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold } : {},
|
|
272
|
+
...procedureThreshold !== void 0 ? { procedureThreshold } : {}
|
|
273
|
+
} : void 0
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function normalizeClaimSlotPolicyConfig(value) {
|
|
277
|
+
if (value === void 0) {
|
|
278
|
+
return { ok: true, value: void 0 };
|
|
279
|
+
}
|
|
280
|
+
if (!isRecord(value)) {
|
|
281
|
+
return { ok: false, errors: ["memoryPolicy.slotPolicies must be an object when provided"] };
|
|
282
|
+
}
|
|
283
|
+
const errors = [];
|
|
284
|
+
const attributeHeads = normalizeClaimSlotPolicyAttributeHeads(value.attributeHeads, errors);
|
|
285
|
+
const allowedKeys = /* @__PURE__ */ new Set(["attributeHeads"]);
|
|
286
|
+
for (const key of Object.keys(value)) {
|
|
287
|
+
if (!allowedKeys.has(key)) {
|
|
288
|
+
errors.push(`unknown config field: memoryPolicy.slotPolicies.${key}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (errors.length > 0) {
|
|
292
|
+
return { ok: false, errors };
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
ok: true,
|
|
296
|
+
value: attributeHeads ? { attributeHeads } : void 0
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function normalizeClaimSlotPolicyAttributeHeads(value, errors) {
|
|
300
|
+
if (value === void 0) {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
if (!isRecord(value)) {
|
|
304
|
+
errors.push("memoryPolicy.slotPolicies.attributeHeads must be an object when provided");
|
|
305
|
+
return void 0;
|
|
306
|
+
}
|
|
307
|
+
const normalized = {};
|
|
308
|
+
for (const [rawKey, rawPolicy] of Object.entries(value)) {
|
|
309
|
+
const attributeHead = rawKey.trim().toLowerCase();
|
|
310
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(attributeHead)) {
|
|
311
|
+
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${rawKey} must use a canonical attribute-head label`);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (rawPolicy !== "exclusive" && rawPolicy !== "multivalued") {
|
|
315
|
+
errors.push(`memoryPolicy.slotPolicies.attributeHeads.${attributeHead} must be "exclusive" or "multivalued"`);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
normalized[attributeHead] = rawPolicy;
|
|
319
|
+
}
|
|
320
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
321
|
+
}
|
|
322
|
+
function isRecord(value) {
|
|
323
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/app/session-memory/normalize.ts
|
|
327
|
+
function normalizeOptionalString(value) {
|
|
328
|
+
const normalized = value?.trim();
|
|
329
|
+
return normalized ? normalized : void 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/app/session-start/context-sections.ts
|
|
333
|
+
function buildSessionStartContextSections(continuitySummaryText, recentSessionText) {
|
|
334
|
+
const sections = [];
|
|
335
|
+
const normalizedContinuitySummary = normalizeOptionalString(continuitySummaryText);
|
|
336
|
+
if (normalizedContinuitySummary) {
|
|
337
|
+
sections.push({
|
|
338
|
+
kind: "continuity_summary",
|
|
339
|
+
title: "Previous session summary",
|
|
340
|
+
content: normalizedContinuitySummary
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const normalizedRecentSession = normalizeOptionalString(recentSessionText);
|
|
344
|
+
if (normalizedRecentSession) {
|
|
345
|
+
sections.push({
|
|
346
|
+
kind: "recent_session",
|
|
347
|
+
title: "Recent session",
|
|
348
|
+
content: normalizedRecentSession
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return sections;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/app/session-start/service.ts
|
|
355
|
+
var DEFAULT_MAX_CORE_ENTRIES = 4;
|
|
356
|
+
var DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES = 3;
|
|
357
|
+
var DEFAULT_MAX_DURABLE_ENTRIES = 5;
|
|
358
|
+
var DEFAULT_MAX_ARTIFACT_CHARS = 1200;
|
|
359
|
+
async function runSessionStart(input, deps) {
|
|
360
|
+
const policy = normalizePolicy(input.policy);
|
|
361
|
+
const contextSections = buildSessionStartContextSections(input.continuitySummaryText, input.recentSessionText);
|
|
362
|
+
const coreEntries = await deps.repository.listCoreEntries(policy.maxCoreEntries);
|
|
363
|
+
const coreItems = coreEntries.map((entry) => buildCorePatchItem(entry));
|
|
364
|
+
const diagnostics = {
|
|
365
|
+
coreCandidateCount: coreEntries.length,
|
|
366
|
+
artifactRecallCandidateCount: 0,
|
|
367
|
+
artifactRecallUsed: false,
|
|
368
|
+
notices: []
|
|
369
|
+
};
|
|
370
|
+
const artifactRecallQuery = policy.enableArtifactRecall ? buildArtifactRecallQuery(contextSections, policy.maxArtifactChars) : void 0;
|
|
371
|
+
if (!policy.enableArtifactRecall) {
|
|
372
|
+
diagnostics.notices.push("Artifact-grounded durable recall disabled by session-start policy.");
|
|
373
|
+
}
|
|
374
|
+
const artifactRecallItems = artifactRecallQuery ? await runArtifactRecallSelection(artifactRecallQuery, input.sessionKey, policy, deps, diagnostics) : [];
|
|
375
|
+
const durableMemory = assignRanks(mergeDurableMemory(coreItems, artifactRecallItems, policy.maxDurableEntries));
|
|
376
|
+
return {
|
|
377
|
+
contextSections,
|
|
378
|
+
durableMemory,
|
|
379
|
+
diagnostics
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
async function runArtifactRecallSelection(query, sessionKey, policy, deps, diagnostics) {
|
|
383
|
+
diagnostics.artifactRecallUsed = true;
|
|
384
|
+
diagnostics.artifactRecallQuery = query;
|
|
385
|
+
let artifactRecallTrace;
|
|
386
|
+
try {
|
|
387
|
+
const recalled = await recall(
|
|
388
|
+
{
|
|
389
|
+
text: query,
|
|
390
|
+
limit: policy.maxArtifactRecallEntries,
|
|
391
|
+
threshold: policy.recallThreshold,
|
|
392
|
+
sessionKey
|
|
393
|
+
},
|
|
394
|
+
deps.recall,
|
|
395
|
+
{
|
|
396
|
+
trace: {
|
|
397
|
+
reportSummary(summary) {
|
|
398
|
+
artifactRecallTrace = summary;
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
slotPolicyConfig: deps.slotPolicyConfig
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
405
|
+
diagnostics.artifactRecallCandidateCount = recalled.length;
|
|
406
|
+
if (artifactRecallTrace?.degraded.notices.length) {
|
|
407
|
+
diagnostics.notices.push(...artifactRecallTrace.degraded.notices);
|
|
408
|
+
}
|
|
409
|
+
return recalled.map((item) => buildArtifactRecallPatchItem(item, deps));
|
|
410
|
+
} catch (error) {
|
|
411
|
+
diagnostics.artifactRecallTrace = artifactRecallTrace;
|
|
412
|
+
diagnostics.notices.push(`Artifact-grounded durable recall failed: ${formatErrorMessage2(error)}`);
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function buildCorePatchItem(entry) {
|
|
417
|
+
return {
|
|
418
|
+
rank: 0,
|
|
419
|
+
entry,
|
|
420
|
+
sourceKind: "core",
|
|
421
|
+
whySurfaced: {
|
|
422
|
+
summary: `always-on core memory; importance ${entry.importance}`,
|
|
423
|
+
reasons: ["always-on core memory", `importance ${entry.importance}`, `expiry ${entry.expiry}`]
|
|
424
|
+
},
|
|
425
|
+
memoryState: resolveMemoryState(entry),
|
|
426
|
+
claimStatus: resolveClaimStatus(entry),
|
|
427
|
+
freshnessLabel: buildFreshnessLabel(entry),
|
|
428
|
+
...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function buildArtifactRecallPatchItem(recalled, deps) {
|
|
432
|
+
const projected = projectClaimCentricRecallEntry(recalled, {
|
|
433
|
+
slotPolicyConfig: deps.slotPolicyConfig
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
rank: 0,
|
|
437
|
+
entry: recalled.entry,
|
|
438
|
+
sourceKind: "artifact_recall",
|
|
439
|
+
score: recalled.score,
|
|
440
|
+
whySurfaced: projected.whySurfaced,
|
|
441
|
+
memoryState: projected.memoryState,
|
|
442
|
+
claimStatus: projected.claimStatus,
|
|
443
|
+
freshnessLabel: projected.freshness.label,
|
|
444
|
+
...formatProjectedProvenance(projected.provenance) ? { provenanceSummary: formatProjectedProvenance(projected.provenance) } : {}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function buildArtifactRecallQuery(sections, maxChars) {
|
|
448
|
+
if (sections.length === 0 || maxChars <= 0) {
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
let remaining = maxChars;
|
|
452
|
+
const parts = [];
|
|
453
|
+
for (const section of sections) {
|
|
454
|
+
if (remaining <= 0) {
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
const normalizedContent = normalizeWhitespace(section.content);
|
|
458
|
+
if (normalizedContent.length === 0) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
const labeled = `${section.title}: ${normalizedContent}`;
|
|
462
|
+
const truncated = truncate(labeled, remaining);
|
|
463
|
+
if (truncated.length === 0) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
parts.push(truncated);
|
|
467
|
+
remaining -= truncated.length;
|
|
468
|
+
}
|
|
469
|
+
const query = normalizeWhitespace(parts.join("\n"));
|
|
470
|
+
return query.length > 0 ? query : void 0;
|
|
471
|
+
}
|
|
472
|
+
function mergeDurableMemory(coreItems, artifactRecallItems, maxDurableEntries) {
|
|
473
|
+
const merged = [];
|
|
474
|
+
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
475
|
+
for (const item of [...coreItems, ...artifactRecallItems]) {
|
|
476
|
+
if (seenEntryIds.has(item.entry.id)) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
seenEntryIds.add(item.entry.id);
|
|
480
|
+
merged.push(item);
|
|
481
|
+
if (merged.length >= maxDurableEntries) {
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return merged;
|
|
486
|
+
}
|
|
487
|
+
function assignRanks(items) {
|
|
488
|
+
return items.map((item, index) => ({
|
|
489
|
+
...item,
|
|
490
|
+
rank: index + 1
|
|
491
|
+
}));
|
|
492
|
+
}
|
|
493
|
+
function normalizePolicy(policy) {
|
|
494
|
+
const maxCoreEntries = normalizeCount(policy?.maxCoreEntries, DEFAULT_MAX_CORE_ENTRIES);
|
|
495
|
+
const maxArtifactRecallEntries = normalizeCount(policy?.maxArtifactRecallEntries, DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES);
|
|
496
|
+
const maxDurableEntries = Math.max(maxCoreEntries, normalizeCount(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES));
|
|
497
|
+
return {
|
|
498
|
+
maxCoreEntries,
|
|
499
|
+
enableArtifactRecall: policy?.enableArtifactRecall !== false,
|
|
500
|
+
maxArtifactRecallEntries,
|
|
501
|
+
maxDurableEntries,
|
|
502
|
+
maxArtifactChars: normalizeCount(policy?.maxArtifactChars, DEFAULT_MAX_ARTIFACT_CHARS),
|
|
503
|
+
recallThreshold: normalizeThreshold(policy?.recallThreshold)
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function normalizeCount(value, fallback) {
|
|
507
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
508
|
+
return fallback;
|
|
509
|
+
}
|
|
510
|
+
return Math.max(0, Math.trunc(value));
|
|
511
|
+
}
|
|
512
|
+
function normalizeThreshold(value) {
|
|
513
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
514
|
+
return 0;
|
|
515
|
+
}
|
|
516
|
+
return Math.min(1, Math.max(0, value));
|
|
517
|
+
}
|
|
518
|
+
function resolveMemoryState(entry) {
|
|
519
|
+
if (entry.superseded_by) {
|
|
520
|
+
return "superseded";
|
|
521
|
+
}
|
|
522
|
+
if (entry.retired || entry.valid_to) {
|
|
523
|
+
return "historical";
|
|
524
|
+
}
|
|
525
|
+
return "current";
|
|
526
|
+
}
|
|
527
|
+
function resolveClaimStatus(entry) {
|
|
528
|
+
if (!normalizeOptionalString2(entry.claim_key)) {
|
|
529
|
+
return "no_key";
|
|
530
|
+
}
|
|
531
|
+
return entry.claim_key_status ?? "legacy";
|
|
532
|
+
}
|
|
533
|
+
function buildFreshnessLabel(entry) {
|
|
534
|
+
const parts = [`created ${entry.created_at}`];
|
|
535
|
+
const validFrom = normalizeOptionalString2(entry.valid_from);
|
|
536
|
+
const validTo = normalizeOptionalString2(entry.valid_to);
|
|
537
|
+
if (validFrom || validTo) {
|
|
538
|
+
parts.push(`valid ${validFrom ?? "?"} -> ${validTo ?? "ongoing"}`);
|
|
539
|
+
}
|
|
540
|
+
return parts.join(" | ");
|
|
541
|
+
}
|
|
542
|
+
function buildProvenanceSummary(entry) {
|
|
543
|
+
const parts = [
|
|
544
|
+
entry.superseded_by ? `superseded_by=${entry.superseded_by}` : void 0,
|
|
545
|
+
entry.supersession_kind ? `kind=${entry.supersession_kind}` : void 0,
|
|
546
|
+
entry.supersession_reason ? `reason=${entry.supersession_reason}` : void 0,
|
|
547
|
+
entry.claim_support_source_kind ? `support=${entry.claim_support_source_kind}` : void 0,
|
|
548
|
+
entry.claim_support_mode ? `support_mode=${entry.claim_support_mode}` : void 0,
|
|
549
|
+
entry.claim_support_observed_at ? `observed=${entry.claim_support_observed_at}` : void 0,
|
|
550
|
+
entry.claim_support_locator ? `locator=${entry.claim_support_locator}` : void 0
|
|
551
|
+
].filter((value) => value !== void 0);
|
|
552
|
+
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
553
|
+
}
|
|
554
|
+
function formatProjectedProvenance(provenance) {
|
|
555
|
+
const parts = [
|
|
556
|
+
provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
|
|
557
|
+
provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
|
|
558
|
+
provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
|
|
559
|
+
provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
|
|
560
|
+
provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
|
|
561
|
+
provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
|
|
562
|
+
provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
|
|
563
|
+
].filter((value) => value !== void 0);
|
|
564
|
+
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
565
|
+
}
|
|
566
|
+
function normalizeOptionalString2(value) {
|
|
567
|
+
const normalized = value?.trim();
|
|
568
|
+
return normalized && normalized.length > 0 ? normalized : void 0;
|
|
569
|
+
}
|
|
570
|
+
function normalizeWhitespace(value) {
|
|
571
|
+
return value.replace(/\s+/g, " ").trim();
|
|
572
|
+
}
|
|
573
|
+
function truncate(value, maxChars) {
|
|
574
|
+
if (maxChars <= 0) {
|
|
575
|
+
return "";
|
|
576
|
+
}
|
|
577
|
+
if (value.length <= maxChars) {
|
|
578
|
+
return value;
|
|
579
|
+
}
|
|
580
|
+
return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
581
|
+
}
|
|
582
|
+
function formatErrorMessage2(error) {
|
|
583
|
+
if (error instanceof Error) {
|
|
584
|
+
return error.message;
|
|
585
|
+
}
|
|
586
|
+
return String(error);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// src/adapters/shared/injection/message-text.ts
|
|
590
|
+
var MEMORY_HEADINGS = [
|
|
591
|
+
"## Previous session summary",
|
|
592
|
+
"## Recent session",
|
|
593
|
+
"## Agenr Session Recall",
|
|
594
|
+
"### Core Memory",
|
|
595
|
+
"### Relevant Durable Memory",
|
|
596
|
+
"## Agenr Before-Turn Recall",
|
|
597
|
+
"### Suggested Procedure"
|
|
598
|
+
];
|
|
599
|
+
function extractAgentMessageText(content) {
|
|
600
|
+
if (typeof content === "string") {
|
|
601
|
+
return content;
|
|
602
|
+
}
|
|
603
|
+
if (!Array.isArray(content)) {
|
|
604
|
+
return "";
|
|
605
|
+
}
|
|
606
|
+
const blocks = [];
|
|
607
|
+
for (const block of content) {
|
|
608
|
+
if (typeof block === "string") {
|
|
609
|
+
blocks.push(block);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (!block || typeof block !== "object") {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
const typed = block;
|
|
616
|
+
if (typeof typed.text === "string") {
|
|
617
|
+
blocks.push(typed.text);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const type = typeof typed.type === "string" ? typed.type.trim().toLowerCase() : "";
|
|
621
|
+
if (typeof typed.content === "string" && (type === "text" || type === "input_text" || type === "output_text")) {
|
|
622
|
+
blocks.push(typed.content);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return blocks.join("\n");
|
|
626
|
+
}
|
|
627
|
+
function extractRecentTurnsFromMessages(messages, options = {}) {
|
|
628
|
+
const turns = [];
|
|
629
|
+
for (const message of messages) {
|
|
630
|
+
const role = message.role === "user" || message.role === "assistant" ? message.role : void 0;
|
|
631
|
+
if (!role) {
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const text = sanitizeRecentTurnText(extractAgentMessageText(message.content), role, options);
|
|
635
|
+
if (!text) {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
turns.push({ role, text });
|
|
639
|
+
}
|
|
640
|
+
return turns;
|
|
641
|
+
}
|
|
642
|
+
function normalizePromptText(prompt, options = {}) {
|
|
643
|
+
let cleaned = stripAgenrMemoryContext(prompt);
|
|
644
|
+
if (options.stripInlineMetadata) {
|
|
645
|
+
cleaned = stripInlineMetadata(cleaned, options.inlineMetadataSentinels ?? []);
|
|
646
|
+
}
|
|
647
|
+
if (options.stripTimestampPrefix) {
|
|
648
|
+
cleaned = cleaned.replace(/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]\s*/u, "");
|
|
649
|
+
}
|
|
650
|
+
if (options.stripUserPrefix) {
|
|
651
|
+
cleaned = cleaned.replace(/^\s*U:\s*/u, "");
|
|
652
|
+
}
|
|
653
|
+
cleaned = collapseWhitespace(cleaned);
|
|
654
|
+
return cleaned.length > 0 ? cleaned : void 0;
|
|
655
|
+
}
|
|
656
|
+
function sanitizeRecentTurnText(text, role, options = {}) {
|
|
657
|
+
if (!text.trim()) {
|
|
658
|
+
return "";
|
|
659
|
+
}
|
|
660
|
+
const wrapperDetected = containsAgenrMemoryContext(text) || text.includes("## Agenr Session Recall") || text.includes("## Agenr Before-Turn Recall") || options.stripMemoryCheck === true && text.includes("[MEMORY CHECK]");
|
|
661
|
+
let cleaned = stripAgenrMemoryContext(text);
|
|
662
|
+
for (const heading of MEMORY_HEADINGS) {
|
|
663
|
+
cleaned = cleaned.split(heading).join(" ");
|
|
664
|
+
}
|
|
665
|
+
if (options.stripMemoryCheck === true) {
|
|
666
|
+
cleaned = cleaned.replace(/\[MEMORY CHECK\][^\n]*/gu, " ");
|
|
667
|
+
}
|
|
668
|
+
cleaned = collapseWhitespace(cleaned);
|
|
669
|
+
if (!wrapperDetected) {
|
|
670
|
+
return cleaned;
|
|
671
|
+
}
|
|
672
|
+
const segments = stripAgenrMemoryContext(text).split(/\n\s*\n/gu).map((segment) => collapseWhitespace(segment)).filter((segment) => segment.length > 0);
|
|
673
|
+
const fallbackSegment = segments.at(-1);
|
|
674
|
+
if (fallbackSegment) {
|
|
675
|
+
return role === "user" ? fallbackSegment : collapseWhitespace(cleaned);
|
|
676
|
+
}
|
|
677
|
+
return cleaned;
|
|
678
|
+
}
|
|
679
|
+
function stripInlineMetadata(text, sentinels) {
|
|
680
|
+
let cleaned = text;
|
|
681
|
+
for (const sentinel of sentinels) {
|
|
682
|
+
const escapedSentinel = escapeForRegExp(sentinel);
|
|
683
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:\`\`\`json\\s*)?\\{[\\s\\S]*?\\}(?:\\s*\`\`\`)?`, "gu"), " ");
|
|
684
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
|
|
685
|
+
]*`, "gu"), " ");
|
|
686
|
+
}
|
|
687
|
+
cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
|
|
688
|
+
return cleaned;
|
|
689
|
+
}
|
|
690
|
+
function collapseWhitespace(value) {
|
|
691
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
692
|
+
}
|
|
693
|
+
function escapeForRegExp(value) {
|
|
694
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/adapters/shared/injection/policy.ts
|
|
698
|
+
var DEFAULT_SESSION_START_POLICY = {
|
|
699
|
+
maxCoreEntries: 4,
|
|
700
|
+
maxArtifactRecallEntries: 3,
|
|
701
|
+
maxDurableEntries: 5,
|
|
702
|
+
maxArtifactChars: 1200
|
|
703
|
+
};
|
|
704
|
+
var DEFAULT_BEFORE_TURN_POLICY = {
|
|
705
|
+
maxDurableEntries: 1,
|
|
706
|
+
maxHighConfidenceDurableEntries: 2,
|
|
707
|
+
maxRecentTurns: 2,
|
|
708
|
+
maxQueryChars: 450,
|
|
709
|
+
maxProcedureCandidates: 3,
|
|
710
|
+
recallThreshold: 0.6,
|
|
711
|
+
highConfidenceRecallThreshold: 0.85,
|
|
712
|
+
procedureThreshold: 0.72
|
|
713
|
+
};
|
|
714
|
+
function resolveSessionStartPolicy(memoryPolicy) {
|
|
715
|
+
return {
|
|
716
|
+
...DEFAULT_SESSION_START_POLICY,
|
|
717
|
+
...memoryPolicy?.sessionStart?.coreMemory === false ? { maxCoreEntries: 0 } : {},
|
|
718
|
+
enableArtifactRecall: memoryPolicy?.sessionStart?.relevantDurableMemory !== false
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function resolveBeforeTurnPolicy(memoryPolicy) {
|
|
722
|
+
return {
|
|
723
|
+
...DEFAULT_BEFORE_TURN_POLICY,
|
|
724
|
+
enableProcedureSuggestion: memoryPolicy?.beforeTurn?.procedureSuggestion !== false,
|
|
725
|
+
...memoryPolicy?.beforeTurn?.maxDurableEntries !== void 0 ? { maxDurableEntries: memoryPolicy.beforeTurn.maxDurableEntries } : {},
|
|
726
|
+
...memoryPolicy?.beforeTurn?.recallThreshold !== void 0 ? { recallThreshold: memoryPolicy.beforeTurn.recallThreshold } : {},
|
|
727
|
+
...memoryPolicy?.beforeTurn?.highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold: memoryPolicy.beforeTurn.highConfidenceRecallThreshold } : {},
|
|
728
|
+
...memoryPolicy?.beforeTurn?.procedureThreshold !== void 0 ? { procedureThreshold: memoryPolicy.beforeTurn.procedureThreshold } : {}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function isWorkingContextPolicyEnabled(memoryPolicy) {
|
|
732
|
+
return memoryPolicy?.workingContext?.enabled !== false;
|
|
733
|
+
}
|
|
734
|
+
function resolveWorkingContextGate(workingMemory, memoryPolicy) {
|
|
735
|
+
const capability = resolveWorkingMemoryCapability(workingMemory);
|
|
736
|
+
if (capability === "disabled") {
|
|
737
|
+
return { ok: false, reason: "features.workingMemory=false" };
|
|
738
|
+
}
|
|
739
|
+
if (capability === "misconfigured") {
|
|
740
|
+
return { ok: false, reason: "features.workingMemory enabled without repository" };
|
|
741
|
+
}
|
|
742
|
+
if (!isWorkingContextPolicyEnabled(memoryPolicy)) {
|
|
743
|
+
return { ok: false, reason: "memoryPolicy.workingContext.enabled=false" };
|
|
744
|
+
}
|
|
745
|
+
return { ok: true };
|
|
746
|
+
}
|
|
747
|
+
function resolveWorkingMemoryCapability(workingMemory) {
|
|
748
|
+
if (typeof workingMemory === "string") {
|
|
749
|
+
return workingMemory;
|
|
750
|
+
}
|
|
751
|
+
return workingMemory.workingMemory ? "enabled" : "disabled";
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/adapters/shared/injection/session-start-format.ts
|
|
755
|
+
function formatAgenrSessionStartRecall(patch) {
|
|
756
|
+
if (patch.contextSections.length === 0 && patch.durableMemory.length === 0) {
|
|
757
|
+
return "";
|
|
758
|
+
}
|
|
759
|
+
const lines = [];
|
|
760
|
+
for (const section of patch.contextSections) {
|
|
761
|
+
lines.push(`## ${section.title}`);
|
|
762
|
+
lines.push(section.content);
|
|
763
|
+
lines.push("");
|
|
764
|
+
}
|
|
765
|
+
const durableSections = buildSections(patch);
|
|
766
|
+
if (durableSections.length > 0) {
|
|
767
|
+
const recallLines = [
|
|
768
|
+
"## Agenr Session Recall",
|
|
769
|
+
"Use this as prior context. Confirm anything important if the current conversation conflicts with it.",
|
|
770
|
+
""
|
|
771
|
+
];
|
|
772
|
+
for (const section of durableSections) {
|
|
773
|
+
recallLines.push(`### ${section.title}`);
|
|
774
|
+
for (const item of section.entries) {
|
|
775
|
+
recallLines.push(formatInjectionEntryHeader(item));
|
|
776
|
+
recallLines.push(...formatInjectionEntryBodyLines(item));
|
|
777
|
+
}
|
|
778
|
+
recallLines.push("");
|
|
779
|
+
}
|
|
780
|
+
lines.push(wrapAgenrMemoryContext(recallLines.join("\n").trim()));
|
|
781
|
+
}
|
|
782
|
+
return lines.join("\n").trim();
|
|
783
|
+
}
|
|
784
|
+
function buildSections(patch) {
|
|
785
|
+
const sections = [];
|
|
786
|
+
const coreEntries = patch.durableMemory.filter((item) => item.sourceKind === "core");
|
|
787
|
+
if (coreEntries.length > 0) {
|
|
788
|
+
sections.push({ title: "Core Memory", entries: coreEntries });
|
|
789
|
+
}
|
|
790
|
+
const artifactRecallEntries = patch.durableMemory.filter((item) => item.sourceKind === "artifact_recall");
|
|
791
|
+
if (artifactRecallEntries.length > 0) {
|
|
792
|
+
sections.push({ title: "Relevant Durable Memory", entries: artifactRecallEntries });
|
|
793
|
+
}
|
|
794
|
+
return sections;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/adapters/plugin-runtime/claim-extraction.ts
|
|
798
|
+
async function buildClaimExtractionRuntime(config, resolveLlm) {
|
|
799
|
+
const claimExtractionConfig = resolveClaimExtractionConfig(config);
|
|
800
|
+
if (!claimExtractionConfig.enabled) {
|
|
801
|
+
return void 0;
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
return {
|
|
805
|
+
llm: await resolveLlm(),
|
|
806
|
+
config: claimExtractionConfig
|
|
807
|
+
};
|
|
808
|
+
} catch {
|
|
809
|
+
return void 0;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function createClaimExtractionFromAgenrConfig(config) {
|
|
813
|
+
const claimExtractionConfig = resolveClaimExtractionConfig(config);
|
|
814
|
+
if (!claimExtractionConfig.enabled) {
|
|
815
|
+
return void 0;
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
const { provider, modelId } = resolveModel(config, "claim");
|
|
819
|
+
const apiKey = resolveLlmApiKey(config, provider);
|
|
820
|
+
return {
|
|
821
|
+
llm: createLlmClient(provider, modelId, { apiKey }),
|
|
822
|
+
config: claimExtractionConfig
|
|
823
|
+
};
|
|
824
|
+
} catch {
|
|
825
|
+
return void 0;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// src/adapters/db/session-memory-repository.ts
|
|
830
|
+
import { randomUUID } from "crypto";
|
|
831
|
+
|
|
832
|
+
// src/app/working-memory/limits.ts
|
|
833
|
+
function normalizeBoundedLimit(value, fallback, max) {
|
|
834
|
+
if (value === void 0 || !Number.isInteger(value) || value <= 0) {
|
|
835
|
+
return fallback;
|
|
836
|
+
}
|
|
837
|
+
return Math.min(value, max);
|
|
838
|
+
}
|
|
839
|
+
function normalizeEventLimit(value) {
|
|
840
|
+
return normalizeBoundedLimit(value, 50, 200);
|
|
841
|
+
}
|
|
842
|
+
function normalizeListLimit(value) {
|
|
843
|
+
return normalizeBoundedLimit(value, 20, 100);
|
|
844
|
+
}
|
|
845
|
+
var CLOSE_EVENT_HISTORY_LIMIT = 1e3;
|
|
846
|
+
|
|
847
|
+
// src/adapters/db/json.ts
|
|
848
|
+
function serializeOptionalJson(value) {
|
|
849
|
+
return value === void 0 ? null : JSON.stringify(value);
|
|
850
|
+
}
|
|
851
|
+
function parseOptionalJson(value, label) {
|
|
852
|
+
if (!value) {
|
|
853
|
+
return void 0;
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
return JSON.parse(value);
|
|
857
|
+
} catch (error) {
|
|
858
|
+
throw new Error(`Failed to parse ${label}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/app/session-memory/types.ts
|
|
863
|
+
var SESSION_ARTIFACT_KINDS = ["continuity_summary", "recent_session", "compaction_checkpoint", "branch_abandonment", "session_episode"];
|
|
864
|
+
var SESSION_LINEAGE_REASONS = ["fork", "clone", "resume", "subagent_spawn"];
|
|
865
|
+
var SESSION_START_TRANSITION_REASONS = ["new", "unknown", ...SESSION_LINEAGE_REASONS];
|
|
866
|
+
|
|
867
|
+
// src/adapters/db/session-memory-parsing.ts
|
|
868
|
+
function parseSessionArtifactKind(value) {
|
|
869
|
+
if (SESSION_ARTIFACT_KINDS.includes(value)) {
|
|
870
|
+
return value;
|
|
871
|
+
}
|
|
872
|
+
throw new Error(`Unsupported session artifact kind "${value}".`);
|
|
873
|
+
}
|
|
874
|
+
function parseSessionLineageReason(value) {
|
|
875
|
+
if (SESSION_LINEAGE_REASONS.includes(value)) {
|
|
876
|
+
return value;
|
|
877
|
+
}
|
|
878
|
+
throw new Error(`Unsupported session lineage reason "${value}".`);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/adapters/db/session-memory-repository.ts
|
|
882
|
+
var SESSION_LINEAGE_EDGE_SELECT_COLUMNS = `
|
|
883
|
+
id,
|
|
884
|
+
child_session_key,
|
|
885
|
+
parent_session_key,
|
|
886
|
+
parent_source_ref,
|
|
887
|
+
reason,
|
|
888
|
+
fork_entry_id,
|
|
889
|
+
fork_position,
|
|
890
|
+
observed_at
|
|
891
|
+
`;
|
|
892
|
+
var SESSION_ARTIFACT_SELECT_COLUMNS = `
|
|
893
|
+
id,
|
|
894
|
+
kind,
|
|
895
|
+
session_key,
|
|
896
|
+
source,
|
|
897
|
+
source_id,
|
|
898
|
+
source_ref,
|
|
899
|
+
content_hash,
|
|
900
|
+
summary,
|
|
901
|
+
metadata_json,
|
|
902
|
+
created_at,
|
|
903
|
+
expires_at
|
|
904
|
+
`;
|
|
905
|
+
function createSessionMemoryRepository(database) {
|
|
906
|
+
return {
|
|
907
|
+
upsertLineageEdge: (input) => upsertLineageEdge(database, input),
|
|
908
|
+
upsertSessionArtifact: (input) => upsertSessionArtifact(database, input),
|
|
909
|
+
recordTriggerIntake: (input) => recordTriggerIntake(database, input),
|
|
910
|
+
listSessionArtifacts: (filter) => listSessionArtifacts(database, filter),
|
|
911
|
+
listSessionArtifactsBySourceRef: (filter) => listSessionArtifactsBySourceRef(database, filter),
|
|
912
|
+
getLatestLineageEdgeForChild: (childSessionKey) => getLatestLineageEdgeForChild(database, childSessionKey)
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
async function upsertLineageEdge(database, input) {
|
|
916
|
+
return database.withTransaction(async (transaction) => upsertLineageEdgeWithExecutor(transaction, input));
|
|
917
|
+
}
|
|
918
|
+
async function recordTriggerIntake(database, input) {
|
|
919
|
+
return database.withTransaction(async (transaction) => {
|
|
920
|
+
const executor = transaction;
|
|
921
|
+
const result = {};
|
|
922
|
+
if (input.lineage) {
|
|
923
|
+
result.lineageEdge = await upsertLineageEdgeWithExecutor(executor, input.lineage);
|
|
924
|
+
}
|
|
925
|
+
if (input.artifact) {
|
|
926
|
+
result.artifact = await upsertSessionArtifact(executor, input.artifact);
|
|
927
|
+
}
|
|
928
|
+
return result;
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
async function upsertLineageEdgeWithExecutor(executor, input) {
|
|
932
|
+
const existing = await findMatchingLineageEdge(executor, input);
|
|
933
|
+
if (existing) {
|
|
934
|
+
return existing;
|
|
935
|
+
}
|
|
936
|
+
const id = randomUUID();
|
|
937
|
+
await executor.execute({
|
|
938
|
+
sql: `
|
|
939
|
+
INSERT INTO session_lineage_edges (
|
|
940
|
+
id,
|
|
941
|
+
child_session_key,
|
|
942
|
+
parent_session_key,
|
|
943
|
+
parent_source_ref,
|
|
944
|
+
reason,
|
|
945
|
+
fork_entry_id,
|
|
946
|
+
fork_position,
|
|
947
|
+
observed_at
|
|
948
|
+
)
|
|
949
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
950
|
+
`,
|
|
951
|
+
args: [
|
|
952
|
+
id,
|
|
953
|
+
input.childSessionKey,
|
|
954
|
+
input.parentSessionKey ?? null,
|
|
955
|
+
input.parentSourceRef ?? null,
|
|
956
|
+
input.reason,
|
|
957
|
+
input.forkEntryId ?? null,
|
|
958
|
+
input.forkPosition ?? null,
|
|
959
|
+
input.observedAt
|
|
960
|
+
]
|
|
961
|
+
});
|
|
962
|
+
const edge = await getLineageEdge(executor, id);
|
|
963
|
+
if (!edge) {
|
|
964
|
+
throw new Error(`Session lineage edge ${id} was not found after write.`);
|
|
965
|
+
}
|
|
966
|
+
return edge;
|
|
967
|
+
}
|
|
968
|
+
async function upsertSessionArtifact(executor, input) {
|
|
969
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
970
|
+
await executor.execute({
|
|
971
|
+
sql: `
|
|
972
|
+
INSERT INTO session_artifacts (
|
|
973
|
+
id,
|
|
974
|
+
kind,
|
|
975
|
+
session_key,
|
|
976
|
+
source,
|
|
977
|
+
source_id,
|
|
978
|
+
source_ref,
|
|
979
|
+
content_hash,
|
|
980
|
+
summary,
|
|
981
|
+
metadata_json,
|
|
982
|
+
created_at,
|
|
983
|
+
expires_at
|
|
984
|
+
)
|
|
985
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
986
|
+
ON CONFLICT(kind, source, source_id) DO UPDATE SET
|
|
987
|
+
session_key = excluded.session_key,
|
|
988
|
+
source_ref = excluded.source_ref,
|
|
989
|
+
content_hash = excluded.content_hash,
|
|
990
|
+
summary = excluded.summary,
|
|
991
|
+
metadata_json = excluded.metadata_json,
|
|
992
|
+
expires_at = excluded.expires_at
|
|
993
|
+
`,
|
|
994
|
+
args: [
|
|
995
|
+
randomUUID(),
|
|
996
|
+
input.kind,
|
|
997
|
+
input.sessionKey,
|
|
998
|
+
input.source,
|
|
999
|
+
input.sourceId,
|
|
1000
|
+
input.sourceRef ?? null,
|
|
1001
|
+
input.contentHash,
|
|
1002
|
+
input.summary,
|
|
1003
|
+
serializeOptionalJson(input.metadata),
|
|
1004
|
+
createdAt,
|
|
1005
|
+
input.expiresAt ?? null
|
|
1006
|
+
]
|
|
1007
|
+
});
|
|
1008
|
+
const artifact = await getSessionArtifactBySource(executor, input.kind, input.source, input.sourceId);
|
|
1009
|
+
if (!artifact) {
|
|
1010
|
+
throw new Error(`Session artifact ${input.kind}/${input.source}/${input.sourceId} was not found after write.`);
|
|
1011
|
+
}
|
|
1012
|
+
return artifact;
|
|
1013
|
+
}
|
|
1014
|
+
async function listSessionArtifacts(executor, filter) {
|
|
1015
|
+
const sessionKey = filter.sessionKey.trim();
|
|
1016
|
+
if (!sessionKey) {
|
|
1017
|
+
return [];
|
|
1018
|
+
}
|
|
1019
|
+
return querySessionArtifacts(executor, {
|
|
1020
|
+
conditions: ["session_key = ?"],
|
|
1021
|
+
args: [sessionKey],
|
|
1022
|
+
kinds: filter.kinds,
|
|
1023
|
+
limit: filter.limit
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
async function listSessionArtifactsBySourceRef(executor, filter) {
|
|
1027
|
+
const sourceRef = filter.sourceRef.trim();
|
|
1028
|
+
if (!sourceRef) {
|
|
1029
|
+
return [];
|
|
1030
|
+
}
|
|
1031
|
+
return querySessionArtifacts(executor, {
|
|
1032
|
+
conditions: ["source_ref = ?"],
|
|
1033
|
+
args: [sourceRef],
|
|
1034
|
+
kinds: filter.kinds,
|
|
1035
|
+
limit: filter.limit
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
async function getLatestLineageEdgeForChild(executor, childSessionKey) {
|
|
1039
|
+
const normalizedChild = childSessionKey.trim();
|
|
1040
|
+
if (!normalizedChild) {
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
const result = await executor.execute({
|
|
1044
|
+
sql: `
|
|
1045
|
+
SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
|
|
1046
|
+
FROM session_lineage_edges
|
|
1047
|
+
WHERE child_session_key = ?
|
|
1048
|
+
ORDER BY observed_at DESC, id ASC
|
|
1049
|
+
LIMIT 1
|
|
1050
|
+
`,
|
|
1051
|
+
args: [normalizedChild]
|
|
1052
|
+
});
|
|
1053
|
+
const row = result.rows[0];
|
|
1054
|
+
return row ? mapSessionLineageEdgeRow(row) : null;
|
|
1055
|
+
}
|
|
1056
|
+
async function findMatchingLineageEdge(executor, input) {
|
|
1057
|
+
const parentSessionKeyClause = input.parentSessionKey ? "parent_session_key = ?" : "parent_session_key IS NULL";
|
|
1058
|
+
const parentSourceRefClause = input.parentSourceRef ? "parent_source_ref = ?" : "parent_source_ref IS NULL";
|
|
1059
|
+
const args = [
|
|
1060
|
+
input.childSessionKey,
|
|
1061
|
+
input.reason,
|
|
1062
|
+
...input.parentSessionKey ? [input.parentSessionKey] : [],
|
|
1063
|
+
...input.parentSourceRef ? [input.parentSourceRef] : []
|
|
1064
|
+
];
|
|
1065
|
+
const result = await executor.execute({
|
|
1066
|
+
sql: `
|
|
1067
|
+
SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
|
|
1068
|
+
FROM session_lineage_edges
|
|
1069
|
+
WHERE child_session_key = ?
|
|
1070
|
+
AND reason = ?
|
|
1071
|
+
AND ${parentSessionKeyClause}
|
|
1072
|
+
AND ${parentSourceRefClause}
|
|
1073
|
+
ORDER BY observed_at DESC, id ASC
|
|
1074
|
+
LIMIT 1
|
|
1075
|
+
`,
|
|
1076
|
+
args
|
|
1077
|
+
});
|
|
1078
|
+
const row = result.rows[0];
|
|
1079
|
+
return row ? mapSessionLineageEdgeRow(row) : null;
|
|
1080
|
+
}
|
|
1081
|
+
async function getLineageEdge(executor, id) {
|
|
1082
|
+
const result = await executor.execute({
|
|
1083
|
+
sql: `
|
|
1084
|
+
SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
|
|
1085
|
+
FROM session_lineage_edges
|
|
1086
|
+
WHERE id = ?
|
|
1087
|
+
LIMIT 1
|
|
1088
|
+
`,
|
|
1089
|
+
args: [id]
|
|
1090
|
+
});
|
|
1091
|
+
const row = result.rows[0];
|
|
1092
|
+
return row ? mapSessionLineageEdgeRow(row) : null;
|
|
1093
|
+
}
|
|
1094
|
+
async function getSessionArtifactBySource(executor, kind, source, sourceId) {
|
|
1095
|
+
const result = await executor.execute({
|
|
1096
|
+
sql: `
|
|
1097
|
+
SELECT ${SESSION_ARTIFACT_SELECT_COLUMNS}
|
|
1098
|
+
FROM session_artifacts
|
|
1099
|
+
WHERE kind = ?
|
|
1100
|
+
AND source = ?
|
|
1101
|
+
AND source_id = ?
|
|
1102
|
+
LIMIT 1
|
|
1103
|
+
`,
|
|
1104
|
+
args: [kind, source, sourceId]
|
|
1105
|
+
});
|
|
1106
|
+
const row = result.rows[0];
|
|
1107
|
+
return row ? mapSessionArtifactRow(row) : null;
|
|
1108
|
+
}
|
|
1109
|
+
function appendKindFilter(conditions, args, kinds) {
|
|
1110
|
+
if (kinds && kinds.length > 0) {
|
|
1111
|
+
conditions.push(`kind IN (${kinds.map(() => "?").join(", ")})`);
|
|
1112
|
+
args.push(...kinds);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async function querySessionArtifacts(executor, filter) {
|
|
1116
|
+
const conditions = [...filter.conditions];
|
|
1117
|
+
const args = [...filter.args];
|
|
1118
|
+
appendKindFilter(conditions, args, filter.kinds);
|
|
1119
|
+
const limit = normalizeBoundedLimit(filter.limit, 20, 100);
|
|
1120
|
+
const result = await executor.execute({
|
|
1121
|
+
sql: `
|
|
1122
|
+
SELECT ${SESSION_ARTIFACT_SELECT_COLUMNS}
|
|
1123
|
+
FROM session_artifacts
|
|
1124
|
+
WHERE ${conditions.join(" AND ")}
|
|
1125
|
+
ORDER BY created_at DESC, id ASC
|
|
1126
|
+
LIMIT ?
|
|
1127
|
+
`,
|
|
1128
|
+
args: [...args, limit]
|
|
1129
|
+
});
|
|
1130
|
+
return result.rows.map((row) => mapSessionArtifactRow(row));
|
|
1131
|
+
}
|
|
1132
|
+
function mapSessionLineageEdgeRow(row) {
|
|
1133
|
+
return {
|
|
1134
|
+
id: readRequiredString(row, "id"),
|
|
1135
|
+
childSessionKey: readRequiredString(row, "child_session_key"),
|
|
1136
|
+
parentSessionKey: readOptionalString(row, "parent_session_key"),
|
|
1137
|
+
parentSourceRef: readOptionalString(row, "parent_source_ref"),
|
|
1138
|
+
reason: parseSessionLineageReason(readRequiredString(row, "reason")),
|
|
1139
|
+
forkEntryId: readOptionalString(row, "fork_entry_id"),
|
|
1140
|
+
forkPosition: readOptionalString(row, "fork_position"),
|
|
1141
|
+
observedAt: readRequiredString(row, "observed_at")
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
function mapSessionArtifactRow(row) {
|
|
1145
|
+
return {
|
|
1146
|
+
id: readRequiredString(row, "id"),
|
|
1147
|
+
kind: parseSessionArtifactKind(readRequiredString(row, "kind")),
|
|
1148
|
+
sessionKey: readRequiredString(row, "session_key"),
|
|
1149
|
+
source: readRequiredString(row, "source"),
|
|
1150
|
+
sourceId: readRequiredString(row, "source_id"),
|
|
1151
|
+
sourceRef: readOptionalString(row, "source_ref"),
|
|
1152
|
+
contentHash: readRequiredString(row, "content_hash"),
|
|
1153
|
+
summary: readRequiredString(row, "summary"),
|
|
1154
|
+
metadata: parseOptionalJson(readOptionalString(row, "metadata_json"), "metadata_json"),
|
|
1155
|
+
createdAt: readRequiredString(row, "created_at"),
|
|
1156
|
+
expiresAt: readOptionalString(row, "expires_at")
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// src/adapters/db/session-start-repository.ts
|
|
1161
|
+
function createSessionStartRepository(executor) {
|
|
1162
|
+
return {
|
|
1163
|
+
listCoreEntries: async (limit) => listCoreEntries(executor, limit)
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
async function listCoreEntries(executor, limit) {
|
|
1167
|
+
if (limit <= 0) {
|
|
1168
|
+
return [];
|
|
1169
|
+
}
|
|
1170
|
+
const result = await executor.execute({
|
|
1171
|
+
sql: `
|
|
1172
|
+
SELECT
|
|
1173
|
+
${ENTRY_SELECT_COLUMNS}
|
|
1174
|
+
FROM entries
|
|
1175
|
+
WHERE ${buildActiveEntryClause()}
|
|
1176
|
+
AND expiry = 'core'
|
|
1177
|
+
ORDER BY importance DESC, created_at DESC
|
|
1178
|
+
LIMIT ?
|
|
1179
|
+
`,
|
|
1180
|
+
args: [limit]
|
|
1181
|
+
});
|
|
1182
|
+
return result.rows.map((row) => mapEntryRow(row));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/adapters/db/working-memory-repository.ts
|
|
1186
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1187
|
+
|
|
1188
|
+
// src/app/working-memory/operations/manifest.ts
|
|
1189
|
+
var MODEL_VISIBLE_OPERATION_TYPES = [
|
|
1190
|
+
"set_objective",
|
|
1191
|
+
"replace_plan",
|
|
1192
|
+
"merge_checkpoint",
|
|
1193
|
+
"add_file_note",
|
|
1194
|
+
"add_command_note",
|
|
1195
|
+
"record_decision",
|
|
1196
|
+
"record_assumption",
|
|
1197
|
+
"set_next_actions",
|
|
1198
|
+
"add_candidate"
|
|
1199
|
+
];
|
|
1200
|
+
var TRUSTED_HOST_ONLY_OPERATION_TYPES = ["configure_budget", "account_usage", "set_continuation_policy"];
|
|
1201
|
+
var HOST_ONLY_OPERATION_TYPES = ["set_status", ...TRUSTED_HOST_ONLY_OPERATION_TYPES];
|
|
1202
|
+
var WORKING_UPDATE_OPERATION_TYPES = [...MODEL_VISIBLE_OPERATION_TYPES, ...HOST_ONLY_OPERATION_TYPES];
|
|
1203
|
+
function isModelVisibleOperationType(type) {
|
|
1204
|
+
return MODEL_VISIBLE_OPERATION_TYPES.includes(type);
|
|
1205
|
+
}
|
|
1206
|
+
function isTrustedHostOnlyWorkingOperation(type) {
|
|
1207
|
+
return TRUSTED_HOST_ONLY_OPERATION_TYPES.includes(type);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// src/app/working-memory/constants.ts
|
|
1211
|
+
var OPEN_WORKING_SET_STATUSES = ["active", "paused", "blocked", "waiting", "needs_review", "budget_limited"];
|
|
1212
|
+
var CURRENT_WORKING_SET_STATUSES = [...OPEN_WORKING_SET_STATUSES, "complete"];
|
|
1213
|
+
var CLOSE_MANAGED_WORKING_SET_STATUSES = ["closed", "abandoned"];
|
|
1214
|
+
var TRUSTED_HOST_MUTATION_SOURCES = ["goal_command", "lifecycle_hook", "consolidation_job"];
|
|
1215
|
+
var WORKING_CANDIDATE_PROMOTION_STATUSES = ["pending", "promoted", "dismissed"];
|
|
1216
|
+
function isMutableWorkingSetStatus(status) {
|
|
1217
|
+
return OPEN_WORKING_SET_STATUSES.includes(status);
|
|
1218
|
+
}
|
|
1219
|
+
function isCloseManagedStatus(status) {
|
|
1220
|
+
return CLOSE_MANAGED_WORKING_SET_STATUSES.includes(status);
|
|
1221
|
+
}
|
|
1222
|
+
function isTrustedHostMutationSource(source) {
|
|
1223
|
+
return source !== void 0 && TRUSTED_HOST_MUTATION_SOURCES.includes(source);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/adapters/db/working-memory-columns.ts
|
|
1227
|
+
var WORKING_SET_COLUMN_NAMES = [
|
|
1228
|
+
"id",
|
|
1229
|
+
"scope_key",
|
|
1230
|
+
"scope_kind",
|
|
1231
|
+
"title",
|
|
1232
|
+
"objective",
|
|
1233
|
+
"status",
|
|
1234
|
+
"summary",
|
|
1235
|
+
"snapshot_json",
|
|
1236
|
+
"revision",
|
|
1237
|
+
"project",
|
|
1238
|
+
"session_id",
|
|
1239
|
+
"conversation_key",
|
|
1240
|
+
"cwd",
|
|
1241
|
+
"git_root",
|
|
1242
|
+
"git_branch",
|
|
1243
|
+
"task_id",
|
|
1244
|
+
"source",
|
|
1245
|
+
"created_at",
|
|
1246
|
+
"updated_at",
|
|
1247
|
+
"last_active_at",
|
|
1248
|
+
"closed_at",
|
|
1249
|
+
"close_reason",
|
|
1250
|
+
"episode_id"
|
|
1251
|
+
];
|
|
1252
|
+
var WORKING_SET_SELECT_COLUMNS = WORKING_SET_COLUMN_NAMES.join(",\n ");
|
|
1253
|
+
var WORKING_SET_INSERT_COLUMNS = WORKING_SET_COLUMN_NAMES.join(",\n ");
|
|
1254
|
+
var WORKING_SET_INSERT_PLACEHOLDERS = WORKING_SET_COLUMN_NAMES.map(() => "?").join(", ");
|
|
1255
|
+
var WORKING_SET_UPDATE_COLUMNS = [
|
|
1256
|
+
"title",
|
|
1257
|
+
"objective",
|
|
1258
|
+
"status",
|
|
1259
|
+
"summary",
|
|
1260
|
+
"snapshot_json",
|
|
1261
|
+
"revision",
|
|
1262
|
+
"updated_at",
|
|
1263
|
+
"last_active_at",
|
|
1264
|
+
"closed_at",
|
|
1265
|
+
"close_reason",
|
|
1266
|
+
"episode_id"
|
|
1267
|
+
];
|
|
1268
|
+
var WORKING_SET_SEMANTIC_ONLY_UPDATE_COLUMNS = /* @__PURE__ */ new Set(["revision", "closed_at", "close_reason", "episode_id"]);
|
|
1269
|
+
var WORKING_SET_USAGE_PATCH_COLUMNS = WORKING_SET_UPDATE_COLUMNS.filter((column) => !WORKING_SET_SEMANTIC_ONLY_UPDATE_COLUMNS.has(column));
|
|
1270
|
+
var WORKING_EVENT_SELECT_COLUMNS = `
|
|
1271
|
+
id,
|
|
1272
|
+
working_set_id,
|
|
1273
|
+
sequence,
|
|
1274
|
+
event_type,
|
|
1275
|
+
payload_json,
|
|
1276
|
+
actor,
|
|
1277
|
+
source,
|
|
1278
|
+
host_event_id,
|
|
1279
|
+
turn_id,
|
|
1280
|
+
created_at
|
|
1281
|
+
`;
|
|
1282
|
+
function buildWorkingSetUpdateSetClause() {
|
|
1283
|
+
return WORKING_SET_UPDATE_COLUMNS.map((column) => `${column} = ?`).join(",\n ");
|
|
1284
|
+
}
|
|
1285
|
+
function buildWorkingSetUsagePatchSetClause() {
|
|
1286
|
+
return WORKING_SET_USAGE_PATCH_COLUMNS.map((column) => `${column} = ?`).join(",\n ");
|
|
1287
|
+
}
|
|
1288
|
+
function mapWorkingSetRow(row) {
|
|
1289
|
+
const snapshot = parseJson(readRequiredString(row, "snapshot_json"), "snapshot_json");
|
|
1290
|
+
return {
|
|
1291
|
+
id: readRequiredString(row, "id"),
|
|
1292
|
+
scopeKey: readRequiredString(row, "scope_key"),
|
|
1293
|
+
scopeKind: readRequiredString(row, "scope_kind"),
|
|
1294
|
+
title: readOptionalString(row, "title"),
|
|
1295
|
+
objective: readOptionalString(row, "objective"),
|
|
1296
|
+
status: readRequiredString(row, "status"),
|
|
1297
|
+
summary: readOptionalString(row, "summary"),
|
|
1298
|
+
snapshot,
|
|
1299
|
+
revision: readNumber(row, "revision", 0),
|
|
1300
|
+
project: readOptionalString(row, "project"),
|
|
1301
|
+
sessionId: readOptionalString(row, "session_id"),
|
|
1302
|
+
conversationKey: readOptionalString(row, "conversation_key"),
|
|
1303
|
+
cwd: readOptionalString(row, "cwd"),
|
|
1304
|
+
gitRoot: readOptionalString(row, "git_root"),
|
|
1305
|
+
gitBranch: readOptionalString(row, "git_branch"),
|
|
1306
|
+
taskId: readOptionalString(row, "task_id"),
|
|
1307
|
+
source: readOptionalString(row, "source"),
|
|
1308
|
+
createdAt: readRequiredString(row, "created_at"),
|
|
1309
|
+
updatedAt: readRequiredString(row, "updated_at"),
|
|
1310
|
+
lastActiveAt: readRequiredString(row, "last_active_at"),
|
|
1311
|
+
closedAt: readOptionalString(row, "closed_at"),
|
|
1312
|
+
closeReason: readOptionalString(row, "close_reason"),
|
|
1313
|
+
episodeId: readOptionalString(row, "episode_id")
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
function mapWorkingEventRow(row) {
|
|
1317
|
+
return {
|
|
1318
|
+
id: readRequiredString(row, "id"),
|
|
1319
|
+
workingSetId: readRequiredString(row, "working_set_id"),
|
|
1320
|
+
sequence: readNumber(row, "sequence", 0),
|
|
1321
|
+
eventType: readRequiredString(row, "event_type"),
|
|
1322
|
+
payload: parseJson(readRequiredString(row, "payload_json"), "payload_json"),
|
|
1323
|
+
actor: readOptionalString(row, "actor"),
|
|
1324
|
+
source: readOptionalString(row, "source"),
|
|
1325
|
+
hostEventId: readOptionalString(row, "host_event_id"),
|
|
1326
|
+
turnId: readOptionalString(row, "turn_id"),
|
|
1327
|
+
createdAt: readRequiredString(row, "created_at")
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
function parseJson(value, column) {
|
|
1331
|
+
try {
|
|
1332
|
+
return JSON.parse(value);
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1335
|
+
throw new Error(`Invalid JSON in working-memory column ${column}: ${message}`, { cause: error });
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// src/adapters/db/working-memory-repository.ts
|
|
1340
|
+
function createWorkingMemoryRepository(database) {
|
|
1341
|
+
return {
|
|
1342
|
+
getWorkingSet: (id) => getWorkingSet(database, id),
|
|
1343
|
+
findCurrentWorkingSets: (scope) => findWorkingSetsByScope(database, scope, CURRENT_WORKING_SET_STATUSES),
|
|
1344
|
+
listWorkingSets: (filter) => listWorkingSets(database, filter),
|
|
1345
|
+
listWorkingEvents: (workingSetId, limit) => listWorkingEvents(database, workingSetId, limit),
|
|
1346
|
+
createWorkingSet: (input) => createWorkingSet(database, input),
|
|
1347
|
+
updateWorkingSet: (input) => updateWorkingSet(database, input),
|
|
1348
|
+
patchWorkingSetUsage: (input) => patchWorkingSetUsage(database, input)
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
async function getWorkingSet(executor, id) {
|
|
1352
|
+
const normalizedId = id.trim();
|
|
1353
|
+
if (!normalizedId) {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
const result = await executor.execute({
|
|
1357
|
+
sql: `
|
|
1358
|
+
SELECT ${WORKING_SET_SELECT_COLUMNS}
|
|
1359
|
+
FROM working_sets
|
|
1360
|
+
WHERE id = ?
|
|
1361
|
+
LIMIT 1
|
|
1362
|
+
`,
|
|
1363
|
+
args: [normalizedId]
|
|
1364
|
+
});
|
|
1365
|
+
const row = result.rows[0];
|
|
1366
|
+
return row ? mapWorkingSetRow(row) : null;
|
|
1367
|
+
}
|
|
1368
|
+
async function findWorkingSetsByScope(executor, scope, statuses) {
|
|
1369
|
+
const result = await executor.execute({
|
|
1370
|
+
sql: `
|
|
1371
|
+
SELECT ${WORKING_SET_SELECT_COLUMNS}
|
|
1372
|
+
FROM working_sets
|
|
1373
|
+
WHERE scope_key = ?
|
|
1374
|
+
AND status IN (${statuses.map(() => "?").join(", ")})
|
|
1375
|
+
ORDER BY last_active_at DESC, id ASC
|
|
1376
|
+
`,
|
|
1377
|
+
args: [scope.scopeKey, ...statuses]
|
|
1378
|
+
});
|
|
1379
|
+
return result.rows.map((row) => mapWorkingSetRow(row));
|
|
1380
|
+
}
|
|
1381
|
+
async function listWorkingSets(executor, filter) {
|
|
1382
|
+
const conditions = [];
|
|
1383
|
+
const args = [];
|
|
1384
|
+
if (filter.scope) {
|
|
1385
|
+
conditions.push("scope_key = ?");
|
|
1386
|
+
args.push(filter.scope.scopeKey);
|
|
1387
|
+
}
|
|
1388
|
+
if (filter.statuses && filter.statuses.length > 0) {
|
|
1389
|
+
conditions.push(`status IN (${filter.statuses.map(() => "?").join(", ")})`);
|
|
1390
|
+
args.push(...filter.statuses);
|
|
1391
|
+
}
|
|
1392
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1393
|
+
const limit = normalizeBoundedLimit(filter.limit, 20, 100);
|
|
1394
|
+
const result = await executor.execute({
|
|
1395
|
+
sql: `
|
|
1396
|
+
SELECT ${WORKING_SET_SELECT_COLUMNS}
|
|
1397
|
+
FROM working_sets
|
|
1398
|
+
${where}
|
|
1399
|
+
ORDER BY last_active_at DESC, id ASC
|
|
1400
|
+
LIMIT ?
|
|
1401
|
+
`,
|
|
1402
|
+
args: [...args, limit]
|
|
1403
|
+
});
|
|
1404
|
+
return result.rows.map((row) => mapWorkingSetRow(row));
|
|
1405
|
+
}
|
|
1406
|
+
async function listWorkingEvents(executor, workingSetId, limit) {
|
|
1407
|
+
const normalizedId = workingSetId.trim();
|
|
1408
|
+
if (!normalizedId) {
|
|
1409
|
+
return [];
|
|
1410
|
+
}
|
|
1411
|
+
const normalizedLimit = normalizeBoundedLimit(limit, 50, 1e3);
|
|
1412
|
+
const result = await executor.execute({
|
|
1413
|
+
sql: `
|
|
1414
|
+
SELECT ${WORKING_EVENT_SELECT_COLUMNS}
|
|
1415
|
+
FROM working_events
|
|
1416
|
+
WHERE working_set_id = ?
|
|
1417
|
+
ORDER BY sequence DESC
|
|
1418
|
+
LIMIT ?
|
|
1419
|
+
`,
|
|
1420
|
+
args: [normalizedId, normalizedLimit]
|
|
1421
|
+
});
|
|
1422
|
+
return result.rows.map((row) => mapWorkingEventRow(row)).reverse();
|
|
1423
|
+
}
|
|
1424
|
+
async function createWorkingSet(database, input) {
|
|
1425
|
+
return database.withTransaction(async (transaction) => {
|
|
1426
|
+
const executor = transaction;
|
|
1427
|
+
const existing = await findWorkingSetsByScope(executor, input.scope, CURRENT_WORKING_SET_STATUSES);
|
|
1428
|
+
if (existing.length > 0) {
|
|
1429
|
+
return { kind: "active_set_exists", scopeKey: input.scope.scopeKey };
|
|
1430
|
+
}
|
|
1431
|
+
const id = randomUUID2();
|
|
1432
|
+
const event = buildEvent({
|
|
1433
|
+
workingSetId: id,
|
|
1434
|
+
sequence: 1,
|
|
1435
|
+
eventType: "created",
|
|
1436
|
+
payload: {
|
|
1437
|
+
objective: input.objective,
|
|
1438
|
+
scope: input.scope
|
|
1439
|
+
},
|
|
1440
|
+
actor: input.actor,
|
|
1441
|
+
source: input.source,
|
|
1442
|
+
now: input.now
|
|
1443
|
+
});
|
|
1444
|
+
await executor.execute({
|
|
1445
|
+
sql: `
|
|
1446
|
+
INSERT INTO working_sets (
|
|
1447
|
+
${WORKING_SET_INSERT_COLUMNS}
|
|
1448
|
+
)
|
|
1449
|
+
VALUES (
|
|
1450
|
+
${WORKING_SET_INSERT_PLACEHOLDERS}
|
|
1451
|
+
)
|
|
1452
|
+
`,
|
|
1453
|
+
args: [
|
|
1454
|
+
id,
|
|
1455
|
+
input.scope.scopeKey,
|
|
1456
|
+
input.scope.scopeKind,
|
|
1457
|
+
toNullableString(input.title),
|
|
1458
|
+
toNullableString(input.objective),
|
|
1459
|
+
input.status,
|
|
1460
|
+
toNullableString(input.snapshot.summary),
|
|
1461
|
+
serializeJson(input.snapshot),
|
|
1462
|
+
1,
|
|
1463
|
+
toNullableString(input.scope.project),
|
|
1464
|
+
toNullableString(input.sessionId ?? input.scope.sessionId),
|
|
1465
|
+
toNullableString(input.scope.conversationKey),
|
|
1466
|
+
toNullableString(input.scope.cwd),
|
|
1467
|
+
toNullableString(input.scope.gitRoot),
|
|
1468
|
+
toNullableString(input.scope.gitBranch),
|
|
1469
|
+
toNullableString(input.scope.taskId),
|
|
1470
|
+
toNullableString(input.sourceLabel),
|
|
1471
|
+
input.now,
|
|
1472
|
+
input.now,
|
|
1473
|
+
input.now,
|
|
1474
|
+
null,
|
|
1475
|
+
null,
|
|
1476
|
+
null
|
|
1477
|
+
]
|
|
1478
|
+
});
|
|
1479
|
+
await insertWorkingEvent(executor, event);
|
|
1480
|
+
const workingSet = await requireWorkingSet(executor, id);
|
|
1481
|
+
return { workingSet, event };
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
async function updateWorkingSet(database, input) {
|
|
1485
|
+
return database.withTransaction(async (transaction) => {
|
|
1486
|
+
const executor = transaction;
|
|
1487
|
+
const current = await getWorkingSet(executor, input.workingSetId);
|
|
1488
|
+
if (!current) {
|
|
1489
|
+
return { kind: "not_found" };
|
|
1490
|
+
}
|
|
1491
|
+
if (!canApplyWorkingSetUpdate(current.status, input.status)) {
|
|
1492
|
+
return { kind: "terminal_status", status: current.status };
|
|
1493
|
+
}
|
|
1494
|
+
if (current.revision !== input.expectedRevision) {
|
|
1495
|
+
return { kind: "revision_conflict", actualRevision: current.revision };
|
|
1496
|
+
}
|
|
1497
|
+
const nextRevision = current.revision + 1;
|
|
1498
|
+
const event = buildEvent({
|
|
1499
|
+
workingSetId: current.id,
|
|
1500
|
+
sequence: nextRevision,
|
|
1501
|
+
eventType: input.eventType,
|
|
1502
|
+
payload: input.payload,
|
|
1503
|
+
actor: input.actor,
|
|
1504
|
+
source: input.source,
|
|
1505
|
+
now: input.now
|
|
1506
|
+
});
|
|
1507
|
+
await executor.execute({
|
|
1508
|
+
sql: `
|
|
1509
|
+
UPDATE working_sets
|
|
1510
|
+
SET ${buildWorkingSetUpdateSetClause()}
|
|
1511
|
+
WHERE id = ?
|
|
1512
|
+
AND revision = ?
|
|
1513
|
+
`,
|
|
1514
|
+
args: [
|
|
1515
|
+
...buildWorkingSetSnapshotUpdateArgs({
|
|
1516
|
+
title: input.title,
|
|
1517
|
+
objective: input.objective,
|
|
1518
|
+
status: input.status,
|
|
1519
|
+
snapshot: input.snapshot,
|
|
1520
|
+
current
|
|
1521
|
+
}),
|
|
1522
|
+
nextRevision,
|
|
1523
|
+
input.now,
|
|
1524
|
+
input.now,
|
|
1525
|
+
toNullableString(input.closedAt ?? current.closedAt),
|
|
1526
|
+
toNullableString(input.closeReason ?? current.closeReason),
|
|
1527
|
+
toNullableString(input.episodeId ?? current.episodeId),
|
|
1528
|
+
current.id,
|
|
1529
|
+
input.expectedRevision
|
|
1530
|
+
]
|
|
1531
|
+
});
|
|
1532
|
+
const workingSet = await requireWorkingSet(executor, current.id);
|
|
1533
|
+
if (workingSet.revision !== nextRevision) {
|
|
1534
|
+
return { kind: "revision_conflict", actualRevision: workingSet.revision };
|
|
1535
|
+
}
|
|
1536
|
+
await insertWorkingEvent(executor, event);
|
|
1537
|
+
return { workingSet, event };
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
async function patchWorkingSetUsage(database, input) {
|
|
1541
|
+
return database.withTransaction(async (transaction) => {
|
|
1542
|
+
const executor = transaction;
|
|
1543
|
+
const current = await getWorkingSet(executor, input.workingSetId);
|
|
1544
|
+
if (!current) {
|
|
1545
|
+
return { kind: "not_found" };
|
|
1546
|
+
}
|
|
1547
|
+
if (!canApplyWorkingSetUpdate(current.status, input.status)) {
|
|
1548
|
+
return { kind: "terminal_status", status: current.status };
|
|
1549
|
+
}
|
|
1550
|
+
if (current.revision !== input.expectedRevision) {
|
|
1551
|
+
return { kind: "revision_conflict", actualRevision: current.revision };
|
|
1552
|
+
}
|
|
1553
|
+
await executor.execute({
|
|
1554
|
+
sql: `
|
|
1555
|
+
UPDATE working_sets
|
|
1556
|
+
SET ${buildWorkingSetUsagePatchSetClause()}
|
|
1557
|
+
WHERE id = ?
|
|
1558
|
+
AND revision = ?
|
|
1559
|
+
`,
|
|
1560
|
+
args: [
|
|
1561
|
+
...buildWorkingSetSnapshotUpdateArgs({
|
|
1562
|
+
title: input.title,
|
|
1563
|
+
objective: input.objective,
|
|
1564
|
+
status: input.status,
|
|
1565
|
+
snapshot: input.snapshot,
|
|
1566
|
+
current
|
|
1567
|
+
}),
|
|
1568
|
+
input.now,
|
|
1569
|
+
input.now,
|
|
1570
|
+
current.id,
|
|
1571
|
+
input.expectedRevision
|
|
1572
|
+
]
|
|
1573
|
+
});
|
|
1574
|
+
const workingSet = await requireWorkingSet(executor, current.id);
|
|
1575
|
+
if (workingSet.revision !== input.expectedRevision) {
|
|
1576
|
+
return { kind: "revision_conflict", actualRevision: workingSet.revision };
|
|
1577
|
+
}
|
|
1578
|
+
return { workingSet };
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
async function insertWorkingEvent(executor, event) {
|
|
1582
|
+
await executor.execute({
|
|
1583
|
+
sql: `
|
|
1584
|
+
INSERT INTO working_events (
|
|
1585
|
+
id,
|
|
1586
|
+
working_set_id,
|
|
1587
|
+
sequence,
|
|
1588
|
+
event_type,
|
|
1589
|
+
payload_json,
|
|
1590
|
+
actor,
|
|
1591
|
+
source,
|
|
1592
|
+
host_event_id,
|
|
1593
|
+
turn_id,
|
|
1594
|
+
created_at
|
|
1595
|
+
)
|
|
1596
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1597
|
+
`,
|
|
1598
|
+
args: [
|
|
1599
|
+
event.id,
|
|
1600
|
+
event.workingSetId,
|
|
1601
|
+
event.sequence,
|
|
1602
|
+
event.eventType,
|
|
1603
|
+
serializeJson(event.payload),
|
|
1604
|
+
toNullableString(event.actor),
|
|
1605
|
+
toNullableString(event.source),
|
|
1606
|
+
toNullableString(event.hostEventId),
|
|
1607
|
+
toNullableString(event.turnId),
|
|
1608
|
+
event.createdAt
|
|
1609
|
+
]
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
function buildWorkingSetSnapshotUpdateArgs(input) {
|
|
1613
|
+
return [
|
|
1614
|
+
toNullableString(input.title ?? input.current.title),
|
|
1615
|
+
toNullableString(input.objective ?? input.snapshot.objective),
|
|
1616
|
+
input.status,
|
|
1617
|
+
toNullableString(input.snapshot.summary),
|
|
1618
|
+
serializeJson(input.snapshot)
|
|
1619
|
+
];
|
|
1620
|
+
}
|
|
1621
|
+
function buildEvent(input) {
|
|
1622
|
+
return {
|
|
1623
|
+
id: randomUUID2(),
|
|
1624
|
+
workingSetId: input.workingSetId,
|
|
1625
|
+
sequence: input.sequence,
|
|
1626
|
+
eventType: input.eventType,
|
|
1627
|
+
payload: input.payload,
|
|
1628
|
+
...input.actor ? { actor: input.actor } : {},
|
|
1629
|
+
...input.source ? { source: input.source } : {},
|
|
1630
|
+
createdAt: input.now
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
function canApplyWorkingSetUpdate(currentStatus, nextStatus) {
|
|
1634
|
+
if (OPEN_WORKING_SET_STATUSES.includes(currentStatus)) {
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
return currentStatus === "complete" && CLOSE_MANAGED_WORKING_SET_STATUSES.includes(nextStatus);
|
|
1638
|
+
}
|
|
1639
|
+
async function requireWorkingSet(executor, id) {
|
|
1640
|
+
const workingSet = await getWorkingSet(executor, id);
|
|
1641
|
+
if (!workingSet) {
|
|
1642
|
+
throw new Error(`Working set ${id} was not found after write.`);
|
|
1643
|
+
}
|
|
1644
|
+
return workingSet;
|
|
1645
|
+
}
|
|
1646
|
+
function serializeJson(value) {
|
|
1647
|
+
return JSON.stringify(value);
|
|
1648
|
+
}
|
|
1649
|
+
function toNullableString(value) {
|
|
1650
|
+
const trimmed = value?.trim();
|
|
1651
|
+
return trimmed && trimmed.length > 0 ? trimmed : null;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// src/adapters/plugin-runtime/embed-query.ts
|
|
1655
|
+
function createEmbedQuery(embedding, available) {
|
|
1656
|
+
if (!available) {
|
|
1657
|
+
return void 0;
|
|
1658
|
+
}
|
|
1659
|
+
return async (text) => {
|
|
1660
|
+
const vectors = await embedding.embed([text]);
|
|
1661
|
+
const vector = vectors[0];
|
|
1662
|
+
if (!vector) {
|
|
1663
|
+
throw new Error("Embedding provider returned no vector for the query.");
|
|
1664
|
+
}
|
|
1665
|
+
return vector;
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/adapters/plugin-runtime/create-memory-runtime.ts
|
|
1670
|
+
async function createPluginMemoryRuntime(input) {
|
|
1671
|
+
const embeddingStatus = resolveEmbeddingStatus(input.agenrConfig);
|
|
1672
|
+
const database = await createDatabase(input.dbPath);
|
|
1673
|
+
const embedding = embeddingStatus.available ? createEmbeddingClient(requireApiKey(embeddingStatus), embeddingStatus.model) : createUnavailableEmbeddingPort(embeddingStatus.error ?? "Embeddings are unavailable.");
|
|
1674
|
+
const baseRecall = createRecallAdapter(database, embedding);
|
|
1675
|
+
const recall2 = attachCrossEncoderPort(baseRecall, resolveCrossEncoder(input.agenrConfig));
|
|
1676
|
+
const slotPolicies = input.slotPolicies;
|
|
1677
|
+
let closed = false;
|
|
1678
|
+
return {
|
|
1679
|
+
entries: database,
|
|
1680
|
+
episodes: database,
|
|
1681
|
+
procedures: database,
|
|
1682
|
+
memory: createMemoryRepository(database, {
|
|
1683
|
+
claimSlotPolicyConfig: slotPolicies
|
|
1684
|
+
}),
|
|
1685
|
+
workingMemoryRepository: createWorkingMemoryRepository(database),
|
|
1686
|
+
sessionMemoryRepository: createSessionMemoryRepository(database),
|
|
1687
|
+
sessionStart: {
|
|
1688
|
+
repository: createSessionStartRepository(database),
|
|
1689
|
+
recall: recall2,
|
|
1690
|
+
slotPolicyConfig: slotPolicies
|
|
1691
|
+
},
|
|
1692
|
+
beforeTurn: {
|
|
1693
|
+
recall: recall2,
|
|
1694
|
+
procedures: database,
|
|
1695
|
+
embedQuery: createEmbedQuery(embedding, embeddingStatus.available),
|
|
1696
|
+
slotPolicyConfig: slotPolicies
|
|
1697
|
+
},
|
|
1698
|
+
embedding,
|
|
1699
|
+
recall: recall2,
|
|
1700
|
+
claimExtraction: input.claimExtraction,
|
|
1701
|
+
embeddingStatus: toPublicEmbeddingStatus(embeddingStatus),
|
|
1702
|
+
async close() {
|
|
1703
|
+
if (closed) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
closed = true;
|
|
1707
|
+
if (input.onBeforeClose) {
|
|
1708
|
+
await input.onBeforeClose();
|
|
1709
|
+
}
|
|
1710
|
+
await database.close();
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
function resolveEmbeddingStatus(config) {
|
|
1715
|
+
const model = resolveEmbeddingModel(config);
|
|
1716
|
+
try {
|
|
1717
|
+
return {
|
|
1718
|
+
available: true,
|
|
1719
|
+
provider: "openai",
|
|
1720
|
+
requestedProvider: "openai",
|
|
1721
|
+
model,
|
|
1722
|
+
apiKey: resolveEmbeddingApiKey(config)
|
|
1723
|
+
};
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
return {
|
|
1726
|
+
available: false,
|
|
1727
|
+
provider: "unconfigured",
|
|
1728
|
+
requestedProvider: "openai",
|
|
1729
|
+
model,
|
|
1730
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function toPublicEmbeddingStatus(status) {
|
|
1735
|
+
return {
|
|
1736
|
+
available: status.available,
|
|
1737
|
+
provider: status.provider,
|
|
1738
|
+
requestedProvider: status.requestedProvider,
|
|
1739
|
+
model: status.model,
|
|
1740
|
+
...status.error ? { error: status.error } : {}
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
function createUnavailableEmbeddingPort(errorMessage) {
|
|
1744
|
+
return {
|
|
1745
|
+
async embed() {
|
|
1746
|
+
throw new Error(errorMessage);
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
function requireApiKey(status) {
|
|
1751
|
+
if (!status.apiKey) {
|
|
1752
|
+
throw new Error("Embedding API key is unavailable.");
|
|
1753
|
+
}
|
|
1754
|
+
return status.apiKey;
|
|
1755
|
+
}
|
|
1756
|
+
function resolveCrossEncoder(config) {
|
|
1757
|
+
try {
|
|
1758
|
+
const apiKey = resolveCrossEncoderApiKey(config);
|
|
1759
|
+
const { modelId } = resolveModel(config, "cross_encoder");
|
|
1760
|
+
return createOpenAICrossEncoder({ apiKey, model: modelId });
|
|
1761
|
+
} catch {
|
|
1762
|
+
return void 0;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/app/plugin-runtime/resolve-paths.ts
|
|
1767
|
+
function resolvePluginRuntimeConfig(config, resolvePath) {
|
|
1768
|
+
const dbPathOverride = resolveOptionalPath(config.dbPath, resolvePath);
|
|
1769
|
+
const configPathOverride = resolveOptionalPath(config.configPath, resolvePath);
|
|
1770
|
+
const configPath = resolveConfigPath({
|
|
1771
|
+
configPath: configPathOverride,
|
|
1772
|
+
dbPath: dbPathOverride
|
|
1773
|
+
});
|
|
1774
|
+
const loadedConfig = readConfig({
|
|
1775
|
+
configPath,
|
|
1776
|
+
dbPath: dbPathOverride
|
|
1777
|
+
});
|
|
1778
|
+
const dbPath = dbPathOverride ?? resolveDbPath(loadedConfig);
|
|
1779
|
+
return {
|
|
1780
|
+
resolvedConfig: {
|
|
1781
|
+
dbPath,
|
|
1782
|
+
configPath
|
|
1783
|
+
},
|
|
1784
|
+
agenrConfig: {
|
|
1785
|
+
...loadedConfig,
|
|
1786
|
+
dbPath
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
function resolveOptionalPath(value, resolvePath) {
|
|
1791
|
+
const normalized = value?.trim();
|
|
1792
|
+
if (!normalized) {
|
|
1793
|
+
return void 0;
|
|
1794
|
+
}
|
|
1795
|
+
return resolvePath ? resolvePath(normalized) : normalized;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// src/adapters/plugin-runtime/compose-host-services.ts
|
|
1799
|
+
async function composeHostPluginServices(input) {
|
|
1800
|
+
const { resolvedConfig, agenrConfig } = resolvePluginRuntimeConfig(input.config, input.resolvePath);
|
|
1801
|
+
const claimExtraction = input.resolveClaimExtraction ? await input.resolveClaimExtraction({ agenrConfig, hostConfig: input.config }) : void 0;
|
|
1802
|
+
const runtimeServices = await createPluginMemoryRuntime({
|
|
1803
|
+
dbPath: resolvedConfig.dbPath,
|
|
1804
|
+
agenrConfig,
|
|
1805
|
+
slotPolicies: input.readSlotPolicies?.(input.config),
|
|
1806
|
+
claimExtraction,
|
|
1807
|
+
onBeforeClose: input.onBeforeClose
|
|
1808
|
+
});
|
|
1809
|
+
return input.extend({
|
|
1810
|
+
config: input.config,
|
|
1811
|
+
resolvedConfig,
|
|
1812
|
+
agenrConfig,
|
|
1813
|
+
runtimeServices
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// src/adapters/shared/recall-format.ts
|
|
1818
|
+
function formatUnifiedRecallResults(result) {
|
|
1819
|
+
const lines = [
|
|
1820
|
+
"Recall Route",
|
|
1821
|
+
`requested=${result.routing.requested} detected=${result.routing.detectedIntent} queried=${result.routing.queried.join(", ") || "none"}`,
|
|
1822
|
+
result.routing.reason,
|
|
1823
|
+
""
|
|
1824
|
+
];
|
|
1825
|
+
if (result.timeWindow) {
|
|
1826
|
+
lines.push("Resolved Time Window");
|
|
1827
|
+
lines.push(`${result.timeWindow.start} -> ${result.timeWindow.end} (${result.timeWindow.timezone}) from ${JSON.stringify(result.timeWindow.resolvedFrom)}`);
|
|
1828
|
+
lines.push("");
|
|
1829
|
+
}
|
|
1830
|
+
if (result.asOf) {
|
|
1831
|
+
lines.push("As Of");
|
|
1832
|
+
lines.push(result.asOf);
|
|
1833
|
+
lines.push("");
|
|
1834
|
+
}
|
|
1835
|
+
if (result.routing.queried.includes("procedures") || result.procedure || result.procedureCandidates.length > 0 || result.procedureNotices.length > 0) {
|
|
1836
|
+
appendProcedureMatches(lines, result);
|
|
1837
|
+
lines.push("");
|
|
1838
|
+
}
|
|
1839
|
+
const renderEntriesFirst = result.routing.detectedIntent === "historical_state";
|
|
1840
|
+
if (renderEntriesFirst) {
|
|
1841
|
+
appendEntryMatches(lines, result);
|
|
1842
|
+
lines.push("");
|
|
1843
|
+
appendClaimTransitions(lines, result);
|
|
1844
|
+
lines.push("");
|
|
1845
|
+
appendEpisodeMatches(lines, result);
|
|
1846
|
+
} else {
|
|
1847
|
+
appendEpisodeMatches(lines, result);
|
|
1848
|
+
lines.push("");
|
|
1849
|
+
appendEntryMatches(lines, result);
|
|
1850
|
+
lines.push("");
|
|
1851
|
+
appendClaimTransitions(lines, result);
|
|
1852
|
+
}
|
|
1853
|
+
if (result.notices.length > 0) {
|
|
1854
|
+
lines.push("");
|
|
1855
|
+
lines.push("Notices");
|
|
1856
|
+
for (const notice of result.notices) {
|
|
1857
|
+
lines.push(`- ${notice}`);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
return lines.join("\n");
|
|
1861
|
+
}
|
|
1862
|
+
function appendProcedureMatches(lines, result) {
|
|
1863
|
+
lines.push("Procedure Matches");
|
|
1864
|
+
if (!result.procedure && result.procedureCandidates.length === 0) {
|
|
1865
|
+
lines.push("None.");
|
|
1866
|
+
} else {
|
|
1867
|
+
if (result.procedure) {
|
|
1868
|
+
appendCanonicalProcedure(lines, result.procedure, result.procedureCandidates);
|
|
1869
|
+
} else {
|
|
1870
|
+
lines.push("Canonical procedure: none.");
|
|
1871
|
+
}
|
|
1872
|
+
const additionalCandidates = result.procedureCandidates.filter((candidate) => candidate.procedure.id !== result.procedure?.id);
|
|
1873
|
+
if (additionalCandidates.length > 0) {
|
|
1874
|
+
lines.push("Other Candidates");
|
|
1875
|
+
for (const [index, candidate] of additionalCandidates.entries()) {
|
|
1876
|
+
lines.push(
|
|
1877
|
+
`${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)}`
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
if (result.procedureNotices.length > 0) {
|
|
1883
|
+
lines.push("Procedure Notices");
|
|
1884
|
+
for (const notice of result.procedureNotices) {
|
|
1885
|
+
lines.push(`- ${notice}`);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
function appendEntryMatches(lines, result) {
|
|
1890
|
+
lines.push("Entry Matches");
|
|
1891
|
+
if (result.projectedEntries.length === 0) {
|
|
1892
|
+
lines.push("None.");
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
for (const [familyIndex, family] of result.entryFamilies.entries()) {
|
|
1896
|
+
lines.push(
|
|
1897
|
+
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}`
|
|
1898
|
+
);
|
|
1899
|
+
for (const [entryIndex, entry] of family.entries.entries()) {
|
|
1900
|
+
lines.push(
|
|
1901
|
+
` ${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)}`
|
|
1902
|
+
);
|
|
1903
|
+
lines.push(` ${truncate2(entry.recall.entry.content, 220)}`);
|
|
1904
|
+
lines.push(` freshness=${entry.freshness.label}`);
|
|
1905
|
+
const provenance = formatProjectedEntryProvenance(entry);
|
|
1906
|
+
if (provenance) {
|
|
1907
|
+
lines.push(` provenance=${provenance}`);
|
|
1908
|
+
}
|
|
1909
|
+
lines.push(` why_surfaced=${entry.whySurfaced.summary}`);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
function appendEpisodeMatches(lines, result) {
|
|
1914
|
+
lines.push("Episode Matches");
|
|
1915
|
+
if (result.episodes.length === 0) {
|
|
1916
|
+
lines.push("None.");
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
for (const [index, episode] of result.episodes.entries()) {
|
|
1920
|
+
lines.push(
|
|
1921
|
+
`${index + 1}. ${episode.episode.id} | ${episode.episode.source} | ${episode.episode.startedAt} -> ${episode.episode.endedAt ?? episode.episode.startedAt} | score ${episode.score.toFixed(2)}`
|
|
1922
|
+
);
|
|
1923
|
+
lines.push(` ${index < 3 ? episode.episode.summary.trim() : truncate2(episode.episode.summary.trim(), 220)}`);
|
|
1924
|
+
lines.push(` why_matched=${describeEpisodeMatch(episode)}`);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
function appendCanonicalProcedure(lines, procedure, candidates) {
|
|
1928
|
+
const leadCandidate = candidates.find((candidate) => candidate.procedure.id === procedure.id);
|
|
1929
|
+
lines.push(
|
|
1930
|
+
leadCandidate ? `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title} | score ${leadCandidate.score.toFixed(2)}` : `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title}`
|
|
1931
|
+
);
|
|
1932
|
+
lines.push(` goal=${procedure.goal}`);
|
|
1933
|
+
appendLabeledList(lines, "when_to_use", procedure.when_to_use);
|
|
1934
|
+
appendLabeledList(lines, "when_not_to_use", procedure.when_not_to_use);
|
|
1935
|
+
appendLabeledList(lines, "prerequisites", procedure.prerequisites);
|
|
1936
|
+
lines.push(" steps");
|
|
1937
|
+
for (const [index, step] of procedure.steps.entries()) {
|
|
1938
|
+
lines.push(` ${index + 1}. [${step.kind}] ${step.instruction}`);
|
|
1939
|
+
const stepDetails = formatProcedureStepDetails(step);
|
|
1940
|
+
if (stepDetails.length > 0) {
|
|
1941
|
+
for (const detail of stepDetails) {
|
|
1942
|
+
lines.push(` ${detail}`);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
appendLabeledList(lines, "verification", procedure.verification);
|
|
1947
|
+
appendLabeledList(lines, "failure_modes", procedure.failure_modes);
|
|
1948
|
+
lines.push(" sources");
|
|
1949
|
+
for (const source of procedure.sources) {
|
|
1950
|
+
lines.push(` - ${formatProcedureSource(source)}`);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
function appendClaimTransitions(lines, result) {
|
|
1954
|
+
lines.push("Claim Transitions");
|
|
1955
|
+
if (result.claimTransitions.length === 0) {
|
|
1956
|
+
lines.push("None.");
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
for (const [index, transition] of result.claimTransitions.entries()) {
|
|
1960
|
+
lines.push(
|
|
1961
|
+
`${index + 1}. family=${transition.claimKey ?? transition.familyKey} | slot_policy=${transition.slotPolicy}${transition.currentEntryId ? ` | current=${transition.currentEntryId}` : ""}${transition.priorEntryId ? ` | prior=${transition.priorEntryId}` : ""}`
|
|
1962
|
+
);
|
|
1963
|
+
lines.push(` ${transition.summary}`);
|
|
1964
|
+
if (transition.episodeContext) {
|
|
1965
|
+
lines.push(
|
|
1966
|
+
` episode=${transition.episodeContext.episodeId} | ${transition.episodeContext.startedAt} -> ${transition.episodeContext.endedAt ?? transition.episodeContext.startedAt}`
|
|
1967
|
+
);
|
|
1968
|
+
lines.push(` ${truncate2(transition.episodeContext.summary.trim(), 220)}`);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
function appendLabeledList(lines, label, values) {
|
|
1973
|
+
lines.push(` ${label}`);
|
|
1974
|
+
if (values.length === 0) {
|
|
1975
|
+
lines.push(" - none");
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
for (const value of values) {
|
|
1979
|
+
lines.push(` - ${value}`);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
function formatProcedureStepDetails(step) {
|
|
1983
|
+
switch (step.kind) {
|
|
1984
|
+
case "run_command":
|
|
1985
|
+
return [`command=${step.command}`];
|
|
1986
|
+
case "read_reference":
|
|
1987
|
+
return [`ref=${formatProcedureSource(step.ref)}`];
|
|
1988
|
+
case "inspect_state":
|
|
1989
|
+
return [step.target ? `target=${step.target}` : void 0, step.query ? `query=${step.query}` : void 0].filter(
|
|
1990
|
+
(value) => value !== void 0
|
|
1991
|
+
);
|
|
1992
|
+
case "edit_file":
|
|
1993
|
+
return [`path=${step.path}`, `edit=${step.edit}`];
|
|
1994
|
+
case "ask_user":
|
|
1995
|
+
return [`prompt=${step.prompt}`];
|
|
1996
|
+
case "invoke_tool":
|
|
1997
|
+
return [step.tool ? `tool=${step.tool}` : void 0, step.arguments ? `arguments=${JSON.stringify(step.arguments)}` : void 0].filter(
|
|
1998
|
+
(value) => value !== void 0
|
|
1999
|
+
);
|
|
2000
|
+
case "verify":
|
|
2001
|
+
return step.checks.map((check) => `check=${check}`);
|
|
2002
|
+
default:
|
|
2003
|
+
return [];
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
function formatProcedureSource(source) {
|
|
2007
|
+
const parts = [source.kind, source.label, source.path, source.locator].filter((value) => Boolean(value && value.length > 0));
|
|
2008
|
+
return parts.join(" | ");
|
|
2009
|
+
}
|
|
2010
|
+
function describeEpisodeMatch(result) {
|
|
2011
|
+
if (result.scores.semantic > 0 && result.scores.temporal > 0) {
|
|
2012
|
+
return "Semantic match within the resolved time window.";
|
|
2013
|
+
}
|
|
2014
|
+
if (result.scores.semantic > 0) {
|
|
2015
|
+
return "Semantic match to the episode summary.";
|
|
2016
|
+
}
|
|
2017
|
+
if (result.scores.temporal > 0) {
|
|
2018
|
+
return "Session overlaps the resolved time window.";
|
|
2019
|
+
}
|
|
2020
|
+
return "Matched episodic recall ranking.";
|
|
2021
|
+
}
|
|
2022
|
+
function formatClaimStatus(status) {
|
|
2023
|
+
return status === "no_key" ? "no-key" : status;
|
|
2024
|
+
}
|
|
2025
|
+
function formatProjectedEntryProvenance(entry) {
|
|
2026
|
+
const parts = [
|
|
2027
|
+
entry.provenance.supersededById ? `superseded_by=${entry.provenance.supersededById}` : void 0,
|
|
2028
|
+
entry.provenance.supersessionKind ? `kind=${entry.provenance.supersessionKind}` : void 0,
|
|
2029
|
+
entry.provenance.supersessionReason ? `reason=${truncate2(entry.provenance.supersessionReason, 120)}` : void 0,
|
|
2030
|
+
entry.provenance.supportSourceKind ? `support=${entry.provenance.supportSourceKind}` : void 0,
|
|
2031
|
+
entry.provenance.supportMode ? `support_mode=${entry.provenance.supportMode}` : void 0,
|
|
2032
|
+
entry.provenance.supportObservedAt ? `observed=${entry.provenance.supportObservedAt}` : void 0,
|
|
2033
|
+
entry.provenance.supportLocator ? `locator=${truncate2(entry.provenance.supportLocator, 120)}` : void 0
|
|
2034
|
+
].filter((value) => value !== void 0);
|
|
2035
|
+
return parts.join(" | ");
|
|
2036
|
+
}
|
|
2037
|
+
function truncate2(value, maxChars) {
|
|
2038
|
+
if (value.length <= maxChars) {
|
|
2039
|
+
return value;
|
|
2040
|
+
}
|
|
2041
|
+
return `${value.slice(0, maxChars - 3).trimEnd()}...`;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
// src/adapters/shared/entry-tools.ts
|
|
2045
|
+
var ENTRY_TYPE_DESCRIPTION = "Knowledge type to store. Use fact for durable truth about a person, system, place, or how something works. Use decision for a standing rule, constraint, policy, or chosen approach future sessions should follow - not a progress update or completed action. Use preference for what someone likes, wants, values, or wants avoided. Use lesson for a non-obvious takeaway learned from experience that should change future behavior. Use milestone for a rare one-time event with durable future significance - not ordinary execution progress. Use relationship for a meaningful durable connection between people, groups, or systems.";
|
|
2046
|
+
var EXPIRY_DESCRIPTION = "Lifetime bucket: core (always injected at session start, use sparingly), permanent (durable and recalled on demand), or temporary (short-horizon).";
|
|
2047
|
+
var UPDATE_EXPIRY_DESCRIPTION = `${EXPIRY_DESCRIPTION} Accepted values: ${EXPIRY_LEVELS.join(", ")}.`;
|
|
2048
|
+
var RECALL_MODES = ["auto", "entries", "episodes", "procedures"];
|
|
2049
|
+
function asRecord(value) {
|
|
2050
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
2051
|
+
return value;
|
|
2052
|
+
}
|
|
2053
|
+
throw new Error("Tool parameters must be an object.");
|
|
2054
|
+
}
|
|
2055
|
+
function parseExpiry(value) {
|
|
2056
|
+
if (value === void 0) {
|
|
2057
|
+
return void 0;
|
|
2058
|
+
}
|
|
2059
|
+
if (EXPIRY_LEVELS.includes(value)) {
|
|
2060
|
+
return value;
|
|
2061
|
+
}
|
|
2062
|
+
throw new Error(`Unsupported expiry "${value}".`);
|
|
2063
|
+
}
|
|
2064
|
+
function parseEntryTypes(values) {
|
|
2065
|
+
return normalizeStringArray(values).map((value) => parseEntryType(value));
|
|
2066
|
+
}
|
|
2067
|
+
function parseEntryType(value) {
|
|
2068
|
+
if (ENTRY_TYPES.includes(value)) {
|
|
2069
|
+
return value;
|
|
2070
|
+
}
|
|
2071
|
+
throw new Error(`Unsupported entry type "${value}".`);
|
|
2072
|
+
}
|
|
2073
|
+
function parseRecallMode(value) {
|
|
2074
|
+
if (value === void 0) {
|
|
2075
|
+
return void 0;
|
|
2076
|
+
}
|
|
2077
|
+
if (RECALL_MODES.includes(value)) {
|
|
2078
|
+
return value;
|
|
2079
|
+
}
|
|
2080
|
+
throw new Error(`Unsupported recall mode "${value}".`);
|
|
2081
|
+
}
|
|
2082
|
+
function normalizeStringArray(values) {
|
|
2083
|
+
if (!values) {
|
|
2084
|
+
return [];
|
|
2085
|
+
}
|
|
2086
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));
|
|
2087
|
+
}
|
|
2088
|
+
function formatTargetSelector(id, subject, last) {
|
|
2089
|
+
if (last === true) {
|
|
2090
|
+
return "last";
|
|
2091
|
+
}
|
|
2092
|
+
if (id) {
|
|
2093
|
+
return `id:${JSON.stringify(id)}`;
|
|
2094
|
+
}
|
|
2095
|
+
if (subject) {
|
|
2096
|
+
return `subject:${JSON.stringify(subject)}`;
|
|
2097
|
+
}
|
|
2098
|
+
return "unknown";
|
|
2099
|
+
}
|
|
2100
|
+
function sanitizeUpdateToolParams(params) {
|
|
2101
|
+
return {
|
|
2102
|
+
...params.id ? { id: params.id } : {},
|
|
2103
|
+
...params.subject ? { subject: params.subject } : {},
|
|
2104
|
+
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
2105
|
+
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
2106
|
+
...params.claimKey !== void 0 ? { hasClaimKey: true } : {},
|
|
2107
|
+
...params.validFrom !== void 0 ? { hasValidFrom: true } : {},
|
|
2108
|
+
...params.validTo !== void 0 ? { hasValidTo: true } : {}
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
// src/adapters/shared/resolve-target.ts
|
|
2113
|
+
async function resolveTargetEntry(ports, params, options = {}) {
|
|
2114
|
+
const id = readOptionalStringParam(params, "id");
|
|
2115
|
+
const subject = readOptionalStringParam(params, "subject");
|
|
2116
|
+
const last = options.allowLast ? readBooleanParam(params, "last") : void 0;
|
|
2117
|
+
const selectorCount = (id ? 1 : 0) + (subject ? 1 : 0) + (last === true ? 1 : 0);
|
|
2118
|
+
const selectorDescription = options.allowLast ? "id, subject, or last" : "id or subject";
|
|
2119
|
+
if (selectorCount !== 1) {
|
|
2120
|
+
throw new Error(`Provide exactly one target selector: ${selectorDescription}.`);
|
|
2121
|
+
}
|
|
2122
|
+
if (last) {
|
|
2123
|
+
const entry2 = await ports.findMostRecentEntry();
|
|
2124
|
+
if (!entry2) {
|
|
2125
|
+
throw new Error("No agenr entries exist yet.");
|
|
2126
|
+
}
|
|
2127
|
+
return entry2;
|
|
2128
|
+
}
|
|
2129
|
+
if (id) {
|
|
2130
|
+
const entry2 = await ports.getEntryById(id);
|
|
2131
|
+
if (!entry2) {
|
|
2132
|
+
throw new Error(`No agenr entry found for id ${id}.`);
|
|
2133
|
+
}
|
|
2134
|
+
return entry2;
|
|
2135
|
+
}
|
|
2136
|
+
const entry = await ports.findEntryBySubject(subject ?? "");
|
|
2137
|
+
if (!entry) {
|
|
2138
|
+
throw new Error(`No agenr entry found for subject "${subject}".`);
|
|
2139
|
+
}
|
|
2140
|
+
return entry;
|
|
2141
|
+
}
|
|
2142
|
+
function readBooleanParam(params, key) {
|
|
2143
|
+
const value = params[key];
|
|
2144
|
+
if (value === void 0) {
|
|
2145
|
+
return void 0;
|
|
2146
|
+
}
|
|
2147
|
+
if (typeof value === "boolean") {
|
|
2148
|
+
return value;
|
|
2149
|
+
}
|
|
2150
|
+
throw new Error(`${key} must be a boolean.`);
|
|
2151
|
+
}
|
|
2152
|
+
function readOptionalStringParam(params, key) {
|
|
2153
|
+
const value = params[key];
|
|
2154
|
+
if (value === void 0 || value === null) {
|
|
2155
|
+
return void 0;
|
|
2156
|
+
}
|
|
2157
|
+
if (typeof value !== "string") {
|
|
2158
|
+
throw new Error(`${key} must be a string.`);
|
|
2159
|
+
}
|
|
2160
|
+
const normalized = value.trim();
|
|
2161
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// src/adapters/shared/claim-support.ts
|
|
2165
|
+
function buildSessionSourceFile(session, prefix) {
|
|
2166
|
+
const target = session.sessionKey ?? session.sessionId ?? session.agentId ?? "unknown";
|
|
2167
|
+
return `${prefix}:${target}`;
|
|
2168
|
+
}
|
|
2169
|
+
function buildToolCallClaimSupport(session, prefix, toolName, observedAt) {
|
|
2170
|
+
return {
|
|
2171
|
+
claim_support_source_kind: "tool_call",
|
|
2172
|
+
claim_support_locator: `${buildSessionSourceFile(session, prefix)}#${toolName}`,
|
|
2173
|
+
claim_support_observed_at: observedAt,
|
|
2174
|
+
claim_support_mode: "explicit"
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// src/adapters/shared/memory-tools.ts
|
|
2179
|
+
function buildRecallToolServices(services) {
|
|
2180
|
+
return {
|
|
2181
|
+
episodes: services.episodes,
|
|
2182
|
+
procedures: services.procedures,
|
|
2183
|
+
recall: services.recall,
|
|
2184
|
+
embeddingStatus: services.embeddingStatus,
|
|
2185
|
+
embedQuery: services.beforeTurn.embedQuery
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
var STORE_TOOL_PARAMETERS = {
|
|
2189
|
+
type: "object",
|
|
2190
|
+
additionalProperties: false,
|
|
2191
|
+
properties: {
|
|
2192
|
+
type: {
|
|
2193
|
+
type: "string",
|
|
2194
|
+
enum: [...ENTRY_TYPES],
|
|
2195
|
+
description: ENTRY_TYPE_DESCRIPTION
|
|
2196
|
+
},
|
|
2197
|
+
subject: {
|
|
2198
|
+
type: "string",
|
|
2199
|
+
description: "Short subject line future recall can match. Name the durable takeaway, person, system, rule, relationship, or milestone directly."
|
|
2200
|
+
},
|
|
2201
|
+
content: {
|
|
2202
|
+
type: "string",
|
|
2203
|
+
description: "What a fresh session should remember. Store the durable takeaway, not the activity log, canonical record, or transient progress snapshot."
|
|
2204
|
+
},
|
|
2205
|
+
importance: {
|
|
2206
|
+
type: "integer",
|
|
2207
|
+
minimum: 1,
|
|
2208
|
+
maximum: 10,
|
|
2209
|
+
description: "Importance from 1 to 10. Use 7 for normal durable memory, 9 for critical constraints, and 10 only rarely."
|
|
2210
|
+
},
|
|
2211
|
+
expiry: {
|
|
2212
|
+
type: "string",
|
|
2213
|
+
enum: ["core", "permanent", "temporary"],
|
|
2214
|
+
description: EXPIRY_DESCRIPTION
|
|
2215
|
+
},
|
|
2216
|
+
tags: {
|
|
2217
|
+
type: "array",
|
|
2218
|
+
items: { type: "string" },
|
|
2219
|
+
description: "Optional tags for entities, systems, teams, or themes that should improve later recall."
|
|
2220
|
+
},
|
|
2221
|
+
sourceContext: {
|
|
2222
|
+
type: "string",
|
|
2223
|
+
description: "Optional provenance note explaining why this memory was stored or what situation produced it."
|
|
2224
|
+
},
|
|
2225
|
+
supersedes: {
|
|
2226
|
+
type: "string",
|
|
2227
|
+
description: "ID of an entry this replaces. The old entry will be marked as superseded."
|
|
2228
|
+
},
|
|
2229
|
+
claimKey: {
|
|
2230
|
+
type: "string",
|
|
2231
|
+
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.'
|
|
2232
|
+
},
|
|
2233
|
+
validFrom: {
|
|
2234
|
+
type: "string",
|
|
2235
|
+
description: "ISO 8601 timestamp for when this fact became true in the world."
|
|
2236
|
+
},
|
|
2237
|
+
validTo: {
|
|
2238
|
+
type: "string",
|
|
2239
|
+
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
2240
|
+
}
|
|
2241
|
+
},
|
|
2242
|
+
required: ["type", "subject", "content"]
|
|
2243
|
+
};
|
|
2244
|
+
var RECALL_TOOL_PARAMETERS = {
|
|
2245
|
+
type: "object",
|
|
2246
|
+
additionalProperties: false,
|
|
2247
|
+
properties: {
|
|
2248
|
+
query: {
|
|
2249
|
+
type: "string",
|
|
2250
|
+
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'."
|
|
2251
|
+
},
|
|
2252
|
+
mode: {
|
|
2253
|
+
type: "string",
|
|
2254
|
+
enum: [...RECALL_MODES],
|
|
2255
|
+
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."
|
|
2256
|
+
},
|
|
2257
|
+
limit: {
|
|
2258
|
+
type: "integer",
|
|
2259
|
+
minimum: 1,
|
|
2260
|
+
maximum: 10,
|
|
2261
|
+
description: "Maximum results to return. Lower this when you want a tighter shortlist."
|
|
2262
|
+
},
|
|
2263
|
+
threshold: {
|
|
2264
|
+
type: "number",
|
|
2265
|
+
minimum: 0,
|
|
2266
|
+
maximum: 1,
|
|
2267
|
+
description: "Minimum final score from 0 to 1. Raise this when you want fewer, higher-confidence matches."
|
|
2268
|
+
},
|
|
2269
|
+
types: {
|
|
2270
|
+
type: "array",
|
|
2271
|
+
items: {
|
|
2272
|
+
type: "string",
|
|
2273
|
+
enum: [...ENTRY_TYPES]
|
|
2274
|
+
},
|
|
2275
|
+
description: "Optional knowledge types to filter by, such as decision, preference, lesson, fact, milestone, or relationship."
|
|
2276
|
+
},
|
|
2277
|
+
tags: {
|
|
2278
|
+
type: "array",
|
|
2279
|
+
items: { type: "string" },
|
|
2280
|
+
description: "Optional tags to filter by once you already know the relevant entity, system, or theme."
|
|
2281
|
+
},
|
|
2282
|
+
asOf: {
|
|
2283
|
+
type: "string",
|
|
2284
|
+
description: "Optional reference time for current-vs-prior resolution. Supports ISO timestamps and natural-language date phrases."
|
|
2285
|
+
}
|
|
2286
|
+
},
|
|
2287
|
+
required: ["query"]
|
|
2288
|
+
};
|
|
2289
|
+
var UPDATE_TOOL_PARAMETERS = {
|
|
2290
|
+
type: "object",
|
|
2291
|
+
additionalProperties: false,
|
|
2292
|
+
properties: {
|
|
2293
|
+
id: {
|
|
2294
|
+
type: "string",
|
|
2295
|
+
description: "Entry id to update. Provide exactly one of id or subject."
|
|
2296
|
+
},
|
|
2297
|
+
subject: {
|
|
2298
|
+
type: "string",
|
|
2299
|
+
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."
|
|
2300
|
+
},
|
|
2301
|
+
importance: {
|
|
2302
|
+
type: "integer",
|
|
2303
|
+
description: "New importance from 1 to 10. Use 7 for normal durable memory and reserve 9 to 10 for rare critical entries."
|
|
2304
|
+
},
|
|
2305
|
+
expiry: {
|
|
2306
|
+
type: "string",
|
|
2307
|
+
description: UPDATE_EXPIRY_DESCRIPTION
|
|
2308
|
+
},
|
|
2309
|
+
claimKey: {
|
|
2310
|
+
type: "string",
|
|
2311
|
+
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.'
|
|
2312
|
+
},
|
|
2313
|
+
validFrom: {
|
|
2314
|
+
type: "string",
|
|
2315
|
+
description: "ISO 8601 timestamp for when this fact became true."
|
|
2316
|
+
},
|
|
2317
|
+
validTo: {
|
|
2318
|
+
type: "string",
|
|
2319
|
+
description: "ISO 8601 timestamp for when this fact stopped being true."
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
};
|
|
2323
|
+
function parseStoreToolParams(rawParams, reader) {
|
|
2324
|
+
const params = asRecord(rawParams);
|
|
2325
|
+
const type = parseEntryType(reader.readString(params, "type", { required: true, label: "type" }) ?? "");
|
|
2326
|
+
return {
|
|
2327
|
+
type,
|
|
2328
|
+
subject: reader.readString(params, "subject", { required: true, label: "subject" }) ?? "",
|
|
2329
|
+
content: reader.readString(params, "content", { required: true, label: "content" }) ?? "",
|
|
2330
|
+
importance: reader.readNumber(params, "importance", { integer: true, strict: true }),
|
|
2331
|
+
expiry: parseExpiry(reader.readString(params, "expiry")),
|
|
2332
|
+
tags: normalizeStringArray(reader.readStringArray(params, "tags")),
|
|
2333
|
+
sourceContext: reader.readString(params, "sourceContext"),
|
|
2334
|
+
supersedes: reader.readString(params, "supersedes"),
|
|
2335
|
+
claimKey: reader.readString(params, "claimKey", { trim: false }),
|
|
2336
|
+
validFrom: reader.readString(params, "validFrom"),
|
|
2337
|
+
validTo: reader.readString(params, "validTo")
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
function parseRecallToolParams(rawParams, reader) {
|
|
2341
|
+
const params = asRecord(rawParams);
|
|
2342
|
+
return {
|
|
2343
|
+
query: reader.readString(params, "query", { required: true, label: "query" }) ?? "",
|
|
2344
|
+
mode: parseRecallMode(reader.readString(params, "mode")),
|
|
2345
|
+
limit: reader.readNumber(params, "limit", { integer: true, strict: true }),
|
|
2346
|
+
threshold: reader.readNumber(params, "threshold", { strict: true }),
|
|
2347
|
+
types: parseEntryTypes(reader.readStringArray(params, "types")),
|
|
2348
|
+
tags: normalizeStringArray(reader.readStringArray(params, "tags")),
|
|
2349
|
+
asOf: reader.readString(params, "asOf")
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
function parseUpdateToolParams(rawParams, reader) {
|
|
2353
|
+
const params = asRecord(rawParams);
|
|
2354
|
+
return {
|
|
2355
|
+
id: reader.readString(params, "id"),
|
|
2356
|
+
subject: reader.readString(params, "subject"),
|
|
2357
|
+
importance: reader.readNumber(params, "importance", { integer: true, strict: true }),
|
|
2358
|
+
expiry: parseExpiry(reader.readString(params, "expiry")),
|
|
2359
|
+
claimKeyInput: reader.readString(params, "claimKey", { trim: false }),
|
|
2360
|
+
validFrom: reader.readString(params, "validFrom"),
|
|
2361
|
+
validTo: reader.readString(params, "validTo")
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
async function runStoreMemoryTool(params, services, options) {
|
|
2365
|
+
const result = await storeEntriesDetailed(
|
|
2366
|
+
[
|
|
2367
|
+
{
|
|
2368
|
+
type: params.type,
|
|
2369
|
+
subject: params.subject,
|
|
2370
|
+
content: params.content,
|
|
2371
|
+
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
2372
|
+
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
2373
|
+
...params.tags.length > 0 ? { tags: params.tags } : {},
|
|
2374
|
+
...params.supersedes ? { supersedes: params.supersedes } : {},
|
|
2375
|
+
...params.claimKey ? {
|
|
2376
|
+
claim_key: params.claimKey,
|
|
2377
|
+
claim_key_raw: params.claimKey,
|
|
2378
|
+
...buildToolCallClaimSupport(options.session, options.sourcePrefix, "agenr_store", (/* @__PURE__ */ new Date()).toISOString())
|
|
2379
|
+
} : {},
|
|
2380
|
+
...params.validFrom ? { valid_from: params.validFrom } : {},
|
|
2381
|
+
...params.validTo ? { valid_to: params.validTo } : {},
|
|
2382
|
+
source_file: buildSessionSourceFile(options.session, options.sourcePrefix),
|
|
2383
|
+
source_context: params.sourceContext ?? options.defaultSourceContext,
|
|
2384
|
+
...options.session.project ? { project: options.session.project } : {}
|
|
2385
|
+
}
|
|
2386
|
+
],
|
|
2387
|
+
services.entries,
|
|
2388
|
+
services.embedding,
|
|
2389
|
+
{
|
|
2390
|
+
...services.claimExtraction ? {
|
|
2391
|
+
claimExtraction: {
|
|
2392
|
+
llm: services.claimExtraction.llm,
|
|
2393
|
+
db: services.entries,
|
|
2394
|
+
config: services.claimExtraction.config
|
|
2395
|
+
}
|
|
2396
|
+
} : {},
|
|
2397
|
+
onWarning: options.onWarning
|
|
2398
|
+
}
|
|
2399
|
+
);
|
|
2400
|
+
const storedEntry = await services.memory.findEntryBySubject(params.subject);
|
|
2401
|
+
if (result.stored > 0) {
|
|
2402
|
+
return okOutcome(`Stored "${params.subject}".`, {
|
|
2403
|
+
status: "stored",
|
|
2404
|
+
subject: params.subject,
|
|
2405
|
+
entryId: storedEntry?.id,
|
|
2406
|
+
result,
|
|
2407
|
+
...options.extraDetails
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
if (result.skipped > 0) {
|
|
2411
|
+
return okOutcome(`Skipped "${params.subject}" because an active duplicate already exists.`, {
|
|
2412
|
+
status: "skipped",
|
|
2413
|
+
subject: params.subject,
|
|
2414
|
+
entryId: storedEntry?.id,
|
|
2415
|
+
result,
|
|
2416
|
+
...options.extraDetails
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
return failedOutcome(`Rejected "${params.subject}". Check the supplied type, content, and metadata.`, {
|
|
2420
|
+
status: "failed",
|
|
2421
|
+
subject: params.subject,
|
|
2422
|
+
result,
|
|
2423
|
+
...options.extraDetails
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
async function runRecallMemoryTool(params, services, options = {}) {
|
|
2427
|
+
return runUnifiedRecall(
|
|
2428
|
+
{
|
|
2429
|
+
text: params.query,
|
|
2430
|
+
...params.mode ? { mode: params.mode } : {},
|
|
2431
|
+
...params.limit !== void 0 ? { limit: params.limit } : {},
|
|
2432
|
+
...params.threshold !== void 0 ? { threshold: params.threshold } : {},
|
|
2433
|
+
...params.types.length > 0 ? { types: params.types } : {},
|
|
2434
|
+
...params.tags.length > 0 ? { tags: params.tags } : {},
|
|
2435
|
+
...params.asOf ? { asOf: params.asOf } : {},
|
|
2436
|
+
...options.sessionKey ? { sessionKey: options.sessionKey } : {}
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
database: services.episodes,
|
|
2440
|
+
procedures: services.procedures,
|
|
2441
|
+
recall: services.recall,
|
|
2442
|
+
embeddingAvailable: services.embeddingStatus.available,
|
|
2443
|
+
embeddingError: services.embeddingStatus.error,
|
|
2444
|
+
claimSlotPolicyConfig: options.slotPolicyConfig,
|
|
2445
|
+
debugLog: options.debugLog,
|
|
2446
|
+
embedQuery: services.embedQuery,
|
|
2447
|
+
recallOptions: {
|
|
2448
|
+
slotPolicyConfig: options.slotPolicyConfig
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
async function runUpdateMemoryTool(params, services, options) {
|
|
2454
|
+
const claimSupport = params.claimKeyInput === void 0 ? void 0 : buildToolCallClaimSupport(options.session, options.sourcePrefix, "agenr_update", (/* @__PURE__ */ new Date()).toISOString());
|
|
2455
|
+
const normalizedClaimKeyUpdate = params.claimKeyInput === void 0 ? void 0 : (() => {
|
|
2456
|
+
try {
|
|
2457
|
+
return normalizeManualClaimKeyUpdate({
|
|
2458
|
+
claimKey: params.claimKeyInput,
|
|
2459
|
+
rawClaimKey: params.claimKeyInput,
|
|
2460
|
+
supportSourceKind: claimSupport?.claim_support_source_kind,
|
|
2461
|
+
supportLocator: claimSupport?.claim_support_locator,
|
|
2462
|
+
supportObservedAt: claimSupport?.claim_support_observed_at,
|
|
2463
|
+
supportMode: claimSupport?.claim_support_mode
|
|
2464
|
+
});
|
|
2465
|
+
} catch {
|
|
2466
|
+
throw new Error("claimKey must use canonical entity/attribute format.");
|
|
2467
|
+
}
|
|
2468
|
+
})();
|
|
2469
|
+
const entry = await resolveTargetEntry(
|
|
2470
|
+
{
|
|
2471
|
+
getEntryById: async (entryId) => await services.entries.getEntry(entryId) ?? (await services.memory.getEntryTrace(entryId))?.entry ?? null,
|
|
2472
|
+
findEntryBySubject: async (entrySubject) => services.memory.findEntryBySubject(entrySubject),
|
|
2473
|
+
findMostRecentEntry: async () => services.memory.findMostRecentEntry()
|
|
2474
|
+
},
|
|
2475
|
+
{ id: params.id, subject: params.subject }
|
|
2476
|
+
);
|
|
2477
|
+
if (params.importance === void 0 && params.expiry === void 0 && normalizedClaimKeyUpdate === void 0 && params.validFrom === void 0 && params.validTo === void 0) {
|
|
2478
|
+
throw new Error("Provide at least one update field: importance, expiry, claimKey, validFrom, or validTo.");
|
|
2479
|
+
}
|
|
2480
|
+
const mergedValidity = validateTemporalValidityRange(params.validFrom ?? entry.valid_from, params.validTo ?? entry.valid_to);
|
|
2481
|
+
if (!mergedValidity.ok) {
|
|
2482
|
+
throw new Error(mergedValidity.message);
|
|
2483
|
+
}
|
|
2484
|
+
const normalizedValidFrom = params.validFrom !== void 0 ? mergedValidity.value.validFrom : void 0;
|
|
2485
|
+
const normalizedValidTo = params.validTo !== void 0 ? mergedValidity.value.validTo : void 0;
|
|
2486
|
+
const updated = await services.entries.updateEntry(entry.id, {
|
|
2487
|
+
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
2488
|
+
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
2489
|
+
...normalizedClaimKeyUpdate?.updateFields ?? {},
|
|
2490
|
+
...params.validFrom !== void 0 ? { valid_from: normalizedValidFrom } : {},
|
|
2491
|
+
...params.validTo !== void 0 ? { valid_to: normalizedValidTo } : {}
|
|
2492
|
+
});
|
|
2493
|
+
if (!updated) {
|
|
2494
|
+
return failedOutcome(`Entry ${entry.id} is not active, so it could not be updated.`, {
|
|
2495
|
+
status: "failed",
|
|
2496
|
+
entryId: entry.id,
|
|
2497
|
+
...options.failureDetails
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
return okOutcome(`Updated "${entry.subject}".`, {
|
|
2501
|
+
status: "updated",
|
|
2502
|
+
entryId: entry.id,
|
|
2503
|
+
subject: entry.subject,
|
|
2504
|
+
...params.importance !== void 0 ? { importance: params.importance } : {},
|
|
2505
|
+
...params.expiry !== void 0 ? { expiry: params.expiry } : {},
|
|
2506
|
+
...normalizedClaimKeyUpdate !== void 0 ? { claimKey: normalizedClaimKeyUpdate.claimKey } : {},
|
|
2507
|
+
...params.validFrom !== void 0 ? { validFrom: normalizedValidFrom } : {},
|
|
2508
|
+
...params.validTo !== void 0 ? { validTo: normalizedValidTo } : {},
|
|
2509
|
+
...options.successDetails
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
function okOutcome(text, details) {
|
|
2513
|
+
return { text, details, failed: false };
|
|
2514
|
+
}
|
|
2515
|
+
function failedOutcome(text, details) {
|
|
2516
|
+
return { text, details, failed: true };
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
// src/adapters/shared/bounded-episode-summary.ts
|
|
2520
|
+
var EPISODE_SUMMARY_TIMEOUT_MS = 45e3;
|
|
2521
|
+
var EPISODE_SUMMARY_TIMEOUT_MESSAGE = "Episode summary generation timed out.";
|
|
2522
|
+
var EpisodeSummaryTimeoutError = class extends Error {
|
|
2523
|
+
/**
|
|
2524
|
+
* Creates a timeout error with a stable name for caller-side handling.
|
|
2525
|
+
*/
|
|
2526
|
+
constructor() {
|
|
2527
|
+
super(EPISODE_SUMMARY_TIMEOUT_MESSAGE);
|
|
2528
|
+
this.name = "EpisodeSummaryTimeoutError";
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
async function raceEpisodeSummaryWithinTimeout(task, timeoutMs) {
|
|
2532
|
+
let timeout;
|
|
2533
|
+
try {
|
|
2534
|
+
return await Promise.race([
|
|
2535
|
+
task,
|
|
2536
|
+
new Promise((_, reject) => {
|
|
2537
|
+
timeout = setTimeout(() => reject(new EpisodeSummaryTimeoutError()), timeoutMs);
|
|
2538
|
+
})
|
|
2539
|
+
]);
|
|
2540
|
+
} finally {
|
|
2541
|
+
if (timeout) {
|
|
2542
|
+
clearTimeout(timeout);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
// src/adapters/shared/deadline-aware-episode-summary-llm.ts
|
|
2548
|
+
function createDeadlineAwareEpisodeSummaryLlm(baseLlm, deadlineMs) {
|
|
2549
|
+
const usage = cloneUsageStats(baseLlm.metadata.usage);
|
|
2550
|
+
const completeWithTimeout = async (task) => {
|
|
2551
|
+
usage.calls += 1;
|
|
2552
|
+
const remainingMs = Math.max(0, deadlineMs - Date.now());
|
|
2553
|
+
return raceEpisodeSummaryWithinTimeout(task, remainingMs);
|
|
2554
|
+
};
|
|
2555
|
+
return {
|
|
2556
|
+
complete: async (systemPrompt, userMessage) => completeWithTimeout(baseLlm.complete(systemPrompt, userMessage)),
|
|
2557
|
+
completeJson: async (systemPrompt, userMessage) => completeWithTimeout(baseLlm.completeJson(systemPrompt, userMessage)),
|
|
2558
|
+
metadata: {
|
|
2559
|
+
...baseLlm.metadata,
|
|
2560
|
+
usage
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
function cloneUsageStats(usage) {
|
|
2565
|
+
return { ...usage };
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// src/adapters/shared/bounded-episode-embedding.ts
|
|
2569
|
+
var EPISODE_EMBEDDING_RACE_TIMEOUT = /* @__PURE__ */ Symbol("episode-embedding-race-timeout");
|
|
2570
|
+
var EPISODE_EMBEDDING_MIN_HEADROOM_MS = 5e3;
|
|
2571
|
+
async function embedEpisodeSummaryWithinBudget(params) {
|
|
2572
|
+
const minHeadroomMs = params.minHeadroomMs ?? EPISODE_EMBEDDING_MIN_HEADROOM_MS;
|
|
2573
|
+
if (!params.embeddingAvailable) {
|
|
2574
|
+
params.logger.info(`${params.logContext} reason=embedding_unavailable`);
|
|
2575
|
+
return void 0;
|
|
2576
|
+
}
|
|
2577
|
+
const remainingBudgetMs = params.deadlineMs - Date.now();
|
|
2578
|
+
if (remainingBudgetMs < minHeadroomMs) {
|
|
2579
|
+
params.logger.info(`${params.logContext} reason=budget_tight remainingMs=${Math.max(0, remainingBudgetMs)}`);
|
|
2580
|
+
return void 0;
|
|
2581
|
+
}
|
|
2582
|
+
try {
|
|
2583
|
+
const result = await raceEmbeddingWithTimeout(params.embedding.embed([params.summary]), remainingBudgetMs);
|
|
2584
|
+
if (result === EPISODE_EMBEDDING_RACE_TIMEOUT) {
|
|
2585
|
+
params.logger.info(`${params.logContext} reason=embedding_timeout budgetMs=${remainingBudgetMs}`);
|
|
2586
|
+
return void 0;
|
|
2587
|
+
}
|
|
2588
|
+
const vector = result[0]?.map((value) => Number.isFinite(value) ? value : 0);
|
|
2589
|
+
if (!vector || vector.length === 0) {
|
|
2590
|
+
params.logger.info(`${params.logContext} reason=empty_embedding`);
|
|
2591
|
+
return void 0;
|
|
2592
|
+
}
|
|
2593
|
+
return vector;
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
params.logger.info(`${params.logContext} reason=${formatErrorMessage(error)}`);
|
|
2596
|
+
return void 0;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
async function raceEmbeddingWithTimeout(promise, timeoutMs) {
|
|
2600
|
+
return new Promise((resolve, reject) => {
|
|
2601
|
+
const timeout = setTimeout(() => {
|
|
2602
|
+
resolve(EPISODE_EMBEDDING_RACE_TIMEOUT);
|
|
2603
|
+
}, timeoutMs);
|
|
2604
|
+
promise.then(
|
|
2605
|
+
(value) => {
|
|
2606
|
+
clearTimeout(timeout);
|
|
2607
|
+
resolve(value);
|
|
2608
|
+
},
|
|
2609
|
+
(error) => {
|
|
2610
|
+
clearTimeout(timeout);
|
|
2611
|
+
reject(error);
|
|
2612
|
+
}
|
|
2613
|
+
);
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/adapters/shared/bounded-episode-ingest-log.ts
|
|
2618
|
+
function logBoundedEpisodeTranscriptIngestOutcome(params) {
|
|
2619
|
+
const prefix = `[agenr] ${params.actionLabel}`;
|
|
2620
|
+
const fileField = params.fileField ?? "file";
|
|
2621
|
+
const fileRef = `${fileField}=${params.filePath}`;
|
|
2622
|
+
const countField = params.shortCountField ?? "materialTurns";
|
|
2623
|
+
if (params.result.kind === "skipped") {
|
|
2624
|
+
const skipped = params.result.skipped;
|
|
2625
|
+
if (skipped.reason === "skipped_exists") {
|
|
2626
|
+
params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=already_exists episode=${skipped.existingEpisode?.id}`);
|
|
2627
|
+
return false;
|
|
2628
|
+
}
|
|
2629
|
+
if (skipped.reason === "skipped_short") {
|
|
2630
|
+
params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=too_short ${countField}=${skipped.messageCount}`);
|
|
2631
|
+
return false;
|
|
2632
|
+
}
|
|
2633
|
+
params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=${skipped.reason} ${countField}=${skipped.messageCount}`);
|
|
2634
|
+
return false;
|
|
2635
|
+
}
|
|
2636
|
+
if (params.result.kind === "invalid") {
|
|
2637
|
+
params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=invalid_transcript ${countField}=${params.result.invalid.messageCount}`);
|
|
2638
|
+
return false;
|
|
2639
|
+
}
|
|
2640
|
+
const session = params.result.session;
|
|
2641
|
+
if (session.action === "failed") {
|
|
2642
|
+
if (session.error === EPISODE_SUMMARY_TIMEOUT_MESSAGE) {
|
|
2643
|
+
params.logger.info(`${prefix} timed_out for ${params.context} ${fileRef} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`);
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
if (session.error === "invalid_response" && params.failureModelRef) {
|
|
2647
|
+
params.logger.info(`${prefix} failed for ${params.context} ${fileRef} reason=invalid_response model=${params.failureModelRef}`);
|
|
2648
|
+
return false;
|
|
2649
|
+
}
|
|
2650
|
+
params.logger.info(`${prefix} failed for ${params.context} ${fileRef} reason=${session.error ?? "unknown"}`);
|
|
2651
|
+
return false;
|
|
2652
|
+
}
|
|
2653
|
+
params.logger.info(`${prefix} ${session.action} for ${params.context} ${fileRef} episode=${session.episodeId}`);
|
|
2654
|
+
return true;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
// src/adapters/shared/bounded-episode-write.ts
|
|
2658
|
+
async function writeBoundedSingleTranscriptEpisode(params) {
|
|
2659
|
+
const fileField = params.fileField ?? "file";
|
|
2660
|
+
const fileRef = `${fileField}=${params.filePath}`;
|
|
2661
|
+
try {
|
|
2662
|
+
const ingestResult = await raceEpisodeSummaryWithinTimeout(
|
|
2663
|
+
ingestEpisodeTranscript(params.filePath, params.ports, params.ingestOptions),
|
|
2664
|
+
EPISODE_SUMMARY_TIMEOUT_MS
|
|
2665
|
+
);
|
|
2666
|
+
logBoundedEpisodeTranscriptIngestOutcome({
|
|
2667
|
+
logger: params.logger,
|
|
2668
|
+
actionLabel: params.actionLabel,
|
|
2669
|
+
context: params.context,
|
|
2670
|
+
filePath: params.filePath,
|
|
2671
|
+
...params.fileField ? { fileField: params.fileField } : {},
|
|
2672
|
+
...params.shortCountField ? { shortCountField: params.shortCountField } : {},
|
|
2673
|
+
result: ingestResult,
|
|
2674
|
+
...params.failureModelRef ? { failureModelRef: params.failureModelRef } : {}
|
|
2675
|
+
});
|
|
2676
|
+
} catch (error) {
|
|
2677
|
+
if (error instanceof EpisodeSummaryTimeoutError) {
|
|
2678
|
+
params.logger.info(`[agenr] ${params.actionLabel} timed_out for ${params.context} ${fileRef} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`);
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
const message = formatErrorMessage(error);
|
|
2682
|
+
if (params.unexpectedFailureLevel === "info") {
|
|
2683
|
+
params.logger.info(`[agenr] ${params.actionLabel} failed for ${params.context} ${fileRef} reason=${message}`);
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2686
|
+
params.logger.warn(`[agenr] ${params.actionLabel} failed for ${params.context} ${fileRef} reason=${message}`);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
export {
|
|
2691
|
+
formatUnifiedRecallResults,
|
|
2692
|
+
formatErrorMessage,
|
|
2693
|
+
asRecord,
|
|
2694
|
+
formatTargetSelector,
|
|
2695
|
+
sanitizeUpdateToolParams,
|
|
2696
|
+
resolveTargetEntry,
|
|
2697
|
+
readBooleanParam,
|
|
2698
|
+
buildRecallToolServices,
|
|
2699
|
+
STORE_TOOL_PARAMETERS,
|
|
2700
|
+
RECALL_TOOL_PARAMETERS,
|
|
2701
|
+
UPDATE_TOOL_PARAMETERS,
|
|
2702
|
+
parseStoreToolParams,
|
|
2703
|
+
parseRecallToolParams,
|
|
2704
|
+
parseUpdateToolParams,
|
|
2705
|
+
runStoreMemoryTool,
|
|
2706
|
+
runRecallMemoryTool,
|
|
2707
|
+
runUpdateMemoryTool,
|
|
2708
|
+
normalizeOptionalBoolean,
|
|
2709
|
+
normalizeOptionalPositiveInteger,
|
|
2710
|
+
normalizePluginInjectionMemoryPolicyConfig,
|
|
2711
|
+
resolveSessionIdentityKey,
|
|
2712
|
+
createSessionStartTracker,
|
|
2713
|
+
normalizeOptionalString,
|
|
2714
|
+
runSessionStart,
|
|
2715
|
+
EPISODE_SUMMARY_TIMEOUT_MS,
|
|
2716
|
+
createDeadlineAwareEpisodeSummaryLlm,
|
|
2717
|
+
embedEpisodeSummaryWithinBudget,
|
|
2718
|
+
writeBoundedSingleTranscriptEpisode,
|
|
2719
|
+
extractRecentTurnsFromMessages,
|
|
2720
|
+
normalizePromptText,
|
|
2721
|
+
resolveSessionStartPolicy,
|
|
2722
|
+
resolveBeforeTurnPolicy,
|
|
2723
|
+
resolveWorkingContextGate,
|
|
2724
|
+
formatAgenrSessionStartRecall,
|
|
2725
|
+
buildClaimExtractionRuntime,
|
|
2726
|
+
createClaimExtractionFromAgenrConfig,
|
|
2727
|
+
normalizeEventLimit,
|
|
2728
|
+
normalizeListLimit,
|
|
2729
|
+
CLOSE_EVENT_HISTORY_LIMIT,
|
|
2730
|
+
SESSION_LINEAGE_REASONS,
|
|
2731
|
+
isModelVisibleOperationType,
|
|
2732
|
+
isTrustedHostOnlyWorkingOperation,
|
|
2733
|
+
WORKING_CANDIDATE_PROMOTION_STATUSES,
|
|
2734
|
+
isMutableWorkingSetStatus,
|
|
2735
|
+
isCloseManagedStatus,
|
|
2736
|
+
isTrustedHostMutationSource,
|
|
2737
|
+
composeHostPluginServices
|
|
2738
|
+
};
|