agenr 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,70 +1,353 @@
1
1
  import {
2
- CLOSE_EVENT_HISTORY_LIMIT,
3
- EPISODE_SUMMARY_TIMEOUT_MS,
4
- FETCH_TOOL_PARAMETERS,
5
- RECALL_TOOL_PARAMETERS,
6
- SESSION_LINEAGE_REASONS,
7
- STORE_TOOL_PARAMETERS,
8
- UPDATE_TOOL_PARAMETERS,
9
- WORKING_CANDIDATE_PROMOTION_STATUSES,
10
- buildRecallToolServices,
11
- composeHostPluginServices,
12
- createClaimExtractionFromAgenrConfig,
13
- createDeadlineAwareEpisodeSummaryLlm,
14
- createSessionStartTracker,
15
- embedEpisodeSummaryWithinBudget,
16
- extractRecentTurnsFromMessages,
17
- formatAgenrSessionStartRecall,
18
- formatUnifiedRecallResults,
19
- isCloseManagedStatus,
20
- isModelVisibleOperationType,
21
- isMutableWorkingSetStatus,
22
- isTrustedHostMutationSource,
23
- isTrustedHostOnlyWorkingOperation,
24
- normalizeEventLimit,
25
- normalizeListLimit,
26
- normalizeOptionalString,
27
- normalizePluginInjectionMemoryPolicyConfig,
28
- normalizePromptText,
29
- parseFetchToolParams,
30
- parseRecallToolParams,
31
- parseStoreToolParams,
32
- parseUpdateToolParams,
33
- resolveBeforeTurnPolicy,
34
- resolveSessionIdentityKey,
35
- resolveSessionStartPolicy,
36
- resolveWorkingContextGate,
37
- runFetchMemoryTool,
38
- runRecallMemoryTool,
39
- runSessionStart,
40
- runStoreMemoryTool,
41
- runUpdateMemoryTool,
42
- writeBoundedSingleTranscriptEpisode
43
- } from "../../chunk-E2DHUFZK.js";
44
- import {
2
+ ENTRY_TYPE_DESCRIPTION,
3
+ EXPIRY_DESCRIPTION,
4
+ RECALL_MODES,
5
+ UPDATE_EXPIRY_DESCRIPTION,
45
6
  asRecord,
7
+ buildEntryMemoryResolverPorts,
8
+ createMemoryRepository,
46
9
  createSingleTranscriptDiscoveryPort,
47
10
  formatErrorMessage,
48
11
  formatTargetSelector,
12
+ ingestEpisodeTranscript,
13
+ normalizeStringArray,
14
+ parseEntryType,
15
+ parseEntryTypes,
16
+ parseExpiry,
17
+ parseRecallMode,
18
+ resolveTargetEntry,
49
19
  sanitizeFetchToolParams,
50
- sanitizeUpdateToolParams
51
- } from "../../chunk-EEEL53X4.js";
20
+ sanitizeUpdateToolParams,
21
+ storeEntriesDetailed
22
+ } from "../../chunk-M5M65AYP.js";
52
23
  import {
24
+ containsAgenrMemoryContext,
53
25
  formatAgenrBeforeTurnRecall,
54
- runBeforeTurn
55
- } from "../../chunk-V5CDMHRN.js";
26
+ formatInjectionEntryBodyLines,
27
+ formatInjectionEntryHeader,
28
+ runBeforeTurn,
29
+ stripAgenrMemoryContext,
30
+ wrapAgenrMemoryContext
31
+ } from "../../chunk-KH52KJSJ.js";
56
32
  import {
57
33
  AGENR_FEATURE_FLAG_KEYS,
58
34
  DEFAULT_AGENR_FEATURE_FLAGS,
35
+ ENTRY_PREVIEW_MAX_CHARS,
36
+ ENTRY_SELECT_COLUMNS,
37
+ ENTRY_TYPES,
38
+ assertEntryFetchableContentLength,
39
+ attachCrossEncoderPort,
40
+ buildActiveEntryClause,
41
+ buildEntryRecallPreview,
42
+ buildFetchToolDetails,
59
43
  buildRecallToolDetails,
44
+ createDatabase,
45
+ createEmbeddingClient,
60
46
  createLlmClient,
47
+ createOpenAICrossEncoder,
48
+ createRecallAdapter,
49
+ formatFetchedEntryText,
61
50
  isRecord,
51
+ mapEntryRow,
52
+ normalizeManualClaimKeyUpdate,
53
+ projectClaimCentricRecallEntry,
54
+ readConfig,
55
+ readNumber,
62
56
  readOptionalFiniteNumber,
57
+ readOptionalString,
63
58
  readOptionalTrimmedString,
59
+ readRequiredString,
60
+ recallResultHasTruncatedEntryPreviews,
61
+ resolveClaimExtractionConfig,
62
+ resolveConfigPath,
63
+ resolveCrossEncoderApiKey,
64
+ resolveDbPath,
65
+ resolveEmbeddingApiKey,
66
+ resolveEmbeddingModel,
64
67
  resolveLlmApiKey,
65
- resolveModel
66
- } from "../../chunk-NNO2V4GH.js";
67
- import "../../chunk-5LADPJ4C.js";
68
+ resolveModel,
69
+ runUnifiedRecall,
70
+ truncate,
71
+ validateTemporalValidityRange
72
+ } from "../../chunk-Z5X7T4QZ.js";
73
+ import {
74
+ recall
75
+ } from "../../chunk-5LADPJ4C.js";
76
+
77
+ // src/app/plugin-runtime/session-tracking.ts
78
+ function resolveSessionIdentityKey(sessionId, sessionKey) {
79
+ const normalizedSessionId = sessionId?.trim();
80
+ if (normalizedSessionId) {
81
+ return `session:${normalizedSessionId}`;
82
+ }
83
+ const normalizedSessionKey = sessionKey?.trim();
84
+ if (normalizedSessionKey) {
85
+ return `key:${normalizedSessionKey}`;
86
+ }
87
+ return void 0;
88
+ }
89
+ function createSessionStartTracker() {
90
+ const seenSessionIdentities = /* @__PURE__ */ new Set();
91
+ const resumedFromBySessionId = /* @__PURE__ */ new Map();
92
+ return {
93
+ consume(sessionId, sessionKey) {
94
+ const identityKey = resolveSessionIdentityKey(sessionId, sessionKey);
95
+ if (!identityKey) {
96
+ return {
97
+ isFirst: false,
98
+ activeCount: seenSessionIdentities.size
99
+ };
100
+ }
101
+ if (seenSessionIdentities.has(identityKey)) {
102
+ return {
103
+ isFirst: false,
104
+ activeCount: seenSessionIdentities.size
105
+ };
106
+ }
107
+ seenSessionIdentities.add(identityKey);
108
+ return {
109
+ isFirst: true,
110
+ activeCount: seenSessionIdentities.size
111
+ };
112
+ },
113
+ rememberSessionStart(sessionId, _sessionKey, resumedFrom) {
114
+ const normalizedSessionId = sessionId?.trim();
115
+ const normalizedResumedFrom = resumedFrom?.trim();
116
+ if (!normalizedSessionId || !normalizedResumedFrom) {
117
+ return;
118
+ }
119
+ resumedFromBySessionId.set(normalizedSessionId, normalizedResumedFrom);
120
+ },
121
+ getResumedFrom(sessionId) {
122
+ const normalizedSessionId = sessionId?.trim();
123
+ return normalizedSessionId ? resumedFromBySessionId.get(normalizedSessionId) : void 0;
124
+ }
125
+ };
126
+ }
127
+
128
+ // src/adapters/shared/plugin-config-validators.ts
129
+ function normalizeOptionalBoolean(value, label, errors) {
130
+ if (value === void 0) {
131
+ return void 0;
132
+ }
133
+ if (typeof value !== "boolean") {
134
+ errors.push(`${label} must be a boolean when provided`);
135
+ return void 0;
136
+ }
137
+ return value;
138
+ }
139
+ function normalizeOptionalPositiveInteger(value, label, errors) {
140
+ if (value === void 0) {
141
+ return void 0;
142
+ }
143
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
144
+ errors.push(`${label} must be a positive integer when provided`);
145
+ return void 0;
146
+ }
147
+ return value;
148
+ }
149
+ function normalizeOptionalUnitInterval(value, label, errors) {
150
+ if (value === void 0) {
151
+ return void 0;
152
+ }
153
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
154
+ errors.push(`${label} must be a number between 0 and 1 when provided`);
155
+ return void 0;
156
+ }
157
+ return value;
158
+ }
159
+
160
+ // src/adapters/shared/plugin-memory-policy-config.ts
161
+ function normalizePluginInjectionMemoryPolicyConfig(value) {
162
+ if (value === void 0) {
163
+ return { ok: true, value: void 0 };
164
+ }
165
+ if (!isRecord2(value)) {
166
+ return { ok: false, errors: ["memoryPolicy must be an object when provided"] };
167
+ }
168
+ const errors = [];
169
+ const slotPoliciesResult = normalizeClaimSlotPolicyConfig(value.slotPolicies);
170
+ if (!slotPoliciesResult.ok) {
171
+ errors.push(...slotPoliciesResult.errors);
172
+ }
173
+ const sessionStartResult = normalizeSessionStartMemoryPolicyConfig(value.sessionStart);
174
+ if (!sessionStartResult.ok) {
175
+ errors.push(...sessionStartResult.errors);
176
+ }
177
+ const beforeTurnResult = normalizeBeforeTurnMemoryPolicyConfig(value.beforeTurn);
178
+ if (!beforeTurnResult.ok) {
179
+ errors.push(...beforeTurnResult.errors);
180
+ }
181
+ const workingContextResult = normalizeWorkingContextMemoryPolicyConfig(value.workingContext);
182
+ if (!workingContextResult.ok) {
183
+ errors.push(...workingContextResult.errors);
184
+ }
185
+ const allowedKeys = /* @__PURE__ */ new Set(["slotPolicies", "sessionStart", "beforeTurn", "workingContext"]);
186
+ for (const key of Object.keys(value)) {
187
+ if (!allowedKeys.has(key)) {
188
+ errors.push(`unknown config field: memoryPolicy.${key}`);
189
+ }
190
+ }
191
+ if (errors.length > 0) {
192
+ return { ok: false, errors };
193
+ }
194
+ return {
195
+ ok: true,
196
+ value: slotPoliciesResult.ok && slotPoliciesResult.value || sessionStartResult.ok && sessionStartResult.value || beforeTurnResult.ok && beforeTurnResult.value || workingContextResult.ok && workingContextResult.value ? {
197
+ ...slotPoliciesResult.ok && slotPoliciesResult.value ? { slotPolicies: slotPoliciesResult.value } : {},
198
+ ...sessionStartResult.ok && sessionStartResult.value ? { sessionStart: sessionStartResult.value } : {},
199
+ ...beforeTurnResult.ok && beforeTurnResult.value ? { beforeTurn: beforeTurnResult.value } : {},
200
+ ...workingContextResult.ok && workingContextResult.value ? { workingContext: workingContextResult.value } : {}
201
+ } : void 0
202
+ };
203
+ }
204
+ function normalizeWorkingContextMemoryPolicyConfig(value) {
205
+ if (value === void 0) {
206
+ return { ok: true, value: void 0 };
207
+ }
208
+ if (!isRecord2(value)) {
209
+ return { ok: false, errors: ["memoryPolicy.workingContext must be an object when provided"] };
210
+ }
211
+ const errors = [];
212
+ const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.workingContext.enabled", errors);
213
+ const allowedKeys = /* @__PURE__ */ new Set(["enabled"]);
214
+ for (const key of Object.keys(value)) {
215
+ if (!allowedKeys.has(key)) {
216
+ errors.push(`unknown config field: memoryPolicy.workingContext.${key}`);
217
+ }
218
+ }
219
+ if (errors.length > 0) {
220
+ return { ok: false, errors };
221
+ }
222
+ return {
223
+ ok: true,
224
+ value: enabled !== void 0 ? { enabled } : void 0
225
+ };
226
+ }
227
+ function normalizeSessionStartMemoryPolicyConfig(value) {
228
+ if (value === void 0) {
229
+ return { ok: true, value: void 0 };
230
+ }
231
+ if (!isRecord2(value)) {
232
+ return { ok: false, errors: ["memoryPolicy.sessionStart must be an object when provided"] };
233
+ }
234
+ const errors = [];
235
+ const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.sessionStart.enabled", errors);
236
+ const coreMemory = normalizeOptionalBoolean(value.coreMemory, "memoryPolicy.sessionStart.coreMemory", errors);
237
+ const relevantDurableMemory = normalizeOptionalBoolean(value.relevantDurableMemory, "memoryPolicy.sessionStart.relevantDurableMemory", errors);
238
+ const allowedKeys = /* @__PURE__ */ new Set(["enabled", "coreMemory", "relevantDurableMemory"]);
239
+ for (const key of Object.keys(value)) {
240
+ if (!allowedKeys.has(key)) {
241
+ errors.push(`unknown config field: memoryPolicy.sessionStart.${key}`);
242
+ }
243
+ }
244
+ if (errors.length > 0) {
245
+ return { ok: false, errors };
246
+ }
247
+ return {
248
+ ok: true,
249
+ value: enabled !== void 0 || coreMemory !== void 0 || relevantDurableMemory !== void 0 ? {
250
+ ...enabled !== void 0 ? { enabled } : {},
251
+ ...coreMemory !== void 0 ? { coreMemory } : {},
252
+ ...relevantDurableMemory !== void 0 ? { relevantDurableMemory } : {}
253
+ } : void 0
254
+ };
255
+ }
256
+ function normalizeBeforeTurnMemoryPolicyConfig(value) {
257
+ if (value === void 0) {
258
+ return { ok: true, value: void 0 };
259
+ }
260
+ if (!isRecord2(value)) {
261
+ return { ok: false, errors: ["memoryPolicy.beforeTurn must be an object when provided"] };
262
+ }
263
+ const errors = [];
264
+ const enabled = normalizeOptionalBoolean(value.enabled, "memoryPolicy.beforeTurn.enabled", errors);
265
+ const procedureSuggestion = normalizeOptionalBoolean(value.procedureSuggestion, "memoryPolicy.beforeTurn.procedureSuggestion", errors);
266
+ const maxDurableEntries = normalizeOptionalPositiveInteger(value.maxDurableEntries, "memoryPolicy.beforeTurn.maxDurableEntries", errors);
267
+ const recallThreshold = normalizeOptionalUnitInterval(value.recallThreshold, "memoryPolicy.beforeTurn.recallThreshold", errors);
268
+ const highConfidenceRecallThreshold = normalizeOptionalUnitInterval(
269
+ value.highConfidenceRecallThreshold,
270
+ "memoryPolicy.beforeTurn.highConfidenceRecallThreshold",
271
+ errors
272
+ );
273
+ const procedureThreshold = normalizeOptionalUnitInterval(value.procedureThreshold, "memoryPolicy.beforeTurn.procedureThreshold", errors);
274
+ const allowedKeys = /* @__PURE__ */ new Set([
275
+ "enabled",
276
+ "procedureSuggestion",
277
+ "maxDurableEntries",
278
+ "recallThreshold",
279
+ "highConfidenceRecallThreshold",
280
+ "procedureThreshold"
281
+ ]);
282
+ for (const key of Object.keys(value)) {
283
+ if (!allowedKeys.has(key)) {
284
+ errors.push(`unknown config field: memoryPolicy.beforeTurn.${key}`);
285
+ }
286
+ }
287
+ if (errors.length > 0) {
288
+ return { ok: false, errors };
289
+ }
290
+ return {
291
+ ok: true,
292
+ value: enabled !== void 0 || procedureSuggestion !== void 0 || maxDurableEntries !== void 0 || recallThreshold !== void 0 || highConfidenceRecallThreshold !== void 0 || procedureThreshold !== void 0 ? {
293
+ ...enabled !== void 0 ? { enabled } : {},
294
+ ...procedureSuggestion !== void 0 ? { procedureSuggestion } : {},
295
+ ...maxDurableEntries !== void 0 ? { maxDurableEntries } : {},
296
+ ...recallThreshold !== void 0 ? { recallThreshold } : {},
297
+ ...highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold } : {},
298
+ ...procedureThreshold !== void 0 ? { procedureThreshold } : {}
299
+ } : void 0
300
+ };
301
+ }
302
+ function normalizeClaimSlotPolicyConfig(value) {
303
+ if (value === void 0) {
304
+ return { ok: true, value: void 0 };
305
+ }
306
+ if (!isRecord2(value)) {
307
+ return { ok: false, errors: ["memoryPolicy.slotPolicies must be an object when provided"] };
308
+ }
309
+ const errors = [];
310
+ const attributeHeads = normalizeClaimSlotPolicyAttributeHeads(value.attributeHeads, errors);
311
+ const allowedKeys = /* @__PURE__ */ new Set(["attributeHeads"]);
312
+ for (const key of Object.keys(value)) {
313
+ if (!allowedKeys.has(key)) {
314
+ errors.push(`unknown config field: memoryPolicy.slotPolicies.${key}`);
315
+ }
316
+ }
317
+ if (errors.length > 0) {
318
+ return { ok: false, errors };
319
+ }
320
+ return {
321
+ ok: true,
322
+ value: attributeHeads ? { attributeHeads } : void 0
323
+ };
324
+ }
325
+ function normalizeClaimSlotPolicyAttributeHeads(value, errors) {
326
+ if (value === void 0) {
327
+ return void 0;
328
+ }
329
+ if (!isRecord2(value)) {
330
+ errors.push("memoryPolicy.slotPolicies.attributeHeads must be an object when provided");
331
+ return void 0;
332
+ }
333
+ const normalized = {};
334
+ for (const [rawKey, rawPolicy] of Object.entries(value)) {
335
+ const attributeHead = rawKey.trim().toLowerCase();
336
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(attributeHead)) {
337
+ errors.push(`memoryPolicy.slotPolicies.attributeHeads.${rawKey} must use a canonical attribute-head label`);
338
+ continue;
339
+ }
340
+ if (rawPolicy !== "exclusive" && rawPolicy !== "multivalued") {
341
+ errors.push(`memoryPolicy.slotPolicies.attributeHeads.${attributeHead} must be "exclusive" or "multivalued"`);
342
+ continue;
343
+ }
344
+ normalized[attributeHead] = rawPolicy;
345
+ }
346
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
347
+ }
348
+ function isRecord2(value) {
349
+ return typeof value === "object" && value !== null && !Array.isArray(value);
350
+ }
68
351
 
