@agenr/openclaw-plugin 1.5.0 → 2026.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -661
  2. package/README.md +1 -0
  3. package/dist/build-before-turn-artifact-NPUHVWFE.js +71 -0
  4. package/dist/build-recall-artifact-F3LS3PZX.js +62 -0
  5. package/dist/chunk-5AXMFBHR.js +14 -0
  6. package/dist/chunk-5AYIXQRF.js +4452 -0
  7. package/dist/chunk-5TIP2EPP.js +6944 -0
  8. package/dist/chunk-GAERET5Q.js +2070 -0
  9. package/dist/chunk-GF3PX3VM.js +41 -0
  10. package/dist/chunk-GKZQ5AG5.js +44 -0
  11. package/dist/chunk-LDJN7CVU.js +3231 -0
  12. package/dist/chunk-MC3C2XM5.js +148 -0
  13. package/dist/chunk-NBS62ES5.js +3012 -0
  14. package/dist/chunk-NSLTJBUC.js +270 -0
  15. package/dist/chunk-OJSIZDZD.js +9 -0
  16. package/dist/chunk-OWGQWQUP.js +45 -0
  17. package/dist/chunk-Q5UTJXHZ.js +1069 -0
  18. package/dist/chunk-SOQW7356.js +2416 -0
  19. package/dist/chunk-VBPYU7GO.js +597 -0
  20. package/dist/chunk-VTHBPXDQ.js +1750 -0
  21. package/dist/chunk-XFJ4S4G2.js +1679 -0
  22. package/dist/chunk-Y5NB3FTH.js +106 -0
  23. package/dist/chunk-ZX55JBV2.js +4451 -0
  24. package/dist/index.js +1905 -15298
  25. package/dist/lifecycle-checkpoint-IAC5FCQU.js +154 -0
  26. package/dist/scan-6JKPOQHD.js +6 -0
  27. package/dist/service-EKFACEN6.js +15 -0
  28. package/dist/service-RHNB5AEQ.js +861 -0
  29. package/dist/sink-AUAAWC5O.js +8 -0
  30. package/openclaw.plugin.json +148 -11
  31. package/package.json +7 -5
  32. package/dist/anthropic-RE4XNAKE.js +0 -5515
  33. package/dist/azure-openai-responses-IQLXOCZS.js +0 -190
  34. package/dist/chunk-6DQXEU2A.js +0 -32306
  35. package/dist/chunk-EAQYK3U2.js +0 -41
  36. package/dist/chunk-HNWLZUWE.js +0 -31
  37. package/dist/chunk-JRUUYSFL.js +0 -262
  38. package/dist/chunk-OLOUBEE5.js +0 -14022
  39. package/dist/chunk-P5HNPYGQ.js +0 -174
  40. package/dist/chunk-RD7BUOBD.js +0 -416
  41. package/dist/chunk-RWWH2U4W.js +0 -7056
  42. package/dist/chunk-SEOMNQGB.js +0 -86
  43. package/dist/chunk-SQLXP7LT.js +0 -4792
  44. package/dist/chunk-URGOKODJ.js +0 -17
  45. package/dist/dist-R6ESEJ6P.js +0 -1244
  46. package/dist/google-NAVXTQLO.js +0 -371
  47. package/dist/google-gemini-cli-NKYJWHX2.js +0 -712
  48. package/dist/google-vertex-ZBJ2EDRH.js +0 -414
  49. package/dist/mistral-SBQYC4J5.js +0 -38407
  50. package/dist/multipart-parser-DV373IRF.js +0 -371
  51. package/dist/openai-codex-responses-XN3T3DEN.js +0 -712
  52. package/dist/openai-completions-75ZFOFU6.js +0 -657
  53. package/dist/openai-responses-DCK4BVNT.js +0 -198
  54. package/dist/src-T5RRS2HN.js +0 -1408
