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