69
352
  // src/adapters/skeln/config.ts
70
353
  function parseSkelnMemoryPolicyJson(raw) {
@@ -305,6 +588,312 @@ function toWorkingContextAuditPointer(projection) {
305
588
  };
306
589
  }
307
590
 
591
+ // src/app/session-memory/normalize.ts
592
+ function normalizeOptionalString(value) {
593
+ const normalized = value?.trim();
594
+ return normalized ? normalized : void 0;
595
+ }
596
+
597
+ // src/app/session-start/context-sections.ts
598
+ function buildSessionStartContextSections(continuitySummaryText, recentSessionText) {
599
+ const sections = [];
600
+ const normalizedContinuitySummary = normalizeOptionalString(continuitySummaryText);
601
+ if (normalizedContinuitySummary) {
602
+ sections.push({
603
+ kind: "continuity_summary",
604
+ title: "Previous session summary",
605
+ content: normalizedContinuitySummary
606
+ });
607
+ }
608
+ const normalizedRecentSession = normalizeOptionalString(recentSessionText);
609
+ if (normalizedRecentSession) {
610
+ sections.push({
611
+ kind: "recent_session",
612
+ title: "Recent session",
613
+ content: normalizedRecentSession
614
+ });
615
+ }
616
+ return sections;
617
+ }
618
+
619
+ // src/app/session-start/service.ts
620
+ var DEFAULT_MAX_CORE_ENTRIES = 4;
621
+ var DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES = 3;
622
+ var DEFAULT_MAX_DURABLE_ENTRIES = 5;
623
+ var DEFAULT_MAX_ARTIFACT_CHARS = 1200;
624
+ async function runSessionStart(input, deps) {
625
+ const policy = normalizePolicy(input.policy);
626
+ const contextSections = buildSessionStartContextSections(input.continuitySummaryText, input.recentSessionText);
627
+ const coreEntries = await deps.repository.listCoreEntries(policy.maxCoreEntries);
628
+ const coreItems = coreEntries.map((entry) => buildCorePatchItem(entry));
629
+ const diagnostics = {
630
+ coreCandidateCount: coreEntries.length,
631
+ artifactRecallCandidateCount: 0,
632
+ artifactRecallUsed: false,
633
+ notices: []
634
+ };
635
+ const artifactRecallQuery = policy.enableArtifactRecall ? buildArtifactRecallQuery(contextSections, policy.maxArtifactChars) : void 0;
636
+ if (!policy.enableArtifactRecall) {
637
+ diagnostics.notices.push("Artifact-grounded durable recall disabled by session-start policy.");
638
+ }
639
+ const artifactRecallItems = artifactRecallQuery ? await runArtifactRecallSelection(artifactRecallQuery, input.sessionKey, policy, deps, diagnostics) : [];
640
+ const durableMemory = assignRanks(mergeDurableMemory(coreItems, artifactRecallItems, policy.maxDurableEntries));
641
+ return {
642
+ contextSections,
643
+ durableMemory,
644
+ diagnostics
645
+ };
646
+ }
647
+ async function runArtifactRecallSelection(query, sessionKey, policy, deps, diagnostics) {
648
+ diagnostics.artifactRecallUsed = true;
649
+ diagnostics.artifactRecallQuery = query;
650
+ let artifactRecallTrace;
651
+ try {
652
+ const recalled = await recall(
653
+ {
654
+ text: query,
655
+ limit: policy.maxArtifactRecallEntries,
656
+ threshold: policy.recallThreshold,
657
+ sessionKey
658
+ },
659
+ deps.recall,
660
+ {
661
+ trace: {
662
+ reportSummary(summary) {
663
+ artifactRecallTrace = summary;
664
+ }
665
+ },
666
+ slotPolicyConfig: deps.slotPolicyConfig
667
+ }
668
+ );
669
+ diagnostics.artifactRecallTrace = artifactRecallTrace;
670
+ diagnostics.artifactRecallCandidateCount = recalled.length;
671
+ if (artifactRecallTrace?.degraded.notices.length) {
672
+ diagnostics.notices.push(...artifactRecallTrace.degraded.notices);
673
+ }
674
+ return recalled.map((item) => buildArtifactRecallPatchItem(item, deps));
675
+ } catch (error) {
676
+ diagnostics.artifactRecallTrace = artifactRecallTrace;
677
+ diagnostics.notices.push(`Artifact-grounded durable recall failed: ${formatErrorMessage2(error)}`);
678
+ return [];
679
+ }
680
+ }
681
+ function buildCorePatchItem(entry) {
682
+ return {
683
+ rank: 0,
684
+ entry,
685
+ sourceKind: "core",
686
+ whySurfaced: {
687
+ summary: `always-on core memory; importance ${entry.importance}`,
688
+ reasons: ["always-on core memory", `importance ${entry.importance}`, `expiry ${entry.expiry}`]
689
+ },
690
+ memoryState: resolveMemoryState(entry),
691
+ claimStatus: resolveClaimStatus(entry),
692
+ freshnessLabel: buildFreshnessLabel(entry),
693
+ ...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
694
+ };
695
+ }
696
+ function buildArtifactRecallPatchItem(recalled, deps) {
697
+ const projected = projectClaimCentricRecallEntry(recalled, {
698
+ slotPolicyConfig: deps.slotPolicyConfig
699
+ });
700
+ return {
701
+ rank: 0,
702
+ entry: recalled.entry,
703
+ sourceKind: "artifact_recall",
704
+ score: recalled.score,
705
+ whySurfaced: projected.whySurfaced,
706
+ memoryState: projected.memoryState,
707
+ claimStatus: projected.claimStatus,
708
+ freshnessLabel: projected.freshness.label,
709
+ ...formatProjectedProvenance(projected.provenance) ? { provenanceSummary: formatProjectedProvenance(projected.provenance) } : {}
710
+ };
711
+ }
712
+ function buildArtifactRecallQuery(sections, maxChars) {
713
+ if (sections.length === 0 || maxChars <= 0) {
714
+ return void 0;
715
+ }
716
+ let remaining = maxChars;
717
+ const parts = [];
718
+ for (const section of sections) {
719
+ if (remaining <= 0) {
720
+ break;
721
+ }
722
+ const normalizedContent = normalizeWhitespace(section.content);
723
+ if (normalizedContent.length === 0) {
724
+ continue;
725
+ }
726
+ const labeled = `${section.title}: ${normalizedContent}`;
727
+ const truncated = truncate2(labeled, remaining);
728
+ if (truncated.length === 0) {
729
+ continue;
730
+ }
731
+ parts.push(truncated);
732
+ remaining -= truncated.length;
733
+ }
734
+ const query = normalizeWhitespace(parts.join("\n"));
735
+ return query.length > 0 ? query : void 0;
736
+ }
737
+ function mergeDurableMemory(coreItems, artifactRecallItems, maxDurableEntries) {
738
+ const merged = [];
739
+ const seenEntryIds = /* @__PURE__ */ new Set();
740
+ for (const item of [...coreItems, ...artifactRecallItems]) {
741
+ if (seenEntryIds.has(item.entry.id)) {
742
+ continue;
743
+ }
744
+ seenEntryIds.add(item.entry.id);
745
+ merged.push(item);
746
+ if (merged.length >= maxDurableEntries) {
747
+ break;
748
+ }
749
+ }
750
+ return merged;
751
+ }
752
+ function assignRanks(items) {
753
+ return items.map((item, index) => ({
754
+ ...item,
755
+ rank: index + 1
756
+ }));
757
+ }
758
+ function normalizePolicy(policy) {
759
+ const maxCoreEntries = normalizeCount(policy?.maxCoreEntries, DEFAULT_MAX_CORE_ENTRIES);
760
+ const maxArtifactRecallEntries = normalizeCount(policy?.maxArtifactRecallEntries, DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES);
761
+ const maxDurableEntries = Math.max(maxCoreEntries, normalizeCount(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES));
762
+ return {
763
+ maxCoreEntries,
764
+ enableArtifactRecall: policy?.enableArtifactRecall !== false,
765
+ maxArtifactRecallEntries,
766
+ maxDurableEntries,
767
+ maxArtifactChars: normalizeCount(policy?.maxArtifactChars, DEFAULT_MAX_ARTIFACT_CHARS),
768
+ recallThreshold: normalizeThreshold(policy?.recallThreshold)
769
+ };
770
+ }
771
+ function normalizeCount(value, fallback) {
772
+ if (typeof value !== "number" || !Number.isFinite(value)) {
773
+ return fallback;
774
+ }
775
+ return Math.max(0, Math.trunc(value));
776
+ }
777
+ function normalizeThreshold(value) {
778
+ if (typeof value !== "number" || !Number.isFinite(value)) {
779
+ return 0;
780
+ }
781
+ return Math.min(1, Math.max(0, value));
782
+ }
783
+ function resolveMemoryState(entry) {
784
+ if (entry.superseded_by) {
785
+ return "superseded";
786
+ }
787
+ if (entry.retired || entry.valid_to) {
788
+ return "historical";
789
+ }
790
+ return "current";
791
+ }
792
+ function resolveClaimStatus(entry) {
793
+ if (!normalizeOptionalString2(entry.claim_key)) {
794
+ return "no_key";
795
+ }
796
+ return entry.claim_key_status ?? "legacy";
797
+ }
798
+ function buildFreshnessLabel(entry) {
799
+ const parts = [`created ${entry.created_at}`];
800
+ const validFrom = normalizeOptionalString2(entry.valid_from);
801
+ const validTo = normalizeOptionalString2(entry.valid_to);
802
+ if (validFrom || validTo) {
803
+ parts.push(`valid ${validFrom ?? "?"} -> ${validTo ?? "ongoing"}`);
804
+ }
805
+ return parts.join(" | ");
806
+ }
807
+ function buildProvenanceSummary(entry) {
808
+ const parts = [
809
+ entry.superseded_by ? `superseded_by=${entry.superseded_by}` : void 0,
810
+ entry.supersession_kind ? `kind=${entry.supersession_kind}` : void 0,
811
+ entry.supersession_reason ? `reason=${entry.supersession_reason}` : void 0,
812
+ entry.claim_support_source_kind ? `support=${entry.claim_support_source_kind}` : void 0,
813
+ entry.claim_support_mode ? `support_mode=${entry.claim_support_mode}` : void 0,
814
+ entry.claim_support_observed_at ? `observed=${entry.claim_support_observed_at}` : void 0,
815
+ entry.claim_support_locator ? `locator=${entry.claim_support_locator}` : void 0
816
+ ].filter((value) => value !== void 0);
817
+ return parts.length > 0 ? parts.join(" | ") : void 0;
818
+ }
819
+ function formatProjectedProvenance(provenance) {
820
+ const parts = [
821
+ provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
822
+ provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
823
+ provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
824
+ provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
825
+ provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
826
+ provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
827
+ provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
828
+ ].filter((value) => value !== void 0);
829
+ return parts.length > 0 ? parts.join(" | ") : void 0;
830
+ }
831
+ function normalizeOptionalString2(value) {
832
+ const normalized = value?.trim();
833
+ return normalized && normalized.length > 0 ? normalized : void 0;
834
+ }
835
+ function normalizeWhitespace(value) {
836
+ return value.replace(/\s+/g, " ").trim();
837
+ }
838
+ function truncate2(value, maxChars) {
839
+ if (maxChars <= 0) {
840
+ return "";
841
+ }
842
+ if (value.length <= maxChars) {
843
+ return value;
844
+ }
845
+ return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
846
+ }
847
+ function formatErrorMessage2(error) {
848
+ if (error instanceof Error) {
849
+ return error.message;
850
+ }
851
+ return String(error);
852
+ }
853
+
854
+ // src/adapters/shared/injection/session-start-format.ts
855
+ function formatAgenrSessionStartRecall(patch) {
856
+ if (patch.contextSections.length === 0 && patch.durableMemory.length === 0) {
857
+ return "";
858
+ }
859
+ const lines = [];
860
+ for (const section of patch.contextSections) {
861
+ lines.push(`## ${section.title}`);
862
+ lines.push(section.content);
863
+ lines.push("");
864
+ }
865
+ const durableSections = buildSections(patch);
866
+ if (durableSections.length > 0) {
867
+ const recallLines = [
868
+ "## Agenr Session Recall",
869
+ "Use this as prior context. Confirm anything important if the current conversation conflicts with it.",
870
+ ""
871
+ ];
872
+ for (const section of durableSections) {
873
+ recallLines.push(`### ${section.title}`);
874
+ for (const item of section.entries) {
875
+ recallLines.push(formatInjectionEntryHeader(item));
876
+ recallLines.push(...formatInjectionEntryBodyLines(item));
877
+ }
878
+ recallLines.push("");
879
+ }
880
+ lines.push(wrapAgenrMemoryContext(recallLines.join("\n").trim()));
881
+ }
882
+ return lines.join("\n").trim();
883
+ }
884
+ function buildSections(patch) {
885
+ const sections = [];
886
+ const coreEntries = patch.durableMemory.filter((item) => item.sourceKind === "core");
887
+ if (coreEntries.length > 0) {
888
+ sections.push({ title: "Core Memory", entries: coreEntries });
889
+ }
890
+ const artifactRecallEntries = patch.durableMemory.filter((item) => item.sourceKind === "artifact_recall");
891
+ if (artifactRecallEntries.length > 0) {
892
+ sections.push({ title: "Relevant Durable Memory", entries: artifactRecallEntries });
893
+ }
894
+ return sections;
895
+ }
896
+
308
897
  // src/adapters/skeln/session/scope.ts
309
898
  function resolveSkelnSessionKey(sessionId, cwd) {
310
899
  const normalizedSessionId = sessionId.trim();
@@ -522,6 +1111,171 @@ function traceSystemPromptDoctrineInjected(baseSystemPrompt, updatedSystemPrompt
522
1111
  });