@@ -0,0 +1,1679 @@
1
+ import {
2
+ DURABLE_SELECT_COLUMNS,
3
+ buildActiveDurableClause,
4
+ buildValidAsOfClause,
5
+ getDurables,
6
+ mapDurableRow,
7
+ parseJsonStringArray,
8
+ projectClaimCentricRecallEntry,
9
+ readOptionalString,
10
+ readRequiredString,
11
+ runProcedureRecall,
12
+ truncate
13
+ } from "./chunk-5TIP2EPP.js";
14
+ import {
15
+ recall
16
+ } from "./chunk-GAERET5Q.js";
17
+ import {
18
+ MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX,
19
+ isDirectiveDurable,
20
+ isProactiveDirectiveDurable,
21
+ parseDirectiveMetadata
22
+ } from "./chunk-VTHBPXDQ.js";
23
+ import {
24
+ isCurrentlyValidMemory,
25
+ isWithinValidityWindow,
26
+ resolveKeyedDurableLifecycleStatus
27
+ } from "./chunk-VBPYU7GO.js";
28
+
29
+ // src/core/directives/abstain.ts
30
+ var ABSTAIN_VERB_PATTERN = "(?:mention|mentioning|bring up|bringing up|discuss|discussing|talk about|talking about|reference|referencing|raise|raising|recommend|recommending|suggest|suggesting|surface|surfacing|remind me about|remind me of)";
31
+ var ABSTAIN_LEAD_PATTERN = "(?:do not|don't|do n't|never|please do not|please don't|stop|avoid|no longer)";
32
+ var ABSTAIN_PHRASE_PATTERN = new RegExp(`\\b${ABSTAIN_LEAD_PATTERN}\\s+${ABSTAIN_VERB_PATTERN}\\s+(.+?)(?:[.!?;]|$)`, "giu");
33
+ var TRAILING_QUALIFIER_PATTERN = /\b(?:again|anymore|any more|ever again|ever|at all|please|with me|to me)\s*$/giu;
34
+ var LEADING_DETERMINER_PATTERN = /^(?:the|a|an|any|my|our|that|this|these|those)\s+/iu;
35
+ var MIN_BLOCKED_TERM_LENGTH = 2;
36
+ function parseAbstainDirective(entry) {
37
+ const metadata = parseDirectiveMetadata(entry);
38
+ if (!metadata || metadata.polarity !== "abstain") {
39
+ return null;
40
+ }
41
+ const claimKey = entry.claim_key?.trim() ?? `${MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX}${entry.id}`;
42
+ const blockedTerms = extractBlockedTerms(entry, claimKey);
43
+ if (blockedTerms.length === 0) {
44
+ return null;
45
+ }
46
+ return {
47
+ id: entry.id,
48
+ claimKey,
49
+ blockedTerms
50
+ };
51
+ }
52
+ function collectAbstainDirectives(entries) {
53
+ const directives = [];
54
+ for (const entry of entries) {
55
+ const directive = parseAbstainDirective(entry);
56
+ if (directive) {
57
+ directives.push(directive);
58
+ }
59
+ }
60
+ return directives;
61
+ }
62
+ function findAbstainViolation(entry, directives) {
63
+ if (directives.length === 0) {
64
+ return null;
65
+ }
66
+ const haystack = normalizeForMatch(`${entry.subject} ${entry.content}`);
67
+ if (haystack.length === 0) {
68
+ return null;
69
+ }
70
+ for (const directive of directives) {
71
+ if (directive.id === entry.id) {
72
+ continue;
73
+ }
74
+ for (const blockedTerm of directive.blockedTerms) {
75
+ if (mentionsBlockedTerm(haystack, blockedTerm)) {
76
+ return {
77
+ directiveId: directive.id,
78
+ blockedTerm
79
+ };
80
+ }
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ function normalizeTextForPhraseMatch(value) {
86
+ return normalizeForMatch(value);
87
+ }
88
+ function textMentionsPhrase(normalizedHaystack, normalizedPhrase) {
89
+ return mentionsBlockedTerm(normalizedHaystack, normalizedPhrase);
90
+ }
91
+ function textMatchesTopicTrigger(normalizedHaystack, trigger) {
92
+ if (!trigger.startsWith("topic:")) {
93
+ return false;
94
+ }
95
+ const topic = trigger.slice("topic:".length);
96
+ return textMentionsPhrase(normalizedHaystack, topic);
97
+ }
98
+ function extractBlockedTerms(entry, claimKey) {
99
+ const terms = /* @__PURE__ */ new Set();
100
+ for (const source of [entry.content, entry.subject]) {
101
+ for (const phrase of matchAbstainPhrases(source)) {
102
+ const cleaned = cleanBlockedTerm(phrase);
103
+ if (cleaned.length >= MIN_BLOCKED_TERM_LENGTH) {
104
+ terms.add(cleaned);
105
+ }
106
+ }
107
+ }
108
+ if (terms.size === 0) {
109
+ const fallback = cleanBlockedTerm(deriveTermFromClaimKey(claimKey));
110
+ if (fallback.length >= MIN_BLOCKED_TERM_LENGTH) {
111
+ terms.add(fallback);
112
+ }
113
+ }
114
+ return Array.from(terms);
115
+ }
116
+ function matchAbstainPhrases(source) {
117
+ const phrases = [];
118
+ const pattern = new RegExp(ABSTAIN_PHRASE_PATTERN.source, ABSTAIN_PHRASE_PATTERN.flags);
119
+ let match = pattern.exec(source);
120
+ while (match !== null) {
121
+ const captured = match[1];
122
+ if (captured) {
123
+ phrases.push(captured);
124
+ }
125
+ match = pattern.exec(source);
126
+ }
127
+ return phrases;
128
+ }
129
+ function deriveTermFromClaimKey(claimKey) {
130
+ const suffix = claimKey.slice(MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX.length);
131
+ return suffix.replace(/^(?:do[_-]?not[_-]?mention[_-]?|avoid[_-]?|no[_-]?)/iu, "").replace(/[_-]+/gu, " ").trim();
132
+ }
133
+ function cleanBlockedTerm(phrase) {
134
+ const collapsed = phrase.replace(/\s+/gu, " ").trim();
135
+ const withoutLead = collapsed.replace(LEADING_DETERMINER_PATTERN, "");
136
+ const withoutTrailing = withoutLead.replace(TRAILING_QUALIFIER_PATTERN, "").trim();
137
+ return normalizeForMatch(withoutTrailing);
138
+ }
139
+ function mentionsBlockedTerm(haystack, blockedTerm) {
140
+ if (blockedTerm.length < MIN_BLOCKED_TERM_LENGTH) {
141
+ return false;
142
+ }
143
+ const escaped = escapeRegExp(blockedTerm).replace(/\\?\s+/gu, "\\s+");
144
+ const pattern = new RegExp(`(?:^|[^\\p{L}\\p{N}])${escaped}(?:[^\\p{L}\\p{N}]|$)`, "iu");
145
+ return pattern.test(haystack);
146
+ }
147
+ function normalizeForMatch(value) {
148
+ return value.replace(/\s+/gu, " ").trim().normalize("NFKC").toLocaleLowerCase();
149
+ }
150
+ function escapeRegExp(value) {
151
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
152
+ }
153
+
154
+ // src/app/directives/abstain-filter.ts
155
+ var ABSTAIN_DIRECTIVE_LOOKUP_FAILED_NOTICE = "Memory-directive lookup failed; blocked-topic suppression was skipped this pass and only directive rows were withheld.";
156
+ async function applyAbstainDirectives(items, listActiveAbstainDirectives2) {
157
+ if (items.length === 0) {
158
+ return { kept: [...items], suppressed: [], lookupFailed: false };
159
+ }
160
+ const suppressed = [];
161
+ const nonDirectiveItems = [];
162
+ for (const item of items) {
163
+ if (isDirectiveDurable(item.entry) && !isAllowedDirectiveInjectionItem(item)) {
164
+ suppressed.push({ entryId: item.entry.id, reason: "directive_self" });
165
+ continue;
166
+ }
167
+ nonDirectiveItems.push(item);
168
+ }
169
+ if (!listActiveAbstainDirectives2 || nonDirectiveItems.length === 0) {
170
+ return { kept: nonDirectiveItems, suppressed, lookupFailed: false };
171
+ }
172
+ let directiveRows;
173
+ try {
174
+ directiveRows = await listActiveAbstainDirectives2();
175
+ } catch {
176
+ return { kept: nonDirectiveItems, suppressed, lookupFailed: true };
177
+ }
178
+ const directives = collectAbstainDirectives(directiveRows);
179
+ if (directives.length === 0) {
180
+ return { kept: nonDirectiveItems, suppressed, lookupFailed: false };
181
+ }
182
+ const kept = [];
183
+ for (const item of nonDirectiveItems) {
184
+ const violation = findAbstainViolation(item.entry, directives);
185
+ if (violation) {
186
+ suppressed.push({
187
+ entryId: item.entry.id,
188
+ reason: "directive_topic",
189
+ directiveId: violation.directiveId,
190
+ blockedTerm: violation.blockedTerm
191
+ });
192
+ continue;
193
+ }
194
+ kept.push(item);
195
+ }
196
+ return { kept, suppressed, lookupFailed: false };
197
+ }
198
+ function buildAbstentionNotice(suppression) {
199
+ if (suppression.reason === "directive_self") {
200
+ return `Skipped injecting memory directive ${suppression.entryId}; directives are not surfaced as memory.`;
201
+ }
202
+ const directive = suppression.directiveId ?? "unknown";
203
+ const term = suppression.blockedTerm ?? "a blocked topic";
204
+ return `Suppressed durable ${suppression.entryId} because memory directive ${directive} blocks "${term}".`;
205
+ }
206
+ async function applyAbstainDirectivesForInjection(items, listActiveAbstainDirectives2, diagnostics) {
207
+ const result = await applyAbstainDirectives(items, listActiveAbstainDirectives2);
208
+ if (result.lookupFailed) {
209
+ diagnostics.notices.push(ABSTAIN_DIRECTIVE_LOOKUP_FAILED_NOTICE);
210
+ }
211
+ if (result.suppressed.length > 0) {
212
+ diagnostics.directiveAbstentions = result.suppressed;
213
+ for (const suppression of result.suppressed) {
214
+ diagnostics.notices.push(buildAbstentionNotice(suppression));
215
+ }
216
+ }
217
+ return result.kept;
218
+ }
219
+ function isAllowedDirectiveInjectionItem(item) {
220
+ return item.sourceKind === "directive" && isProactiveDirectiveDurable(item.entry);
221
+ }
222
+
223
+ // src/app/before-turn/format-provenance.ts
224
+ function formatProjectedProvenance(provenance) {
225
+ const parts = [
226
+ provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
227
+ provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
228
+ provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
229
+ provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
230
+ provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
231
+ provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
232
+ provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
233
+ ].filter((value) => value !== void 0);
234
+ return parts.length > 0 ? parts.join(" | ") : void 0;
235
+ }
236
+
237
+ // src/app/before-turn/select-patch-items.ts
238
+ function selectDurablePatchItems(items, policy, diagnostics) {
239
+ if (policy.maxDurableEntries <= 0 || items.length === 0) {
240
+ return [];
241
+ }
242
+ const boundedItems = items.slice(0, policy.maxDurableEntries);
243
+ const expandedLimit = Math.max(policy.maxDurableEntries, policy.maxHighConfidenceDurableEntries);
244
+ if (expandedLimit <= policy.maxDurableEntries || items.length <= policy.maxDurableEntries) {
245
+ return boundedItems;
246
+ }
247
+ const expansionCandidates = items.slice(0, expandedLimit);
248
+ const canExpand = expansionCandidates.length > policy.maxDurableEntries && expansionCandidates.every((item) => item.score >= policy.highConfidenceRecallThreshold);
249
+ if (canExpand) {
250
+ diagnostics.notices.push(`Before-turn durable recall expanded to ${expansionCandidates.length} high-confidence items.`);
251
+ return expansionCandidates;
252
+ }
253
+ diagnostics.notices.push(
254
+ `Before-turn durable recall kept the top ${boundedItems.length} item${boundedItems.length === 1 ? "" : "s"} because additional candidates were not high confidence.`
255
+ );
256
+ return boundedItems;
257
+ }
258
+
259
+ // src/app/before-turn/topic-directives.ts
260
+ var TOPIC_PROACTIVE_DIRECTIVE_LOOKUP_FAILED_NOTICE = "Topic proactive directive lookup failed; topic directive surfacing was skipped this pass.";
261
+ async function injectTopicProactiveDirectives(currentTurnText, recalledItems, policy, deps, diagnostics) {
262
+ if (!deps.listActiveTopicProactiveDirectives) {
263
+ return recalledItems;
264
+ }
265
+ let directiveRows;
266
+ try {
267
+ directiveRows = await deps.listActiveTopicProactiveDirectives();
268
+ } catch {
269
+ diagnostics.notices.push(TOPIC_PROACTIVE_DIRECTIVE_LOOKUP_FAILED_NOTICE);
270
+ return recalledItems;
271
+ }
272
+ const now = deps.now ?? /* @__PURE__ */ new Date();
273
+ const nowMs = now.getTime();
274
+ const activeDirectives = filterCurrentEntries(directiveRows, nowMs);
275
+ diagnostics.topicProactiveDirectiveCandidateCount = activeDirectives.length;
276
+ const normalizedTurn = normalizeTextForPhraseMatch(currentTurnText);
277
+ const matchedDirectives = activeDirectives.filter((entry) => {
278
+ const metadata = parseDirectiveMetadata(entry);
279
+ return metadata?.polarity === "proactive" && metadata.trigger.startsWith("topic:") && textMatchesTopicTrigger(normalizedTurn, metadata.trigger);
280
+ });
281
+ diagnostics.topicProactiveDirectiveMatchedCount = matchedDirectives.length;
282
+ if (matchedDirectives.length === 0) {
283
+ return recalledItems;
284
+ }
285
+ const directiveItems = matchedDirectives.map((entry) => buildTopicDirectivePatchItem(entry, deps, policy.highConfidenceRecallThreshold));
286
+ return mergeTopicDirectivePatchItems(directiveItems, recalledItems, policy, diagnostics);
287
+ }
288
+ function filterCurrentEntries(entries, nowMs) {
289
+ return entries.filter((entry) => isWithinValidityWindow(entry.valid_from, entry.valid_to, nowMs));
290
+ }
291
+ function buildTopicDirectivePatchItem(entry, deps, score) {
292
+ const metadata = parseDirectiveMetadata(entry);
293
+ const projected = projectClaimCentricRecallEntry(buildSyntheticRecallOutput(entry, score), {
294
+ slotPolicyConfig: deps.slotPolicyConfig
295
+ });
296
+ const provenanceSummary = formatProjectedProvenance(projected.provenance);
297
+ return {
298
+ rank: 0,
299
+ entry,
300
+ sourceKind: "directive",
301
+ score,
302
+ whySurfaced: {
303
+ summary: `proactive memory directive; trigger ${metadata?.trigger ?? "topic"}`,
304
+ reasons: ["proactive memory directive", `trigger ${metadata?.trigger ?? "topic"}`, `importance ${entry.importance}`]
305
+ },
306
+ memoryState: projected.memoryState,
307
+ claimStatus: projected.claimStatus,
308
+ freshnessLabel: projected.freshness.label,
309
+ ...provenanceSummary ? { provenanceSummary } : {}
310
+ };
311
+ }
312
+ function buildSyntheticRecallOutput(entry, score) {
313
+ return {
314
+ entry,
315
+ score,
316
+ scores: {
317
+ relevance: score,
318
+ rrf: score,
319
+ vector: 0,
320
+ lexical: 0,
321
+ recency: 1,
322
+ importance: entry.importance / 10,
323
+ historicalLineage: 0,
324
+ neighborhoodBoost: 0,
325
+ claimKeyTrustPenalty: 0,
326
+ claimKeyRedundancyPenalty: 0
327
+ }
328
+ };
329
+ }
330
+ function mergeTopicDirectivePatchItems(directiveItems, recalledItems, policy, diagnostics) {
331
+ const seenEntryIds = /* @__PURE__ */ new Set();
332
+ const merged = [];
333
+ for (const item of [...directiveItems, ...recalledItems]) {
334
+ if (seenEntryIds.has(item.entry.id)) {
335
+ continue;
336
+ }
337
+ seenEntryIds.add(item.entry.id);
338
+ merged.push(item);
339
+ }
340
+ return selectDurablePatchItems(merged, policy, diagnostics);
341
+ }
342
+
343
+ // src/app/before-turn/service.ts
344
+ var DEFAULT_MAX_DURABLE_ENTRIES = 1;
345
+ var DEFAULT_MAX_HIGH_CONFIDENCE_DURABLE_ENTRIES = 2;
346
+ var DEFAULT_MAX_RECENT_TURNS = 2;
347
+ var DEFAULT_MAX_QUERY_CHARS = 450;
348
+ var DEFAULT_MAX_PROCEDURE_CANDIDATES = 3;
349
+ var DEFAULT_RECALL_THRESHOLD = 0.6;
350
+ var DEFAULT_HIGH_CONFIDENCE_RECALL_THRESHOLD = 0.97;
351
+ var DEFAULT_PROCEDURE_THRESHOLD = 0.72;
352
+ var DEFAULT_SKIP_TRIVIAL_TURNS = true;
353
+ var DEFAULT_REQUIRE_TURN_SIGNAL = true;
354
+ var SHORT_TURN_MAX_WORDS = 4;
355
+ var SHORT_TURN_MAX_CHARS = 24;
356
+ var SOCIAL_TURN_RE = /^(?:hi|hello|hey|hey there|hello there|thanks|thank you|ok|okay|cool|sounds good|got it|yep|yes|no|nice|great|awesome|perfect|ping)(?:[.!?]+)?$/iu;
357
+ var TASK_SIGNAL_RE = /\b(?:do|make|create|draft|write|send|schedule|book|reserve|organize|arrange|prepare|plan|choose|decide|contact|call|email|message|buy|order|find|search|check|compare|review|explain|summarize|investigate|research|use|apply|remember|recall|assist|help|should|need to|help me)\b/iu;
358
+ var FACTUAL_SIGNAL_RE = /\b(?:what(?:'s|\s+is|\s+was|\s+were)?|which|where|who|when|how much|how many|what time|what day|did we|do we|previous|prior|earlier|before|last|again|decision|preference|fact|rule|policy|status|availability|location|address|phone|email|price|cost|budget|deadline|date|time|name|called|uses|used|change(?:d)?|order|reservation|appointment|account|contact)\b/iu;
359
+ var PROCEDURAL_SIGNAL_RE = /\b(?:how do i|how should i|how can i|how to|what should i do|what do i need to do|steps|procedure|process|workflow|runbook|playbook|guide|guidance|instructions|checklist|recipe|template|walk me through|step by step|best way to|planning|arrange|prepare|book|reserve|schedule|rollout|migration|incident response)\b/iu;
360
+ var CONTEXT_REFERENCE_RE = /\b(?:it|its|that|this|they|them|their|those|these|he|him|his|she|her|hers|other one|other ones)\b/iu;
361
+ var HARD_CONTEXT_PREFIX_RE = /^(?:and\b|also\b|what about\b|how about\b|same\b|same as\b)/iu;
362
+ var SOFT_CONTEXT_FALLBACK_RE = /\b(?:next|follow up|follow-up|continue|continuation)\b/iu;
363
+ var CONTEXT_QUESTION_PREFIX_RE = /^(?:when|where|why|should|does|is|are|what should)\b/iu;
364
+ var MAX_CONTEXT_ANCHOR_CHARS = 120;
365
+ var DIRECTNESS_STABLE_GAP = 0.08;
366
+ var DIRECTNESS_SUBJECT_ENTITY_MATCH_BONUS = 0.16;
367
+ var DIRECTNESS_SUBJECT_IDENTITY_WRAPPER_BONUS = 0.12;
368
+ var DIRECTNESS_DEFINITIONAL_CONTENT_BONUS = 0.22;
369
+ var DIRECTNESS_CLAIM_KEY_ENTITY_MATCH_BONUS = 0.18;
370
+ var DIRECTNESS_ADJACENT_RELATIONSHIP_PENALTY = 0.18;
371
+ var DIRECTNESS_LIST_LORE_PENALTY = 0.08;
372
+ var ENTITY_DIRECTNESS_MAX_WORDS = 5;
373
+ var ENTITY_DIRECTNESS_RECALL_CANDIDATE_LIMIT = 5;
374
+ var DIRECTNESS_IDENTITY_WRAPPERS = /* @__PURE__ */ new Set(["identity", "profile", "bio", "biography", "definition", "overview", "summary"]);
375
+ var DIRECTNESS_RELATIONSHIP_KEYWORDS = /* @__PURE__ */ new Set([
376
+ "cousin",
377
+ "cousins",
378
+ "family",
379
+ "brother",
380
+ "brothers",
381
+ "sister",
382
+ "sisters",
383
+ "mother",
384
+ "father",
385
+ "parent",
386
+ "parents",
387
+ "friend",
388
+ "friends",
389
+ "relationship",
390
+ "relationships",
391
+ "owner",
392
+ "owners"
393
+ ]);
394
+ var DIRECTNESS_LIST_LORE_KEYWORDS = /* @__PURE__ */ new Set(["list", "notes", "timeline", "history", "background", "facts", "lore"]);
395
+ async function runBeforeTurn(input, deps) {
396
+ const policy = normalizePolicy(input.policy);
397
+ const currentTurnText = normalizeOptionalString(input.currentTurnText);
398
+ const recentTurns = normalizeRecentTurns(input.recentTurns, policy.maxRecentTurns, currentTurnText);
399
+ const diagnostics = {
400
+ queryVariants: [],
401
+ recentTurnCount: recentTurns.length,
402
+ turnSignalLabels: [],
403
+ durableRecallUsed: false,
404
+ durableRecallCandidateCount: 0,
405
+ procedureRecallUsed: false,
406
+ procedureCandidateCount: 0,
407
+ abstained: false,
408
+ abstentionReasons: [],
409
+ notices: []
410
+ };
411
+ if (!currentTurnText) {
412
+ diagnostics.abstained = true;
413
+ diagnostics.abstentionReasons.push("Current turn text was empty after normalization.");
414
+ return {
415
+ durableMemory: [],
416
+ diagnostics
417
+ };
418
+ }
419
+ const turnSignal = inspectTurnSignal(currentTurnText);
420
+ diagnostics.turnSignalLabels = turnSignal.signalLabels;
421
+ if (turnSignal.suppressedTurnCategory) {
422
+ diagnostics.suppressedTurnCategory = turnSignal.suppressedTurnCategory;
423
+ }
424
+ if (policy.skipTrivialTurns && turnSignal.suppressedTurnCategory && turnSignal.suppressedTurnCategory !== "low_signal") {
425
+ diagnostics.abstained = true;
426
+ diagnostics.abstentionReasons.push(turnSignal.reason);
427
+ return {
428
+ durableMemory: [],
429
+ diagnostics
430
+ };
431
+ }
432
+ if (policy.requireTurnSignal && turnSignal.signalLabels.length === 0) {
433
+ diagnostics.abstained = true;
434
+ diagnostics.abstentionReasons.push(turnSignal.reason);
435
+ return {
436
+ durableMemory: [],
437
+ diagnostics
438
+ };
439
+ }
440
+ const durableQueryPlan = buildDurableRecallQueryPlan(currentTurnText, recentTurns, policy.maxQueryChars);
441
+ const procedureQuery = buildProcedureQuery(currentTurnText, recentTurns, policy.maxQueryChars);
442
+ if (!durableQueryPlan) {
443
+ diagnostics.abstained = true;
444
+ diagnostics.abstentionReasons.push("No usable before-turn query could be derived from the turn context.");
445
+ return {
446
+ durableMemory: [],
447
+ diagnostics
448
+ };
449
+ }
450
+ const [recalledDurableMemory, procedure] = await Promise.all([
451
+ policy.enableDurableRecall ? runDurableRecallSelection(currentTurnText, durableQueryPlan, input.sessionKey, policy, deps, diagnostics) : Promise.resolve([]),
452
+ policy.enableProcedureSuggestion && procedureQuery ? runProcedureSelection(procedureQuery, policy, deps, diagnostics) : Promise.resolve(void 0)
453
+ ]);
454
+ const durableMemoryWithTopicDirectives = await injectTopicProactiveDirectives(currentTurnText, recalledDurableMemory, policy, deps, diagnostics);
455
+ const durableMemory = await applyAbstainDirectivesForInjection(durableMemoryWithTopicDirectives, deps.listActiveAbstainDirectives, diagnostics);
456
+ if (!policy.enableDurableRecall) {
457
+ diagnostics.abstentionReasons.push("Durable recall disabled by before-turn policy.");
458
+ } else if (durableMemory.length === 0) {
459
+ diagnostics.abstentionReasons.push("No durable memory entries cleared the before-turn threshold.");
460
+ }
461
+ if (!policy.enableProcedureSuggestion) {
462
+ diagnostics.abstentionReasons.push("Procedure suggestion disabled by before-turn policy.");
463
+ } else if (!procedure) {
464
+ diagnostics.abstentionReasons.push("No canonical procedure suggestion cleared the before-turn threshold.");
465
+ }
466
+ diagnostics.abstained = durableMemory.length === 0 && !procedure;
467
+ return {
468
+ durableMemory: assignRanks(durableMemory),
469
+ ...procedure ? { procedure } : {},
470
+ diagnostics
471
+ };
472
+ }
473
+ async function runDurableRecallSelection(currentTurnText, queryPlan, sessionKey, policy, deps, diagnostics) {
474
+ diagnostics.durableRecallUsed = true;
475
+ const attemptedVariants = [];
476
+ const primaryResult = await runDurableRecallAttempt(currentTurnText, queryPlan.primary.query, sessionKey, policy, deps, diagnostics);
477
+ attemptedVariants.push({
478
+ kind: queryPlan.primary.kind,
479
+ query: queryPlan.primary.query,
480
+ candidateCount: primaryResult.candidateCount,
481
+ selected: primaryResult.items.length > 0
482
+ });
483
+ if (queryPlan.fallback === void 0 || primaryResult.items.length > 0 && !shouldRetryWeakPrimaryWithContext(primaryResult, policy)) {
484
+ diagnostics.query = queryPlan.primary.query;
485
+ diagnostics.queryPolicy = queryPlan.policy;
486
+ diagnostics.queryVariants = attemptedVariants;
487
+ diagnostics.durableRecallTrace = primaryResult.durableRecallTrace;
488
+ diagnostics.durableRecallCandidateCount = primaryResult.candidateCount;
489
+ diagnostics.directness = primaryResult.directness;
490
+ if (primaryResult.notices.length > 0) {
491
+ diagnostics.notices.push(...primaryResult.notices);
492
+ }
493
+ if (primaryResult.directness?.decision === "abstained") {
494
+ diagnostics.abstentionReasons.push(primaryResult.directness.reason);
495
+ }
496
+ return primaryResult.items;
497
+ }
498
+ const fallbackPlan = queryPlan.fallback;
499
+ const primaryItems = primaryResult.items;
500
+ const fallbackResult = await runDurableRecallAttempt(currentTurnText, fallbackPlan.query, sessionKey, policy, deps, diagnostics);
501
+ const shouldUseFallback = fallbackResult.items.length > 0;
502
+ attemptedVariants[0] = {
503
+ ...attemptedVariants[0],
504
+ selected: primaryItems.length > 0 && !shouldUseFallback
505
+ };
506
+ attemptedVariants.push({
507
+ kind: fallbackPlan.kind,
508
+ query: fallbackPlan.query,
509
+ candidateCount: fallbackResult.candidateCount,
510
+ selected: shouldUseFallback
511
+ });
512
+ const selectedResult = shouldUseFallback ? fallbackResult : primaryResult;
513
+ const selectedQuery = shouldUseFallback ? fallbackPlan.query : queryPlan.primary.query;
514
+ const selectedPolicy = shouldUseFallback ? "contextual_fallback" : queryPlan.policy;
515
+ diagnostics.query = selectedQuery;
516
+ diagnostics.queryPolicy = selectedPolicy;
517
+ diagnostics.queryVariants = attemptedVariants;
518
+ diagnostics.durableRecallTrace = selectedResult.durableRecallTrace;
519
+ diagnostics.durableRecallCandidateCount = selectedResult.candidateCount;
520
+ diagnostics.directness = selectedResult.directness;
521
+ if (primaryResult.notices.length > 0) {
522
+ diagnostics.notices.push(...primaryResult.notices);
523
+ }
524
+ if (fallbackResult.notices.length > 0) {
525
+ diagnostics.notices.push(...fallbackResult.notices);
526
+ }
527
+ if (selectedResult.directness?.decision === "abstained") {
528
+ diagnostics.abstentionReasons.push(selectedResult.directness.reason);
529
+ }
530
+ return selectedResult.items;
531
+ }
532
+ function shouldRetryWeakPrimaryWithContext(primaryResult, policy) {
533
+ const topScore = primaryResult.items[0]?.score;
534
+ return typeof topScore === "number" && topScore < policy.highConfidenceRecallThreshold;
535
+ }
536
+ async function runDurableRecallAttempt(currentTurnText, query, sessionKey, policy, deps, diagnostics) {
537
+ const directnessQuery = detectEntityDefinitionTurn(currentTurnText);
538
+ const durableRecallLimit = directnessQuery ? Math.max(policy.maxDurableEntries, policy.maxHighConfidenceDurableEntries, ENTITY_DIRECTNESS_RECALL_CANDIDATE_LIMIT) : Math.max(policy.maxDurableEntries, policy.maxHighConfidenceDurableEntries);
539
+ let durableRecallTrace;
540
+ try {
541
+ const recalled = await recall(
542
+ {
543
+ text: query,
544
+ limit: durableRecallLimit,
545
+ threshold: policy.recallThreshold,
546
+ sessionKey
547
+ },
548
+ deps.recall,
549
+ {
550
+ trace: {
551
+ reportSummary(summary) {
552
+ durableRecallTrace = summary;
553
+ }
554
+ },
555
+ slotPolicyConfig: deps.slotPolicyConfig,
556
+ ...deps.now ? { now: deps.now } : {}
557
+ }
558
+ );
559
+ const notices = durableRecallTrace?.degraded.notices.length ? [...durableRecallTrace.degraded.notices] : [];
560
+ const directnessSelection = applyDirectnessSelection(
561
+ currentTurnText,
562
+ recalled.map((item) => buildDurablePatchItem(item, deps))
563
+ );
564
+ return {
565
+ items: selectDurablePatchItems(directnessSelection.items, policy, diagnostics),
566
+ candidateCount: recalled.length,
567
+ durableRecallTrace,
568
+ directness: directnessSelection.diagnostics,
569
+ notices
570
+ };
571
+ } catch (error) {
572
+ return {
573
+ items: [],
574
+ candidateCount: 0,
575
+ durableRecallTrace,
576
+ notices: [`Before-turn durable recall failed: ${formatErrorMessage(error)}`]
577
+ };
578
+ }
579
+ }
580
+ async function runProcedureSelection(query, policy, deps, diagnostics) {
581
+ diagnostics.procedureRecallUsed = true;
582
+ try {
583
+ const result = await runProcedureRecall(
584
+ {
585
+ text: query,
586
+ limit: policy.maxProcedureCandidates,
587
+ threshold: policy.procedureThreshold
588
+ },
589
+ {
590
+ db: deps.procedures,
591
+ ...deps.embedQuery ? { embedQuery: deps.embedQuery } : {}
592
+ }
593
+ );
594
+ diagnostics.procedureCandidateCount = result.candidates.length;
595
+ if (result.notices.length > 0) {
596
+ diagnostics.notices.push(...result.notices);
597
+ }
598
+ const canonicalProcedure = result.canonicalProcedure;
599
+ if (!canonicalProcedure) {
600
+ return void 0;
601
+ }
602
+ const leader = result.candidates.find((candidate) => candidate.procedure.id === canonicalProcedure.id);
603
+ if (!leader) {
604
+ diagnostics.notices.push("Procedure recall returned a canonical procedure without a matching ranked candidate.");
605
+ return void 0;
606
+ }
607
+ return {
608
+ procedure: canonicalProcedure,
609
+ score: leader.score,
610
+ scores: {
611
+ relevance: leader.scores.relevance,
612
+ lexical: leader.scores.lexical,
613
+ vector: leader.scores.vector
614
+ },
615
+ whySurfaced: {
616
+ summary: `canonical procedure match; score ${leader.score.toFixed(2)}`,
617
+ reasons: [
618
+ "canonical procedure match",
619
+ `score ${leader.score.toFixed(2)}`,
620
+ `lexical ${leader.scores.lexical.toFixed(2)}`,
621
+ `vector ${leader.scores.vector.toFixed(2)}`
622
+ ]
623
+ }
624
+ };
625
+ } catch (error) {
626
+ diagnostics.notices.push(`Before-turn procedure recall failed: ${formatErrorMessage(error)}`);
627
+ return void 0;
628
+ }
629
+ }
630
+ function buildDurablePatchItem(recalled, deps) {
631
+ const projected = projectClaimCentricRecallEntry(recalled, {
632
+ slotPolicyConfig: deps.slotPolicyConfig
633
+ });
634
+ return {
635
+ rank: 0,
636
+ entry: recalled.entry,
637
+ sourceKind: "turn_recall",
638
+ score: recalled.score,
639
+ whySurfaced: projected.whySurfaced,
640
+ memoryState: projected.memoryState,
641
+ claimStatus: projected.claimStatus,
642
+ freshnessLabel: projected.freshness.label,
643
+ ...formatProjectedProvenance(projected.provenance) ? { provenanceSummary: formatProjectedProvenance(projected.provenance) } : {}
644
+ };
645
+ }
646
+ function buildDurableRecallQueryPlan(currentTurnText, recentTurns, maxChars) {
647
+ const currentOnlyQuery = buildCurrentTurnOnlyQuery(currentTurnText, maxChars);
648
+ if (!currentOnlyQuery) {
649
+ return void 0;
650
+ }
651
+ const contextualQuery = buildContextualAnchorQuery(currentOnlyQuery, recentTurns, maxChars);
652
+ if (!contextualQuery) {
653
+ return {
654
+ policy: "current_only",
655
+ primary: {
656
+ kind: "current_only",
657
+ query: currentOnlyQuery
658
+ }
659
+ };
660
+ }
661
+ if (requiresContextualQuery(currentTurnText)) {
662
+ return {
663
+ policy: "contextual_required",
664
+ primary: {
665
+ kind: "contextual_anchor",
666
+ query: contextualQuery
667
+ }
668
+ };
669
+ }
670
+ if (shouldAllowContextualFallback(currentTurnText, recentTurns)) {
671
+ return {
672
+ policy: "current_only",
673
+ primary: {
674
+ kind: "current_only",
675
+ query: currentOnlyQuery
676
+ },
677
+ fallback: {
678
+ kind: "contextual_anchor",
679
+ query: contextualQuery
680
+ }
681
+ };
682
+ }
683
+ return {
684
+ policy: "current_only",
685
+ primary: {
686
+ kind: "current_only",
687
+ query: currentOnlyQuery
688
+ }
689
+ };
690
+ }
691
+ function buildCurrentTurnOnlyQuery(currentTurnText, maxChars) {
692
+ if (maxChars <= 0) {
693
+ return void 0;
694
+ }
695
+ const query = truncate2(normalizeWhitespace(currentTurnText), maxChars);
696
+ return query.length > 0 ? query : void 0;
697
+ }
698
+ function buildContextualAnchorQuery(currentTurnQuery, recentTurns, maxChars) {
699
+ const anchor = buildCompactContextAnchor(recentTurns);
700
+ if (!anchor || maxChars <= currentTurnQuery.length) {
701
+ return void 0;
702
+ }
703
+ const prefix = "Topic: ";
704
+ const separator = "\n";
705
+ const remaining = maxChars - currentTurnQuery.length - separator.length - prefix.length;
706
+ if (remaining <= 0) {
707
+ return void 0;
708
+ }
709
+ return `${currentTurnQuery}${separator}${prefix}${truncate2(anchor, remaining)}`;
710
+ }
711
+ function buildCompactContextAnchor(recentTurns) {
712
+ const recentTurn = recentTurns[recentTurns.length - 1];
713
+ if (!recentTurn) {
714
+ return void 0;
715
+ }
716
+ const normalized = normalizeWhitespace(recentTurn.text);
717
+ return normalized.length > 0 ? truncate2(normalized, MAX_CONTEXT_ANCHOR_CHARS) : void 0;
718
+ }
719
+ function requiresContextualQuery(currentTurnText) {
720
+ const normalizedTurn = normalizeWhitespace(currentTurnText);
721
+ const lowerTurn = normalizedTurn.toLowerCase();
722
+ const wordCount = normalizedTurn.split(/\s+/u).filter((token) => token.length > 0).length;
723
+ const hasContextReference = CONTEXT_REFERENCE_RE.test(lowerTurn);
724
+ if (HARD_CONTEXT_PREFIX_RE.test(lowerTurn) && (hasContextReference || lowerTurn.includes("other one"))) {
725
+ return true;
726
+ }
727
+ if (CONTEXT_QUESTION_PREFIX_RE.test(lowerTurn) && hasContextReference && wordCount <= 8) {
728
+ return true;
729
+ }
730
+ return hasContextReference && wordCount <= 6;
731
+ }
732
+ function shouldAllowContextualFallback(currentTurnText, recentTurns) {
733
+ if (recentTurns.length === 0 || requiresContextualQuery(currentTurnText)) {
734
+ return false;
735
+ }
736
+ return SOFT_CONTEXT_FALLBACK_RE.test(normalizeWhitespace(currentTurnText).toLowerCase());
737
+ }
738
+ function buildProcedureQuery(currentTurnText, recentTurns, maxChars) {
739
+ const normalizedCurrentTurn = normalizeWhitespace(currentTurnText);
740
+ if (normalizedCurrentTurn.length > 0) {
741
+ return truncate2(normalizedCurrentTurn, maxChars);
742
+ }
743
+ const recentUserTurn = [...recentTurns].reverse().find((turn) => turn.role === "user");
744
+ return recentUserTurn ? truncate2(normalizeWhitespace(recentUserTurn.text), maxChars) : void 0;
745
+ }
746
+ function applyDirectnessSelection(currentTurnText, items) {
747
+ if (items.length === 0) {
748
+ return { items };
749
+ }
750
+ const queryMatch = detectEntityDefinitionTurn(currentTurnText);
751
+ if (!queryMatch) {
752
+ return { items };
753
+ }
754
+ const scoredCandidates = items.map((item, index) => scoreDirectnessCandidate(queryMatch, item, index + 1));
755
+ const rerankedCandidates = [...scoredCandidates].sort(compareDirectnessCandidates);
756
+ const winner = rerankedCandidates[0];
757
+ const runnerUp = rerankedCandidates[1];
758
+ const winnerGap = runnerUp ? winner.adjustedScore - runnerUp.adjustedScore : void 0;
759
+ const winnerHasPositiveIdentitySignal = hasPositiveIdentitySignal(winner);
760
+ const runnerUpHasPositiveIdentitySignal = runnerUp ? hasPositiveIdentitySignal(runnerUp) : false;
761
+ const winnerHasOnlyAdjacentSignals = winner.signals.includes("adjacent_relationship") && !winner.signals.includes("definitional_content") && !winner.signals.includes("subject_entity_match") && !winner.signals.includes("subject_identity_wrapper");
762
+ const requiresStrictStableGap = runnerUpHasPositiveIdentitySignal;
763
+ const winnerGapTooSmall = requiresStrictStableGap && runnerUp !== void 0 && winnerGap !== void 0 && winnerGap < DIRECTNESS_STABLE_GAP;
764
+ if (!winnerHasPositiveIdentitySignal || winnerHasOnlyAdjacentSignals || winnerGapTooSmall) {
765
+ const reason2 = !winnerHasPositiveIdentitySignal || winnerHasOnlyAdjacentSignals ? `Before-turn directness check abstained for "${queryMatch.entity}" because the top candidate looked adjacent rather than definitional.` : `Before-turn directness check abstained for "${queryMatch.entity}" because the top candidates remained too close after reranking.`;
766
+ return {
767
+ items: [],
768
+ diagnostics: buildDirectnessDiagnostics(queryMatch, "abstained", reason2, rerankedCandidates, winnerGap)
769
+ };
770
+ }
771
+ const decision = winner.baseRank === 1 ? "kept" : "reranked";
772
+ const reason = decision === "kept" ? `Before-turn directness check kept ${winner.item.entry.id} because it stayed the clearest definitional match for "${queryMatch.entity}".` : `Before-turn directness check reranked ${winner.item.entry.id} ahead of an adjacent match for "${queryMatch.entity}".`;
773
+ return {
774
+ items: [winner.item],
775
+ diagnostics: buildDirectnessDiagnostics(queryMatch, decision, reason, rerankedCandidates, winnerGap)
776
+ };
777
+ }
778
+ function detectEntityDefinitionTurn(currentTurnText) {
779
+ const normalizedTurn = normalizeWhitespace(currentTurnText);
780
+ const patterns = [/^(?:who|what)\s+is\s+(.+?)(?:\s+again)?[?!.,]*$/iu, /^who'?s\s+(.+?)(?:\s+again)?[?!.,]*$/iu, /^tell\s+me\s+about\s+(.+?)[?!.,]*$/iu];
781
+ for (const pattern of patterns) {
782
+ const match = pattern.exec(normalizedTurn);
783
+ const candidateEntity = normalizeDirectnessEntity(match?.[1]);
784
+ if (candidateEntity) {
785
+ return {
786
+ kind: "entity_definition",
787
+ entity: candidateEntity,
788
+ normalizedEntity: normalizeDirectnessText(candidateEntity)
789
+ };
790
+ }
791
+ }
792
+ return void 0;
793
+ }
794
+ function normalizeDirectnessEntity(entityText) {
795
+ const cleaned = entityText ? normalizeWhitespace(entityText).replace(/^[("'`]+/u, "").replace(/[)"'`?!.,]+$/u, "").replace(/^(?:the|a|an)\s+/iu, "").trim() : "";
796
+ if (cleaned.length === 0) {
797
+ return void 0;
798
+ }
799
+ const wordCount = cleaned.split(/\s+/u).filter((token) => token.length > 0).length;
800
+ const normalized = normalizeDirectnessText(cleaned);
801
+ if (wordCount === 0 || wordCount > ENTITY_DIRECTNESS_MAX_WORDS || CONTEXT_REFERENCE_RE.test(normalized) || normalized.includes("'s") || containsKeyword(normalized, DIRECTNESS_RELATIONSHIP_KEYWORDS)) {
802
+ return void 0;
803
+ }
804
+ return cleaned;
805
+ }
806
+ function scoreDirectnessCandidate(queryMatch, item, baseRank) {
807
+ const subject = normalizeDirectnessText(item.entry.subject);
808
+ const content = normalizeDirectnessText(item.entry.content);
809
+ const signals = [];
810
+ let directnessDelta = 0;
811
+ if (subject === queryMatch.normalizedEntity) {
812
+ signals.push("subject_entity_match");
813
+ directnessDelta += DIRECTNESS_SUBJECT_ENTITY_MATCH_BONUS;
814
+ } else if (isIdentityWrapperSubject(subject, queryMatch.normalizedEntity)) {
815
+ signals.push("subject_identity_wrapper");
816
+ directnessDelta += DIRECTNESS_SUBJECT_IDENTITY_WRAPPER_BONUS;
817
+ }
818
+ if (hasDefinitionalContent(content, queryMatch.normalizedEntity)) {
819
+ signals.push("definitional_content");
820
+ directnessDelta += DIRECTNESS_DEFINITIONAL_CONTENT_BONUS;
821
+ }
822
+ if (hasEntityClaimKey(item.entry.claim_key, queryMatch.normalizedEntity)) {
823
+ signals.push("claim_key_entity_match");
824
+ directnessDelta += DIRECTNESS_CLAIM_KEY_ENTITY_MATCH_BONUS;
825
+ }
826
+ if (looksLikeAdjacentRelationship(subject, content, queryMatch.normalizedEntity)) {
827
+ signals.push("adjacent_relationship");
828
+ directnessDelta -= DIRECTNESS_ADJACENT_RELATIONSHIP_PENALTY;
829
+ }
830
+ if (looksLikeListLore(subject, content)) {
831
+ signals.push("list_lore");
832
+ directnessDelta -= DIRECTNESS_LIST_LORE_PENALTY;
833
+ }
834
+ return {
835
+ item,
836
+ baseRank,
837
+ baseScore: item.score,
838
+ directnessDelta,
839
+ adjustedScore: item.score + directnessDelta,
840
+ signals
841
+ };
842
+ }
843
+ function buildDirectnessDiagnostics(queryMatch, decision, reason, candidates, winnerGap) {
844
+ const winner = decision === "abstained" ? void 0 : candidates[0];
845
+ const runnerUp = candidates[1];
846
+ return {
847
+ queryKind: queryMatch.kind,
848
+ entity: queryMatch.entity,
849
+ decision,
850
+ winnerEntryId: winner?.item.entry.id,
851
+ runnerUpEntryId: runnerUp?.item.entry.id,
852
+ ...winnerGap !== void 0 ? { winnerGap: roundToThreeDecimals(winnerGap) } : {},
853
+ reason,
854
+ candidates: candidates.map((candidate) => ({
855
+ entryId: candidate.item.entry.id,
856
+ baseRank: candidate.baseRank,
857
+ baseScore: roundToThreeDecimals(candidate.baseScore),
858
+ directnessDelta: roundToThreeDecimals(candidate.directnessDelta),
859
+ adjustedScore: roundToThreeDecimals(candidate.adjustedScore),
860
+ signals: candidate.signals
861
+ }))
862
+ };
863
+ }
864
+ function hasPositiveIdentitySignal(candidate) {
865
+ return candidate.signals.includes("definitional_content") || candidate.signals.includes("subject_entity_match") || candidate.signals.includes("subject_identity_wrapper");
866
+ }
867
+ function isIdentityWrapperSubject(subject, entity) {
868
+ for (const wrapper of DIRECTNESS_IDENTITY_WRAPPERS) {
869
+ if (subject === `${entity} ${wrapper}` || subject === `${wrapper} ${entity}`) {
870
+ return true;
871
+ }
872
+ }
873
+ return false;
874
+ }
875
+ function hasDefinitionalContent(content, entity) {
876
+ const escapedEntity = escapeRegExp2(entity);
877
+ if (startsWithBareRelationshipPredicate(content, escapedEntity)) {
878
+ return false;
879
+ }
880
+ const anchoredPatterns = [new RegExp(`^${escapedEntity}\\s+(?:is|was|means)\\b`, "u"), new RegExp(`^${escapedEntity}\\s+refers\\s+to\\b`, "u")];
881
+ if (anchoredPatterns.some((pattern) => pattern.test(content))) {
882
+ return true;
883
+ }
884
+ return hasEmbeddedDefinitionalContent(content, escapedEntity);
885
+ }
886
+ function hasEmbeddedDefinitionalContent(content, escapedEntity) {
887
+ const embeddedLead = `(?:^|[.!?;:]\\s+)${escapedEntity}\\s+(?:is|was)\\s+`;
888
+ const fullNameLead = `(?:^|[.!?;:]\\s+)${escapedEntity}(?:\\s+[\\p{L}\\p{N}]+){1,2}\\s+(?:is|was)\\s+`;
889
+ if (startsWithBareRelationshipPredicate(content, escapedEntity)) {
890
+ return false;
891
+ }
892
+ const patterns = [
893
+ new RegExp(`${embeddedLead}(?:a|an|the)\\b`, "u"),
894
+ new RegExp(`${embeddedLead}[\\p{L}\\p{N}]+(?:['\u2019]s)\\b`, "u"),
895
+ // Allow short-name queries like "who is John?" to match a leading
896
+ // full-name clause such as "John Doe is married to Beverly".
897
+ new RegExp(`${fullNameLead}(?:a|an|the)\\b`, "u"),
898
+ new RegExp(`${fullNameLead}[\\p{L}\\p{N}]+\\b`, "u")
899
+ ];
900
+ return patterns.some((pattern) => pattern.test(content));
901
+ }
902
+ function startsWithBareRelationshipPredicate(content, escapedEntity) {
903
+ const relationshipAlternation = Array.from(DIRECTNESS_RELATIONSHIP_KEYWORDS).join("|");
904
+ const embeddedLead = `(?:^|[.!?;:]\\s+)${escapedEntity}\\s+(?:is|was)\\s+`;
905
+ return new RegExp(`${embeddedLead}(?:${relationshipAlternation})\\b`, "u").test(content);
906
+ }
907
+ function looksLikeAdjacentRelationship(subject, content, entity) {
908
+ if (!subject.includes(entity) && !content.includes(entity)) {
909
+ return false;
910
+ }
911
+ return containsKeyword(subject, DIRECTNESS_RELATIONSHIP_KEYWORDS) || containsKeyword(content, DIRECTNESS_RELATIONSHIP_KEYWORDS);
912
+ }
913
+ function looksLikeListLore(subject, content) {
914
+ return containsKeyword(subject, DIRECTNESS_LIST_LORE_KEYWORDS) || containsKeyword(content, DIRECTNESS_LIST_LORE_KEYWORDS);
915
+ }
916
+ function compareDirectnessCandidates(left, right) {
917
+ if (right.adjustedScore !== left.adjustedScore) {
918
+ return right.adjustedScore - left.adjustedScore;
919
+ }
920
+ if (right.baseScore !== left.baseScore) {
921
+ return right.baseScore - left.baseScore;
922
+ }
923
+ return left.baseRank - right.baseRank;
924
+ }
925
+ function normalizeDirectnessText(value) {
926
+ return normalizeWhitespace(value).toLocaleLowerCase();
927
+ }
928
+ function hasEntityClaimKey(claimKey, entity) {
929
+ const head = claimKey?.split("/", 1)[0]?.replace(/[-_]+/g, " ");
930
+ return normalizeDirectnessText(head ?? "") === entity;
931
+ }
932
+ function containsKeyword(text, keywords) {
933
+ const tokens = new Set(text.match(/[\p{L}\p{N}]+/gu) ?? []);
934
+ for (const keyword of keywords) {
935
+ if (tokens.has(keyword)) {
936
+ return true;
937
+ }
938
+ }
939
+ return false;
940
+ }
941
+ function escapeRegExp2(value) {
942
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
943
+ }
944
+ function roundToThreeDecimals(value) {
945
+ return Math.round(value * 1e3) / 1e3;
946
+ }
947
+ function inspectTurnSignal(currentTurnText) {
948
+ const normalizedTurn = normalizeWhitespace(currentTurnText);
949
+ const lowerTurn = normalizedTurn.toLowerCase();
950
+ const signalLabels = collectTurnSignalLabels(lowerTurn);
951
+ const wordCount = normalizedTurn.split(/\s+/u).filter((token) => token.length > 0).length;
952
+ const isShortTurn = wordCount <= SHORT_TURN_MAX_WORDS || normalizedTurn.length <= SHORT_TURN_MAX_CHARS;
953
+ if (signalLabels.length === 0 && (SOCIAL_TURN_RE.test(lowerTurn) || isShortTurn)) {
954
+ return {
955
+ signalLabels,
956
+ suppressedTurnCategory: "short_social",
957
+ reason: "Current turn was short or social without clear factual, procedural, or task intent."
958
+ };
959
+ }
960
+ if (signalLabels.length === 0) {
961
+ return {
962
+ signalLabels,
963
+ suppressedTurnCategory: "low_signal",
964
+ reason: "Current turn lacked clear factual, procedural, or task signal, so before-turn recall abstained."
965
+ };
966
+ }
967
+ return {
968
+ signalLabels,
969
+ reason: `Current turn showed ${signalLabels.join(", ")} recall signal.`
970
+ };
971
+ }
972
+ function collectTurnSignalLabels(lowerTurn) {
973
+ const labels = [];
974
+ if (TASK_SIGNAL_RE.test(lowerTurn)) {
975
+ labels.push("task");
976
+ }
977
+ if (FACTUAL_SIGNAL_RE.test(lowerTurn)) {
978
+ labels.push("factual");
979
+ }
980
+ if (PROCEDURAL_SIGNAL_RE.test(lowerTurn)) {
981
+ labels.push("procedural");
982
+ }
983
+ return labels;
984
+ }
985
+ function assignRanks(items) {
986
+ return items.map((item, index) => ({
987
+ ...item,
988
+ rank: index + 1
989
+ }));
990
+ }
991
+ function normalizePolicy(policy) {
992
+ const maxDurableEntries = normalizeCount(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES);
993
+ const maxHighConfidenceDurableEntries = Math.max(
994
+ maxDurableEntries,
995
+ normalizeCount(policy?.maxHighConfidenceDurableEntries, DEFAULT_MAX_HIGH_CONFIDENCE_DURABLE_ENTRIES)
996
+ );
997
+ return {
998
+ enableDurableRecall: policy?.enableDurableRecall !== false,
999
+ enableProcedureSuggestion: policy?.enableProcedureSuggestion !== false,
1000
+ maxRecentTurns: normalizeCount(policy?.maxRecentTurns, DEFAULT_MAX_RECENT_TURNS),
1001
+ maxQueryChars: normalizeCount(policy?.maxQueryChars, DEFAULT_MAX_QUERY_CHARS),
1002
+ maxDurableEntries,
1003
+ maxHighConfidenceDurableEntries,
1004
+ maxProcedureCandidates: normalizeCount(policy?.maxProcedureCandidates, DEFAULT_MAX_PROCEDURE_CANDIDATES),
1005
+ recallThreshold: normalizeThreshold(policy?.recallThreshold, DEFAULT_RECALL_THRESHOLD),
1006
+ highConfidenceRecallThreshold: normalizeThreshold(policy?.highConfidenceRecallThreshold, DEFAULT_HIGH_CONFIDENCE_RECALL_THRESHOLD),
1007
+ procedureThreshold: normalizeThreshold(policy?.procedureThreshold, DEFAULT_PROCEDURE_THRESHOLD),
1008
+ skipTrivialTurns: policy?.skipTrivialTurns ?? DEFAULT_SKIP_TRIVIAL_TURNS,
1009
+ requireTurnSignal: policy?.requireTurnSignal ?? DEFAULT_REQUIRE_TURN_SIGNAL
1010
+ };
1011
+ }
1012
+ function normalizeCount(value, fallback) {
1013
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1014
+ return fallback;
1015
+ }
1016
+ return Math.max(0, Math.trunc(value));
1017
+ }
1018
+ function normalizeThreshold(value, fallback) {
1019
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1020
+ return fallback;
1021
+ }
1022
+ return Math.min(1, Math.max(0, value));
1023
+ }
1024
+ function normalizeRecentTurns(recentTurns, maxRecentTurns, currentTurnText) {
1025
+ if (!recentTurns || recentTurns.length === 0 || maxRecentTurns <= 0) {
1026
+ return [];
1027
+ }
1028
+ const normalizedTurns = recentTurns.flatMap((turn) => {
1029
+ if (turn.role !== "user" && turn.role !== "assistant") {
1030
+ return [];
1031
+ }
1032
+ const text = normalizeOptionalString(turn.text);
1033
+ return text ? [{ role: turn.role, text }] : [];
1034
+ });
1035
+ const currentTurnFingerprint = currentTurnText ? normalizeWhitespace(currentTurnText).toLowerCase() : void 0;
1036
+ const deduped = [...normalizedTurns];
1037
+ while (deduped.length > 0 && currentTurnFingerprint) {
1038
+ const last = deduped[deduped.length - 1];
1039
+ if (!last || last.role !== "user") {
1040
+ break;
1041
+ }
1042
+ if (normalizeWhitespace(last.text).toLowerCase() !== currentTurnFingerprint) {
1043
+ break;
1044
+ }
1045
+ deduped.pop();
1046
+ }
1047
+ return deduped.slice(-maxRecentTurns);
1048
+ }
1049
+ function normalizeOptionalString(value) {
1050
+ const normalized = value?.trim();
1051
+ return normalized && normalized.length > 0 ? normalized : void 0;
1052
+ }
1053
+ function normalizeWhitespace(value) {
1054
+ return value.replace(/\s+/g, " ").trim();
1055
+ }
1056
+ function truncate2(value, maxChars) {
1057
+ if (maxChars <= 0) {
1058
+ return "";
1059
+ }
1060
+ if (value.length <= maxChars) {
1061
+ return value;
1062
+ }
1063
+ return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
1064
+ }
1065
+ function formatErrorMessage(error) {
1066
+ if (error instanceof Error) {
1067
+ return error.message;
1068
+ }
1069
+ return String(error);
1070
+ }
1071
+
1072
+ // src/adapters/shared/injection/entry-lines.ts
1073
+ var MAX_CONTENT_CHARS = 220;
1074
+ function formatInjectionEntryHeader(item) {
1075
+ const metadata = [
1076
+ `rank ${item.rank}`,
1077
+ item.entry.id,
1078
+ item.entry.type,
1079
+ item.entry.expiry,
1080
+ `importance ${item.entry.importance}`,
1081
+ item.score !== void 0 ? `score ${item.score.toFixed(2)}` : void 0
1082
+ ].filter((value) => value !== void 0);
1083
+ return `- [${metadata.join(" | ")}] ${item.entry.subject}`;
1084
+ }
1085
+ function formatInjectionEntryBodyLines(item) {
1086
+ const lines = [` ${truncate(item.entry.content.trim(), MAX_CONTENT_CHARS)}`];
1087
+ lines.push(` why: ${item.whySurfaced.summary}`);
1088
+ const metadata = [
1089
+ item.entry.tags.length > 0 ? `tags: ${item.entry.tags.join(", ")}` : void 0,
1090
+ item.freshnessLabel ? `freshness: ${item.freshnessLabel}` : void 0,
1091
+ item.provenanceSummary ? `provenance: ${truncate(item.provenanceSummary, MAX_CONTENT_CHARS)}` : void 0
1092
+ ].filter((value) => value !== void 0);
1093
+ if (metadata.length > 0) {
1094
+ lines.push(` ${metadata.join(" | ")}`);
1095
+ }
1096
+ return lines;
1097
+ }
1098
+
1099
+ // src/adapters/shared/injection/memory-context.ts
1100
+ var AGENR_MEMORY_CONTEXT_BLOCK_RE = /<agenr-memory-context>[\s\S]*?<\/agenr-memory-context>/giu;
1101
+ var AGENR_MEMORY_CONTEXT_OPEN_RE = /<agenr-memory-context>/giu;
1102
+ var AGENR_MEMORY_CONTEXT_CLOSE_RE = /<\/agenr-memory-context>/giu;
1103
+ var AGENR_MEMORY_CONTEXT_NOTE_RE = /\[System note: The following is recalled Agenr memory context, NOT new user input\. Treat it as background context and use it silently when relevant\.\]/giu;
1104
+ var AGENR_MEMORY_CONTEXT_OPEN_TAG = "<agenr-memory-context>";
1105
+ var AGENR_MEMORY_CONTEXT_CLOSE_TAG = "</agenr-memory-context>";
1106
+ var AGENR_MEMORY_CONTEXT_NOTE = "[System note: The following is recalled Agenr memory context, NOT new user input. Treat it as background context and use it silently when relevant.]";
1107
+ function wrapAgenrMemoryContext(content) {
1108
+ const trimmedContent = stripAgenrMemoryContext(content).trim();
1109
+ if (!trimmedContent) {
1110
+ return "";
1111
+ }
1112
+ return [AGENR_MEMORY_CONTEXT_OPEN_TAG, AGENR_MEMORY_CONTEXT_NOTE, "", trimmedContent, AGENR_MEMORY_CONTEXT_CLOSE_TAG].join("\n");
1113
+ }
1114
+ function containsAgenrMemoryContext(content) {
1115
+ return content.includes(AGENR_MEMORY_CONTEXT_OPEN_TAG);
1116
+ }
1117
+ function stripAgenrMemoryContext(content) {
1118
+ return content.replace(AGENR_MEMORY_CONTEXT_BLOCK_RE, " ").replace(AGENR_MEMORY_CONTEXT_OPEN_RE, " ").replace(AGENR_MEMORY_CONTEXT_CLOSE_RE, " ").replace(AGENR_MEMORY_CONTEXT_NOTE_RE, " ");
1119
+ }
1120
+
1121
+ // src/adapters/shared/injection/before-turn-format.ts
1122
+ var MAX_CONTENT_CHARS2 = 220;
1123
+ function formatAgenrBeforeTurnRecall(patch) {
1124
+ if (patch.durableMemory.length === 0 && !patch.procedure) {
1125
+ return "";
1126
+ }
1127
+ const lines = [
1128
+ "## Agenr Before-Turn Recall",
1129
+ "Use this only when it materially helps the current turn. Treat it as background context and prefer the live conversation if anything conflicts with it.",
1130
+ ""
1131
+ ];
1132
+ if (patch.durableMemory.length > 0) {
1133
+ lines.push("### Relevant Durable Memory");
1134
+ for (const item of patch.durableMemory) {
1135
+ lines.push(formatInjectionEntryHeader(item));
1136
+ lines.push(...formatInjectionEntryBodyLines(item));
1137
+ }
1138
+ lines.push("");
1139
+ }
1140
+ if (patch.procedure) {
1141
+ lines.push("### Suggested Procedure");
1142
+ lines.push(formatProcedureHeader(patch.procedure));
1143
+ lines.push(...formatProcedureBodyLines(patch.procedure));
1144
+ lines.push("");
1145
+ }
1146
+ return wrapAgenrMemoryContext(lines.join("\n").trim());
1147
+ }
1148
+ function formatProcedureHeader(suggestion) {
1149
+ const metadata = [
1150
+ suggestion.procedure.id,
1151
+ suggestion.procedure.procedure_key,
1152
+ `score ${suggestion.score.toFixed(2)}`,
1153
+ `lexical ${suggestion.scores.lexical.toFixed(2)}`,
1154
+ `vector ${suggestion.scores.vector.toFixed(2)}`
1155
+ ];
1156
+ return `- [${metadata.join(" | ")}] ${suggestion.procedure.title}`;
1157
+ }
1158
+ function formatProcedureBodyLines(suggestion) {
1159
+ const lines = [` goal: ${truncate(suggestion.procedure.goal.trim(), MAX_CONTENT_CHARS2)}`];
1160
+ lines.push(` why: ${suggestion.whySurfaced.summary}`);
1161
+ if (suggestion.procedure.when_to_use.length > 0) {
1162
+ lines.push(` when to use: ${truncate(suggestion.procedure.when_to_use.join("; "), MAX_CONTENT_CHARS2)}`);
1163
+ }
1164
+ if (suggestion.procedure.verification.length > 0) {
1165
+ lines.push(` verification: ${truncate(suggestion.procedure.verification.join("; "), MAX_CONTENT_CHARS2)}`);
1166
+ }
1167
+ return lines;
1168
+ }
1169
+
1170
+ // src/app/session-memory/predecessor-artifacts.ts
1171
+ var SESSION_START_ARTIFACT_KINDS = [
1172
+ "compaction_checkpoint",
1173
+ "branch_abandonment",
1174
+ "session_episode"
1175
+ ];
1176
+ async function resolvePredecessorSessionArtifacts(input, repository) {
1177
+ const sessionKey = input.sessionKey.trim();
1178
+ if (!sessionKey) {
1179
+ return {
1180
+ artifacts: []
1181
+ };
1182
+ }
1183
+ const lineageEdge = await repository.getLatestLineageEdgeForChild(sessionKey);
1184
+ const predecessorSessionKey = lineageEdge?.parentSessionKey?.trim();
1185
+ const parentSourceRef = lineageEdge?.parentSourceRef?.trim();
1186
+ if (!predecessorSessionKey && !parentSourceRef) {
1187
+ return {
1188
+ ...lineageEdge ? { lineageEdge } : {},
1189
+ artifacts: []
1190
+ };
1191
+ }
1192
+ const artifacts = predecessorSessionKey ? await repository.listSessionArtifacts({
1193
+ sessionKey: predecessorSessionKey,
1194
+ kinds: [...SESSION_START_ARTIFACT_KINDS],
1195
+ limit: 10
1196
+ }) : await repository.listSessionArtifactsBySourceRef({
1197
+ sourceRef: parentSourceRef,
1198
+ kinds: [...SESSION_START_ARTIFACT_KINDS],
1199
+ limit: 10
1200
+ });
1201
+ return {
1202
+ ...lineageEdge ? { lineageEdge } : {},
1203
+ artifacts
1204
+ };
1205
+ }
1206
+
1207
+ // src/app/session-start/artifact-recall-query.ts
1208
+ var ARTIFACT_QUERY_TITLES = {
1209
+ compaction_checkpoint: "Compaction checkpoint",
1210
+ branch_abandonment: "Branch abandonment",
1211
+ session_episode: "Session episode"
1212
+ };
1213
+ var SESSION_START_QUERY_ARTIFACT_ORDER = new Map(SESSION_START_ARTIFACT_KINDS.map((kind, index) => [kind, index]));
1214
+ function buildSessionStartArtifactRecallQuery(artifacts, maxChars) {
1215
+ if (artifacts.length === 0 || maxChars <= 0) {
1216
+ return void 0;
1217
+ }
1218
+ const orderedArtifacts = [...artifacts].sort(compareSessionStartArtifacts);
1219
+ let remaining = maxChars;
1220
+ const parts = [];
1221
+ for (const artifact of orderedArtifacts) {
1222
+ if (remaining <= 0) {
1223
+ break;
1224
+ }
1225
+ const title = ARTIFACT_QUERY_TITLES[artifact.kind];
1226
+ if (!title) {
1227
+ continue;
1228
+ }
1229
+ const normalizedContent = normalizeWhitespace2(artifact.summary);
1230
+ if (normalizedContent.length === 0) {
1231
+ continue;
1232
+ }
1233
+ const labeled = `${title}: ${normalizedContent}`;
1234
+ const truncated = truncate3(labeled, remaining);
1235
+ if (truncated.length === 0) {
1236
+ continue;
1237
+ }
1238
+ parts.push(truncated);
1239
+ remaining -= truncated.length;
1240
+ }
1241
+ const query = normalizeWhitespace2(parts.join("\n"));
1242
+ return query.length > 0 ? query : void 0;
1243
+ }
1244
+ function compareSessionStartArtifacts(left, right) {
1245
+ const leftOrder = SESSION_START_QUERY_ARTIFACT_ORDER.get(left.kind) ?? Number.MAX_SAFE_INTEGER;
1246
+ const rightOrder = SESSION_START_QUERY_ARTIFACT_ORDER.get(right.kind) ?? Number.MAX_SAFE_INTEGER;
1247
+ if (leftOrder !== rightOrder) {
1248
+ return leftOrder - rightOrder;
1249
+ }
1250
+ return right.createdAt.localeCompare(left.createdAt);
1251
+ }
1252
+ function normalizeWhitespace2(value) {
1253
+ return value.replace(/\s+/g, " ").trim();
1254
+ }
1255
+ function truncate3(value, maxChars) {
1256
+ if (maxChars <= 0) {
1257
+ return "";
1258
+ }
1259
+ if (value.length <= maxChars) {
1260
+ return value;
1261
+ }
1262
+ return `${value.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
1263
+ }
1264
+
1265
+ // src/app/session-start/service.ts
1266
+ var DEFAULT_MAX_CORE_ENTRIES = 4;
1267
+ var DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES = 3;
1268
+ var DEFAULT_MAX_DURABLE_ENTRIES2 = 5;
1269
+ var DEFAULT_MAX_ARTIFACT_CHARS = 1200;
1270
+ var DEFAULT_MAX_PROFILE_SNAPSHOT_AGE_HOURS = 48;
1271
+ async function runSessionStart(input, deps) {
1272
+ const policy = normalizePolicy2(input.policy);
1273
+ const now = deps.now ?? /* @__PURE__ */ new Date();
1274
+ const nowMs = now.getTime();
1275
+ const profileSnapshot = await deps.repository.getActiveProfileSnapshot(policy.maxProfileSnapshotAgeHours * 60 * 60 * 1e3, now);
1276
+ const profileEntries = profileSnapshot ? filterCurrentEntries2(await deps.repository.listEntriesByIds(profileSnapshot.durableIds), nowMs) : [];
1277
+ const profileItems = profileEntries.map((entry) => buildProfilePatchItem(entry, profileSnapshot?.id ?? "unknown", nowMs));
1278
+ const proactiveDirectiveEntries = filterCurrentEntries2(await deps.listActiveProactiveDirectives?.() ?? [], nowMs);
1279
+ const proactiveDirectiveItems = proactiveDirectiveEntries.map((entry) => buildDirectivePatchItem(entry, nowMs));
1280
+ const coreEntries = await deps.repository.listCoreEntries(policy.maxCoreEntries, now);
1281
+ const coreItems = coreEntries.map((entry) => buildCorePatchItem(entry, nowMs));
1282
+ const diagnostics = {
1283
+ coreCandidateCount: coreEntries.length,
1284
+ profileCandidateCount: profileEntries.length,
1285
+ ...profileSnapshot ? { activeProfileSnapshotId: profileSnapshot.id } : {},
1286
+ proactiveDirectiveCandidateCount: proactiveDirectiveEntries.length,
1287
+ artifactRecallCandidateCount: 0,
1288
+ artifactRecallUsed: false,
1289
+ notices: []
1290
+ };
1291
+ const artifactRecallQuery = await resolveSessionStartArtifactRecallQuery(input.sessionKey, policy, deps);
1292
+ if (!policy.enableArtifactRecall) {
1293
+ diagnostics.notices.push("Artifact-grounded durable recall disabled by session-start policy.");
1294
+ }
1295
+ const artifactRecallItems = artifactRecallQuery ? await runArtifactRecallSelection(artifactRecallQuery, input.sessionKey, policy, deps, diagnostics) : [];
1296
+ const mergedDurableMemory = mergeDurableMemory(profileItems, proactiveDirectiveItems, coreItems, artifactRecallItems, policy.maxDurableEntries);
1297
+ const visibleDurableMemory = await applyAbstainDirectivesForInjection(mergedDurableMemory, deps.listActiveAbstainDirectives, diagnostics);
1298
+ const durableMemory = assignRanks2(visibleDurableMemory);
1299
+ return {
1300
+ durableMemory,
1301
+ diagnostics
1302
+ };
1303
+ }
1304
+ async function resolveSessionStartArtifactRecallQuery(sessionKey, policy, deps) {
1305
+ if (!policy.enableArtifactRecall) {
1306
+ return void 0;
1307
+ }
1308
+ const normalizedSessionKey = sessionKey?.trim();
1309
+ if (!normalizedSessionKey || !deps.sessionMemoryRepository) {
1310
+ return void 0;
1311
+ }
1312
+ const predecessor = await resolvePredecessorSessionArtifacts({ sessionKey: normalizedSessionKey }, deps.sessionMemoryRepository);
1313
+ return buildSessionStartArtifactRecallQuery(predecessor.artifacts, policy.maxArtifactChars);
1314
+ }
1315
+ function buildProfilePatchItem(entry, snapshotId, nowMs) {
1316
+ return {
1317
+ rank: 0,
1318
+ entry,
1319
+ sourceKind: "profile",
1320
+ whySurfaced: {
1321
+ summary: `active profile snapshot ${snapshotId}`,
1322
+ reasons: ["active profile snapshot", `snapshot ${snapshotId}`, `importance ${entry.importance}`]
1323
+ },
1324
+ memoryState: resolveMemoryState(entry, nowMs),
1325
+ claimStatus: resolveClaimStatus(entry),
1326
+ freshnessLabel: buildFreshnessLabel(entry),
1327
+ ...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
1328
+ };
1329
+ }
1330
+ function buildDirectivePatchItem(entry, nowMs) {
1331
+ const metadata = parseDirectiveMetadata(entry);
1332
+ return {
1333
+ rank: 0,
1334
+ entry,
1335
+ sourceKind: "directive",
1336
+ whySurfaced: {
1337
+ summary: `proactive memory directive; trigger ${metadata?.trigger ?? "session_start"}`,
1338
+ reasons: ["proactive memory directive", `trigger ${metadata?.trigger ?? "session_start"}`, `importance ${entry.importance}`]
1339
+ },
1340
+ memoryState: resolveMemoryState(entry, nowMs),
1341
+ claimStatus: resolveClaimStatus(entry),
1342
+ freshnessLabel: buildFreshnessLabel(entry),
1343
+ ...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
1344
+ };
1345
+ }
1346
+ async function runArtifactRecallSelection(query, sessionKey, policy, deps, diagnostics) {
1347
+ diagnostics.artifactRecallUsed = true;
1348
+ diagnostics.artifactRecallQuery = query;
1349
+ let artifactRecallTrace;
1350
+ try {
1351
+ const recalled = await recall(
1352
+ {
1353
+ text: query,
1354
+ limit: policy.maxArtifactRecallEntries,
1355
+ threshold: policy.recallThreshold,
1356
+ sessionKey
1357
+ },
1358
+ deps.recall,
1359
+ {
1360
+ trace: {
1361
+ reportSummary(summary) {
1362
+ artifactRecallTrace = summary;
1363
+ }
1364
+ },
1365
+ slotPolicyConfig: deps.slotPolicyConfig,
1366
+ ...deps.now ? { now: deps.now } : {}
1367
+ }
1368
+ );
1369
+ diagnostics.artifactRecallTrace = artifactRecallTrace;
1370
+ diagnostics.artifactRecallCandidateCount = recalled.length;
1371
+ if (artifactRecallTrace?.degraded.notices.length) {
1372
+ diagnostics.notices.push(...artifactRecallTrace.degraded.notices);
1373
+ }
1374
+ return recalled.map((item) => buildArtifactRecallPatchItem(item, deps));
1375
+ } catch (error) {
1376
+ diagnostics.artifactRecallTrace = artifactRecallTrace;
1377
+ diagnostics.notices.push(`Artifact-grounded durable recall failed: ${formatErrorMessage2(error)}`);
1378
+ return [];
1379
+ }
1380
+ }
1381
+ function buildCorePatchItem(entry, nowMs) {
1382
+ return {
1383
+ rank: 0,
1384
+ entry,
1385
+ sourceKind: "core",
1386
+ whySurfaced: {
1387
+ summary: `always-on core memory; importance ${entry.importance}`,
1388
+ reasons: ["always-on core memory", `importance ${entry.importance}`, `expiry ${entry.expiry}`]
1389
+ },
1390
+ memoryState: resolveMemoryState(entry, nowMs),
1391
+ claimStatus: resolveClaimStatus(entry),
1392
+ freshnessLabel: buildFreshnessLabel(entry),
1393
+ ...buildProvenanceSummary(entry) ? { provenanceSummary: buildProvenanceSummary(entry) } : {}
1394
+ };
1395
+ }
1396
+ function buildArtifactRecallPatchItem(recalled, deps) {
1397
+ const projected = projectClaimCentricRecallEntry(recalled, {
1398
+ slotPolicyConfig: deps.slotPolicyConfig
1399
+ });
1400
+ return {
1401
+ rank: 0,
1402
+ entry: recalled.entry,
1403
+ sourceKind: "artifact_recall",
1404
+ score: recalled.score,
1405
+ whySurfaced: projected.whySurfaced,
1406
+ memoryState: projected.memoryState,
1407
+ claimStatus: projected.claimStatus,
1408
+ freshnessLabel: projected.freshness.label,
1409
+ ...formatProjectedProvenance2(projected.provenance) ? { provenanceSummary: formatProjectedProvenance2(projected.provenance) } : {}
1410
+ };
1411
+ }
1412
+ function mergeDurableMemory(profileItems, directiveItems, coreItems, artifactRecallItems, maxDurableEntries) {
1413
+ const merged = [];
1414
+ const seenEntryIds = /* @__PURE__ */ new Set();
1415
+ const tryAdd = (item) => {
1416
+ if (merged.length >= maxDurableEntries || seenEntryIds.has(item.entry.id)) {
1417
+ return false;
1418
+ }
1419
+ seenEntryIds.add(item.entry.id);
1420
+ merged.push(item);
1421
+ return true;
1422
+ };
1423
+ const addFrom = (items, maxAdd = Number.POSITIVE_INFINITY) => {
1424
+ let added = 0;
1425
+ for (const item of items) {
1426
+ if (merged.length >= maxDurableEntries || added >= maxAdd) {
1427
+ return;
1428
+ }
1429
+ if (tryAdd(item)) {
1430
+ added += 1;
1431
+ }
1432
+ }
1433
+ };
1434
+ addFrom(profileItems);
1435
+ addFrom(directiveItems);
1436
+ if (merged.length >= maxDurableEntries) {
1437
+ return merged;
1438
+ }
1439
+ const uniqueArtifact = artifactRecallItems.find((item) => !seenEntryIds.has(item.entry.id));
1440
+ const remainingSlots = maxDurableEntries - merged.length;
1441
+ const coreLimit = uniqueArtifact ? Math.max(0, remainingSlots - 1) : remainingSlots;
1442
+ addFrom(coreItems, coreLimit);
1443
+ if (uniqueArtifact) {
1444
+ tryAdd(uniqueArtifact);
1445
+ }
1446
+ addFrom(coreItems);
1447
+ addFrom(artifactRecallItems);
1448
+ return merged;
1449
+ }
1450
+ function assignRanks2(items) {
1451
+ return items.map((item, index) => ({
1452
+ ...item,
1453
+ rank: index + 1
1454
+ }));
1455
+ }
1456
+ function normalizePolicy2(policy) {
1457
+ const maxCoreEntries = normalizeCount2(policy?.maxCoreEntries, DEFAULT_MAX_CORE_ENTRIES);
1458
+ const maxArtifactRecallEntries = normalizeCount2(policy?.maxArtifactRecallEntries, DEFAULT_MAX_ARTIFACT_RECALL_ENTRIES);
1459
+ const maxDurableEntries = Math.max(maxCoreEntries, normalizeCount2(policy?.maxDurableEntries, DEFAULT_MAX_DURABLE_ENTRIES2));
1460
+ return {
1461
+ maxCoreEntries,
1462
+ enableArtifactRecall: policy?.enableArtifactRecall !== false,
1463
+ maxArtifactRecallEntries,
1464
+ maxDurableEntries,
1465
+ maxArtifactChars: normalizeCount2(policy?.maxArtifactChars, DEFAULT_MAX_ARTIFACT_CHARS),
1466
+ recallThreshold: normalizeThreshold2(policy?.recallThreshold),
1467
+ maxProfileSnapshotAgeHours: normalizeCount2(policy?.maxProfileSnapshotAgeHours, DEFAULT_MAX_PROFILE_SNAPSHOT_AGE_HOURS)
1468
+ };
1469
+ }
1470
+ function filterCurrentEntries2(entries, nowMs) {
1471
+ return entries.filter((entry) => isWithinValidityWindow(entry.valid_from, entry.valid_to, nowMs));
1472
+ }
1473
+ function normalizeCount2(value, fallback) {
1474
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1475
+ return fallback;
1476
+ }
1477
+ return Math.max(0, Math.trunc(value));
1478
+ }
1479
+ function normalizeThreshold2(value) {
1480
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1481
+ return 0;
1482
+ }
1483
+ return Math.min(1, Math.max(0, value));
1484
+ }
1485
+ function resolveMemoryState(entry, nowMs) {
1486
+ if (entry.superseded_by) {
1487
+ return "superseded";
1488
+ }
1489
+ if (!isCurrentlyValidMemory(entry, nowMs)) {
1490
+ return "historical";
1491
+ }
1492
+ return "current";
1493
+ }
1494
+ function resolveClaimStatus(entry) {
1495
+ return resolveKeyedDurableLifecycleStatus(entry);
1496
+ }
1497
+ function buildFreshnessLabel(entry) {
1498
+ const parts = [`created ${entry.created_at}`];
1499
+ const validFrom = normalizeOptionalString2(entry.valid_from);
1500
+ const validTo = normalizeOptionalString2(entry.valid_to);
1501
+ if (validFrom || validTo) {
1502
+ parts.push(`valid ${validFrom ?? "?"} -> ${validTo ?? "ongoing"}`);
1503
+ }
1504
+ return parts.join(" | ");
1505
+ }
1506
+ function buildProvenanceSummary(entry) {
1507
+ const parts = [
1508
+ entry.superseded_by ? `superseded_by=${entry.superseded_by}` : void 0,
1509
+ entry.supersession_kind ? `kind=${entry.supersession_kind}` : void 0,
1510
+ entry.supersession_reason ? `reason=${entry.supersession_reason}` : void 0,
1511
+ entry.claim_support_source_kind ? `support=${entry.claim_support_source_kind}` : void 0,
1512
+ entry.claim_support_mode ? `support_mode=${entry.claim_support_mode}` : void 0,
1513
+ entry.claim_support_observed_at ? `observed=${entry.claim_support_observed_at}` : void 0,
1514
+ entry.claim_support_locator ? `locator=${entry.claim_support_locator}` : void 0
1515
+ ].filter((value) => value !== void 0);
1516
+ return parts.length > 0 ? parts.join(" | ") : void 0;
1517
+ }
1518
+ function formatProjectedProvenance2(provenance) {
1519
+ const parts = [
1520
+ provenance.supersededById ? `superseded_by=${provenance.supersededById}` : void 0,
1521
+ provenance.supersessionKind ? `kind=${provenance.supersessionKind}` : void 0,
1522
+ provenance.supersessionReason ? `reason=${provenance.supersessionReason}` : void 0,
1523
+ provenance.supportSourceKind ? `support=${provenance.supportSourceKind}` : void 0,
1524
+ provenance.supportMode ? `support_mode=${provenance.supportMode}` : void 0,
1525
+ provenance.supportObservedAt ? `observed=${provenance.supportObservedAt}` : void 0,
1526
+ provenance.supportLocator ? `locator=${provenance.supportLocator}` : void 0
1527
+ ].filter((value) => value !== void 0);
1528
+ return parts.length > 0 ? parts.join(" | ") : void 0;
1529
+ }
1530
+ function normalizeOptionalString2(value) {
1531
+ const normalized = value?.trim();
1532
+ return normalized && normalized.length > 0 ? normalized : void 0;
1533
+ }
1534
+ function formatErrorMessage2(error) {
1535
+ if (error instanceof Error) {
1536
+ return error.message;
1537
+ }
1538
+ return String(error);
1539
+ }
1540
+
1541
+ // src/adapters/db/directives-repository.ts
1542
+ var MAX_DIRECTIVES = 50;
1543
+ async function listActiveAbstainDirectives(executor, now = /* @__PURE__ */ new Date()) {
1544
+ const nowIso = now.toISOString();
1545
+ const result = await executor.execute({
1546
+ sql: `
1547
+ SELECT
1548
+ ${DURABLE_SELECT_COLUMNS}
1549
+ FROM durables
1550
+ WHERE ${buildActiveDurableClause()}
1551
+ AND (
1552
+ (type = 'directive' AND directive_polarity = 'abstain')
1553
+ OR (claim_key LIKE ? AND (directive_polarity IS NULL OR directive_polarity = 'abstain'))
1554
+ )
1555
+ AND ${buildValidAsOfClause()}
1556
+ ORDER BY created_at DESC
1557
+ LIMIT ?
1558
+ `,
1559
+ args: [`${MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX}%`, nowIso, nowIso, MAX_DIRECTIVES]
1560
+ });
1561
+ return result.rows.map((row) => mapDurableRow(row));
1562
+ }
1563
+ async function listActiveSessionStartProactiveDirectives(executor, now = /* @__PURE__ */ new Date()) {
1564
+ const nowIso = now.toISOString();
1565
+ const result = await executor.execute({
1566
+ sql: `
1567
+ SELECT
1568
+ ${DURABLE_SELECT_COLUMNS}
1569
+ FROM durables
1570
+ WHERE ${buildActiveDurableClause()}
1571
+ AND type = 'directive'
1572
+ AND directive_polarity = 'proactive'
1573
+ AND directive_trigger IN ('session_start', 'always')
1574
+ AND ${buildValidAsOfClause()}
1575
+ ORDER BY importance DESC, created_at DESC
1576
+ LIMIT ?
1577
+ `,
1578
+ args: [nowIso, nowIso, MAX_DIRECTIVES]
1579
+ });
1580
+ return result.rows.map((row) => mapDurableRow(row));
1581
+ }
1582
+ async function listActiveTopicProactiveDirectives(executor, now = /* @__PURE__ */ new Date()) {
1583
+ const nowIso = now.toISOString();
1584
+ const result = await executor.execute({
1585
+ sql: `
1586
+ SELECT
1587
+ ${DURABLE_SELECT_COLUMNS}
1588
+ FROM durables
1589
+ WHERE ${buildActiveDurableClause()}
1590
+ AND type = 'directive'
1591
+ AND directive_polarity = 'proactive'
1592
+ AND directive_trigger LIKE 'topic:%'
1593
+ AND ${buildValidAsOfClause()}
1594
+ ORDER BY importance DESC, created_at DESC
1595
+ LIMIT ?
1596
+ `,
1597
+ args: [nowIso, nowIso, MAX_DIRECTIVES]
1598
+ });
1599
+ return result.rows.map((row) => mapDurableRow(row));
1600
+ }
1601
+
1602
+ // src/adapters/db/session-start-repository.ts
1603
+ function createSessionStartRepository(executor) {
1604
+ return {
1605
+ listCoreEntries: async (limit, now) => listCoreEntries(executor, limit, now),
1606
+ getActiveProfileSnapshot: async (maxAgeMs, now) => getActiveProfileSnapshot(executor, maxAgeMs, now),
1607
+ listEntriesByIds: async (ids) => getDurables(executor, ids)
1608
+ };
1609
+ }
1610
+ async function getActiveProfileSnapshot(executor, maxAgeMs, now = /* @__PURE__ */ new Date()) {
1611
+ if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) {
1612
+ return null;
1613
+ }
1614
+ const minCreatedAt = new Date(now.getTime() - maxAgeMs).toISOString();
1615
+ const result = await executor.execute({
1616
+ sql: `
1617
+ SELECT
1618
+ p.id,
1619
+ p.durable_ids,
1620
+ p.directive_ids,
1621
+ p.as_of,
1622
+ p.run_id,
1623
+ p.created_at
1624
+ FROM dream_state AS s
1625
+ JOIN profile_snapshots AS p ON p.id = s.active_profile_snapshot_id
1626
+ WHERE s.id = 'default'
1627
+ AND datetime(p.created_at) >= datetime(?)
1628
+ LIMIT 1
1629
+ `,
1630
+ args: [minCreatedAt]
1631
+ });
1632
+ const row = result.rows[0];
1633
+ if (!row) {
1634
+ return null;
1635
+ }
1636
+ return {
1637
+ id: readRequiredString(row, "id"),
1638
+ durableIds: parseJsonStringArray(readOptionalString(row, "durable_ids")),
1639
+ directiveIds: parseJsonStringArray(readOptionalString(row, "directive_ids")),
1640
+ asOf: readRequiredString(row, "as_of"),
1641
+ runId: readOptionalString(row, "run_id") ?? null,
1642
+ createdAt: readRequiredString(row, "created_at")
1643
+ };
1644
+ }
1645
+ async function listCoreEntries(executor, limit, now = /* @__PURE__ */ new Date()) {
1646
+ if (limit <= 0) {
1647
+ return [];
1648
+ }
1649
+ const nowIso = now.toISOString();
1650
+ const result = await executor.execute({
1651
+ sql: `
1652
+ SELECT
1653
+ ${DURABLE_SELECT_COLUMNS}
1654
+ FROM durables
1655
+ WHERE ${buildActiveDurableClause()}
1656
+ AND expiry = 'core'
1657
+ AND ${buildValidAsOfClause()}
1658
+ ORDER BY importance DESC, created_at DESC
1659
+ LIMIT ?
1660
+ `,
1661
+ args: [nowIso, nowIso, limit]
1662
+ });
1663
+ return result.rows.map((row) => mapDurableRow(row));
1664
+ }
1665
+
1666
+ export {
1667
+ runBeforeTurn,
1668
+ listActiveAbstainDirectives,
1669
+ listActiveSessionStartProactiveDirectives,
1670
+ listActiveTopicProactiveDirectives,
1671
+ createSessionStartRepository,
1672
+ formatInjectionEntryHeader,
1673
+ formatInjectionEntryBodyLines,
1674
+ wrapAgenrMemoryContext,
1675
+ containsAgenrMemoryContext,
1676
+ stripAgenrMemoryContext,
1677
+ formatAgenrBeforeTurnRecall,
1678
+ runSessionStart
1679
+ };