523
1112
  }
524
1113
 
1114
+ // src/adapters/shared/injection/policy.ts
1115
+ var DEFAULT_SESSION_START_POLICY = {
1116
+ maxCoreEntries: 4,
1117
+ maxArtifactRecallEntries: 3,
1118
+ maxDurableEntries: 5,
1119
+ maxArtifactChars: 1200
1120
+ };
1121
+ var DEFAULT_BEFORE_TURN_POLICY = {
1122
+ maxDurableEntries: 1,
1123
+ maxHighConfidenceDurableEntries: 2,
1124
+ maxRecentTurns: 2,
1125
+ maxQueryChars: 450,
1126
+ maxProcedureCandidates: 3,
1127
+ recallThreshold: 0.6,
1128
+ highConfidenceRecallThreshold: 0.85,
1129
+ procedureThreshold: 0.72
1130
+ };
1131
+ function resolveSessionStartPolicy(memoryPolicy) {
1132
+ return {
1133
+ ...DEFAULT_SESSION_START_POLICY,
1134
+ ...memoryPolicy?.sessionStart?.coreMemory === false ? { maxCoreEntries: 0 } : {},
1135
+ enableArtifactRecall: memoryPolicy?.sessionStart?.relevantDurableMemory !== false
1136
+ };
1137
+ }
1138
+ function resolveBeforeTurnPolicy(memoryPolicy) {
1139
+ return {
1140
+ ...DEFAULT_BEFORE_TURN_POLICY,
1141
+ enableProcedureSuggestion: memoryPolicy?.beforeTurn?.procedureSuggestion !== false,
1142
+ ...memoryPolicy?.beforeTurn?.maxDurableEntries !== void 0 ? { maxDurableEntries: memoryPolicy.beforeTurn.maxDurableEntries } : {},
1143
+ ...memoryPolicy?.beforeTurn?.recallThreshold !== void 0 ? { recallThreshold: memoryPolicy.beforeTurn.recallThreshold } : {},
1144
+ ...memoryPolicy?.beforeTurn?.highConfidenceRecallThreshold !== void 0 ? { highConfidenceRecallThreshold: memoryPolicy.beforeTurn.highConfidenceRecallThreshold } : {},
1145
+ ...memoryPolicy?.beforeTurn?.procedureThreshold !== void 0 ? { procedureThreshold: memoryPolicy.beforeTurn.procedureThreshold } : {}
1146
+ };
1147
+ }
1148
+ function isWorkingContextPolicyEnabled(memoryPolicy) {
1149
+ return memoryPolicy?.workingContext?.enabled !== false;
1150
+ }
1151
+ function resolveWorkingContextGate(workingMemory, memoryPolicy) {
1152
+ const capability = resolveWorkingMemoryCapability(workingMemory);
1153
+ if (capability === "disabled") {
1154
+ return { ok: false, reason: "features.workingMemory=false" };
1155
+ }
1156
+ if (capability === "misconfigured") {
1157
+ return { ok: false, reason: "features.workingMemory enabled without repository" };
1158
+ }
1159
+ if (!isWorkingContextPolicyEnabled(memoryPolicy)) {
1160
+ return { ok: false, reason: "memoryPolicy.workingContext.enabled=false" };
1161
+ }
1162
+ return { ok: true };
1163
+ }
1164
+ function resolveWorkingMemoryCapability(workingMemory) {
1165
+ if (typeof workingMemory === "string") {
1166
+ return workingMemory;
1167
+ }
1168
+ return workingMemory.workingMemory ? "enabled" : "disabled";
1169
+ }
1170
+
1171
+ // src/adapters/shared/injection/message-text.ts
1172
+ var MEMORY_HEADINGS = [
1173
+ "## Previous session summary",
1174
+ "## Recent session",
1175
+ "## Agenr Session Recall",
1176
+ "### Core Memory",
1177
+ "### Relevant Durable Memory",
1178
+ "## Agenr Before-Turn Recall",
1179
+ "### Suggested Procedure"
1180
+ ];
1181
+ function extractAgentMessageText(content) {
1182
+ if (typeof content === "string") {
1183
+ return content;
1184
+ }
1185
+ if (!Array.isArray(content)) {
1186
+ return "";
1187
+ }
1188
+ const blocks = [];
1189
+ for (const block of content) {
1190
+ if (typeof block === "string") {
1191
+ blocks.push(block);
1192
+ continue;
1193
+ }
1194
+ if (!block || typeof block !== "object") {
1195
+ continue;
1196
+ }
1197
+ const typed = block;
1198
+ if (typeof typed.text === "string") {
1199
+ blocks.push(typed.text);
1200
+ continue;
1201
+ }
1202
+ const type = typeof typed.type === "string" ? typed.type.trim().toLowerCase() : "";
1203
+ if (typeof typed.content === "string" && (type === "text" || type === "input_text" || type === "output_text")) {
1204
+ blocks.push(typed.content);
1205
+ }
1206
+ }
1207
+ return blocks.join("\n");
1208
+ }
1209
+ function extractRecentTurnsFromMessages(messages, options = {}) {
1210
+ const turns = [];
1211
+ for (const message of messages) {
1212
+ const role = message.role === "user" || message.role === "assistant" ? message.role : void 0;
1213
+ if (!role) {
1214
+ continue;
1215
+ }
1216
+ const text = sanitizeRecentTurnText(extractAgentMessageText(message.content), role, options);
1217
+ if (!text) {
1218
+ continue;
1219
+ }
1220
+ turns.push({ role, text });
1221
+ }
1222
+ return turns;
1223
+ }
1224
+ function normalizePromptText(prompt, options = {}) {
1225
+ let cleaned = stripAgenrMemoryContext(prompt);
1226
+ if (options.stripInlineMetadata) {
1227
+ cleaned = stripInlineMetadata(cleaned, options.inlineMetadataSentinels ?? []);
1228
+ }
1229
+ if (options.stripTimestampPrefix) {
1230
+ cleaned = cleaned.replace(/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s[^\]]+\]\s*/u, "");
1231
+ }
1232
+ if (options.stripUserPrefix) {
1233
+ cleaned = cleaned.replace(/^\s*U:\s*/u, "");
1234
+ }
1235
+ cleaned = collapseWhitespace(cleaned);
1236
+ return cleaned.length > 0 ? cleaned : void 0;
1237
+ }
1238
+ function sanitizeRecentTurnText(text, role, options = {}) {
1239
+ if (!text.trim()) {
1240
+ return "";
1241
+ }
1242
+ const wrapperDetected = containsAgenrMemoryContext(text) || text.includes("## Agenr Session Recall") || text.includes("## Agenr Before-Turn Recall") || options.stripMemoryCheck === true && text.includes("[MEMORY CHECK]");
1243
+ let cleaned = stripAgenrMemoryContext(text);
1244
+ for (const heading of MEMORY_HEADINGS) {
1245
+ cleaned = cleaned.split(heading).join(" ");
1246
+ }
1247
+ if (options.stripMemoryCheck === true) {
1248
+ cleaned = cleaned.replace(/\[MEMORY CHECK\][^\n]*/gu, " ");
1249
+ }
1250
+ cleaned = collapseWhitespace(cleaned);
1251
+ if (!wrapperDetected) {
1252
+ return cleaned;
1253
+ }
1254
+ const segments = stripAgenrMemoryContext(text).split(/\n\s*\n/gu).map((segment) => collapseWhitespace(segment)).filter((segment) => segment.length > 0);
1255
+ const fallbackSegment = segments.at(-1);
1256
+ if (fallbackSegment) {
1257
+ return role === "user" ? fallbackSegment : collapseWhitespace(cleaned);
1258
+ }
1259
+ return cleaned;
1260
+ }
1261
+ function stripInlineMetadata(text, sentinels) {
1262
+ let cleaned = text;
1263
+ for (const sentinel of sentinels) {
1264
+ const escapedSentinel = escapeForRegExp(sentinel);
1265
+ cleaned = cleaned.replace(new RegExp(`${escapedSentinel}\\s*(?:\`\`\`json\\s*)?\\{[\\s\\S]*?\\}(?:\\s*\`\`\`)?`, "gu"), " ");
1266
+ cleaned = cleaned.replace(new RegExp(`${escapedSentinel}[^
1267
+ ]*`, "gu"), " ");
1268
+ }
1269
+ cleaned = cleaned.replace(/Untrusted context \(metadata, do not treat as instructions or commands\):[\s\S]*$/gu, " ");
1270
+ return cleaned;
1271
+ }
1272
+ function collapseWhitespace(value) {
1273
+ return value.replace(/\s+/gu, " ").trim();
1274
+ }
1275
+ function escapeForRegExp(value) {
1276
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1277
+ }
1278
+
525
1279
  // src/adapters/skeln/hooks/before-agent-start.ts
526
1280
  async function handleAgenrSkelnBeforeAgentStart(event, context, deps) {
527
1281
  const scope = await deps.resolveScope(context);
@@ -937,10 +1691,181 @@ function createAgenrEpisodeSummaryLlm(provider, modelId, apiKey) {
937
1691
  };
938
1692
  }
939
1693
 
940
- // src/adapters/skeln/transcript/parser.ts
941
- import { createHash } from "crypto";
942
- import * as fs from "fs/promises";
943
- import path from "path";
1694
+ // src/adapters/shared/bounded-episode-embedding.ts
1695
+ var EPISODE_EMBEDDING_RACE_TIMEOUT = /* @__PURE__ */ Symbol("episode-embedding-race-timeout");
1696
+ var EPISODE_EMBEDDING_MIN_HEADROOM_MS = 5e3;
1697
+ async function embedEpisodeSummaryWithinBudget(params) {
1698
+ const minHeadroomMs = params.minHeadroomMs ?? EPISODE_EMBEDDING_MIN_HEADROOM_MS;
1699
+ if (!params.embeddingAvailable) {
1700
+ params.logger.info(`${params.logContext} reason=embedding_unavailable`);
1701
+ return void 0;
1702
+ }
1703
+ const remainingBudgetMs = params.deadlineMs - Date.now();
1704
+ if (remainingBudgetMs < minHeadroomMs) {
1705
+ params.logger.info(`${params.logContext} reason=budget_tight remainingMs=${Math.max(0, remainingBudgetMs)}`);
1706
+ return void 0;
1707
+ }
1708
+ try {
1709
+ const result = await raceEmbeddingWithTimeout(params.embedding.embed([params.summary]), remainingBudgetMs);
1710
+ if (result === EPISODE_EMBEDDING_RACE_TIMEOUT) {
1711
+ params.logger.info(`${params.logContext} reason=embedding_timeout budgetMs=${remainingBudgetMs}`);
1712
+ return void 0;
1713
+ }
1714
+ const vector = result[0]?.map((value) => Number.isFinite(value) ? value : 0);
1715
+ if (!vector || vector.length === 0) {
1716
+ params.logger.info(`${params.logContext} reason=empty_embedding`);
1717
+ return void 0;
1718
+ }
1719
+ return vector;
1720
+ } catch (error) {
1721
+ params.logger.info(`${params.logContext} reason=${formatErrorMessage(error)}`);
1722
+ return void 0;
1723
+ }
1724
+ }
1725
+ async function raceEmbeddingWithTimeout(promise, timeoutMs) {
1726
+ return new Promise((resolve, reject) => {
1727
+ const timeout = setTimeout(() => {
1728
+ resolve(EPISODE_EMBEDDING_RACE_TIMEOUT);
1729
+ }, timeoutMs);
1730
+ promise.then(
1731
+ (value) => {
1732
+ clearTimeout(timeout);
1733
+ resolve(value);
1734
+ },
1735
+ (error) => {
1736
+ clearTimeout(timeout);
1737
+ reject(error);
1738
+ }
1739
+ );
1740
+ });
1741
+ }
1742
+
1743
+ // src/adapters/shared/bounded-episode-summary.ts
1744
+ var EPISODE_SUMMARY_TIMEOUT_MS = 45e3;
1745
+ var EPISODE_SUMMARY_TIMEOUT_MESSAGE = "Episode summary generation timed out.";
1746
+ var EpisodeSummaryTimeoutError = class extends Error {
1747
+ /**
1748
+ * Creates a timeout error with a stable name for caller-side handling.
1749
+ */
1750
+ constructor() {
1751
+ super(EPISODE_SUMMARY_TIMEOUT_MESSAGE);
1752
+ this.name = "EpisodeSummaryTimeoutError";
1753
+ }
1754
+ };
1755
+ async function raceEpisodeSummaryWithinTimeout(task, timeoutMs) {
1756
+ let timeout;
1757
+ try {
1758
+ return await Promise.race([
1759
+ task,
1760
+ new Promise((_, reject) => {
1761
+ timeout = setTimeout(() => reject(new EpisodeSummaryTimeoutError()), timeoutMs);
1762
+ })
1763
+ ]);
1764
+ } finally {
1765
+ if (timeout) {
1766
+ clearTimeout(timeout);
1767
+ }
1768
+ }
1769
+ }
1770
+
1771
+ // src/adapters/shared/bounded-episode-ingest-log.ts
1772
+ function logBoundedEpisodeTranscriptIngestOutcome(params) {
1773
+ const prefix = `[agenr] ${params.actionLabel}`;
1774
+ const fileField = params.fileField ?? "file";
1775
+ const fileRef = `${fileField}=${params.filePath}`;
1776
+ const countField = params.shortCountField ?? "materialTurns";
1777
+ if (params.result.kind === "skipped") {
1778
+ const skipped = params.result.skipped;
1779
+ if (skipped.reason === "skipped_exists") {
1780
+ params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=already_exists episode=${skipped.existingEpisode?.id}`);
1781
+ return false;
1782
+ }
1783
+ if (skipped.reason === "skipped_short") {
1784
+ params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=too_short ${countField}=${skipped.messageCount}`);
1785
+ return false;
1786
+ }
1787
+ params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=${skipped.reason} ${countField}=${skipped.messageCount}`);
1788
+ return false;
1789
+ }
1790
+ if (params.result.kind === "invalid") {
1791
+ params.logger.info(`${prefix} skipped for ${params.context} ${fileRef} reason=invalid_transcript ${countField}=${params.result.invalid.messageCount}`);
1792
+ return false;
1793
+ }
1794
+ const session = params.result.session;
1795
+ if (session.action === "failed") {
1796
+ if (session.error === EPISODE_SUMMARY_TIMEOUT_MESSAGE) {
1797
+ params.logger.info(`${prefix} timed_out for ${params.context} ${fileRef} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`);
1798
+ return false;
1799
+ }
1800
+ if (session.error === "invalid_response" && params.failureModelRef) {
1801
+ params.logger.info(`${prefix} failed for ${params.context} ${fileRef} reason=invalid_response model=${params.failureModelRef}`);
1802
+ return false;
1803
+ }
1804
+ params.logger.info(`${prefix} failed for ${params.context} ${fileRef} reason=${session.error ?? "unknown"}`);
1805
+ return false;
1806
+ }
1807
+ params.logger.info(`${prefix} ${session.action} for ${params.context} ${fileRef} episode=${session.episodeId}`);
1808
+ return true;
1809
+ }
1810
+
1811
+ // src/adapters/shared/bounded-episode-write.ts
1812
+ async function writeBoundedSingleTranscriptEpisode(params) {
1813
+ const fileField = params.fileField ?? "file";
1814
+ const fileRef = `${fileField}=${params.filePath}`;
1815
+ try {
1816
+ const ingestResult = await raceEpisodeSummaryWithinTimeout(
1817
+ ingestEpisodeTranscript(params.filePath, params.ports, params.ingestOptions),
1818
+ EPISODE_SUMMARY_TIMEOUT_MS
1819
+ );
1820
+ logBoundedEpisodeTranscriptIngestOutcome({
1821
+ logger: params.logger,
1822
+ actionLabel: params.actionLabel,
1823
+ context: params.context,
1824
+ filePath: params.filePath,
1825
+ ...params.fileField ? { fileField: params.fileField } : {},
1826
+ ...params.shortCountField ? { shortCountField: params.shortCountField } : {},
1827
+ result: ingestResult,
1828
+ ...params.failureModelRef ? { failureModelRef: params.failureModelRef } : {}
1829
+ });
1830
+ } catch (error) {
1831
+ if (error instanceof EpisodeSummaryTimeoutError) {
1832
+ params.logger.info(`[agenr] ${params.actionLabel} timed_out for ${params.context} ${fileRef} timeoutMs=${EPISODE_SUMMARY_TIMEOUT_MS}`);
1833
+ return;
1834
+ }
1835
+ const message = formatErrorMessage(error);
1836
+ if (params.unexpectedFailureLevel === "info") {
1837
+ params.logger.info(`[agenr] ${params.actionLabel} failed for ${params.context} ${fileRef} reason=${message}`);
1838
+ return;
1839
+ }
1840
+ params.logger.warn(`[agenr] ${params.actionLabel} failed for ${params.context} ${fileRef} reason=${message}`);
1841
+ }
1842
+ }
1843
+
1844
+ // src/adapters/shared/deadline-aware-episode-summary-llm.ts
1845
+ function createDeadlineAwareEpisodeSummaryLlm(baseLlm, deadlineMs) {
1846
+ const usage = cloneUsageStats(baseLlm.metadata.usage);
1847
+ const completeWithTimeout = async (task) => {
1848
+ usage.calls += 1;
1849
+ const remainingMs = Math.max(0, deadlineMs - Date.now());
1850
+ return raceEpisodeSummaryWithinTimeout(task, remainingMs);
1851
+ };
1852
+ return {
1853
+ complete: async (systemPrompt, userMessage) => completeWithTimeout(baseLlm.complete(systemPrompt, userMessage)),
1854
+ completeJson: async (systemPrompt, userMessage) => completeWithTimeout(baseLlm.completeJson(systemPrompt, userMessage)),
1855
+ metadata: {
1856
+ ...baseLlm.metadata,
1857
+ usage
1858
+ }
1859
+ };
1860
+ }
1861
+ function cloneUsageStats(usage) {
1862
+ return { ...usage };
1863
+ }
1864
+
1865
+ // src/adapters/skeln/transcript/parser.ts
1866
+ import { createHash } from "crypto";
1867
+ import * as fs from "fs/promises";
1868
+ import path from "path";
944
1869
  var SkelnTranscriptParser = class {
945
1870
  /**
946
1871
  * Parses one Skeln session JSONL file into normalized transcript messages.
@@ -1176,44 +2101,1050 @@ async function writeSkelnShutdownEpisode(params) {
1176
2101
  skipDetails: `session=${params.target.sessionId}`
1177
2102
  });
1178
2103
  }
1179
-
1180
- // src/adapters/skeln/episode/shutdown-episode-write.ts
1181
- function buildSkelnSessionShutdownEpisodeWork(params) {
1182
- const target = resolveSkelnSessionEpisodeTarget(params.context);
1183
- const shutdownWork = params.event.reason === "quit" ? writeScopedSkelnShutdownEpisode(params.servicesPromise, target, params.logger).finally(() => closeSkelnServicesAfterShutdown(params.servicesPromise)) : writeScopedSkelnShutdownEpisode(params.servicesPromise, target, params.logger);
1184
- return shutdownWork.catch((error) => {
1185
- logSkelnShutdownEpisodeFailure(error, params.logger);
1186
- });
2104
+
2105
+ // src/adapters/skeln/episode/shutdown-episode-write.ts
2106
+ function buildSkelnSessionShutdownEpisodeWork(params) {
2107
+ const target = resolveSkelnSessionEpisodeTarget(params.context);
2108
+ const shutdownWork = params.event.reason === "quit" ? writeScopedSkelnShutdownEpisode(params.servicesPromise, target, params.logger).finally(() => closeSkelnServicesAfterShutdown(params.servicesPromise)) : writeScopedSkelnShutdownEpisode(params.servicesPromise, target, params.logger);
2109
+ return shutdownWork.catch((error) => {
2110
+ logSkelnShutdownEpisodeFailure(error, params.logger);
2111
+ });
2112
+ }
2113
+ function scheduleSkelnSessionShutdownEpisodeWrite(params) {
2114
+ const work = buildSkelnSessionShutdownEpisodeWork(params);
2115
+ if (params.event.reason === "quit" && params.event.deferWork) {
2116
+ params.event.deferWork(work);
2117
+ return Promise.resolve();
2118
+ }
2119
+ if (params.event.reason === "quit") {
2120
+ return work;
2121
+ }
2122
+ void work;
2123
+ return Promise.resolve();
2124
+ }
2125
+ function logSkelnShutdownEpisodeFailure(error, logger) {
2126
+ const log = logger ?? console;
2127
+ log.warn(`[agenr] skeln shutdown episode failed: ${formatErrorMessage(error)}`);
2128
+ }
2129
+ async function closeSkelnServicesAfterShutdown(servicesPromise) {
2130
+ try {
2131
+ const services = await servicesPromise;
2132
+ await services.close();
2133
+ } catch {
2134
+ }
2135
+ }
2136
+ async function writeScopedSkelnShutdownEpisode(servicesPromise, target, logger) {
2137
+ const services = await servicesPromise;
2138
+ if (!services.capabilities.shutdownEpisodes) {
2139
+ return;
2140
+ }
2141
+ await writeSkelnShutdownEpisode({ target, services, logger });
2142
+ }
2143
+
2144
+ // src/adapters/plugin-runtime/claim-extraction.ts
2145
+ function createClaimExtractionFromAgenrConfig(config) {
2146
+ const claimExtractionConfig = resolveClaimExtractionConfig(config);
2147
+ if (!claimExtractionConfig.enabled) {
2148
+ return void 0;
2149
+ }
2150
+ try {
2151
+ const { provider, modelId } = resolveModel(config, "claim");
2152
+ const apiKey = resolveLlmApiKey(config, provider);
2153
+ return {
2154
+ llm: createLlmClient(provider, modelId, { apiKey }),
2155
+ config: claimExtractionConfig
2156
+ };
2157
+ } catch {
2158
+ return void 0;
2159
+ }
2160
+ }
2161
+
2162
+ // src/app/plugin-runtime/resolve-paths.ts
2163
+ function resolvePluginRuntimeConfig(config, resolvePath) {
2164
+ const dbPathOverride = resolveOptionalPath(config.dbPath, resolvePath);
2165
+ const configPathOverride = resolveOptionalPath(config.configPath, resolvePath);
2166
+ const configPath = resolveConfigPath({
2167
+ configPath: configPathOverride,
2168
+ dbPath: dbPathOverride
2169
+ });
2170
+ const loadedConfig = readConfig({
2171
+ configPath,
2172
+ dbPath: dbPathOverride
2173
+ });
2174
+ const dbPath = dbPathOverride ?? resolveDbPath(loadedConfig);
2175
+ return {
2176
+ resolvedConfig: {
2177
+ dbPath,
2178
+ configPath
2179
+ },
2180
+ agenrConfig: {
2181
+ ...loadedConfig,
2182
+ dbPath
2183
+ }
2184
+ };
2185
+ }
2186
+ function resolveOptionalPath(value, resolvePath) {
2187
+ const normalized = value?.trim();
2188
+ if (!normalized) {
2189
+ return void 0;
2190
+ }
2191
+ return resolvePath ? resolvePath(normalized) : normalized;
2192
+ }
2193
+
2194
+ // src/adapters/db/session-memory-repository.ts
2195
+ import { randomUUID } from "crypto";
2196
+
2197
+ // src/app/working-memory/limits.ts
2198
+ function normalizeBoundedLimit(value, fallback, max) {
2199
+ if (value === void 0 || !Number.isInteger(value) || value <= 0) {
2200
+ return fallback;
2201
+ }
2202
+ return Math.min(value, max);
2203
+ }
2204
+ function normalizeEventLimit(value) {
2205
+ return normalizeBoundedLimit(value, 50, 200);
2206
+ }
2207
+ function normalizeListLimit(value) {
2208
+ return normalizeBoundedLimit(value, 20, 100);
2209
+ }
2210
+ var CLOSE_EVENT_HISTORY_LIMIT = 1e3;
2211
+
2212
+ // src/adapters/db/json.ts
2213
+ function serializeOptionalJson(value) {
2214
+ return value === void 0 ? null : JSON.stringify(value);
2215
+ }
2216
+ function parseOptionalJson(value, label) {
2217
+ if (!value) {
2218
+ return void 0;
2219
+ }
2220
+ try {
2221
+ return JSON.parse(value);
2222
+ } catch (error) {
2223
+ throw new Error(`Failed to parse ${label}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
2224
+ }
2225
+ }
2226
+
2227
+ // src/app/session-memory/types.ts
2228
+ var SESSION_ARTIFACT_KINDS = ["continuity_summary", "recent_session", "compaction_checkpoint", "branch_abandonment", "session_episode"];
2229
+ var SESSION_LINEAGE_REASONS = ["fork", "clone", "resume", "subagent_spawn"];
2230
+ var SESSION_START_TRANSITION_REASONS = ["new", "unknown", ...SESSION_LINEAGE_REASONS];
2231
+
2232
+ // src/adapters/db/session-memory-parsing.ts
2233
+ function parseSessionArtifactKind(value) {
2234
+ if (SESSION_ARTIFACT_KINDS.includes(value)) {
2235
+ return value;
2236
+ }
2237
+ throw new Error(`Unsupported session artifact kind "${value}".`);
2238
+ }
2239
+ function parseSessionLineageReason(value) {
2240
+ if (SESSION_LINEAGE_REASONS.includes(value)) {
2241
+ return value;
2242
+ }
2243
+ throw new Error(`Unsupported session lineage reason "${value}".`);
2244
+ }
2245
+
2246
+ // src/adapters/db/session-memory-repository.ts
2247
+ var SESSION_LINEAGE_EDGE_SELECT_COLUMNS = `
2248
+ id,
2249
+ child_session_key,
2250
+ parent_session_key,
2251
+ parent_source_ref,
2252
+ reason,
2253
+ fork_entry_id,
2254
+ fork_position,
2255
+ observed_at
2256
+ `;
2257
+ var SESSION_ARTIFACT_SELECT_COLUMNS = `
2258
+ id,
2259
+ kind,
2260
+ session_key,
2261
+ source,
2262
+ source_id,
2263
+ source_ref,
2264
+ content_hash,
2265
+ summary,
2266
+ metadata_json,
2267
+ created_at,
2268
+ expires_at
2269
+ `;
2270
+ function createSessionMemoryRepository(database) {
2271
+ return {
2272
+ upsertLineageEdge: (input) => upsertLineageEdge(database, input),
2273
+ upsertSessionArtifact: (input) => upsertSessionArtifact(database, input),
2274
+ recordTriggerIntake: (input) => recordTriggerIntake(database, input),
2275
+ listSessionArtifacts: (filter) => listSessionArtifacts(database, filter),
2276
+ listSessionArtifactsBySourceRef: (filter) => listSessionArtifactsBySourceRef(database, filter),
2277
+ getLatestLineageEdgeForChild: (childSessionKey) => getLatestLineageEdgeForChild(database, childSessionKey)
2278
+ };
2279
+ }
2280
+ async function upsertLineageEdge(database, input) {
2281
+ return database.withTransaction(async (transaction) => upsertLineageEdgeWithExecutor(transaction, input));
2282
+ }
2283
+ async function recordTriggerIntake(database, input) {
2284
+ return database.withTransaction(async (transaction) => {
2285
+ const executor = transaction;
2286
+ const result = {};
2287
+ if (input.lineage) {
2288
+ result.lineageEdge = await upsertLineageEdgeWithExecutor(executor, input.lineage);
2289
+ }
2290
+ if (input.artifact) {
2291
+ result.artifact = await upsertSessionArtifact(executor, input.artifact);
2292
+ }
2293
+ return result;
2294
+ });
2295
+ }
2296
+ async function upsertLineageEdgeWithExecutor(executor, input) {
2297
+ const existing = await findMatchingLineageEdge(executor, input);
2298
+ if (existing) {
2299
+ return existing;
2300
+ }
2301
+ const id = randomUUID();
2302
+ await executor.execute({
2303
+ sql: `
2304
+ INSERT INTO session_lineage_edges (
2305
+ id,
2306
+ child_session_key,
2307
+ parent_session_key,
2308
+ parent_source_ref,
2309
+ reason,
2310
+ fork_entry_id,
2311
+ fork_position,
2312
+ observed_at
2313
+ )
2314
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
2315
+ `,
2316
+ args: [
2317
+ id,
2318
+ input.childSessionKey,
2319
+ input.parentSessionKey ?? null,
2320
+ input.parentSourceRef ?? null,
2321
+ input.reason,
2322
+ input.forkEntryId ?? null,
2323
+ input.forkPosition ?? null,
2324
+ input.observedAt
2325
+ ]
2326
+ });
2327
+ const edge = await getLineageEdge(executor, id);
2328
+ if (!edge) {
2329
+ throw new Error(`Session lineage edge ${id} was not found after write.`);
2330
+ }
2331
+ return edge;
2332
+ }
2333
+ async function upsertSessionArtifact(executor, input) {
2334
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2335
+ await executor.execute({
2336
+ sql: `
2337
+ INSERT INTO session_artifacts (
2338
+ id,
2339
+ kind,
2340
+ session_key,
2341
+ source,
2342
+ source_id,
2343
+ source_ref,
2344
+ content_hash,
2345
+ summary,
2346
+ metadata_json,
2347
+ created_at,
2348
+ expires_at
2349
+ )
2350
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2351
+ ON CONFLICT(kind, source, source_id) DO UPDATE SET
2352
+ session_key = excluded.session_key,
2353
+ source_ref = excluded.source_ref,
2354
+ content_hash = excluded.content_hash,
2355
+ summary = excluded.summary,
2356
+ metadata_json = excluded.metadata_json,
2357
+ expires_at = excluded.expires_at
2358
+ `,
2359
+ args: [
2360
+ randomUUID(),
2361
+ input.kind,
2362
+ input.sessionKey,
2363
+ input.source,
2364
+ input.sourceId,
2365
+ input.sourceRef ?? null,
2366
+ input.contentHash,
2367
+ input.summary,
2368
+ serializeOptionalJson(input.metadata),
2369
+ createdAt,
2370
+ input.expiresAt ?? null
2371
+ ]
2372
+ });
2373
+ const artifact = await getSessionArtifactBySource(executor, input.kind, input.source, input.sourceId);
2374
+ if (!artifact) {
2375
+ throw new Error(`Session artifact ${input.kind}/${input.source}/${input.sourceId} was not found after write.`);
2376
+ }
2377
+ return artifact;
2378
+ }
2379
+ async function listSessionArtifacts(executor, filter) {
2380
+ const sessionKey = filter.sessionKey.trim();
2381
+ if (!sessionKey) {
2382
+ return [];
2383
+ }
2384
+ return querySessionArtifacts(executor, {
2385
+ conditions: ["session_key = ?"],
2386
+ args: [sessionKey],
2387
+ kinds: filter.kinds,
2388
+ limit: filter.limit
2389
+ });
2390
+ }
2391
+ async function listSessionArtifactsBySourceRef(executor, filter) {
2392
+ const sourceRef = filter.sourceRef.trim();
2393
+ if (!sourceRef) {
2394
+ return [];
2395
+ }
2396
+ return querySessionArtifacts(executor, {
2397
+ conditions: ["source_ref = ?"],
2398
+ args: [sourceRef],
2399
+ kinds: filter.kinds,
2400
+ limit: filter.limit
2401
+ });
2402
+ }
2403
+ async function getLatestLineageEdgeForChild(executor, childSessionKey) {
2404
+ const normalizedChild = childSessionKey.trim();
2405
+ if (!normalizedChild) {
2406
+ return null;
2407
+ }
2408
+ const result = await executor.execute({
2409
+ sql: `
2410
+ SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
2411
+ FROM session_lineage_edges
2412
+ WHERE child_session_key = ?
2413
+ ORDER BY observed_at DESC, id ASC
2414
+ LIMIT 1
2415
+ `,
2416
+ args: [normalizedChild]
2417
+ });
2418
+ const row = result.rows[0];
2419
+ return row ? mapSessionLineageEdgeRow(row) : null;
2420
+ }
2421
+ async function findMatchingLineageEdge(executor, input) {
2422
+ const parentSessionKeyClause = input.parentSessionKey ? "parent_session_key = ?" : "parent_session_key IS NULL";
2423
+ const parentSourceRefClause = input.parentSourceRef ? "parent_source_ref = ?" : "parent_source_ref IS NULL";
2424
+ const args = [
2425
+ input.childSessionKey,
2426
+ input.reason,
2427
+ ...input.parentSessionKey ? [input.parentSessionKey] : [],
2428
+ ...input.parentSourceRef ? [input.parentSourceRef] : []
2429
+ ];
2430
+ const result = await executor.execute({
2431
+ sql: `
2432
+ SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
2433
+ FROM session_lineage_edges
2434
+ WHERE child_session_key = ?
2435
+ AND reason = ?
2436
+ AND ${parentSessionKeyClause}
2437
+ AND ${parentSourceRefClause}
2438
+ ORDER BY observed_at DESC, id ASC
2439
+ LIMIT 1
2440
+ `,
2441
+ args
2442
+ });
2443
+ const row = result.rows[0];
2444
+ return row ? mapSessionLineageEdgeRow(row) : null;
2445
+ }
2446
+ async function getLineageEdge(executor, id) {
2447
+ const result = await executor.execute({
2448
+ sql: `
2449
+ SELECT ${SESSION_LINEAGE_EDGE_SELECT_COLUMNS}
2450
+ FROM session_lineage_edges
2451
+ WHERE id = ?
2452
+ LIMIT 1
2453
+ `,
2454
+ args: [id]
2455
+ });
2456
+ const row = result.rows[0];
2457
+ return row ? mapSessionLineageEdgeRow(row) : null;
2458
+ }
2459
+ async function getSessionArtifactBySource(executor, kind, source, sourceId) {
2460
+ const result = await executor.execute({
2461
+ sql: `
2462
+ SELECT ${SESSION_ARTIFACT_SELECT_COLUMNS}
2463
+ FROM session_artifacts
2464
+ WHERE kind = ?
2465
+ AND source = ?
2466
+ AND source_id = ?
2467
+ LIMIT 1
2468
+ `,
2469
+ args: [kind, source, sourceId]
2470
+ });
2471
+ const row = result.rows[0];
2472
+ return row ? mapSessionArtifactRow(row) : null;
2473
+ }
2474
+ function appendKindFilter(conditions, args, kinds) {
2475
+ if (kinds && kinds.length > 0) {
2476
+ conditions.push(`kind IN (${kinds.map(() => "?").join(", ")})`);
2477
+ args.push(...kinds);
2478
+ }
2479
+ }
2480
+ async function querySessionArtifacts(executor, filter) {
2481
+ const conditions = [...filter.conditions];
2482
+ const args = [...filter.args];
2483
+ appendKindFilter(conditions, args, filter.kinds);
2484
+ const limit = normalizeBoundedLimit(filter.limit, 20, 100);
2485
+ const result = await executor.execute({
2486
+ sql: `
2487
+ SELECT ${SESSION_ARTIFACT_SELECT_COLUMNS}
2488
+ FROM session_artifacts
2489
+ WHERE ${conditions.join(" AND ")}
2490
+ ORDER BY created_at DESC, id ASC
2491
+ LIMIT ?
2492
+ `,
2493
+ args: [...args, limit]
2494
+ });
2495
+ return result.rows.map((row) => mapSessionArtifactRow(row));
2496
+ }
2497
+ function mapSessionLineageEdgeRow(row) {
2498
+ return {
2499
+ id: readRequiredString(row, "id"),
2500
+ childSessionKey: readRequiredString(row, "child_session_key"),
2501
+ parentSessionKey: readOptionalString(row, "parent_session_key"),
2502
+ parentSourceRef: readOptionalString(row, "parent_source_ref"),
2503
+ reason: parseSessionLineageReason(readRequiredString(row, "reason")),
2504
+ forkEntryId: readOptionalString(row, "fork_entry_id"),
2505
+ forkPosition: readOptionalString(row, "fork_position"),
2506
+ observedAt: readRequiredString(row, "observed_at")
2507
+ };
2508
+ }
2509
+ function mapSessionArtifactRow(row) {
2510
+ return {
2511
+ id: readRequiredString(row, "id"),
2512
+ kind: parseSessionArtifactKind(readRequiredString(row, "kind")),
2513
+ sessionKey: readRequiredString(row, "session_key"),
2514
+ source: readRequiredString(row, "source"),
2515
+ sourceId: readRequiredString(row, "source_id"),
2516
+ sourceRef: readOptionalString(row, "source_ref"),
2517
+ contentHash: readRequiredString(row, "content_hash"),
2518
+ summary: readRequiredString(row, "summary"),
2519
+ metadata: parseOptionalJson(readOptionalString(row, "metadata_json"), "metadata_json"),
2520
+ createdAt: readRequiredString(row, "created_at"),
2521
+ expiresAt: readOptionalString(row, "expires_at")
2522
+ };
2523
+ }
2524
+
2525
+ // src/adapters/db/session-start-repository.ts
2526
+ function createSessionStartRepository(executor) {
2527
+ return {
2528
+ listCoreEntries: async (limit) => listCoreEntries(executor, limit)
2529
+ };
2530
+ }
2531
+ async function listCoreEntries(executor, limit) {
2532
+ if (limit <= 0) {
2533
+ return [];
2534
+ }
2535
+ const result = await executor.execute({
2536
+ sql: `
2537
+ SELECT
2538
+ ${ENTRY_SELECT_COLUMNS}
2539
+ FROM entries
2540
+ WHERE ${buildActiveEntryClause()}
2541
+ AND expiry = 'core'
2542
+ ORDER BY importance DESC, created_at DESC
2543
+ LIMIT ?
2544
+ `,
2545
+ args: [limit]
2546
+ });
2547
+ return result.rows.map((row) => mapEntryRow(row));
2548
+ }
2549
+
2550
+ // src/adapters/db/working-memory-repository.ts
2551
+ import { randomUUID as randomUUID2 } from "crypto";
2552
+
2553
+ // src/app/working-memory/operations/manifest.ts
2554
+ var MODEL_VISIBLE_OPERATION_TYPES = [
2555
+ "set_objective",
2556
+ "replace_plan",
2557
+ "merge_checkpoint",
2558
+ "add_file_note",
2559
+ "add_command_note",
2560
+ "record_decision",
2561
+ "record_assumption",
2562
+ "set_next_actions",
2563
+ "add_candidate"
2564
+ ];
2565
+ var TRUSTED_HOST_ONLY_OPERATION_TYPES = ["configure_budget", "account_usage", "set_continuation_policy"];
2566
+ var HOST_ONLY_OPERATION_TYPES = ["set_status", ...TRUSTED_HOST_ONLY_OPERATION_TYPES];
2567
+ var WORKING_UPDATE_OPERATION_TYPES = [...MODEL_VISIBLE_OPERATION_TYPES, ...HOST_ONLY_OPERATION_TYPES];
2568
+ function isModelVisibleOperationType(type) {
2569
+ return MODEL_VISIBLE_OPERATION_TYPES.includes(type);
2570
+ }
2571
+ function isTrustedHostOnlyWorkingOperation(type) {
2572
+ return TRUSTED_HOST_ONLY_OPERATION_TYPES.includes(type);
2573
+ }
2574
+
2575
+ // src/app/working-memory/constants.ts
2576
+ var OPEN_WORKING_SET_STATUSES = ["active", "paused", "blocked", "waiting", "needs_review", "budget_limited"];
2577
+ var CURRENT_WORKING_SET_STATUSES = [...OPEN_WORKING_SET_STATUSES, "complete"];
2578
+ var CLOSE_MANAGED_WORKING_SET_STATUSES = ["closed", "abandoned"];
2579
+ var TRUSTED_HOST_MUTATION_SOURCES = ["goal_command", "lifecycle_hook", "consolidation_job"];
2580
+ var WORKING_CANDIDATE_PROMOTION_STATUSES = ["pending", "promoted", "dismissed"];
2581
+ function isMutableWorkingSetStatus(status) {
2582
+ return OPEN_WORKING_SET_STATUSES.includes(status);
2583
+ }
2584
+ function isCloseManagedStatus(status) {
2585
+ return CLOSE_MANAGED_WORKING_SET_STATUSES.includes(status);
2586
+ }
2587
+ function isTrustedHostMutationSource(source) {
2588
+ return source !== void 0 && TRUSTED_HOST_MUTATION_SOURCES.includes(source);
2589
+ }
2590
+
2591
+ // src/adapters/db/working-memory-columns.ts
2592
+ var WORKING_SET_COLUMN_NAMES = [
2593
+ "id",
2594
+ "scope_key",
2595
+ "scope_kind",
2596
+ "title",
2597
+ "objective",
2598
+ "status",
2599
+ "summary",
2600
+ "snapshot_json",
2601
+ "revision",
2602
+ "project",
2603
+ "session_id",
2604
+ "conversation_key",
2605
+ "cwd",
2606
+ "git_root",
2607
+ "git_branch",
2608
+ "task_id",
2609
+ "source",
2610
+ "created_at",
2611
+ "updated_at",
2612
+ "last_active_at",
2613
+ "closed_at",
2614
+ "close_reason",
2615
+ "episode_id"
2616
+ ];
2617
+ var WORKING_SET_SELECT_COLUMNS = WORKING_SET_COLUMN_NAMES.join(",\n ");
2618
+ var WORKING_SET_INSERT_COLUMNS = WORKING_SET_COLUMN_NAMES.join(",\n ");
2619
+ var WORKING_SET_INSERT_PLACEHOLDERS = WORKING_SET_COLUMN_NAMES.map(() => "?").join(", ");
2620
+ var WORKING_SET_UPDATE_COLUMNS = [
2621
+ "title",
2622
+ "objective",
2623
+ "status",
2624
+ "summary",
2625
+ "snapshot_json",
2626
+ "revision",
2627
+ "updated_at",
2628
+ "last_active_at",
2629
+ "closed_at",
2630
+ "close_reason",
2631
+ "episode_id"
2632
+ ];
2633
+ var WORKING_SET_SEMANTIC_ONLY_UPDATE_COLUMNS = /* @__PURE__ */ new Set(["revision", "closed_at", "close_reason", "episode_id"]);
2634
+ var WORKING_SET_USAGE_PATCH_COLUMNS = WORKING_SET_UPDATE_COLUMNS.filter((column) => !WORKING_SET_SEMANTIC_ONLY_UPDATE_COLUMNS.has(column));
2635
+ var WORKING_EVENT_SELECT_COLUMNS = `
2636
+ id,
2637
+ working_set_id,
2638
+ sequence,
2639
+ event_type,
2640
+ payload_json,
2641
+ actor,
2642
+ source,
2643
+ host_event_id,
2644
+ turn_id,
2645
+ created_at
2646
+ `;
2647
+ function buildWorkingSetUpdateSetClause() {
2648
+ return WORKING_SET_UPDATE_COLUMNS.map((column) => `${column} = ?`).join(",\n ");
2649
+ }
2650
+ function buildWorkingSetUsagePatchSetClause() {
2651
+ return WORKING_SET_USAGE_PATCH_COLUMNS.map((column) => `${column} = ?`).join(",\n ");
2652
+ }
2653
+ function mapWorkingSetRow(row) {
2654
+ const snapshot = parseJson(readRequiredString(row, "snapshot_json"), "snapshot_json");
2655
+ return {
2656
+ id: readRequiredString(row, "id"),
2657
+ scopeKey: readRequiredString(row, "scope_key"),
2658
+ scopeKind: readRequiredString(row, "scope_kind"),
2659
+ title: readOptionalString(row, "title"),
2660
+ objective: readOptionalString(row, "objective"),
2661
+ status: readRequiredString(row, "status"),
2662
+ summary: readOptionalString(row, "summary"),
2663
+ snapshot,
2664
+ revision: readNumber(row, "revision", 0),
2665
+ project: readOptionalString(row, "project"),
2666
+ sessionId: readOptionalString(row, "session_id"),
2667
+ conversationKey: readOptionalString(row, "conversation_key"),
2668
+ cwd: readOptionalString(row, "cwd"),
2669
+ gitRoot: readOptionalString(row, "git_root"),
2670
+ gitBranch: readOptionalString(row, "git_branch"),
2671
+ taskId: readOptionalString(row, "task_id"),
2672
+ source: readOptionalString(row, "source"),
2673
+ createdAt: readRequiredString(row, "created_at"),
2674
+ updatedAt: readRequiredString(row, "updated_at"),
2675
+ lastActiveAt: readRequiredString(row, "last_active_at"),
2676
+ closedAt: readOptionalString(row, "closed_at"),
2677
+ closeReason: readOptionalString(row, "close_reason"),
2678
+ episodeId: readOptionalString(row, "episode_id")
2679
+ };
2680
+ }
2681
+ function mapWorkingEventRow(row) {
2682
+ return {
2683
+ id: readRequiredString(row, "id"),
2684
+ workingSetId: readRequiredString(row, "working_set_id"),
2685
+ sequence: readNumber(row, "sequence", 0),
2686
+ eventType: readRequiredString(row, "event_type"),
2687
+ payload: parseJson(readRequiredString(row, "payload_json"), "payload_json"),
2688
+ actor: readOptionalString(row, "actor"),
2689
+ source: readOptionalString(row, "source"),
2690
+ hostEventId: readOptionalString(row, "host_event_id"),
2691
+ turnId: readOptionalString(row, "turn_id"),
2692
+ createdAt: readRequiredString(row, "created_at")
2693
+ };
2694
+ }
2695
+ function parseJson(value, column) {
2696
+ try {
2697
+ return JSON.parse(value);
2698
+ } catch (error) {
2699
+ const message = error instanceof Error ? error.message : String(error);
2700
+ throw new Error(`Invalid JSON in working-memory column ${column}: ${message}`, { cause: error });
2701
+ }
2702
+ }
2703
+
2704
+ // src/adapters/db/working-memory-repository.ts
2705
+ function createWorkingMemoryRepository(database) {
2706
+ return {
2707
+ getWorkingSet: (id) => getWorkingSet(database, id),
2708
+ findCurrentWorkingSets: (scope) => findWorkingSetsByScope(database, scope, CURRENT_WORKING_SET_STATUSES),
2709
+ listWorkingSets: (filter) => listWorkingSets(database, filter),
2710
+ listWorkingEvents: (workingSetId, limit) => listWorkingEvents(database, workingSetId, limit),
2711
+ createWorkingSet: (input) => createWorkingSet(database, input),
2712
+ updateWorkingSet: (input) => updateWorkingSet(database, input),
2713
+ patchWorkingSetUsage: (input) => patchWorkingSetUsage(database, input)
2714
+ };
2715
+ }
2716
+ async function getWorkingSet(executor, id) {
2717
+ const normalizedId = id.trim();
2718
+ if (!normalizedId) {
2719
+ return null;
2720
+ }
2721
+ const result = await executor.execute({
2722
+ sql: `
2723
+ SELECT ${WORKING_SET_SELECT_COLUMNS}
2724
+ FROM working_sets
2725
+ WHERE id = ?
2726
+ LIMIT 1
2727
+ `,
2728
+ args: [normalizedId]
2729
+ });
2730
+ const row = result.rows[0];
2731
+ return row ? mapWorkingSetRow(row) : null;
2732
+ }
2733
+ async function findWorkingSetsByScope(executor, scope, statuses) {
2734
+ const result = await executor.execute({
2735
+ sql: `
2736
+ SELECT ${WORKING_SET_SELECT_COLUMNS}
2737
+ FROM working_sets
2738
+ WHERE scope_key = ?
2739
+ AND status IN (${statuses.map(() => "?").join(", ")})
2740
+ ORDER BY last_active_at DESC, id ASC
2741
+ `,
2742
+ args: [scope.scopeKey, ...statuses]
2743
+ });
2744
+ return result.rows.map((row) => mapWorkingSetRow(row));
2745
+ }
2746
+ async function listWorkingSets(executor, filter) {
2747
+ const conditions = [];
2748
+ const args = [];
2749
+ if (filter.scope) {
2750
+ conditions.push("scope_key = ?");
2751
+ args.push(filter.scope.scopeKey);
2752
+ }
2753
+ if (filter.statuses && filter.statuses.length > 0) {
2754
+ conditions.push(`status IN (${filter.statuses.map(() => "?").join(", ")})`);
2755
+ args.push(...filter.statuses);
2756
+ }
2757
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2758
+ const limit = normalizeBoundedLimit(filter.limit, 20, 100);
2759
+ const result = await executor.execute({
2760
+ sql: `
2761
+ SELECT ${WORKING_SET_SELECT_COLUMNS}
2762
+ FROM working_sets
2763
+ ${where}
2764
+ ORDER BY last_active_at DESC, id ASC
2765
+ LIMIT ?
2766
+ `,
2767
+ args: [...args, limit]
2768
+ });
2769
+ return result.rows.map((row) => mapWorkingSetRow(row));
2770
+ }
2771
+ async function listWorkingEvents(executor, workingSetId, limit) {
2772
+ const normalizedId = workingSetId.trim();
2773
+ if (!normalizedId) {
2774
+ return [];
2775
+ }
2776
+ const normalizedLimit = normalizeBoundedLimit(limit, 50, 1e3);
2777
+ const result = await executor.execute({
2778
+ sql: `
2779
+ SELECT ${WORKING_EVENT_SELECT_COLUMNS}
2780
+ FROM working_events
2781
+ WHERE working_set_id = ?
2782
+ ORDER BY sequence DESC
2783
+ LIMIT ?
2784
+ `,
2785
+ args: [normalizedId, normalizedLimit]
2786
+ });
2787
+ return result.rows.map((row) => mapWorkingEventRow(row)).reverse();
2788
+ }
2789
+ async function createWorkingSet(database, input) {
2790
+ return database.withTransaction(async (transaction) => {
2791
+ const executor = transaction;
2792
+ const existing = await findWorkingSetsByScope(executor, input.scope, CURRENT_WORKING_SET_STATUSES);
2793
+ if (existing.length > 0) {
2794
+ return { kind: "active_set_exists", scopeKey: input.scope.scopeKey };
2795
+ }
2796
+ const id = randomUUID2();
2797
+ const event = buildEvent({
2798
+ workingSetId: id,
2799
+ sequence: 1,
2800
+ eventType: "created",
2801
+ payload: {
2802
+ objective: input.objective,
2803
+ scope: input.scope
2804
+ },
2805
+ actor: input.actor,
2806
+ source: input.source,
2807
+ now: input.now
2808
+ });
2809
+ await executor.execute({
2810
+ sql: `
2811
+ INSERT INTO working_sets (
2812
+ ${WORKING_SET_INSERT_COLUMNS}
2813
+ )
2814
+ VALUES (
2815
+ ${WORKING_SET_INSERT_PLACEHOLDERS}
2816
+ )
2817
+ `,
2818
+ args: [
2819
+ id,
2820
+ input.scope.scopeKey,
2821
+ input.scope.scopeKind,
2822
+ toNullableString(input.title),
2823
+ toNullableString(input.objective),
2824
+ input.status,
2825
+ toNullableString(input.snapshot.summary),
2826
+ serializeJson(input.snapshot),
2827
+ 1,
2828
+ toNullableString(input.scope.project),
2829
+ toNullableString(input.sessionId ?? input.scope.sessionId),
2830
+ toNullableString(input.scope.conversationKey),
2831
+ toNullableString(input.scope.cwd),
2832
+ toNullableString(input.scope.gitRoot),
2833
+ toNullableString(input.scope.gitBranch),
2834
+ toNullableString(input.scope.taskId),
2835
+ toNullableString(input.sourceLabel),
2836
+ input.now,
2837
+ input.now,
2838
+ input.now,
2839
+ null,
2840
+ null,
2841
+ null
2842
+ ]
2843
+ });
2844
+ await insertWorkingEvent(executor, event);
2845
+ const workingSet = await requireWorkingSet(executor, id);
2846
+ return { workingSet, event };
2847
+ });
2848
+ }
2849
+ async function updateWorkingSet(database, input) {
2850
+ return database.withTransaction(async (transaction) => {
2851
+ const executor = transaction;
2852
+ const current = await getWorkingSet(executor, input.workingSetId);
2853
+ if (!current) {
2854
+ return { kind: "not_found" };
2855
+ }
2856
+ if (!canApplyWorkingSetUpdate(current.status, input.status)) {
2857
+ return { kind: "terminal_status", status: current.status };
2858
+ }
2859
+ if (current.revision !== input.expectedRevision) {
2860
+ return { kind: "revision_conflict", actualRevision: current.revision };
2861
+ }
2862
+ const nextRevision = current.revision + 1;
2863
+ const event = buildEvent({
2864
+ workingSetId: current.id,
2865
+ sequence: nextRevision,
2866
+ eventType: input.eventType,
2867
+ payload: input.payload,
2868
+ actor: input.actor,
2869
+ source: input.source,
2870
+ now: input.now
2871
+ });
2872
+ await executor.execute({
2873
+ sql: `
2874
+ UPDATE working_sets
2875
+ SET ${buildWorkingSetUpdateSetClause()}
2876
+ WHERE id = ?
2877
+ AND revision = ?
2878
+ `,
2879
+ args: [
2880
+ ...buildWorkingSetSnapshotUpdateArgs({
2881
+ title: input.title,
2882
+ objective: input.objective,
2883
+ status: input.status,
2884
+ snapshot: input.snapshot,
2885
+ current
2886
+ }),
2887
+ nextRevision,
2888
+ input.now,
2889
+ input.now,
2890
+ toNullableString(input.closedAt ?? current.closedAt),
2891
+ toNullableString(input.closeReason ?? current.closeReason),
2892
+ toNullableString(input.episodeId ?? current.episodeId),
2893
+ current.id,
2894
+ input.expectedRevision
2895
+ ]
2896
+ });
2897
+ const workingSet = await requireWorkingSet(executor, current.id);
2898
+ if (workingSet.revision !== nextRevision) {
2899
+ return { kind: "revision_conflict", actualRevision: workingSet.revision };
2900
+ }
2901
+ await insertWorkingEvent(executor, event);
2902
+ return { workingSet, event };
2903
+ });
2904
+ }
2905
+ async function patchWorkingSetUsage(database, input) {
2906
+ return database.withTransaction(async (transaction) => {
2907
+ const executor = transaction;
2908
+ const current = await getWorkingSet(executor, input.workingSetId);
2909
+ if (!current) {
2910
+ return { kind: "not_found" };
2911
+ }
2912
+ if (!canApplyWorkingSetUpdate(current.status, input.status)) {
2913
+ return { kind: "terminal_status", status: current.status };
2914
+ }
2915
+ if (current.revision !== input.expectedRevision) {
2916
+ return { kind: "revision_conflict", actualRevision: current.revision };
2917
+ }
2918
+ await executor.execute({
2919
+ sql: `
2920
+ UPDATE working_sets
2921
+ SET ${buildWorkingSetUsagePatchSetClause()}
2922
+ WHERE id = ?
2923
+ AND revision = ?
2924
+ `,
2925
+ args: [
2926
+ ...buildWorkingSetSnapshotUpdateArgs({
2927
+ title: input.title,
2928
+ objective: input.objective,
2929
+ status: input.status,
2930
+ snapshot: input.snapshot,
2931
+ current
2932
+ }),
2933
+ input.now,
2934
+ input.now,
2935
+ current.id,
2936
+ input.expectedRevision
2937
+ ]
2938
+ });
2939
+ const workingSet = await requireWorkingSet(executor, current.id);
2940
+ if (workingSet.revision !== input.expectedRevision) {
2941
+ return { kind: "revision_conflict", actualRevision: workingSet.revision };
2942
+ }
2943
+ return { workingSet };
2944
+ });
2945
+ }
2946
+ async function insertWorkingEvent(executor, event) {
2947
+ await executor.execute({
2948
+ sql: `
2949
+ INSERT INTO working_events (
2950
+ id,
2951
+ working_set_id,
2952
+ sequence,
2953
+ event_type,
2954
+ payload_json,
2955
+ actor,
2956
+ source,
2957
+ host_event_id,
2958
+ turn_id,
2959
+ created_at
2960
+ )
2961
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2962
+ `,
2963
+ args: [
2964
+ event.id,
2965
+ event.workingSetId,
2966
+ event.sequence,
2967
+ event.eventType,
2968
+ serializeJson(event.payload),
2969
+ toNullableString(event.actor),
2970
+ toNullableString(event.source),
2971
+ toNullableString(event.hostEventId),
2972
+ toNullableString(event.turnId),
2973
+ event.createdAt
2974
+ ]
2975
+ });
2976
+ }
2977
+ function buildWorkingSetSnapshotUpdateArgs(input) {
2978
+ return [
2979
+ toNullableString(input.title ?? input.current.title),
2980
+ toNullableString(input.objective ?? input.snapshot.objective),
2981
+ input.status,
2982
+ toNullableString(input.snapshot.summary),
2983
+ serializeJson(input.snapshot)
2984
+ ];
2985
+ }
2986
+ function buildEvent(input) {
2987
+ return {
2988
+ id: randomUUID2(),
2989
+ workingSetId: input.workingSetId,
2990
+ sequence: input.sequence,
2991
+ eventType: input.eventType,
2992
+ payload: input.payload,
2993
+ ...input.actor ? { actor: input.actor } : {},
2994
+ ...input.source ? { source: input.source } : {},
2995
+ createdAt: input.now
2996
+ };
2997
+ }
2998
+ function canApplyWorkingSetUpdate(currentStatus, nextStatus) {
2999
+ if (OPEN_WORKING_SET_STATUSES.includes(currentStatus)) {
3000
+ return true;
3001
+ }
3002
+ return currentStatus === "complete" && CLOSE_MANAGED_WORKING_SET_STATUSES.includes(nextStatus);
3003
+ }
3004
+ async function requireWorkingSet(executor, id) {
3005
+ const workingSet = await getWorkingSet(executor, id);
3006
+ if (!workingSet) {
3007
+ throw new Error(`Working set ${id} was not found after write.`);
3008
+ }
3009
+ return workingSet;
3010
+ }
3011
+ function serializeJson(value) {
3012
+ return JSON.stringify(value);
3013
+ }
3014
+ function toNullableString(value) {
3015
+ const trimmed = value?.trim();
3016
+ return trimmed && trimmed.length > 0 ? trimmed : null;
1187
3017
  }
1188
- function scheduleSkelnSessionShutdownEpisodeWrite(params) {
1189
- const work = buildSkelnSessionShutdownEpisodeWork(params);
1190
- if (params.event.reason === "quit" && params.event.deferWork) {
1191
- params.event.deferWork(work);
1192
- return Promise.resolve();
3018
+
3019
+ // src/adapters/plugin-runtime/embed-query.ts
3020
+ function createEmbedQuery(embedding, available) {
3021
+ if (!available) {
3022
+ return void 0;
1193
3023
  }
1194
- if (params.event.reason === "quit") {
1195
- return work;
3024
+ return async (text) => {
3025
+ const vectors = await embedding.embed([text]);
3026
+ const vector = vectors[0];
3027
+ if (!vector) {
3028
+ throw new Error("Embedding provider returned no vector for the query.");
3029
+ }
3030
+ return vector;
3031
+ };
3032
+ }
3033
+
3034
+ // src/adapters/plugin-runtime/create-memory-runtime.ts
3035
+ async function createPluginMemoryRuntime(input) {
3036
+ const embeddingStatus = resolveEmbeddingStatus(input.agenrConfig);
3037
+ const database = await createDatabase(input.dbPath);
3038
+ const embedding = embeddingStatus.available ? createEmbeddingClient(requireApiKey(embeddingStatus), embeddingStatus.model) : createUnavailableEmbeddingPort(embeddingStatus.error ?? "Embeddings are unavailable.");
3039
+ const baseRecall = createRecallAdapter(database, embedding);
3040
+ const recall2 = attachCrossEncoderPort(baseRecall, resolveCrossEncoder(input.agenrConfig));
3041
+ const slotPolicies = input.slotPolicies;
3042
+ let closed = false;
3043
+ return {
3044
+ entries: database,
3045
+ episodes: database,
3046
+ procedures: database,
3047
+ memory: createMemoryRepository(database, {
3048
+ claimSlotPolicyConfig: slotPolicies
3049
+ }),
3050
+ workingMemoryRepository: createWorkingMemoryRepository(database),
3051
+ sessionMemoryRepository: createSessionMemoryRepository(database),
3052
+ sessionStart: {
3053
+ repository: createSessionStartRepository(database),
3054
+ recall: recall2,
3055
+ slotPolicyConfig: slotPolicies
3056
+ },
3057
+ beforeTurn: {
3058
+ recall: recall2,
3059
+ procedures: database,
3060
+ embedQuery: createEmbedQuery(embedding, embeddingStatus.available),
3061
+ slotPolicyConfig: slotPolicies
3062
+ },
3063
+ embedding,
3064
+ recall: recall2,
3065
+ claimExtraction: input.claimExtraction,
3066
+ embeddingStatus: toPublicEmbeddingStatus(embeddingStatus),
3067
+ async close() {
3068
+ if (closed) {
3069
+ return;
3070
+ }
3071
+ closed = true;
3072
+ if (input.onBeforeClose) {
3073
+ await input.onBeforeClose();
3074
+ }
3075
+ await database.close();
3076
+ }
3077
+ };
3078
+ }
3079
+ function resolveEmbeddingStatus(config) {
3080
+ const model = resolveEmbeddingModel(config);
3081
+ try {
3082
+ return {
3083
+ available: true,
3084
+ provider: "openai",
3085
+ requestedProvider: "openai",
3086
+ model,
3087
+ apiKey: resolveEmbeddingApiKey(config)
3088
+ };
3089
+ } catch (error) {
3090
+ return {
3091
+ available: false,
3092
+ provider: "unconfigured",
3093
+ requestedProvider: "openai",
3094
+ model,
3095
+ error: error instanceof Error ? error.message : String(error)
3096
+ };
1196
3097
  }
1197
- void work;
1198
- return Promise.resolve();
1199
3098
  }
1200
- function logSkelnShutdownEpisodeFailure(error, logger) {
1201
- const log = logger ?? console;
1202
- log.warn(`[agenr] skeln shutdown episode failed: ${formatErrorMessage(error)}`);
3099
+ function toPublicEmbeddingStatus(status) {
3100
+ return {
3101
+ available: status.available,
3102
+ provider: status.provider,
3103
+ requestedProvider: status.requestedProvider,
3104
+ model: status.model,
3105
+ ...status.error ? { error: status.error } : {}
3106
+ };
1203
3107
  }
1204
- async function closeSkelnServicesAfterShutdown(servicesPromise) {
3108
+ function createUnavailableEmbeddingPort(errorMessage) {
3109
+ return {
3110
+ async embed() {
3111
+ throw new Error(errorMessage);
3112
+ }
3113
+ };
3114
+ }
3115
+ function requireApiKey(status) {
3116
+ if (!status.apiKey) {
3117
+ throw new Error("Embedding API key is unavailable.");
3118
+ }
3119
+ return status.apiKey;
3120
+ }
3121
+ function resolveCrossEncoder(config) {
1205
3122
  try {
1206
- const services = await servicesPromise;
1207
- await services.close();
3123
+ const apiKey = resolveCrossEncoderApiKey(config);
3124
+ const { modelId } = resolveModel(config, "cross_encoder");
3125
+ return createOpenAICrossEncoder({ apiKey, model: modelId });
1208
3126
  } catch {
3127
+ return void 0;
1209
3128
  }
1210
3129
  }
1211
- async function writeScopedSkelnShutdownEpisode(servicesPromise, target, logger) {
1212
- const services = await servicesPromise;
1213
- if (!services.capabilities.shutdownEpisodes) {
1214
- return;
1215
- }
1216
- await writeSkelnShutdownEpisode({ target, services, logger });
3130
+
3131
+ // src/adapters/plugin-runtime/compose-host-services.ts
3132
+ async function composeHostPluginServices(input) {
3133
+ const { resolvedConfig, agenrConfig } = resolvePluginRuntimeConfig(input.config, input.resolvePath);
3134
+ const claimExtraction = input.resolveClaimExtraction ? await input.resolveClaimExtraction({ agenrConfig, hostConfig: input.config }) : void 0;
3135
+ const runtimeServices = await createPluginMemoryRuntime({
3136
+ dbPath: resolvedConfig.dbPath,
3137
+ agenrConfig,
3138
+ slotPolicies: input.readSlotPolicies?.(input.config),
3139
+ claimExtraction,
3140
+ onBeforeClose: input.onBeforeClose
3141
+ });
3142
+ return input.extend({
3143
+ config: input.config,
3144
+ resolvedConfig,
3145
+ agenrConfig,
3146
+ runtimeServices
3147
+ });
1217
3148
  }
1218
3149
 
1219
3150
  // src/app/features/resolve.ts
@@ -2574,6 +4505,391 @@ function createSkelnSessionScopeTracker() {
2574
4505
  };
2575
4506
  }
2576
4507
 
4508
+ // src/adapters/shared/claim-support.ts
4509
+ function buildSessionSourceFile(session, prefix) {
4510
+ const target = session.sessionKey ?? session.sessionId ?? session.agentId ?? "unknown";
4511
+ return `${prefix}:${target}`;
4512
+ }
4513
+ function buildToolCallClaimSupport(session, prefix, toolName, observedAt) {
4514
+ return {
4515
+ claim_support_source_kind: "tool_call",
4516
+ claim_support_locator: `${buildSessionSourceFile(session, prefix)}#${toolName}`,
4517
+ claim_support_observed_at: observedAt,
4518
+ claim_support_mode: "explicit"
4519
+ };
4520
+ }
4521
+
4522
+ // src/adapters/shared/memory-tools.ts
4523
+ function buildRecallToolServices(services) {
4524
+ return {
4525
+ episodes: services.episodes,
4526
+ procedures: services.procedures,
4527
+ recall: services.recall,
4528
+ embeddingStatus: services.embeddingStatus,
4529
+ embedQuery: services.beforeTurn.embedQuery
4530
+ };
4531
+ }
4532
+ var STORE_TOOL_PARAMETERS = {
4533
+ type: "object",
4534
+ additionalProperties: false,
4535
+ properties: {
4536
+ type: {
4537
+ type: "string",
4538
+ enum: [...ENTRY_TYPES],
4539
+ description: ENTRY_TYPE_DESCRIPTION
4540
+ },
4541
+ subject: {
4542
+ type: "string",
4543
+ description: "Short subject line future recall can match. Name the durable takeaway, person, system, rule, relationship, or milestone directly."
4544
+ },
4545
+ content: {
4546
+ type: "string",
4547
+ description: "What a fresh session should remember. Store the durable takeaway, not the activity log, canonical record, or transient progress snapshot."
4548
+ },
4549
+ importance: {
4550
+ type: "integer",
4551
+ minimum: 1,
4552
+ maximum: 10,
4553
+ description: "Importance from 1 to 10. Use 7 for normal durable memory, 9 for critical constraints, and 10 only rarely."
4554
+ },
4555
+ expiry: {
4556
+ type: "string",
4557
+ enum: ["core", "permanent", "temporary"],
4558
+ description: EXPIRY_DESCRIPTION
4559
+ },
4560
+ tags: {
4561
+ type: "array",
4562
+ items: { type: "string" },
4563
+ description: "Optional tags for entities, systems, teams, or themes that should improve later recall."
4564
+ },
4565
+ sourceContext: {
4566
+ type: "string",
4567
+ description: "Optional provenance note explaining why this memory was stored or what situation produced it."
4568
+ },
4569
+ supersedes: {
4570
+ type: "string",
4571
+ description: "ID of an entry this replaces. The old entry will be marked as superseded."
4572
+ },
4573
+ claimKey: {
4574
+ type: "string",
4575
+ description: 'Slot key identifying the specific knowledge slot (entity/attribute format, e.g., "project_name/deploy_strategy" or "postgres/max_connections"). Entries with the same claim key are candidates for supersession.'
4576
+ },
4577
+ validFrom: {
4578
+ type: "string",
4579
+ description: "ISO 8601 timestamp for when this fact became true in the world."
4580
+ },
4581
+ validTo: {
4582
+ type: "string",
4583
+ description: "ISO 8601 timestamp for when this fact stopped being true."
4584
+ }
4585
+ },
4586
+ required: ["type", "subject", "content"]
4587
+ };
4588
+ var RECALL_TOOL_PARAMETERS = {
4589
+ type: "object",
4590
+ additionalProperties: false,
4591
+ properties: {
4592
+ query: {
4593
+ type: "string",
4594
+ 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'."
4595
+ },
4596
+ mode: {
4597
+ type: "string",
4598
+ enum: [...RECALL_MODES],
4599
+ description: "Recall mode: auto routes between exact entry recall, historical-state recall, procedural recall, and episodes; entries forces semantic recall; episodes forces temporal or semantic session recall; procedures forces procedural recall."
4600
+ },
4601
+ limit: {
4602
+ type: "integer",
4603
+ minimum: 1,
4604
+ maximum: 10,
4605
+ description: "Maximum results to return. Lower this when you want a tighter shortlist."
4606
+ },
4607
+ threshold: {
4608
+ type: "number",
4609
+ minimum: 0,
4610
+ maximum: 1,
4611
+ description: "Minimum final score from 0 to 1. Raise this when you want fewer, higher-confidence matches."
4612
+ },
4613
+ budget: {
4614
+ type: "integer",
4615
+ minimum: 1,
4616
+ description: "Approximate token budget applied after entry scoring. Omit when you do not want a budget cap."
4617
+ },
4618
+ types: {
4619
+ type: "array",
4620
+ items: {
4621
+ type: "string",
4622
+ enum: [...ENTRY_TYPES]
4623
+ },
4624
+ description: "Optional knowledge types to filter by, such as decision, preference, lesson, fact, milestone, or relationship."
4625
+ },
4626
+ tags: {
4627
+ type: "array",
4628
+ items: { type: "string" },
4629
+ description: "Optional tags to filter by once you already know the relevant entity, system, or theme."
4630
+ },
4631
+ asOf: {
4632
+ type: "string",
4633
+ description: "Optional reference time for current-vs-prior resolution. Supports ISO timestamps and natural-language date phrases."
4634
+ }
4635
+ },
4636
+ required: ["query"]
4637
+ };
4638
+ var UPDATE_TOOL_PARAMETERS = {
4639
+ type: "object",
4640
+ additionalProperties: false,
4641
+ properties: {
4642
+ id: {
4643
+ type: "string",
4644
+ description: "Entry id to update. Provide exactly one of id or subject."
4645
+ },
4646
+ subject: {
4647
+ type: "string",
4648
+ 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."
4649
+ },
4650
+ importance: {
4651
+ type: "integer",
4652
+ description: "New importance from 1 to 10. Use 7 for normal durable memory and reserve 9 to 10 for rare critical entries."
4653
+ },
4654
+ expiry: {
4655
+ type: "string",
4656
+ description: UPDATE_EXPIRY_DESCRIPTION
4657
+ },
4658
+ claimKey: {
4659
+ type: "string",
4660
+ description: 'Slot key identifying the specific knowledge slot (entity/attribute format, e.g., "project_name/deploy_strategy" or "postgres/max_connections"). Entries with the same claim key are candidates for supersession.'
4661
+ },
4662
+ validFrom: {
4663
+ type: "string",
4664
+ description: "ISO 8601 timestamp for when this fact became true."
4665
+ },
4666
+ validTo: {
4667
+ type: "string",
4668
+ description: "ISO 8601 timestamp for when this fact stopped being true."
4669
+ }
4670
+ }
4671
+ };
4672
+ var FETCH_TOOL_PARAMETERS = {
4673
+ type: "object",
4674
+ additionalProperties: false,
4675
+ properties: {
4676
+ id: {
4677
+ type: "string",
4678
+ description: "Entry id to fetch. Provide exactly one of id or subject."
4679
+ },
4680
+ subject: {
4681
+ type: "string",
4682
+ 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."
4683
+ }
4684
+ }
4685
+ };
4686
+ function parseStoreToolParams(rawParams, reader) {
4687
+ const params = asRecord(rawParams);
4688
+ const type = parseEntryType(reader.readString(params, "type", { required: true, label: "type" }) ?? "");
4689
+ return {
4690
+ type,
4691
+ subject: reader.readString(params, "subject", { required: true, label: "subject" }) ?? "",
4692
+ content: reader.readString(params, "content", { required: true, label: "content" }) ?? "",
4693
+ importance: reader.readNumber(params, "importance", { integer: true, strict: true }),
4694
+ expiry: parseExpiry(reader.readString(params, "expiry")),
4695
+ tags: normalizeStringArray(reader.readStringArray(params, "tags")),
4696
+ sourceContext: reader.readString(params, "sourceContext"),
4697
+ supersedes: reader.readString(params, "supersedes"),
4698
+ claimKey: reader.readString(params, "claimKey", { trim: false }),
4699
+ validFrom: reader.readString(params, "validFrom"),
4700
+ validTo: reader.readString(params, "validTo")
4701
+ };
4702
+ }
4703
+ function parseRecallToolParams(rawParams, reader) {
4704
+ const params = asRecord(rawParams);
4705
+ const budget = reader.readNumber(params, "budget", { integer: true, strict: true });
4706
+ if (budget !== void 0 && budget <= 0) {
4707
+ throw new Error("budget must be a positive integer.");
4708
+ }
4709
+ return {
4710
+ query: reader.readString(params, "query", { required: true, label: "query" }) ?? "",
4711
+ mode: parseRecallMode(reader.readString(params, "mode")),
4712
+ limit: reader.readNumber(params, "limit", { integer: true, strict: true }),
4713
+ threshold: reader.readNumber(params, "threshold", { strict: true }),
4714
+ budget,
4715
+ types: parseEntryTypes(reader.readStringArray(params, "types")),
4716
+ tags: normalizeStringArray(reader.readStringArray(params, "tags")),
4717
+ asOf: reader.readString(params, "asOf")
4718
+ };
4719
+ }
4720
+ function parseUpdateToolParams(rawParams, reader) {
4721
+ const params = asRecord(rawParams);
4722
+ return {
4723
+ id: reader.readString(params, "id"),
4724
+ subject: reader.readString(params, "subject"),
4725
+ importance: reader.readNumber(params, "importance", { integer: true, strict: true }),
4726
+ expiry: parseExpiry(reader.readString(params, "expiry")),
4727
+ claimKeyInput: reader.readString(params, "claimKey", { trim: false }),
4728
+ validFrom: reader.readString(params, "validFrom"),
4729
+ validTo: reader.readString(params, "validTo")
4730
+ };
4731
+ }
4732
+ function parseFetchToolParams(rawParams, reader) {
4733
+ const params = asRecord(rawParams);
4734
+ return {
4735
+ id: reader.readString(params, "id"),
4736
+ subject: reader.readString(params, "subject")
4737
+ };
4738
+ }
4739
+ async function runStoreMemoryTool(params, services, options) {
4740
+ const result = await storeEntriesDetailed(
4741
+ [
4742
+ {
4743
+ type: params.type,
4744
+ subject: params.subject,
4745
+ content: params.content,
4746
+ ...params.importance !== void 0 ? { importance: params.importance } : {},
4747
+ ...params.expiry !== void 0 ? { expiry: params.expiry } : {},
4748
+ ...params.tags.length > 0 ? { tags: params.tags } : {},
4749
+ ...params.supersedes ? { supersedes: params.supersedes } : {},
4750
+ ...params.claimKey ? {
4751
+ claim_key: params.claimKey,
4752
+ claim_key_raw: params.claimKey,
4753
+ ...buildToolCallClaimSupport(options.session, options.sourcePrefix, "agenr_store", (/* @__PURE__ */ new Date()).toISOString())
4754
+ } : {},
4755
+ ...params.validFrom ? { valid_from: params.validFrom } : {},
4756
+ ...params.validTo ? { valid_to: params.validTo } : {},
4757
+ source_file: buildSessionSourceFile(options.session, options.sourcePrefix),
4758
+ source_context: params.sourceContext ?? options.defaultSourceContext,
4759
+ ...options.session.project ? { project: options.session.project } : {}
4760
+ }
4761
+ ],
4762
+ services.entries,
4763
+ services.embedding,
4764
+ {
4765
+ ...services.claimExtraction ? {
4766
+ claimExtraction: {
4767
+ llm: services.claimExtraction.llm,
4768
+ db: services.entries,
4769
+ config: services.claimExtraction.config
4770
+ }
4771
+ } : {},
4772
+ onWarning: options.onWarning
4773
+ }
4774
+ );
4775
+ const storedEntry = await services.memory.findEntryBySubject(params.subject);
4776
+ if (result.stored > 0) {
4777
+ return okOutcome(`Stored "${params.subject}".`, {
4778
+ status: "stored",
4779
+ subject: params.subject,
4780
+ entryId: storedEntry?.id,
4781
+ result,
4782
+ ...options.extraDetails
4783
+ });
4784
+ }
4785
+ if (result.skipped > 0) {
4786
+ return okOutcome(`Skipped "${params.subject}" because an active duplicate already exists.`, {
4787
+ status: "skipped",
4788
+ subject: params.subject,
4789
+ entryId: storedEntry?.id,
4790
+ result,
4791
+ ...options.extraDetails
4792
+ });
4793
+ }
4794
+ return failedOutcome(`Rejected "${params.subject}". Check the supplied type, content, and metadata.`, {
4795
+ status: "failed",
4796
+ subject: params.subject,
4797
+ result,
4798
+ ...options.extraDetails
4799
+ });
4800
+ }
4801
+ async function runRecallMemoryTool(params, services, options = {}) {
4802
+ return runUnifiedRecall(
4803
+ {
4804
+ text: params.query,
4805
+ ...params.mode ? { mode: params.mode } : {},
4806
+ ...params.limit !== void 0 ? { limit: params.limit } : {},
4807
+ ...params.threshold !== void 0 ? { threshold: params.threshold } : {},
4808
+ ...params.budget !== void 0 ? { budget: params.budget } : {},
4809
+ ...params.types.length > 0 ? { types: params.types } : {},
4810
+ ...params.tags.length > 0 ? { tags: params.tags } : {},
4811
+ ...params.asOf ? { asOf: params.asOf } : {},
4812
+ ...options.sessionKey ? { sessionKey: options.sessionKey } : {}
4813
+ },
4814
+ {
4815
+ database: services.episodes,
4816
+ procedures: services.procedures,
4817
+ recall: services.recall,
4818
+ embeddingAvailable: services.embeddingStatus.available,
4819
+ embeddingError: services.embeddingStatus.error,
4820
+ claimSlotPolicyConfig: options.slotPolicyConfig,
4821
+ debugLog: options.debugLog,
4822
+ embedQuery: services.embedQuery,
4823
+ recallOptions: {
4824
+ slotPolicyConfig: options.slotPolicyConfig
4825
+ }
4826
+ }
4827
+ );
4828
+ }
4829
+ async function runFetchMemoryTool(params, services, options = {}) {
4830
+ const entry = await resolveTargetEntry(buildEntryMemoryResolverPorts(services), { id: params.id, subject: params.subject });
4831
+ assertEntryFetchableContentLength(entry.content);
4832
+ return okOutcome(formatFetchedEntryText(entry), buildFetchToolDetails(entry, options.extraDetails));
4833
+ }
4834
+ async function runUpdateMemoryTool(params, services, options) {
4835
+ const claimSupport = params.claimKeyInput === void 0 ? void 0 : buildToolCallClaimSupport(options.session, options.sourcePrefix, "agenr_update", (/* @__PURE__ */ new Date()).toISOString());
4836
+ const normalizedClaimKeyUpdate = params.claimKeyInput === void 0 ? void 0 : (() => {
4837
+ try {
4838
+ return normalizeManualClaimKeyUpdate({
4839
+ claimKey: params.claimKeyInput,
4840
+ rawClaimKey: params.claimKeyInput,
4841
+ supportSourceKind: claimSupport?.claim_support_source_kind,
4842
+ supportLocator: claimSupport?.claim_support_locator,
4843
+ supportObservedAt: claimSupport?.claim_support_observed_at,
4844
+ supportMode: claimSupport?.claim_support_mode
4845
+ });
4846
+ } catch {
4847
+ throw new Error("claimKey must use canonical entity/attribute format.");
4848
+ }
4849
+ })();
4850
+ const entry = await resolveTargetEntry(buildEntryMemoryResolverPorts(services), { id: params.id, subject: params.subject });
4851
+ if (params.importance === void 0 && params.expiry === void 0 && normalizedClaimKeyUpdate === void 0 && params.validFrom === void 0 && params.validTo === void 0) {
4852
+ throw new Error("Provide at least one update field: importance, expiry, claimKey, validFrom, or validTo.");
4853
+ }
4854
+ const mergedValidity = validateTemporalValidityRange(params.validFrom ?? entry.valid_from, params.validTo ?? entry.valid_to);
4855
+ if (!mergedValidity.ok) {
4856
+ throw new Error(mergedValidity.message);
4857
+ }
4858
+ const normalizedValidFrom = params.validFrom !== void 0 ? mergedValidity.value.validFrom : void 0;
4859
+ const normalizedValidTo = params.validTo !== void 0 ? mergedValidity.value.validTo : void 0;
4860
+ const updated = await services.entries.updateEntry(entry.id, {
4861
+ ...params.importance !== void 0 ? { importance: params.importance } : {},
4862
+ ...params.expiry !== void 0 ? { expiry: params.expiry } : {},
4863
+ ...normalizedClaimKeyUpdate?.updateFields ?? {},
4864
+ ...params.validFrom !== void 0 ? { valid_from: normalizedValidFrom } : {},
4865
+ ...params.validTo !== void 0 ? { valid_to: normalizedValidTo } : {}
4866
+ });
4867
+ if (!updated) {
4868
+ return failedOutcome(`Entry ${entry.id} is not active, so it could not be updated.`, {
4869
+ status: "failed",
4870
+ entryId: entry.id,
4871
+ ...options.failureDetails
4872
+ });
4873
+ }
4874
+ return okOutcome(`Updated "${entry.subject}".`, {
4875
+ status: "updated",
4876
+ entryId: entry.id,
4877
+ subject: entry.subject,
4878
+ ...params.importance !== void 0 ? { importance: params.importance } : {},
4879
+ ...params.expiry !== void 0 ? { expiry: params.expiry } : {},
4880
+ ...normalizedClaimKeyUpdate !== void 0 ? { claimKey: normalizedClaimKeyUpdate.claimKey } : {},
4881
+ ...params.validFrom !== void 0 ? { validFrom: normalizedValidFrom } : {},
4882
+ ...params.validTo !== void 0 ? { validTo: normalizedValidTo } : {},
4883
+ ...options.successDetails
4884
+ });
4885
+ }
4886
+ function okOutcome(text, details) {
4887
+ return { text, details, failed: false };
4888
+ }
4889
+ function failedOutcome(text, details) {
4890
+ return { text, details, failed: true };
4891
+ }
4892
+
2577
4893
  // src/adapters/shared/param-readers.ts
2578
4894
  function readStringParam(params, key, options = {}) {
2579
4895
  const value = params[key];
@@ -2899,6 +5215,234 @@ function registerGoalAliasTool(skeln, servicesPromise, resolveScope, definition)
2899
5215
  });
2900
5216
  }
2901
5217
 
5218
+ // src/adapters/shared/recall-format.ts
5219
+ function formatUnifiedRecallResults(result) {
5220
+ const lines = [
5221
+ "Recall Route",
5222
+ `requested=${result.routing.requested} detected=${result.routing.detectedIntent} queried=${result.routing.queried.join(", ") || "none"}`,
5223
+ result.routing.reason,
5224
+ ""
5225
+ ];
5226
+ if (result.timeWindow) {
5227
+ lines.push("Resolved Time Window");
5228
+ lines.push(`${result.timeWindow.start} -> ${result.timeWindow.end} (${result.timeWindow.timezone}) from ${JSON.stringify(result.timeWindow.resolvedFrom)}`);
5229
+ lines.push("");
5230
+ }
5231
+ if (result.asOf) {
5232
+ lines.push("As Of");
5233
+ lines.push(result.asOf);
5234
+ lines.push("");
5235
+ }
5236
+ if (result.routing.queried.includes("procedures") || result.procedure || result.procedureCandidates.length > 0 || result.procedureNotices.length > 0) {
5237
+ appendProcedureMatches(lines, result);
5238
+ lines.push("");
5239
+ }
5240
+ const renderEntriesFirst = result.routing.detectedIntent === "historical_state";
5241
+ if (renderEntriesFirst) {
5242
+ appendEntryMatches(lines, result);
5243
+ lines.push("");
5244
+ appendClaimTransitions(lines, result);
5245
+ lines.push("");
5246
+ appendEpisodeMatches(lines, result);
5247
+ } else {
5248
+ appendEpisodeMatches(lines, result);
5249
+ lines.push("");
5250
+ appendEntryMatches(lines, result);
5251
+ lines.push("");
5252
+ appendClaimTransitions(lines, result);
5253
+ }
5254
+ if (recallResultHasTruncatedEntryPreviews(result)) {
5255
+ lines.push("");
5256
+ lines.push("Fetch Guidance");
5257
+ lines.push("One or more entry previews were truncated. Call agenr_fetch with id when exact stored wording is required.");
5258
+ }
5259
+ if (result.notices.length > 0) {
5260
+ lines.push("");
5261
+ lines.push("Notices");
5262
+ for (const notice of result.notices) {
5263
+ lines.push(`- ${notice}`);
5264
+ }
5265
+ }
5266
+ return lines.join("\n");
5267
+ }
5268
+ function appendProcedureMatches(lines, result) {
5269
+ lines.push("Procedure Matches");
5270
+ if (!result.procedure && result.procedureCandidates.length === 0) {
5271
+ lines.push("None.");
5272
+ } else {
5273
+ if (result.procedure) {
5274
+ appendCanonicalProcedure(lines, result.procedure, result.procedureCandidates);
5275
+ } else {
5276
+ lines.push("Canonical procedure: none.");
5277
+ }
5278
+ const additionalCandidates = result.procedureCandidates.filter((candidate) => candidate.procedure.id !== result.procedure?.id);
5279
+ if (additionalCandidates.length > 0) {
5280
+ lines.push("Other Candidates");
5281
+ for (const [index, candidate] of additionalCandidates.entries()) {
5282
+ lines.push(
5283
+ `${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)}`
5284
+ );
5285
+ }
5286
+ }
5287
+ }
5288
+ if (result.procedureNotices.length > 0) {
5289
+ lines.push("Procedure Notices");
5290
+ for (const notice of result.procedureNotices) {
5291
+ lines.push(`- ${notice}`);
5292
+ }
5293
+ }
5294
+ }
5295
+ function appendEntryMatches(lines, result) {
5296
+ lines.push("Entry Matches");
5297
+ if (result.projectedEntries.length === 0) {
5298
+ lines.push("None.");
5299
+ return;
5300
+ }
5301
+ for (const [familyIndex, family] of result.entryFamilies.entries()) {
5302
+ lines.push(
5303
+ 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}`
5304
+ );
5305
+ for (const [entryIndex, entry] of family.entries.entries()) {
5306
+ const preview = buildEntryRecallPreview(entry.recall.entry.content);
5307
+ lines.push(
5308
+ ` ${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)}`
5309
+ );
5310
+ lines.push(` ${preview.contentPreview}`);
5311
+ lines.push(` content_chars=${preview.contentChars} preview_truncated=${preview.previewTruncated ? "true" : "false"}`);
5312
+ lines.push(` freshness=${entry.freshness.label}`);
5313
+ const provenance = formatProjectedEntryProvenance(entry);
5314
+ if (provenance) {
5315
+ lines.push(` provenance=${provenance}`);
5316
+ }
5317
+ lines.push(` why_surfaced=${entry.whySurfaced.summary}`);
5318
+ }
5319
+ }
5320
+ }
5321
+ function appendEpisodeMatches(lines, result) {
5322
+ lines.push("Episode Matches");
5323
+ if (result.episodes.length === 0) {
5324
+ lines.push("None.");
5325
+ return;
5326
+ }
5327
+ for (const [index, episode] of result.episodes.entries()) {
5328
+ lines.push(
5329
+ `${index + 1}. ${episode.episode.id} | ${episode.episode.source} | ${episode.episode.startedAt} -> ${episode.episode.endedAt ?? episode.episode.startedAt} | score ${episode.score.toFixed(2)}`
5330
+ );
5331
+ lines.push(` ${index < 3 ? episode.episode.summary.trim() : truncate(episode.episode.summary.trim(), ENTRY_PREVIEW_MAX_CHARS)}`);
5332
+ lines.push(` why_matched=${describeEpisodeMatch(episode)}`);
5333
+ }
5334
+ }
5335
+ function appendCanonicalProcedure(lines, procedure, candidates) {
5336
+ const leadCandidate = candidates.find((candidate) => candidate.procedure.id === procedure.id);
5337
+ lines.push(
5338
+ leadCandidate ? `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title} | score ${leadCandidate.score.toFixed(2)}` : `Canonical Procedure. ${procedure.procedure_key} | ${procedure.title}`
5339
+ );
5340
+ lines.push(` goal=${procedure.goal}`);
5341
+ appendLabeledList(lines, "when_to_use", procedure.when_to_use);
5342
+ appendLabeledList(lines, "when_not_to_use", procedure.when_not_to_use);
5343
+ appendLabeledList(lines, "prerequisites", procedure.prerequisites);
5344
+ lines.push(" steps");
5345
+ for (const [index, step] of procedure.steps.entries()) {
5346
+ lines.push(` ${index + 1}. [${step.kind}] ${step.instruction}`);
5347
+ const stepDetails = formatProcedureStepDetails(step);
5348
+ if (stepDetails.length > 0) {
5349
+ for (const detail of stepDetails) {
5350
+ lines.push(` ${detail}`);
5351
+ }
5352
+ }
5353
+ }
5354
+ appendLabeledList(lines, "verification", procedure.verification);
5355
+ appendLabeledList(lines, "failure_modes", procedure.failure_modes);
5356
+ lines.push(" sources");
5357
+ for (const source of procedure.sources) {
5358
+ lines.push(` - ${formatProcedureSource(source)}`);
5359
+ }
5360
+ }
5361
+ function appendClaimTransitions(lines, result) {
5362
+ lines.push("Claim Transitions");
5363
+ if (result.claimTransitions.length === 0) {
5364
+ lines.push("None.");
5365
+ return;
5366
+ }
5367
+ for (const [index, transition] of result.claimTransitions.entries()) {
5368
+ lines.push(
5369
+ `${index + 1}. family=${transition.claimKey ?? transition.familyKey} | slot_policy=${transition.slotPolicy}${transition.currentEntryId ? ` | current=${transition.currentEntryId}` : ""}${transition.priorEntryId ? ` | prior=${transition.priorEntryId}` : ""}`
5370
+ );
5371
+ lines.push(` ${transition.summary}`);
5372
+ if (transition.episodeContext) {
5373
+ lines.push(
5374
+ ` episode=${transition.episodeContext.episodeId} | ${transition.episodeContext.startedAt} -> ${transition.episodeContext.endedAt ?? transition.episodeContext.startedAt}`
5375
+ );
5376
+ lines.push(` ${truncate(transition.episodeContext.summary.trim(), ENTRY_PREVIEW_MAX_CHARS)}`);
5377
+ }
5378
+ }
5379
+ }
5380
+ function appendLabeledList(lines, label, values) {
5381
+ lines.push(` ${label}`);
5382
+ if (values.length === 0) {
5383
+ lines.push(" - none");
5384
+ return;
5385
+ }
5386
+ for (const value of values) {
5387
+ lines.push(` - ${value}`);
5388
+ }
5389
+ }
5390
+ function formatProcedureStepDetails(step) {
5391
+ switch (step.kind) {
5392
+ case "run_command":
5393
+ return [`command=${step.command}`];
5394
+ case "read_reference":
5395
+ return [`ref=${formatProcedureSource(step.ref)}`];
5396
+ case "inspect_state":
5397
+ return [step.target ? `target=${step.target}` : void 0, step.query ? `query=${step.query}` : void 0].filter(
5398
+ (value) => value !== void 0
5399
+ );
5400
+ case "edit_file":
5401
+ return [`path=${step.path}`, `edit=${step.edit}`];
5402
+ case "ask_user":
5403
+ return [`prompt=${step.prompt}`];
5404
+ case "invoke_tool":
5405
+ return [step.tool ? `tool=${step.tool}` : void 0, step.arguments ? `arguments=${JSON.stringify(step.arguments)}` : void 0].filter(
5406
+ (value) => value !== void 0
5407
+ );
5408
+ case "verify":
5409
+ return step.checks.map((check) => `check=${check}`);
5410
+ default:
5411
+ return [];
5412
+ }
5413
+ }
5414
+ function formatProcedureSource(source) {
5415
+ const parts = [source.kind, source.label, source.path, source.locator].filter((value) => Boolean(value && value.length > 0));
5416
+ return parts.join(" | ");
5417
+ }
5418
+ function describeEpisodeMatch(result) {
5419
+ if (result.scores.semantic > 0 && result.scores.temporal > 0) {
5420
+ return "Semantic match within the resolved time window.";
5421
+ }
5422
+ if (result.scores.semantic > 0) {
5423
+ return "Semantic match to the episode summary.";
5424
+ }
5425
+ if (result.scores.temporal > 0) {
5426
+ return "Session overlaps the resolved time window.";
5427
+ }
5428
+ return "Matched episodic recall ranking.";
5429
+ }
5430
+ function formatClaimStatus(status) {
5431
+ return status === "no_key" ? "no-key" : status;
5432
+ }
5433
+ function formatProjectedEntryProvenance(entry) {
5434
+ const parts = [
5435
+ entry.provenance.supersededById ? `superseded_by=${entry.provenance.supersededById}` : void 0,
5436
+ entry.provenance.supersessionKind ? `kind=${entry.provenance.supersessionKind}` : void 0,
5437
+ entry.provenance.supersessionReason ? `reason=${truncate(entry.provenance.supersessionReason, 120)}` : void 0,
5438
+ entry.provenance.supportSourceKind ? `support=${entry.provenance.supportSourceKind}` : void 0,
5439
+ entry.provenance.supportMode ? `support_mode=${entry.provenance.supportMode}` : void 0,
5440
+ entry.provenance.supportObservedAt ? `observed=${entry.provenance.supportObservedAt}` : void 0,
5441
+ entry.provenance.supportLocator ? `locator=${truncate(entry.provenance.supportLocator, 120)}` : void 0
5442
+ ].filter((value) => value !== void 0);
5443
+ return parts.join(" | ");
5444
+ }
5445
+
2902
5446
  // src/adapters/skeln/tools/recall.ts
2903
5447
  function registerAgenrSkelnRecallTool(skeln, servicesPromise, resolveScope) {
2904
5448
  skeln.registerTool({
@@ -3898,11 +6442,11 @@ function registerAgenrSkelnSubagentFindingHooks(skeln, servicesPromise, resolveS
3898
6442
  return void 0;
3899
6443
  });
3900
6444
  }
3901
- async function routeScopedSessionMemoryTrigger(servicesPromise, resolveScope, context, buildEvent) {
6445
+ async function routeScopedSessionMemoryTrigger(servicesPromise, resolveScope, context, buildEvent2) {
3902
6446
  try {
3903
6447
  const scope = await resolveScope(context);
3904
6448
  const services = await servicesPromise;
3905
- logSessionMemoryTriggerResult(await services.routeSessionMemoryTrigger(buildEvent(scope)));
6449
+ logSessionMemoryTriggerResult(await services.routeSessionMemoryTrigger(buildEvent2(scope)));
3906
6450
  } catch (error) {
3907
6451
  console.warn(`[agenr] session-memory trigger failed: ${formatErrorMessage(error)}`);
3908
6452
  }