@agenr/skeln-plugin 3.3.0 → 2026.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/build-before-turn-artifact-NPUHVWFE.js +71 -0
  2. package/dist/build-recall-artifact-F3LS3PZX.js +62 -0
  3. package/dist/chunk-5AXMFBHR.js +14 -0
  4. package/dist/chunk-5AYIXQRF.js +4452 -0
  5. package/dist/{chunk-Z5X7T4QZ.js → chunk-5TIP2EPP.js} +1519 -2565
  6. package/dist/{chunk-5LADPJ4C.js → chunk-GAERET5Q.js} +138 -504
  7. package/dist/chunk-GF3PX3VM.js +41 -0
  8. package/dist/chunk-GKZQ5AG5.js +44 -0
  9. package/dist/chunk-LDJN7CVU.js +3231 -0
  10. package/dist/{chunk-ZYADFKX3.js → chunk-MC3C2XM5.js} +34 -1
  11. package/dist/chunk-NBS62ES5.js +3012 -0
  12. package/dist/chunk-NSLTJBUC.js +270 -0
  13. package/dist/chunk-OJSIZDZD.js +9 -0
  14. package/dist/chunk-OWGQWQUP.js +45 -0
  15. package/dist/chunk-Q5UTJXHZ.js +1069 -0
  16. package/dist/{chunk-M5M65AYP.js → chunk-SOQW7356.js} +271 -1934
  17. package/dist/chunk-VBPYU7GO.js +597 -0
  18. package/dist/chunk-VTHBPXDQ.js +1750 -0
  19. package/dist/{chunk-KH52KJSJ.js → chunk-XFJ4S4G2.js} +844 -39
  20. package/dist/chunk-Y5NB3FTH.js +106 -0
  21. package/dist/{chunk-RYMSM3OS.js → chunk-ZX55JBV2.js} +1710 -322
  22. package/dist/claim-slot-policy-CdrW_1l4.d.ts +13 -0
  23. package/dist/index.d.ts +630 -51
  24. package/dist/index.js +881 -4682
  25. package/dist/lifecycle-checkpoint-IAC5FCQU.js +154 -0
  26. package/dist/{claim-slot-policy-CQ-h0GaV.d.ts → ports-C4QkwDBS.d.ts} +168 -78
  27. package/dist/scan-6JKPOQHD.js +6 -0
  28. package/dist/service-EKFACEN6.js +15 -0
  29. package/dist/service-RHNB5AEQ.js +861 -0
  30. package/dist/sink-AUAAWC5O.js +8 -0
  31. package/package.json +1 -1
  32. package/dist/cli.d.ts +0 -1
  33. package/dist/internal-eval-server.d.ts +0 -1
  34. package/dist/internal-recall-eval-server.d.ts +0 -1
@@ -0,0 +1,4452 @@
1
+ import {
2
+ buildDreamEfficiencySummary
3
+ } from "./chunk-GF3PX3VM.js";
4
+ import {
5
+ buildDurableLocalLexicalTokens,
6
+ buildTrustedClaimKeySupportSeed,
7
+ computeContentHash,
8
+ computeNormContentHash,
9
+ detectClaimKeyEntityFamilyCandidates,
10
+ detectClaimKeySingletonAliasCandidates,
11
+ evaluateClaimKeyCompactness,
12
+ evaluateClaimKeySupport,
13
+ normalizeGroundingTags,
14
+ previewClaimKeyExtraction,
15
+ resolveDurableProjectScope,
16
+ tokenizeGroundingText,
17
+ validateSupersessionRules,
18
+ withDreamingRunLock
19
+ } from "./chunk-SOQW7356.js";
20
+ import {
21
+ DEFAULT_CLAIM_EXTRACTION_CONCURRENCY,
22
+ DEFAULT_DREAMING_DAILY_COST_CAP,
23
+ DEFAULT_DREAMING_EXTRACT_MAX_SESSIONS,
24
+ DEFAULT_DREAMING_LIGHT_MAX_SESSIONS,
25
+ DEFAULT_DREAMING_PRUNE_PROTECT_MIN_IMPORTANCE,
26
+ DEFAULT_DREAMING_PRUNE_PROTECT_RECALLED_DAYS,
27
+ MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX,
28
+ applyClaimKeyLifecycle,
29
+ buildClaimKeyLifecycleAuditDetails,
30
+ buildClaimKeyLifecycleUpdateFields,
31
+ buildReconcileAppliedClaimKeyLifecycleBundle,
32
+ buildReconcileProposalClaimKeyAuditDetails,
33
+ buildReconcileProposalClaimKeyLifecycle,
34
+ buildReconcileProposalLifecycleRationale,
35
+ composeEmbeddingText,
36
+ defaultDirectiveTrigger,
37
+ normalizeMemoryDirectiveClaimKey,
38
+ parseDirectiveMetadata,
39
+ resolveClaimExtractionConfig,
40
+ resolveLocalFilesystemPath
41
+ } from "./chunk-VTHBPXDQ.js";
42
+ import {
43
+ compactClaimKey,
44
+ describeClaimKeyNormalizationFailure,
45
+ describeClaimKeySuspicion,
46
+ inspectClaimKey,
47
+ isCurrentlyValidMemory,
48
+ isStaleMemory,
49
+ isTrustedClaimKeyForCleanup,
50
+ normalizeClaimKey,
51
+ normalizeClaimKeySegment
52
+ } from "./chunk-VBPYU7GO.js";
53
+ import {
54
+ runDreamScan
55
+ } from "./chunk-GKZQ5AG5.js";
56
+
57
+ // src/app/dreaming/service.ts
58
+ import { copyFile, mkdir } from "fs/promises";
59
+ import path from "path";
60
+
61
+ // src/core/dreaming/domain/tier-plan.ts
62
+ function resolveTierStages(tier) {
63
+ switch (tier) {
64
+ case "light":
65
+ return {
66
+ runReconcile: false,
67
+ runPrune: false
68
+ };
69
+ case "standard":
70
+ case "deep":
71
+ return {
72
+ runReconcile: true,
73
+ runPrune: true
74
+ };
75
+ }
76
+ }
77
+
78
+ // src/app/dreaming/abort.ts
79
+ var USER_ABORT_ERROR = "Run aborted by user (SIGINT).";
80
+ function throwIfAborted(signal) {
81
+ if (signal?.aborted) {
82
+ throw new Error(USER_ABORT_ERROR);
83
+ }
84
+ }
85
+
86
+ // src/app/dreaming/extract.ts
87
+ import { randomUUID } from "crypto";
88
+
89
+ // src/core/ingestion/parser.ts
90
+ var IMPORTANCE_TIER_MAP = {
91
+ high: 8,
92
+ standard: 6,
93
+ low: 4
94
+ };
95
+ var TYPE_ALIAS_MAP = {
96
+ fact: "fact",
97
+ facts: "fact",
98
+ decision: "decision",
99
+ decisions: "decision",
100
+ preference: "preference",
101
+ preferences: "preference",
102
+ lesson: "lesson",
103
+ lessons: "lesson",
104
+ event: "milestone",
105
+ events: "milestone",
106
+ milestone: "milestone",
107
+ milestones: "milestone",
108
+ relationship: "relationship",
109
+ relationships: "relationship",
110
+ directive: "directive",
111
+ directives: "directive"
112
+ };
113
+ var EXPIRY_ALIAS_MAP = {
114
+ permanent: "permanent",
115
+ perm: "permanent",
116
+ temporary: "temporary",
117
+ temp: "temporary",
118
+ core: "core"
119
+ };
120
+ var BLOCKED_SUBJECTS = /* @__PURE__ */ new Set([
121
+ "user",
122
+ "assistant",
123
+ "human",
124
+ "ai",
125
+ "bot",
126
+ "developer",
127
+ "engineer",
128
+ "maintainer",
129
+ "team",
130
+ "we",
131
+ "the conversation",
132
+ "this session",
133
+ "the transcript"
134
+ ]);
135
+ function parseExtractionResponse(raw) {
136
+ const warnings = [];
137
+ const payload = coerceResponseObject(raw);
138
+ if (!payload) {
139
+ return {
140
+ entries: [],
141
+ warnings: ["Extraction response was not a valid JSON object."]
142
+ };
143
+ }
144
+ if (!Array.isArray(payload.durables)) {
145
+ return {
146
+ entries: [],
147
+ warnings: ['Extraction response must have a "durables" array.']
148
+ };
149
+ }
150
+ const entries = [];
151
+ for (const [index, value] of payload.durables.entries()) {
152
+ const entry = parseEntry(value, index, warnings);
153
+ if (entry) {
154
+ entries.push(entry);
155
+ }
156
+ }
157
+ return { entries, warnings };
158
+ }
159
+ function coerceResponseObject(raw) {
160
+ if (typeof raw === "string") {
161
+ try {
162
+ const parsed = JSON.parse(stripCodeFence(raw));
163
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
164
+ return parsed;
165
+ }
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
171
+ return raw;
172
+ }
173
+ return null;
174
+ }
175
+ function parseEntry(value, index, warnings) {
176
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
177
+ warnings.push(`Dropped entry ${index + 1}: entry must be an object.`);
178
+ return null;
179
+ }
180
+ const record = value;
181
+ const type = coerceType(record.type);
182
+ if (!type) {
183
+ warnings.push(`Dropped entry ${index + 1}: invalid type.`);
184
+ return null;
185
+ }
186
+ const subject = normalizeString(record.subject);
187
+ if (!subject) {
188
+ warnings.push(`Dropped entry ${index + 1}: subject is required.`);
189
+ return null;
190
+ }
191
+ if (BLOCKED_SUBJECTS.has(subject.toLowerCase())) {
192
+ warnings.push(`Dropped entry ${index + 1}: subject "${subject}" is blocked.`);
193
+ return null;
194
+ }
195
+ const content = normalizeString(record.content);
196
+ if (!content) {
197
+ warnings.push(`Dropped entry ${index + 1}: content is required.`);
198
+ return null;
199
+ }
200
+ if (content.length < 20) {
201
+ warnings.push(`Dropped entry ${index + 1}: content must be at least 20 characters.`);
202
+ return null;
203
+ }
204
+ const expiry = coerceExpiry(record.expiry, type, index, warnings);
205
+ const sourceContext = coerceOptionalString(record.source_context);
206
+ const claimKey = coerceClaimKey(record.claim_key ?? record.claimKey, type, index, warnings);
207
+ const directivePolarity = coerceDirectivePolarity(record.directive_polarity ?? record.polarity, type, index, warnings);
208
+ const directiveTrigger = coerceDirectiveTrigger(record.directive_trigger ?? record.trigger, type, directivePolarity, index, warnings);
209
+ const sourceFile = coerceOptionalString(record.source_file ?? record.sourceFile);
210
+ const userId = coerceOptionalString(record.user_id ?? record.userId);
211
+ const project = coerceOptionalString(record.project);
212
+ if (type === "directive") {
213
+ if (!directivePolarity) {
214
+ warnings.push(`Dropped entry ${index + 1}: directive entries require directive_polarity.`);
215
+ return null;
216
+ }
217
+ if (!claimKey?.claimKey.startsWith(MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX)) {
218
+ warnings.push(`Dropped entry ${index + 1}: directive entries require claim_key prefix ${MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX}.`);
219
+ return null;
220
+ }
221
+ }
222
+ return {
223
+ type,
224
+ subject,
225
+ content,
226
+ importance: coerceImportance(record.importance),
227
+ expiry,
228
+ tags: coerceTags(record.tags),
229
+ source_file: sourceFile,
230
+ claim_key: claimKey?.claimKey,
231
+ claim_key_raw: claimKey?.rawClaimKey,
232
+ directive_polarity: directivePolarity,
233
+ directive_trigger: directiveTrigger,
234
+ source_context: sourceContext,
235
+ user_id: userId,
236
+ project
237
+ };
238
+ }
239
+ function coerceType(value) {
240
+ if (typeof value !== "string") {
241
+ return null;
242
+ }
243
+ return TYPE_ALIAS_MAP[normalizeToken(value)] ?? null;
244
+ }
245
+ function coerceImportance(value) {
246
+ if (typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 10) {
247
+ return value;
248
+ }
249
+ if (typeof value === "string") {
250
+ const normalized = normalizeToken(value);
251
+ const mappedTier = IMPORTANCE_TIER_MAP[normalized];
252
+ if (mappedTier !== void 0) {
253
+ return mappedTier;
254
+ }
255
+ const parsed = Number(normalized);
256
+ if (Number.isInteger(parsed) && parsed >= 1 && parsed <= 10) {
257
+ return parsed;
258
+ }
259
+ }
260
+ return 6;
261
+ }
262
+ function coerceExpiry(value, type, index, warnings) {
263
+ if (typeof value !== "string") {
264
+ return type === "directive" ? "core" : "temporary";
265
+ }
266
+ const normalized = EXPIRY_ALIAS_MAP[normalizeToken(value)];
267
+ if (!normalized) {
268
+ return "temporary";
269
+ }
270
+ if (normalized === "core" && type !== "directive") {
271
+ warnings.push(`Entry ${index + 1}: expiry "core" is reserved and was changed to "temporary".`);
272
+ return "temporary";
273
+ }
274
+ return normalized;
275
+ }
276
+ function coerceDirectivePolarity(value, type, index, warnings) {
277
+ if (type !== "directive") {
278
+ return void 0;
279
+ }
280
+ if (typeof value !== "string") {
281
+ warnings.push(`Entry ${index + 1}: directive_polarity is required for directive entries.`);
282
+ return void 0;
283
+ }
284
+ const normalized = normalizeToken(value);
285
+ if (normalized === "abstain" || normalized === "proactive") {
286
+ return normalized;
287
+ }
288
+ warnings.push(`Entry ${index + 1}: dropped invalid directive_polarity ${JSON.stringify(value)}.`);
289
+ return void 0;
290
+ }
291
+ function coerceDirectiveTrigger(value, type, polarity, index, warnings) {
292
+ if (type !== "directive") {
293
+ return void 0;
294
+ }
295
+ if (typeof value !== "string") {
296
+ return polarity ? defaultDirectiveTrigger(polarity) : void 0;
297
+ }
298
+ const normalized = normalizeToken(value);
299
+ if (normalized === "session_start" || normalized === "always") {
300
+ return normalized;
301
+ }
302
+ if (normalized.startsWith("topic:") && normalized.slice("topic:".length).trim().length > 0) {
303
+ return `topic:${normalizeWhitespace(normalized.slice("topic:".length))}`;
304
+ }
305
+ warnings.push(`Entry ${index + 1}: dropped invalid directive_trigger ${JSON.stringify(value)}.`);
306
+ return void 0;
307
+ }
308
+ function coerceTags(value) {
309
+ if (!Array.isArray(value)) {
310
+ return [];
311
+ }
312
+ const unique = /* @__PURE__ */ new Set();
313
+ for (const tag of value) {
314
+ if (typeof tag !== "string") {
315
+ continue;
316
+ }
317
+ const normalized = normalizeToken(tag);
318
+ if (!normalized) {
319
+ continue;
320
+ }
321
+ unique.add(normalized);
322
+ if (unique.size === 4) {
323
+ break;
324
+ }
325
+ }
326
+ return Array.from(unique);
327
+ }
328
+ function coerceOptionalString(value) {
329
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
330
+ return void 0;
331
+ }
332
+ const normalized = normalizeWhitespace(String(value));
333
+ return normalized.length > 0 ? normalized : void 0;
334
+ }
335
+ function coerceClaimKey(value, type, index, warnings) {
336
+ if (typeof value !== "string") {
337
+ return void 0;
338
+ }
339
+ const rawClaimKey = coerceOptionalString(value);
340
+ if (!rawClaimKey) {
341
+ return void 0;
342
+ }
343
+ if (type === "directive") {
344
+ const directiveClaimKey = normalizeMemoryDirectiveClaimKey(rawClaimKey);
345
+ if (directiveClaimKey) {
346
+ return {
347
+ claimKey: directiveClaimKey,
348
+ rawClaimKey: rawClaimKey !== directiveClaimKey ? rawClaimKey : void 0
349
+ };
350
+ }
351
+ warnings.push(`Entry ${index + 1}: dropped directive claim_key ${JSON.stringify(value)} because it must use ${MEMORY_DIRECTIVE_CLAIM_KEY_PREFIX}<name>.`);
352
+ return void 0;
353
+ }
354
+ const normalized = normalizeClaimKey(rawClaimKey);
355
+ if (normalized.ok) {
356
+ return {
357
+ claimKey: normalized.value.claimKey,
358
+ rawClaimKey: rawClaimKey !== normalized.value.claimKey ? rawClaimKey : void 0
359
+ };
360
+ }
361
+ warnings.push(`Entry ${index + 1}: dropped claim_key ${JSON.stringify(value)} because ${describeClaimKeyNormalizationFailure(normalized.reason)}.`);
362
+ return void 0;
363
+ }
364
+ function normalizeString(value) {
365
+ return typeof value === "string" ? normalizeWhitespace(value) : "";
366
+ }
367
+ function normalizeWhitespace(value) {
368
+ return value.replace(/\s+/g, " ").trim();
369
+ }
370
+ function normalizeToken(value) {
371
+ return normalizeWhitespace(value).toLowerCase();
372
+ }
373
+ function stripCodeFence(text) {
374
+ const trimmed = text.trim();
375
+ const match = /^```(?:json)?\s*([\s\S]+?)\s*```$/i.exec(trimmed);
376
+ return match?.[1]?.trim() ?? trimmed;
377
+ }
378
+
379
+ // src/core/dreaming/prompts.ts
380
+ var GOOD_EPISODE_EXAMPLE = `{
381
+ "type": "preference",
382
+ "subject": "communication style preference",
383
+ "content": "Jim prefers terse responses with an explain-first approach when debugging unfamiliar systems.",
384
+ "importance": "standard",
385
+ "expiry": "permanent",
386
+ "tags": ["communication", "style"],
387
+ "source_context": "User stated response preferences during a personal onboarding session.",
388
+ "claim_key": "jim/communication_style"
389
+ }`;
390
+ var BAD_EPISODE_PROJECT_SCOPE = `BAD:
391
+ {
392
+ "type": "fact",
393
+ "subject": "jim birthday",
394
+ "content": "Jim Martin is 49 years old and his birthday is March 15.",
395
+ "importance": "high",
396
+ "expiry": "permanent",
397
+ "tags": ["family", "personal"],
398
+ "source_context": "User shared family details during casual conversation.",
399
+ "claim_key": "jim/birthday",
400
+ "project": "skeln"
401
+ }
402
+ WHY: Personal family facts are cross-workspace knowledge. Do not tag them with the session workspace just because the chat happened inside one repo.`;
403
+ var BAD_EPISODE_META = `BAD:
404
+ {
405
+ "type": "fact",
406
+ "subject": "this session",
407
+ "content": "The session focused on establishing personal facts and preferences for future recall.",
408
+ "importance": "standard",
409
+ "expiry": "temporary",
410
+ "tags": ["session"]
411
+ }
412
+ WHY: Episode summaries are already meta-narration. Extract only durable facts, preferences, and rules the summary affirms as knowledge.`;
413
+ var EMPTY_EXAMPLE = `If nothing qualifies, return exactly:
414
+ {"durables":[]}`;
415
+ function buildDreamExtractSystemPrompt(options = {}) {
416
+ const dateString = options.currentDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
417
+ return [
418
+ "You are a selective memory synthesis engine for dreaming maintenance.",
419
+ "",
420
+ `Current date: ${dateString}.`,
421
+ "",
422
+ "Input is a condensed episode summary, not a raw transcript. The summary already collapsed tool chatter,",
423
+ "debugging steps, and session narration. Extract only durable knowledge the summary still affirms as",
424
+ "long-term memory.",
425
+ "",
426
+ "Default action: SKIP. Most episode summaries should produce zero entries.",
427
+ "",
428
+ "## Types",
429
+ "",
430
+ "- fact: Verified descriptive information about a person, project, system, or concept.",
431
+ "- decision: A standing rule, requirement, convention, or durable constraint on future behavior.",
432
+ "- preference: A stated preference, opinion, or value that should influence future behavior.",
433
+ "- lesson: A non-obvious insight from a specific experience, not generic advice.",
434
+ "- milestone: A significant one-time transition, launch, or life/project moment.",
435
+ "- relationship: A connection between named entities or people.",
436
+ "- directive: A memory-behavior instruction about what to suppress or proactively surface.",
437
+ "",
438
+ "## Claim Keys",
439
+ "",
440
+ "- Include `claim_key` on every non-directive entry unless the summary gives no stable entity anchor.",
441
+ "- Use canonical `entity/attribute` form: lowercase snake_case segments separated by `/`.",
442
+ "- Prefer person- or project-scoped entities when the summary names them (`jim/timezone`, `agenr/package_manager`).",
443
+ "- For directive entries, use `user/memory_directive/<name>`.",
444
+ "- If the summary quotes an explicit tool-call claim key, preserve that raw value in `claim_key`.",
445
+ "- Do not emit malformed keys with spaces, mixed casing, or more than four segments.",
446
+ "",
447
+ "## Provenance",
448
+ "",
449
+ "- Every entry must include `source_context`: one sentence, max 20 words, describing what evidence in the summary supports the durable.",
450
+ "",
451
+ "## Project Scope",
452
+ "",
453
+ "- `project` tags knowledge *about* a workspace or product, not the workspace where the chat happened.",
454
+ "- Omit `project` for personal, family, health, preference, and other facts that should follow the user across workspaces.",
455
+ "- Include `project` only when the durable is specifically about one repo, codebase, deployment, or product surface.",
456
+ "- A session may include a workspace hint for context. Treat it as a hint, not a default stamp for every entry.",
457
+ '- Claim keys like `agenr/release_policy` may justify `project: "agenr"`; claim keys like `jim/birthday` should stay unscoped.',
458
+ "",
459
+ "## Importance And Expiry",
460
+ "",
461
+ '- Rate importance as "high", "standard", or "low". Standard is the default.',
462
+ '- Use expiry "permanent" for biographical facts, preferences, and standing rules.',
463
+ '- Use expiry "temporary" for active project state and time-bounded context.',
464
+ '- Extraction assigns "core" only for directive entries.',
465
+ "",
466
+ "## Directive Fields",
467
+ "",
468
+ "- Directive entries require `claim_key`, `directive_polarity`, and usually `directive_trigger`.",
469
+ "",
470
+ "## Already Stored In Session",
471
+ "",
472
+ "- The user prompt may list durables already written live through `agenr_store` during this session.",
473
+ "- Do not emit another entry for the same underlying knowledge, even when the episode summary paraphrases it.",
474
+ "- Omit covered facts entirely instead of restating them with a new subject, claim key, or wording.",
475
+ "- When a genuinely new fact remains, emit only that new fact.",
476
+ "- If you must reference an already-stored claim key family, reuse the exact existing `claim_key` rather than inventing a synonym.",
477
+ "",
478
+ "## Anti-Patterns",
479
+ "",
480
+ "- Session summaries, progress narration, or duplicate paraphrases of the same fact.",
481
+ "- Generic advice that could appear in any tutorial.",
482
+ "- Facts the summary only mentions as recalled context without independent affirmation.",
483
+ "",
484
+ "## Calibration",
485
+ "",
486
+ "- Typical good output per episode summary: 0-4 entries.",
487
+ "- Hard maximum: 6 entries. Reaching the cap usually means over-extraction.",
488
+ "- One grounded entry beats three near-duplicates.",
489
+ "",
490
+ "## Output",
491
+ "",
492
+ 'Return JSON only: {"durables":[...]}',
493
+ 'Use {"durables":[]} when nothing qualifies.',
494
+ "",
495
+ "Each entry must have:",
496
+ '{ "type": "fact|decision|preference|lesson|relationship|milestone|directive", "subject": "2-6 word topic noun phrase", "content": "specific declarative statement, min 20 chars", "importance": "high|standard|low", "expiry": "permanent|temporary|core", "tags": ["1-4", "lowercase", "tags"], "source_context": "one sentence, max 20 words", "claim_key": "entity/attribute" }',
497
+ 'Optional: { "project": "workspace-name" } only when the durable is workspace-specific knowledge.',
498
+ "Directive entries also require `directive_polarity` and `directive_trigger` when applicable.",
499
+ "",
500
+ "GOOD:",
501
+ GOOD_EPISODE_EXAMPLE,
502
+ "",
503
+ BAD_EPISODE_PROJECT_SCOPE,
504
+ "",
505
+ BAD_EPISODE_META,
506
+ "",
507
+ EMPTY_EXAMPLE
508
+ ].join("\n");
509
+ }
510
+ function buildDreamExtractChunkPrompt(chunk, options = {}) {
511
+ const sections = ["Episode summary to mine for durable knowledge:"];
512
+ const sessionWorkspace = options.sessionWorkspace?.trim();
513
+ if (sessionWorkspace) {
514
+ sections.push(
515
+ "",
516
+ `Session workspace context: ${sessionWorkspace}`,
517
+ "This names where the conversation happened. Do not default every durable to this project.",
518
+ "Tag `project` only when the knowledge itself is about that workspace."
519
+ );
520
+ }
521
+ if (options.existingSessionDurables && options.existingSessionDurables.length > 0) {
522
+ sections.push(
523
+ "",
524
+ "Already stored live during this session (do not duplicate or paraphrase):",
525
+ ...options.existingSessionDurables.map(
526
+ (durable) => `- [${durable.type}] ${durable.subject}: ${durable.content}${durable.claimKey ? ` (claim_key: ${durable.claimKey})` : ""}`
527
+ ),
528
+ "",
529
+ "Emit only durable knowledge from the summary that is not already covered above."
530
+ );
531
+ }
532
+ sections.push("---", chunk.text, "---", "", "Return JSON only. No markdown fences, no commentary, and no extra keys.");
533
+ return sections.join("\n");
534
+ }
535
+
536
+ // src/core/dreaming/extract.ts
537
+ var MAX_ATTEMPTS = 3;
538
+ async function extractFromEpisodeSummary(transcript, llm, options = {}) {
539
+ if (transcript.messages.length === 0) {
540
+ return {
541
+ entries: [],
542
+ chunks: 0,
543
+ successfulChunks: 0,
544
+ failedChunks: 0,
545
+ chunkDetails: [],
546
+ warnings: []
547
+ };
548
+ }
549
+ const chunk = buildSummaryChunk(transcript);
550
+ const systemPrompt = buildDreamExtractSystemPrompt();
551
+ const observedAt = transcript.metadata.endedAt ?? transcript.metadata.startedAt;
552
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
553
+ try {
554
+ const raw = await llm.completeJson(
555
+ systemPrompt,
556
+ buildDreamExtractChunkPrompt(chunk, {
557
+ sessionWorkspace: options.sessionWorkspace ?? transcript.metadata.project,
558
+ existingSessionDurables: options.existingSessionDurables
559
+ })
560
+ );
561
+ const parsed = parseExtractionResponse(raw);
562
+ if (observedAt) {
563
+ for (const entry of parsed.entries) {
564
+ entry.created_at = observedAt;
565
+ }
566
+ }
567
+ return {
568
+ entries: parsed.entries,
569
+ chunks: 1,
570
+ successfulChunks: 1,
571
+ failedChunks: 0,
572
+ chunkDetails: [{ chunkIndex: 0, messageRange: chunk.message_range, success: true }],
573
+ warnings: parsed.warnings
574
+ };
575
+ } catch {
576
+ if (attempt === MAX_ATTEMPTS) {
577
+ return {
578
+ entries: [],
579
+ chunks: 1,
580
+ successfulChunks: 0,
581
+ failedChunks: 1,
582
+ chunkDetails: [{ chunkIndex: 0, messageRange: chunk.message_range, success: false }],
583
+ warnings: [`Episode summary extraction failed after ${MAX_ATTEMPTS} attempts.`]
584
+ };
585
+ }
586
+ await sleep(backoffMs(attempt));
587
+ }
588
+ }
589
+ return {
590
+ entries: [],
591
+ chunks: 1,
592
+ successfulChunks: 0,
593
+ failedChunks: 1,
594
+ chunkDetails: [{ chunkIndex: 0, messageRange: chunk.message_range, success: false }],
595
+ warnings: ["Episode summary extraction failed."]
596
+ };
597
+ }
598
+ function buildSummaryChunk(transcript) {
599
+ const message = transcript.messages[0];
600
+ return {
601
+ chunk_index: 0,
602
+ message_range: [message?.index ?? 0, message?.index ?? 0],
603
+ text: message?.text ?? ""
604
+ };
605
+ }
606
+ function sleep(ms) {
607
+ return new Promise((resolve) => {
608
+ setTimeout(resolve, ms);
609
+ });
610
+ }
611
+ function backoffMs(attempt) {
612
+ return Math.min(1e3, 100 * 2 ** (attempt - 1));
613
+ }
614
+
615
+ // src/core/dreaming/session-store-context.ts
616
+ function toDreamSessionStoreDurables(durables) {
617
+ return durables.map((durable) => ({
618
+ type: durable.type,
619
+ subject: durable.subject,
620
+ content: durable.content,
621
+ claimKey: durable.claim_key?.trim() ? durable.claim_key : null,
622
+ normContentHash: durable.norm_content_hash?.trim() || computeNormContentHash(durable.content)
623
+ }));
624
+ }
625
+ function buildDreamSessionStoreContext(durables) {
626
+ const claimKeys = /* @__PURE__ */ new Set();
627
+ const normContentHashes = /* @__PURE__ */ new Set();
628
+ for (const durable of durables) {
629
+ normContentHashes.add(durable.normContentHash);
630
+ if (durable.claimKey) {
631
+ claimKeys.add(durable.claimKey);
632
+ }
633
+ }
634
+ return { claimKeys, normContentHashes };
635
+ }
636
+
637
+ // src/app/dreaming/extract.ts
638
+ var DEFAULT_CANDIDATE_IMPORTANCE = 5;
639
+ async function runExtractStage(options, deps) {
640
+ const emptyUsage = { inputTokens: 0, outputTokens: 0, estimatedCostUsd: 0 };
641
+ const emptySummary = {
642
+ episodesScanned: 0,
643
+ candidatesEmitted: 0,
644
+ newCandidates: 0,
645
+ refineCandidates: 0,
646
+ knownCandidates: 0,
647
+ durablesInserted: 0
648
+ };
649
+ if (!deps.createExtractLlm) {
650
+ return { status: "completed", candidates: [], summary: emptySummary, usage: emptyUsage, warnings: [] };
651
+ }
652
+ const since = await resolveExtractSince(deps.port, options.fullBacklog === true);
653
+ const episodes = await deps.port.listEpisodeEvidenceSince(since, {
654
+ ...options.project ? { project: options.project } : {},
655
+ limit: options.maxEpisodes
656
+ });
657
+ if (episodes.length === 0) {
658
+ return { status: "completed", candidates: [], summary: emptySummary, usage: emptyUsage, warnings: [] };
659
+ }
660
+ const llm = deps.createExtractLlm();
661
+ const mined = [];
662
+ const warnings = [];
663
+ let episodesScanned = 0;
664
+ let status = "completed";
665
+ for (const episode of episodes) {
666
+ throwIfAborted(options.signal);
667
+ if (readUsage(llm).estimatedCostUsd >= options.costCapUsd) {
668
+ status = "cost_capped";
669
+ break;
670
+ }
671
+ const episodeWindowEnd = episode.endedAt ?? options.now().toISOString();
672
+ const existingSessionDurables = episode.sessionId !== null ? toDreamSessionStoreDurables(await deps.port.listSessionHostStoreDurables(episode.sessionId, episode.startedAt, episodeWindowEnd)) : [];
673
+ const transcript = buildEpisodeTranscript(episode);
674
+ const extraction = await extractFromEpisodeSummary(transcript, llm, {
675
+ sessionWorkspace: episode.project,
676
+ existingSessionDurables
677
+ });
678
+ episodesScanned += 1;
679
+ warnings.push(...extraction.warnings.map((warning) => `Episode ${episode.id}: ${warning}`));
680
+ if (readUsage(llm).estimatedCostUsd >= options.costCapUsd) {
681
+ status = "cost_capped";
682
+ }
683
+ for (const entry of extraction.entries) {
684
+ mined.push(toMinedCandidate(entry, episode));
685
+ }
686
+ if (status === "cost_capped") {
687
+ break;
688
+ }
689
+ }
690
+ const candidates = await classifyCandidates(mined, {
691
+ port: deps.port,
692
+ contextLookupEnabled: options.contextLookupEnabled
693
+ });
694
+ const summary = {
695
+ episodesScanned,
696
+ candidatesEmitted: candidates.length,
697
+ newCandidates: candidates.filter((candidate) => candidate.disposition === "new").length,
698
+ refineCandidates: candidates.filter((candidate) => candidate.disposition === "refines").length,
699
+ knownCandidates: candidates.filter((candidate) => candidate.disposition === "known").length,
700
+ durablesInserted: 0
701
+ };
702
+ return { status, candidates, summary, usage: readUsage(llm), warnings };
703
+ }
704
+ async function applyExtractedDurables(input, deps) {
705
+ const createdAt = input.now().toISOString();
706
+ const prepared = input.candidates.filter((candidate) => candidate.disposition === "new").map((candidate) => ({
707
+ candidate,
708
+ durable: buildDurableFromCandidate(candidate, {
709
+ claimKeySource: "dreaming_extract",
710
+ claimKeyStatus: "tentative",
711
+ claimKeyConfidence: 0.5,
712
+ claimKeyRationale: "Mined from episode evidence during dreaming extract.",
713
+ createdAt
714
+ })
715
+ }));
716
+ if (prepared.length === 0) {
717
+ return { inserted: 0 };
718
+ }
719
+ const embeddings = await deps.embedding.embed(prepared.map(({ durable }) => composeEmbeddingText(durable)));
720
+ await deps.port.withTransaction(async (tx) => {
721
+ for (const [index, { candidate, durable }] of prepared.entries()) {
722
+ const contentHash = durable.content_hash ?? computeContentHash(durable.content, durable.source_file);
723
+ await tx.insertDurable(durable, embeddings[index] ?? [], contentHash);
724
+ await tx.logRunAction({
725
+ id: randomUUID(),
726
+ runId: input.runId,
727
+ actionType: "insert_durable",
728
+ durableIds: [durable.id],
729
+ reasoning: "Inserted durable mined from episode evidence.",
730
+ details: {
731
+ claim_key: candidate.claimKey,
732
+ evidence_refs: candidate.evidenceRefs.map((ref) => `${ref.kind}:${ref.locator}`)
733
+ },
734
+ createdAt
735
+ });
736
+ }
737
+ });
738
+ return { inserted: prepared.length };
739
+ }
740
+ async function classifyCandidates(mined, deps) {
741
+ if (mined.length === 0) {
742
+ return [];
743
+ }
744
+ const normHashByIndex = mined.map((candidate) => computeNormContentHash(candidate.content));
745
+ const existingNormHashes = await deps.port.findExistingNormContentHashes(normHashByIndex);
746
+ const seenNormHashes = new Set(existingNormHashes);
747
+ const sessionContextByWindow = /* @__PURE__ */ new Map();
748
+ const candidates = [];
749
+ for (const [index, candidate] of mined.entries()) {
750
+ const normHash = normHashByIndex[index] ?? computeNormContentHash(candidate.content);
751
+ let disposition = "new";
752
+ let refinesDurableId = null;
753
+ if (seenNormHashes.has(normHash)) {
754
+ disposition = "known";
755
+ } else if (candidate.sessionId) {
756
+ const sessionContext = await loadSessionStoreContext(candidate, deps.port, sessionContextByWindow);
757
+ if (sessionContext.normContentHashes.has(normHash)) {
758
+ disposition = "known";
759
+ } else if (candidate.claimKey && sessionContext.claimKeys.has(candidate.claimKey)) {
760
+ disposition = "known";
761
+ }
762
+ }
763
+ if (disposition === "new" && deps.contextLookupEnabled && candidate.claimKey) {
764
+ const family = await deps.port.findActiveDurablesByClaimKey(candidate.claimKey);
765
+ const active = family[0];
766
+ if (active) {
767
+ disposition = "refines";
768
+ refinesDurableId = active.id;
769
+ }
770
+ }
771
+ if (disposition !== "known") {
772
+ seenNormHashes.add(normHash);
773
+ }
774
+ candidates.push({
775
+ id: randomUUID(),
776
+ type: candidate.type,
777
+ subject: candidate.subject,
778
+ content: candidate.content,
779
+ importance: candidate.importance,
780
+ expiry: candidate.expiry,
781
+ tags: candidate.tags,
782
+ ...candidate.directivePolarity ? { directivePolarity: candidate.directivePolarity } : {},
783
+ ...candidate.directiveTrigger ? { directiveTrigger: candidate.directiveTrigger } : {},
784
+ claimKey: candidate.claimKey,
785
+ trust: "tentative",
786
+ disposition,
787
+ refinesDurableId,
788
+ evidenceRefs: candidate.evidenceRefs,
789
+ sourceFile: candidate.sourceFile,
790
+ ...candidate.sourceContext ? { sourceContext: candidate.sourceContext } : {},
791
+ ...candidate.project ? { project: candidate.project } : {},
792
+ validFrom: candidate.validFrom
793
+ });
794
+ }
795
+ return candidates;
796
+ }
797
+ function buildDurableFromCandidate(candidate, lifecycle) {
798
+ const sourceFile = candidate.sourceFile;
799
+ const contentHash = computeContentHash(candidate.content, sourceFile);
800
+ const normContentHash = computeNormContentHash(candidate.content);
801
+ const claimKey = lifecycle.claimKeyOverride !== void 0 ? lifecycle.claimKeyOverride : candidate.claimKey;
802
+ const primaryEvidence = candidate.evidenceRefs[0];
803
+ const validFrom = lifecycle.validFrom ?? candidate.validFrom;
804
+ const durable = {
805
+ id: lifecycle.id ?? candidate.id,
806
+ type: candidate.type,
807
+ subject: candidate.subject,
808
+ content: candidate.content,
809
+ importance: candidate.importance,
810
+ expiry: candidate.expiry,
811
+ tags: candidate.tags,
812
+ directive_polarity: candidate.directivePolarity,
813
+ directive_trigger: candidate.directiveTrigger,
814
+ quality_score: 0.5,
815
+ recall_count: 0,
816
+ content_hash: contentHash,
817
+ norm_content_hash: normContentHash,
818
+ created_at: lifecycle.createdAt,
819
+ updated_at: lifecycle.createdAt,
820
+ source_file: sourceFile
821
+ };
822
+ if (candidate.sourceContext) {
823
+ durable.source_context = candidate.sourceContext;
824
+ }
825
+ if (candidate.project) {
826
+ durable.project = candidate.project;
827
+ }
828
+ if (validFrom) {
829
+ durable.valid_from = validFrom;
830
+ }
831
+ durable.claim_support_source_kind = primaryEvidence?.kind ?? "episode";
832
+ if (primaryEvidence?.locator) {
833
+ durable.claim_support_locator = primaryEvidence.locator;
834
+ }
835
+ if (primaryEvidence?.observedAt) {
836
+ durable.claim_support_observed_at = primaryEvidence.observedAt;
837
+ }
838
+ durable.claim_support_mode = "inferred";
839
+ if (claimKey) {
840
+ durable.claim_key = claimKey;
841
+ durable.claim_key_raw = claimKey;
842
+ durable.claim_key_status = lifecycle.claimKeyStatus;
843
+ durable.claim_key_source = lifecycle.claimKeySource;
844
+ durable.claim_key_confidence = lifecycle.claimKeyConfidence;
845
+ durable.claim_key_rationale = lifecycle.claimKeyRationale;
846
+ }
847
+ return durable;
848
+ }
849
+ async function resolveExtractSince(port, fullBacklog) {
850
+ if (fullBacklog) {
851
+ return "1970-01-01T00:00:00.000Z";
852
+ }
853
+ const lastRun = await port.getLastRun();
854
+ const lastSuccessfulAt = lastRun?.status === "completed" ? lastRun.completedAt : null;
855
+ return lastSuccessfulAt ?? "1970-01-01T00:00:00.000Z";
856
+ }
857
+ function buildEpisodeTranscript(episode) {
858
+ return {
859
+ messages: [
860
+ {
861
+ index: 0,
862
+ role: "assistant",
863
+ text: episode.summary,
864
+ timestamp: episode.endedAt ?? episode.startedAt
865
+ }
866
+ ],
867
+ metadata: {
868
+ messageCount: 1,
869
+ transcriptHash: episode.id,
870
+ startedAt: episode.startedAt,
871
+ ...episode.endedAt ? { endedAt: episode.endedAt } : {},
872
+ ...episode.sessionId ? { sessionId: episode.sessionId } : {},
873
+ ...episode.project ? { project: episode.project } : {}
874
+ },
875
+ warnings: []
876
+ };
877
+ }
878
+ function toMinedCandidate(entry, episode) {
879
+ const observedAt = episode.endedAt ?? episode.startedAt;
880
+ const project = resolveDurableProjectScope(
881
+ {
882
+ project: entry.project,
883
+ subject: entry.subject,
884
+ content: entry.content,
885
+ tags: entry.tags ?? [],
886
+ source_context: entry.source_context,
887
+ claim_key: entry.claim_key
888
+ },
889
+ { sessionWorkspace: episode.project }
890
+ );
891
+ return {
892
+ type: entry.type,
893
+ subject: entry.subject,
894
+ content: entry.content,
895
+ importance: typeof entry.importance === "number" ? entry.importance : DEFAULT_CANDIDATE_IMPORTANCE,
896
+ expiry: entry.expiry ?? "permanent",
897
+ tags: entry.tags ?? [],
898
+ directivePolarity: entry.directive_polarity,
899
+ directiveTrigger: entry.directive_trigger,
900
+ claimKey: normalizeCandidateClaimKey(entry.claim_key),
901
+ evidenceRefs: [
902
+ {
903
+ kind: "episode",
904
+ locator: episode.id,
905
+ observedAt
906
+ }
907
+ ],
908
+ sessionId: episode.sessionId,
909
+ episodeStartedAt: episode.startedAt,
910
+ episodeEndedAt: observedAt,
911
+ sourceFile: buildEpisodeSourceFile(episode),
912
+ ...entry.source_context ? { sourceContext: entry.source_context } : {},
913
+ ...project ? { project } : {},
914
+ validFrom: observedAt
915
+ };
916
+ }
917
+ function buildEpisodeSourceFile(episode) {
918
+ if (episode.sessionId) {
919
+ return `episode-session:${episode.sessionId}:${episode.id}`;
920
+ }
921
+ return `episode:${episode.id}`;
922
+ }
923
+ async function loadSessionStoreContext(candidate, port, cache) {
924
+ const sessionId = candidate.sessionId?.trim();
925
+ if (!sessionId) {
926
+ return { claimKeys: /* @__PURE__ */ new Set(), normContentHashes: /* @__PURE__ */ new Set() };
927
+ }
928
+ const cacheKey = `${sessionId}:${candidate.episodeStartedAt}:${candidate.episodeEndedAt}`;
929
+ const cached = cache.get(cacheKey);
930
+ if (cached) {
931
+ return cached;
932
+ }
933
+ const durables = await port.listSessionHostStoreDurables(sessionId, candidate.episodeStartedAt, candidate.episodeEndedAt);
934
+ const resolved = buildDreamSessionStoreContext(toDreamSessionStoreDurables(durables));
935
+ cache.set(cacheKey, resolved);
936
+ return resolved;
937
+ }
938
+ function normalizeCandidateClaimKey(rawClaimKey) {
939
+ if (!rawClaimKey) {
940
+ return null;
941
+ }
942
+ const normalized = normalizeClaimKey(rawClaimKey);
943
+ return normalized.ok ? normalized.value.claimKey : null;
944
+ }
945
+ function readUsage(llm) {
946
+ const usage = llm.metadata?.usage;
947
+ return {
948
+ inputTokens: usage?.inputTokens ?? 0,
949
+ outputTokens: usage?.outputTokens ?? 0,
950
+ estimatedCostUsd: usage?.totalCost ?? 0
951
+ };
952
+ }
953
+
954
+ // src/app/dreaming/progress.ts
955
+ function emitDreamProgress(reporter, event) {
956
+ if (!reporter) {
957
+ return;
958
+ }
959
+ try {
960
+ reporter(event);
961
+ } catch {
962
+ }
963
+ }
964
+
965
+ // src/app/dreaming/prune.ts
966
+ import { randomUUID as randomUUID2 } from "crypto";
967
+
968
+ // src/core/dreaming/domain/stale-candidate-policy.ts
969
+ var STATUS_ARTIFACT_SUBJECT_HINTS = ["session handoff", "status update", "progress snapshot", "session summary", "next steps", "in progress"];
970
+ function isActionableStaleCandidate(candidate) {
971
+ return candidate.expiry === "temporary" || candidate.type === "milestone" && (candidate.importance <= 6 || candidate.expiry === "permanent") || candidate.type === "fact" && candidate.importance <= 5 && candidate.recallCount === 0;
972
+ }
973
+ function compareStaleCandidates(left, right) {
974
+ const tierDelta = getStaleCandidatePriorityTier(left) - getStaleCandidatePriorityTier(right);
975
+ if (tierDelta !== 0) {
976
+ return tierDelta;
977
+ }
978
+ const leftNeverRecalled = left.recallCount === 0;
979
+ const rightNeverRecalled = right.recallCount === 0;
980
+ if (leftNeverRecalled !== rightNeverRecalled) {
981
+ return leftNeverRecalled ? -1 : 1;
982
+ }
983
+ const createdDelta = parseTimestamp(left.createdAt) - parseTimestamp(right.createdAt);
984
+ if (createdDelta !== 0) {
985
+ return createdDelta;
986
+ }
987
+ if (left.importance !== right.importance) {
988
+ return left.importance - right.importance;
989
+ }
990
+ const updatedDelta = left.updatedAt.localeCompare(right.updatedAt);
991
+ if (updatedDelta !== 0) {
992
+ return updatedDelta;
993
+ }
994
+ return left.id.localeCompare(right.id);
995
+ }
996
+ function looksLikeStaleStatusArtifact(subject) {
997
+ const normalized = subject.trim().toLowerCase();
998
+ return normalized.startsWith("handoff") || STATUS_ARTIFACT_SUBJECT_HINTS.some((hint) => normalized.includes(hint));
999
+ }
1000
+ function getStaleCandidatePriorityTier(candidate) {
1001
+ if (candidate.expiry === "temporary") {
1002
+ return 0;
1003
+ }
1004
+ if (candidate.type === "milestone" && candidate.importance <= 4) {
1005
+ return 1;
1006
+ }
1007
+ if (looksLikeStaleStatusArtifact(candidate.subject)) {
1008
+ return 2;
1009
+ }
1010
+ return 3;
1011
+ }
1012
+ function parseTimestamp(value) {
1013
+ const timestamp = Date.parse(value);
1014
+ return Number.isFinite(timestamp) ? timestamp : Number.MAX_SAFE_INTEGER;
1015
+ }
1016
+
1017
+ // src/app/dreaming/prune.ts
1018
+ var DAY_MS = 24 * 60 * 60 * 1e3;
1019
+ async function runPruneStage(options, deps) {
1020
+ const durables = await deps.port.listReconcileDurables({
1021
+ ...options.project ? { project: options.project } : {},
1022
+ includeInactive: false
1023
+ });
1024
+ const activeProfileIds = await loadProtectedProfileIds(deps.port, options.protectedDurableIds ?? []);
1025
+ const evaluated = durables.map(
1026
+ (durable) => evaluatePruneCandidate(durable, {
1027
+ activeProfileIds,
1028
+ now: options.now(),
1029
+ protectMinImportance: options.protectMinImportance,
1030
+ protectRecalledDays: options.protectRecalledDays
1031
+ })
1032
+ ).filter((candidate) => candidate !== null).sort((left, right) => compareStaleCandidates(toStaleCandidatePolicyCandidate(left.durable), toStaleCandidatePolicyCandidate(right.durable)));
1033
+ const protectedCount = evaluated.filter((candidate) => candidate.protectionReason !== null).length;
1034
+ const stalable = evaluated.filter((candidate) => candidate.protectionReason === null);
1035
+ let durablesStaled = 0;
1036
+ if (options.apply && stalable.length > 0) {
1037
+ await deps.port.withTransaction(async (tx) => {
1038
+ for (const candidate of stalable) {
1039
+ const staled = await tx.closeDurableValidity(candidate.durable.id, candidate.staleReason);
1040
+ if (!staled) {
1041
+ continue;
1042
+ }
1043
+ durablesStaled += 1;
1044
+ await tx.logRunAction({
1045
+ id: randomUUID2(),
1046
+ runId: options.runId,
1047
+ actionType: "stale",
1048
+ durableIds: [candidate.durable.id],
1049
+ reasoning: candidate.staleReason,
1050
+ details: {
1051
+ stage: "prune",
1052
+ expiry: candidate.durable.expiry,
1053
+ importance: candidate.durable.importance,
1054
+ recall_count: candidate.durable.recall_count
1055
+ },
1056
+ createdAt: options.now().toISOString()
1057
+ });
1058
+ }
1059
+ });
1060
+ }
1061
+ return {
1062
+ durablesScanned: durables.length,
1063
+ candidatesIdentified: evaluated.length,
1064
+ candidatesProtected: protectedCount,
1065
+ candidatesRetirable: stalable.length,
1066
+ durablesStaled,
1067
+ dryRun: !options.apply
1068
+ };
1069
+ }
1070
+ function evaluatePruneCandidate(durable, options) {
1071
+ if (!isActionableStaleCandidate(toStaleCandidatePolicyCandidate(durable)) && !isStaleMemory(durable, options.now.getTime())) {
1072
+ return null;
1073
+ }
1074
+ return {
1075
+ durable,
1076
+ protectionReason: resolveProtectionReason(durable, options),
1077
+ staleReason: buildStaleReason(durable, options.now)
1078
+ };
1079
+ }
1080
+ async function loadProtectedProfileIds(port, protectedDurableIds) {
1081
+ const snapshot = await port.getActiveProfileSnapshot();
1082
+ const profileIds = new Set(protectedDurableIds);
1083
+ if (!snapshot) {
1084
+ return profileIds;
1085
+ }
1086
+ for (const durableId of [...snapshot.durableIds, ...snapshot.directiveIds]) {
1087
+ profileIds.add(durableId);
1088
+ }
1089
+ return profileIds;
1090
+ }
1091
+ function resolveProtectionReason(durable, options) {
1092
+ if (options.activeProfileIds.has(durable.id)) {
1093
+ return "active_profile";
1094
+ }
1095
+ if (durable.type === "directive") {
1096
+ return "directive";
1097
+ }
1098
+ if (durable.expiry === "core") {
1099
+ return "core_expiry";
1100
+ }
1101
+ if (durable.importance >= options.protectMinImportance) {
1102
+ return "high_importance";
1103
+ }
1104
+ if (wasRecentlyRecalled(durable, options.now, options.protectRecalledDays)) {
1105
+ return "recent_recall";
1106
+ }
1107
+ return null;
1108
+ }
1109
+ function wasRecentlyRecalled(durable, now, protectRecalledDays) {
1110
+ if (!durable.last_recalled_at) {
1111
+ return false;
1112
+ }
1113
+ const recalledAt = Date.parse(durable.last_recalled_at);
1114
+ if (!Number.isFinite(recalledAt)) {
1115
+ return false;
1116
+ }
1117
+ const cutoff = now.getTime() - protectRecalledDays * DAY_MS;
1118
+ return recalledAt >= cutoff;
1119
+ }
1120
+ function buildStaleReason(durable, now) {
1121
+ if (isStaleMemory(durable, now.getTime())) {
1122
+ return "Dream prune staled an expired valid-time durable.";
1123
+ }
1124
+ if (durable.expiry === "temporary") {
1125
+ return "Dream prune staled a temporary durable after synthesis.";
1126
+ }
1127
+ return "Dream prune staled a low-signal durable after synthesis.";
1128
+ }
1129
+ function toStaleCandidatePolicyCandidate(durable) {
1130
+ return {
1131
+ id: durable.id,
1132
+ subject: durable.subject,
1133
+ type: durable.type,
1134
+ importance: durable.importance,
1135
+ expiry: durable.expiry,
1136
+ recallCount: durable.recall_count,
1137
+ createdAt: durable.created_at,
1138
+ updatedAt: durable.updated_at
1139
+ };
1140
+ }
1141
+
1142
+ // src/app/dreaming/project.ts
1143
+ import { createHash, randomUUID as randomUUID3 } from "crypto";
1144
+ var DEFAULT_MAX_PROFILE_DURABLES = 8;
1145
+ var MIN_PROFILE_DURABLES = 1;
1146
+ var MAX_PROFILE_DURABLES = 8;
1147
+ async function runProjectStage(options, deps) {
1148
+ const asOfDate = options.now();
1149
+ const asOf = asOfDate.toISOString();
1150
+ const maxProfileDurables = normalizeMaxProfileDurables(options.maxProfileDurables);
1151
+ const activeCurrent = (await deps.port.listReconcileDurables({ ...options.project ? { project: options.project } : {} })).filter(
1152
+ (durable) => isCurrentDurable(durable, asOfDate.getTime())
1153
+ );
1154
+ const directiveDurables = activeCurrent.filter((durable) => parseDirectiveMetadata(durable) !== null).sort(compareDirectiveDurables);
1155
+ const profileDurables = activeCurrent.filter((durable) => parseDirectiveMetadata(durable) === null).sort(compareProfileDurables).slice(0, maxProfileDurables);
1156
+ const snapshot = profileDurables.length > 0 || directiveDurables.length > 0 ? {
1157
+ id: randomUUID3(),
1158
+ durableIds: profileDurables.map((durable) => durable.id),
1159
+ directiveIds: directiveDurables.map((durable) => durable.id),
1160
+ asOf,
1161
+ contentHash: buildProfileContentHash(profileDurables, directiveDurables),
1162
+ runId: options.runId,
1163
+ createdAt: asOf
1164
+ } : null;
1165
+ return {
1166
+ summary: {
1167
+ profileDurableCount: profileDurables.length,
1168
+ directiveCount: directiveDurables.length,
1169
+ snapshotId: null,
1170
+ applied: false
1171
+ },
1172
+ snapshot
1173
+ };
1174
+ }
1175
+ function normalizeMaxProfileDurables(value) {
1176
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1177
+ return DEFAULT_MAX_PROFILE_DURABLES;
1178
+ }
1179
+ return Math.min(MAX_PROFILE_DURABLES, Math.max(MIN_PROFILE_DURABLES, Math.trunc(value)));
1180
+ }
1181
+ function isCurrentDurable(durable, asOfMs) {
1182
+ return isCurrentlyValidMemory(durable, asOfMs);
1183
+ }
1184
+ function compareProfileDurables(left, right) {
1185
+ const expiryDelta = profileExpiryRank(right) - profileExpiryRank(left);
1186
+ if (expiryDelta !== 0) {
1187
+ return expiryDelta;
1188
+ }
1189
+ const importanceDelta = right.importance - left.importance;
1190
+ if (importanceDelta !== 0) {
1191
+ return importanceDelta;
1192
+ }
1193
+ const qualityDelta = right.quality_score - left.quality_score;
1194
+ if (qualityDelta !== 0) {
1195
+ return qualityDelta;
1196
+ }
1197
+ return right.created_at.localeCompare(left.created_at) || left.id.localeCompare(right.id);
1198
+ }
1199
+ function compareDirectiveDurables(left, right) {
1200
+ const leftMetadata = parseDirectiveMetadata(left);
1201
+ const rightMetadata = parseDirectiveMetadata(right);
1202
+ const polarityDelta = directivePolarityRank(rightMetadata?.polarity) - directivePolarityRank(leftMetadata?.polarity);
1203
+ if (polarityDelta !== 0) {
1204
+ return polarityDelta;
1205
+ }
1206
+ const importanceDelta = right.importance - left.importance;
1207
+ if (importanceDelta !== 0) {
1208
+ return importanceDelta;
1209
+ }
1210
+ return right.created_at.localeCompare(left.created_at) || left.id.localeCompare(right.id);
1211
+ }
1212
+ function profileExpiryRank(durable) {
1213
+ if (durable.expiry === "core") {
1214
+ return 3;
1215
+ }
1216
+ if (durable.expiry === "permanent") {
1217
+ return 2;
1218
+ }
1219
+ return 1;
1220
+ }
1221
+ function directivePolarityRank(polarity) {
1222
+ return polarity === "proactive" ? 2 : 1;
1223
+ }
1224
+ function buildProfileContentHash(profileDurables, directiveDurables) {
1225
+ const payload = {
1226
+ profile: profileDurables.map(toHashableDurable),
1227
+ directives: directiveDurables.map(toHashableDurable)
1228
+ };
1229
+ return createHash("sha256").update(JSON.stringify(payload)).digest("hex");
1230
+ }
1231
+ function toHashableDurable(durable) {
1232
+ return {
1233
+ id: durable.id,
1234
+ updated_at: durable.updated_at,
1235
+ content_hash: durable.content_hash,
1236
+ norm_content_hash: durable.norm_content_hash,
1237
+ importance: durable.importance,
1238
+ expiry: durable.expiry,
1239
+ directive_polarity: durable.directive_polarity,
1240
+ directive_trigger: durable.directive_trigger
1241
+ };
1242
+ }
1243
+
1244
+ // src/app/dreaming/reconcile/constants.ts
1245
+ var HIGH_CONFIDENCE_BACKFILL_THRESHOLD = 0.92;
1246
+ var STRUCTURED_AUTO_APPLY_BACKFILL_THRESHOLD = 0.86;
1247
+ var COMPACTED_SUPPORTED_AUTO_APPLY_BACKFILL_THRESHOLD = 0.78;
1248
+ var PROPOSAL_CONFIDENCE_THRESHOLD = 0.75;
1249
+ var SUPPORTED_PROPOSAL_CONFIDENCE_THRESHOLD = 0.65;
1250
+ var MAX_CLEANUP_ENTITY_HINTS = 12;
1251
+ var MAX_CLEANUP_CLAIM_KEY_HINTS = 8;
1252
+ var CLAIM_KEY_CONCENTRATION_THRESHOLD = 25;
1253
+ var CLAIM_KEY_CONCENTRATION_RATIO = 0.8;
1254
+ var ENTITY_CONCENTRATION_THRESHOLD = 40;
1255
+ var ENTITY_CONCENTRATION_RATIO = 0.85;
1256
+ var COLLISION_SPIKE_THRESHOLD = 30;
1257
+ var COLLISION_SPIKE_RATIO = 0.85;
1258
+ var CLAIM_KEY_PROGRESS_INTERVAL_MS = 5e3;
1259
+ var CLAIM_KEY_PROGRESS_VERBOSE_INTERVAL_MS = 2e3;
1260
+ var CLAIM_KEY_PROGRESS_EVERY_DURABLES = 250;
1261
+ var CLAIM_KEY_PROGRESS_EVERY_VERBOSE_DURABLES = 50;
1262
+ var SHADOW_RESONANCE_MIN_FAMILY_REUSE_COUNT = 20;
1263
+ var SHADOW_RESONANCE_MIN_GROUNDED_RATIO = 0.7;
1264
+ var SHADOW_RESONANCE_MIN_CONFIDENCE = 0.74;
1265
+ var USER_METADATA_ENTITY_ALIASES = /* @__PURE__ */ new Set(["i", "me", "myself", "person", "the_user", "user"]);
1266
+ var PROJECT_METADATA_ENTITY_ALIASES = /* @__PURE__ */ new Set(["app", "application", "project", "the_project", "this_project", "workspace"]);
1267
+ var SHADOW_BUCKET_ORDER = [
1268
+ "high_density_grounded_family",
1269
+ "large_grounding_diluted_grounded_family",
1270
+ "thin_grounded_family_tail",
1271
+ "relaxed_one_sibling_stable_slot",
1272
+ "other_grounded_family_alignment"
1273
+ ];
1274
+
1275
+ // src/app/dreaming/reconcile/helpers/utils.ts
1276
+ function normalizeOptionalString(value) {
1277
+ const trimmed = value?.trim();
1278
+ return trimmed ? trimmed : null;
1279
+ }
1280
+ function normalizeStringArray(values) {
1281
+ return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));
1282
+ }
1283
+ function normalizeOptionalNonNegativeCount(value) {
1284
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1285
+ return 0;
1286
+ }
1287
+ return Math.max(0, Math.floor(value));
1288
+ }
1289
+ function elapsedMs(startedAtMs, nowMs = Date.now()) {
1290
+ return Math.max(0, nowMs - startedAtMs);
1291
+ }
1292
+ function countSetOverlap(left, right) {
1293
+ let count = 0;
1294
+ for (const value of right) {
1295
+ if (left.has(value)) {
1296
+ count += 1;
1297
+ }
1298
+ }
1299
+ return count;
1300
+ }
1301
+ function normalizeMetadataEntity(value) {
1302
+ const normalized = value ? normalizeClaimKeySegment(value) : "";
1303
+ if (normalized.length === 0 || !/[a-z]/u.test(normalized)) {
1304
+ return null;
1305
+ }
1306
+ return normalized;
1307
+ }
1308
+ function describeShadowBucket(bucket) {
1309
+ switch (bucket) {
1310
+ case "high_density_grounded_family":
1311
+ return "high-density grounded-family";
1312
+ case "large_grounding_diluted_grounded_family":
1313
+ return "large grounding-diluted grounded-family";
1314
+ case "thin_grounded_family_tail":
1315
+ return "thin grounded-family tail";
1316
+ case "relaxed_one_sibling_stable_slot":
1317
+ return "relaxed one-sibling stable-slot";
1318
+ case "other_grounded_family_alignment":
1319
+ return "other grounded-family alignment";
1320
+ }
1321
+ }
1322
+
1323
+ // src/app/dreaming/reconcile/helpers/claim-key-inspection.ts
1324
+ function inspectExistingClaimKey(durable) {
1325
+ const rawClaimKey = durable.claim_key?.trim();
1326
+ if (!rawClaimKey) {
1327
+ return { kind: "missing" };
1328
+ }
1329
+ const inspection = inspectClaimKey(rawClaimKey);
1330
+ if (inspection.normalizationFailure) {
1331
+ return { kind: "malformed", inspection };
1332
+ }
1333
+ if (inspection.normalized && !inspection.canonical) {
1334
+ return {
1335
+ kind: "noncanonical",
1336
+ inspection,
1337
+ normalized: inspection.normalized
1338
+ };
1339
+ }
1340
+ if (inspection.suspectReasons.length > 0) {
1341
+ return { kind: "suspect", inspection };
1342
+ }
1343
+ return { kind: "ok", inspection };
1344
+ }
1345
+ function resolveExplicitMetadataRepair(durable, inspection) {
1346
+ const normalized = inspection.normalized;
1347
+ if (!normalized) {
1348
+ return null;
1349
+ }
1350
+ const userEntity = normalizeMetadataEntity(durable.user_id);
1351
+ const projectEntity = normalizeMetadataEntity(durable.project);
1352
+ if (USER_METADATA_ENTITY_ALIASES.has(normalized.entity) && userEntity) {
1353
+ const candidate = `${userEntity}/${normalized.attribute}`;
1354
+ return isTrustedClaimKeyForCleanup(candidate) ? candidate : null;
1355
+ }
1356
+ if (PROJECT_METADATA_ENTITY_ALIASES.has(normalized.entity) && projectEntity) {
1357
+ const candidate = `${projectEntity}/${normalized.attribute}`;
1358
+ return isTrustedClaimKeyForCleanup(candidate) ? candidate : null;
1359
+ }
1360
+ return null;
1361
+ }
1362
+ function resolveMetadataBackfillClaimKey(durable, claimKey) {
1363
+ return resolveExplicitMetadataRepair(durable, inspectClaimKey(claimKey));
1364
+ }
1365
+ function describeSuspicionList(inspection) {
1366
+ if (!inspection.normalized || inspection.suspectReasons.length === 0) {
1367
+ return "it is low-trust";
1368
+ }
1369
+ return inspection.suspectReasons.map((reason) => describeClaimKeySuspicion(reason, inspection.normalized)).join(", ");
1370
+ }
1371
+ function buildSuspectProposalRationale(durable, inspection, metadataRepair, suggestion) {
1372
+ const suspectReason = describeSuspicionList(inspection);
1373
+ const replacementHint = metadataRepair !== null ? ` Explicit metadata suggests "${metadataRepair}".` : suggestion?.claimKey ? ` Claim extraction preview suggested "${suggestion.claimKey}" at confidence ${suggestion.confidence.toFixed(2)}.` : "";
1374
+ return `Claim key "${durable.claim_key}" is structurally canonical but suspect because ${suspectReason}.${replacementHint}`;
1375
+ }
1376
+
1377
+ // src/app/dreaming/reconcile/helpers/claim-key-health-tally.ts
1378
+ function createEmptyClaimKeyInspectionTally() {
1379
+ return {
1380
+ malformedOrNoncanonicalCount: 0,
1381
+ suspectCanonicalCount: 0,
1382
+ missingCount: 0,
1383
+ eligibleMissingCount: 0
1384
+ };
1385
+ }
1386
+ function cloneClaimKeyInspectionTally(tally) {
1387
+ return {
1388
+ malformedOrNoncanonicalCount: tally.malformedOrNoncanonicalCount,
1389
+ suspectCanonicalCount: tally.suspectCanonicalCount,
1390
+ missingCount: tally.missingCount,
1391
+ eligibleMissingCount: tally.eligibleMissingCount
1392
+ };
1393
+ }
1394
+ function untallyExistingClaimKeyInspection(inspection, durableType, eligibleTypes, tally) {
1395
+ switch (inspection.kind) {
1396
+ case "malformed":
1397
+ case "noncanonical":
1398
+ tally.malformedOrNoncanonicalCount -= 1;
1399
+ break;
1400
+ case "suspect":
1401
+ tally.suspectCanonicalCount -= 1;
1402
+ break;
1403
+ case "missing":
1404
+ tally.missingCount -= 1;
1405
+ if (eligibleTypes.includes(durableType)) {
1406
+ tally.eligibleMissingCount -= 1;
1407
+ }
1408
+ break;
1409
+ default:
1410
+ break;
1411
+ }
1412
+ }
1413
+ function refreshDurableInspectionTally(durable, previous, eligibleTypes, tally) {
1414
+ const next = inspectExistingClaimKey(durable);
1415
+ untallyExistingClaimKeyInspection(previous, durable.type, eligibleTypes, tally);
1416
+ tallyExistingClaimKeyInspection(next, durable.type, eligibleTypes, tally);
1417
+ return next;
1418
+ }
1419
+ function tallyExistingClaimKeyInspection(inspection, durableType, eligibleTypes, tally) {
1420
+ switch (inspection.kind) {
1421
+ case "malformed":
1422
+ case "noncanonical":
1423
+ tally.malformedOrNoncanonicalCount += 1;
1424
+ break;
1425
+ case "suspect":
1426
+ tally.suspectCanonicalCount += 1;
1427
+ break;
1428
+ case "missing":
1429
+ tally.missingCount += 1;
1430
+ if (eligibleTypes.includes(durableType)) {
1431
+ tally.eligibleMissingCount += 1;
1432
+ }
1433
+ break;
1434
+ default:
1435
+ break;
1436
+ }
1437
+ }
1438
+
1439
+ // src/app/dreaming/reconcile/helpers/durable.ts
1440
+ function cloneDurable(durable) {
1441
+ return {
1442
+ ...durable,
1443
+ tags: [...durable.tags],
1444
+ embedding: durable.embedding ? [...durable.embedding] : void 0
1445
+ };
1446
+ }
1447
+ function isDurableActive(durable) {
1448
+ return isCurrentlyValidMemory(durable, Date.now());
1449
+ }
1450
+ function findClaimKeyOccupants(durables, claimKey, excludeDurableId) {
1451
+ return durables.filter((durable) => durable.id !== excludeDurableId && durable.claim_key === claimKey);
1452
+ }
1453
+ function findActiveClaimKeyOccupants(durables, claimKey, excludeDurableId) {
1454
+ return durables.filter((durable) => durable.id !== excludeDurableId && durable.claim_key === claimKey && isDurableActive(durable));
1455
+ }
1456
+ function countExactKeyMultiActiveClusters(durables) {
1457
+ const counts = /* @__PURE__ */ new Map();
1458
+ for (const durable of durables) {
1459
+ const claimKey = durable.claim_key?.trim();
1460
+ if (!claimKey) {
1461
+ continue;
1462
+ }
1463
+ counts.set(claimKey, (counts.get(claimKey) ?? 0) + 1);
1464
+ }
1465
+ return [...counts.values()].filter((count) => count >= 2).length;
1466
+ }
1467
+ function findTrustedGroupReuseCandidate(durables, trustedDurableIds, durable) {
1468
+ const normalizedSubject = durable.subject.trim().toLowerCase();
1469
+ if (normalizedSubject.length === 0) {
1470
+ return null;
1471
+ }
1472
+ const trustedPeers = durables.filter((candidate) => {
1473
+ if (candidate.id === durable.id || candidate.type !== durable.type) {
1474
+ return false;
1475
+ }
1476
+ if (!trustedDurableIds.has(candidate.id)) {
1477
+ return false;
1478
+ }
1479
+ if (candidate.subject.trim().toLowerCase() !== normalizedSubject) {
1480
+ return false;
1481
+ }
1482
+ const claimKey2 = candidate.claim_key?.trim();
1483
+ return Boolean(claimKey2 && isTrustedClaimKeyForCleanup(claimKey2));
1484
+ });
1485
+ const trustedClaimKeys = normalizeStringArray(
1486
+ trustedPeers.flatMap((candidate) => {
1487
+ const claimKey2 = candidate.claim_key?.trim();
1488
+ return claimKey2 ? [claimKey2] : [];
1489
+ })
1490
+ );
1491
+ if (trustedClaimKeys.length !== 1) {
1492
+ return null;
1493
+ }
1494
+ const claimKey = trustedClaimKeys[0];
1495
+ if (!claimKey) {
1496
+ return null;
1497
+ }
1498
+ return {
1499
+ claimKey,
1500
+ supportingDurableIds: trustedPeers.map((candidate) => candidate.id)
1501
+ };
1502
+ }
1503
+ function snapshotClaimKeyLifecycle(durable) {
1504
+ return {
1505
+ claimKey: durable.claim_key,
1506
+ claimKeyRaw: durable.claim_key_raw,
1507
+ claimKeyStatus: durable.claim_key_status,
1508
+ claimKeySource: durable.claim_key_source,
1509
+ claimKeyConfidence: durable.claim_key_confidence,
1510
+ claimKeyRationale: durable.claim_key_rationale
1511
+ };
1512
+ }
1513
+ function restoreClaimKeyLifecycle(durable, snapshot) {
1514
+ durable.claim_key = snapshot.claimKey;
1515
+ durable.claim_key_raw = snapshot.claimKeyRaw;
1516
+ durable.claim_key_status = snapshot.claimKeyStatus;
1517
+ durable.claim_key_source = snapshot.claimKeySource;
1518
+ durable.claim_key_confidence = snapshot.claimKeyConfidence;
1519
+ durable.claim_key_rationale = snapshot.claimKeyRationale;
1520
+ }
1521
+
1522
+ // src/app/dreaming/reconcile/helpers/mixed-groups.ts
1523
+ function findMixedKeyGroups(durables, coveredClaimKeys = /* @__PURE__ */ new Set()) {
1524
+ const groups = /* @__PURE__ */ new Map();
1525
+ for (const durable of durables) {
1526
+ const normalizedSubject = durable.subject.trim().toLowerCase();
1527
+ if (normalizedSubject.length === 0) {
1528
+ continue;
1529
+ }
1530
+ const groupKey = `${normalizedSubject}::${durable.type}`;
1531
+ const existing = groups.get(groupKey);
1532
+ if (existing) {
1533
+ existing.push(durable);
1534
+ continue;
1535
+ }
1536
+ groups.set(groupKey, [durable]);
1537
+ }
1538
+ return [...groups.entries()].flatMap(([groupKey, groupDurables]) => {
1539
+ if (groupDurables.length < 2) {
1540
+ return [];
1541
+ }
1542
+ const claimKeys = normalizeStringArray(groupDurables.flatMap((durable) => durable.claim_key ? [durable.claim_key] : []));
1543
+ const hasMissing = groupDurables.some((durable) => !durable.claim_key);
1544
+ const distinctClaimKeyCount = claimKeys.length;
1545
+ if (!hasMissing && distinctClaimKeyCount <= 1) {
1546
+ return [];
1547
+ }
1548
+ if (!hasMissing && distinctClaimKeyCount > 1 && claimKeys.every((claimKey) => coveredClaimKeys.has(claimKey))) {
1549
+ return [];
1550
+ }
1551
+ const trustedClaimKeys = claimKeys.filter((claimKey) => isTrustedClaimKeyForCleanup(claimKey));
1552
+ const proposedClaimKey = trustedClaimKeys.length === 1 ? trustedClaimKeys[0] ?? null : null;
1553
+ return [
1554
+ {
1555
+ groupKey,
1556
+ durables: groupDurables,
1557
+ proposedClaimKey
1558
+ }
1559
+ ];
1560
+ }).sort((left, right) => left.groupKey.localeCompare(right.groupKey));
1561
+ }
1562
+ function buildMixedGroupRationale(group) {
1563
+ const currentClaimKeys = normalizeStringArray(group.durables.flatMap((durable) => durable.claim_key ? [durable.claim_key] : []));
1564
+ if (group.proposedClaimKey) {
1565
+ return `Durables sharing subject/type group "${group.groupKey}" use mixed or missing claim keys. The only trusted canonical family already present is "${group.proposedClaimKey}", so it is the conservative proposed target for later adjudication. Current non-null keys: ${currentClaimKeys.join(", ") || "(none)"}.`;
1566
+ }
1567
+ return `Durables sharing subject/type group "${group.groupKey}" use mixed or missing claim keys, but the group does not expose one uniquely trusted canonical target. Current non-null keys: ${currentClaimKeys.join(", ") || "(none)"}.`;
1568
+ }
1569
+
1570
+ // src/app/dreaming/reconcile/health.ts
1571
+ function buildClaimKeyHealthSnapshot(durables, eligibleTypes, tally) {
1572
+ const activeDurables = durables.filter((durable) => isDurableActive(durable));
1573
+ const withClaimKeys = durables.filter((durable) => typeof durable.claim_key === "string" && durable.claim_key.trim().length > 0);
1574
+ const entityFamilyCandidates = detectClaimKeyEntityFamilyCandidates(durables);
1575
+ const singletonAliasCandidates = detectClaimKeySingletonAliasCandidates(durables);
1576
+ return {
1577
+ totalDurables: durables.length,
1578
+ activeDurables: activeDurables.length,
1579
+ coverageCount: withClaimKeys.length,
1580
+ coveragePct: durables.length > 0 ? withClaimKeys.length / durables.length : 0,
1581
+ missingCount: tally.missingCount,
1582
+ eligibleMissingCount: tally.eligibleMissingCount,
1583
+ malformedOrNoncanonicalCount: tally.malformedOrNoncanonicalCount,
1584
+ suspectCanonicalCount: tally.suspectCanonicalCount,
1585
+ entityFamilyGroupCount: entityFamilyCandidates.length,
1586
+ suspiciousSingletonAliasCount: singletonAliasCandidates.length,
1587
+ mixedGroupCount: findMixedKeyGroups(durables).length,
1588
+ exactKeyMultiActiveClusterCount: countExactKeyMultiActiveClusters(activeDurables)
1589
+ };
1590
+ }
1591
+
1592
+ // src/app/dreaming/reconcile/helpers/reconcile-proposal.ts
1593
+ import { randomUUID as randomUUID5 } from "crypto";
1594
+
1595
+ // src/app/dreaming/reconcile/helpers/ambiguous-proposal.ts
1596
+ function defaultFlagAmbiguousProposal(issueKind) {
1597
+ switch (issueKind) {
1598
+ case "mixed_claim_key_group":
1599
+ case "noncanonical_claim_key":
1600
+ return false;
1601
+ default:
1602
+ return true;
1603
+ }
1604
+ }
1605
+ function resolveFlagAmbiguousProposal(issueKind, override) {
1606
+ return override ?? defaultFlagAmbiguousProposal(issueKind);
1607
+ }
1608
+
1609
+ // src/app/dreaming/reconcile/helpers/audit.ts
1610
+ function buildMissingBackfillSupportAuditDetails(support) {
1611
+ if (!support?.supportedProposal) {
1612
+ return support?.autoApplyClass ? {
1613
+ support_class: support.autoApplyClass
1614
+ } : {};
1615
+ }
1616
+ return {
1617
+ support_evidence: [...support.supportEvidence],
1618
+ supporting_durable_ids: [...support.supportingDurableIds],
1619
+ support_family_reuse_count: support.familyReuseCount,
1620
+ support_grounded_family_reuse_count: support.groundedFamilyReuseCount,
1621
+ support_sibling_slot_resonance_applicable: support.siblingSlotResonance.applicable,
1622
+ support_sibling_slot_resonance_fired: support.siblingSlotResonance.fired,
1623
+ support_sibling_slot_resonance_resonant_sibling_count: support.siblingSlotResonance.resonantSiblingCount,
1624
+ support_sibling_slot_resonance_dominant_shape: support.siblingSlotResonance.dominantShape,
1625
+ support_sibling_slot_resonance_dominant_shape_count: support.siblingSlotResonance.dominantShapeCount,
1626
+ support_sibling_slot_resonance_grounded_share: support.siblingSlotResonance.dominantShapeGroundedShare,
1627
+ support_sibling_slot_resonance_local_shape_coverage: support.siblingSlotResonance.localShapeTokenCoverage,
1628
+ support_sibling_slot_resonance_family_generic_tokens: [...support.siblingSlotResonance.familyGenericTokens],
1629
+ support_sibling_slot_resonance_discriminative_candidate_tokens: [...support.siblingSlotResonance.discriminativeCandidateTokens],
1630
+ support_sibling_slot_resonance_sibling_durable_ids: [...support.siblingSlotResonance.dominantSiblingEntryIds],
1631
+ support_sibling_slot_resonance_sibling_claim_keys: [...support.siblingSlotResonance.dominantSiblingClaimKeys],
1632
+ ...support.strongEntityAttributeLexicalAlignment ? {
1633
+ support_strong_entity_attribute_lexical_alignment: true
1634
+ } : {},
1635
+ ...support.autoApplyClass ? {
1636
+ support_class: support.autoApplyClass
1637
+ } : {},
1638
+ ...support.relaxedStableSlotFamilyGate ? {
1639
+ support_relaxed_stable_slot_family_gate: true
1640
+ } : {}
1641
+ };
1642
+ }
1643
+ function buildAppliedClaimKeyActionDetails(input) {
1644
+ return {
1645
+ issue_kind: input.issueKind,
1646
+ old_claim_key: input.oldClaimKey,
1647
+ new_claim_key: input.newClaimKey,
1648
+ ...buildClaimKeyLifecycleAuditDetails(input.lifecycle),
1649
+ proposal_source: input.proposalSource,
1650
+ confidence: input.confidence,
1651
+ auto_apply_threshold: input.promotion?.autoApplyThreshold,
1652
+ auto_applied: true,
1653
+ promotion_lane: input.promotion?.lane,
1654
+ supported_auto_apply: input.support?.autoApplyClass !== null,
1655
+ ...buildMissingBackfillSupportAuditDetails(input.support),
1656
+ ...buildMissingBackfillShadowAuditDetails(input.shadow),
1657
+ ...buildClaimKeyCompactionAuditDetails(input.compactness),
1658
+ ...buildEntityFamilyAuditDetails(input.entityFamilyAudit)
1659
+ };
1660
+ }
1661
+ function buildProposalClaimKeyActionDetails(proposal, audit) {
1662
+ return {
1663
+ proposal_id: proposal.id,
1664
+ group_id: proposal.groupId,
1665
+ issue_kind: proposal.issueKind,
1666
+ current_claim_keys: proposal.currentClaimKeys,
1667
+ proposed_claim_keys: proposal.proposedClaimKeys,
1668
+ confidence: proposal.confidence,
1669
+ proposal_source: proposal.source,
1670
+ auto_apply_threshold: audit?.promotion?.autoApplyThreshold,
1671
+ auto_applied: false,
1672
+ promotion_lane: audit?.promotion?.lane,
1673
+ eligible_for_apply: proposal.eligibleForApply,
1674
+ supported_candidate: audit?.supportedCandidate === true,
1675
+ ...buildReconcileProposalClaimKeyAuditDetails(audit?.proposalLifecycle),
1676
+ ...buildMissingBackfillSupportAuditDetails(audit?.support),
1677
+ ...buildMissingBackfillShadowAuditDetails(audit?.shadow),
1678
+ ...buildClaimKeyCompactionAuditDetails(audit?.compactness),
1679
+ ...audit?.autoApplyBlocker ? {
1680
+ auto_apply_blocker: audit.autoApplyBlocker
1681
+ } : {},
1682
+ ...buildEntityFamilyAuditDetails(audit?.entityFamilyAudit)
1683
+ };
1684
+ }
1685
+ function buildMissingBackfillShadowAuditDetails(shadow) {
1686
+ if (!shadow) {
1687
+ return {};
1688
+ }
1689
+ return {
1690
+ shadow_threshold_only_bucket: shadow.thresholdOnlyBucket,
1691
+ shadow_would_qualify: shadow.shadowWouldQualify
1692
+ };
1693
+ }
1694
+ function buildClaimKeyCompactionAuditDetails(compactness) {
1695
+ return compactness?.compactedFrom ? {
1696
+ claim_key_compacted_from: compactness.compactedFrom,
1697
+ claim_key_compaction_reason: compactness.compactionReason
1698
+ } : {};
1699
+ }
1700
+ function buildEntityFamilyAuditDetails(entityFamilyAudit) {
1701
+ return entityFamilyAudit ? {
1702
+ competing_entity_prefixes: [...entityFamilyAudit.competingEntityPrefixes],
1703
+ canonical_entity_prefix: entityFamilyAudit.canonicalEntityPrefix,
1704
+ canonical_selection_reasons: [...entityFamilyAudit.canonicalSelectionReasons],
1705
+ entity_family_unresolved_reason: entityFamilyAudit.unresolvedReason,
1706
+ entity_family_evidence: entityFamilyAudit.evidence.map((evidence) => ({ ...evidence })),
1707
+ entity_family_pair_support: entityFamilyAudit.pairSupport.map((support) => ({
1708
+ ...support,
1709
+ entityPrefixes: [...support.entityPrefixes],
1710
+ supportingDurableIds: [...support.supportingDurableIds],
1711
+ sharedAttributes: [...support.sharedAttributes],
1712
+ evidence: support.evidence.map((evidence) => ({ ...evidence }))
1713
+ }))
1714
+ } : {};
1715
+ }
1716
+
1717
+ // src/app/dreaming/reconcile/helpers/proposal.ts
1718
+ import { randomUUID as randomUUID4 } from "crypto";
1719
+ function createProposal(input) {
1720
+ return {
1721
+ id: randomUUID4(),
1722
+ ...input,
1723
+ durableIds: normalizeStringArray(input.durableIds),
1724
+ currentClaimKeys: normalizeStringArray(input.currentClaimKeys),
1725
+ proposedClaimKeys: normalizeStringArray(input.proposedClaimKeys),
1726
+ reviewStatus: "open",
1727
+ reviewedAt: null,
1728
+ reviewReason: null,
1729
+ appliedActionCount: 0
1730
+ };
1731
+ }
1732
+ function buildMalformedClaimKeyPersistInput(durable, inspection, suggestionRecord) {
1733
+ const proposedClaimKeys = suggestionRecord.suggestion?.claimKey ? [suggestionRecord.suggestion.claimKey] : [];
1734
+ const source = suggestionRecord.suggestion?.path ?? "normalize";
1735
+ return {
1736
+ groupId: `claim-key-malformed:${durable.id}`,
1737
+ issueKind: "malformed_claim_key",
1738
+ scope: "single_durable",
1739
+ durableIds: [durable.id],
1740
+ currentClaimKeys: durable.claim_key ? [durable.claim_key] : [],
1741
+ proposedClaimKeys,
1742
+ rationale: `Stored claim key "${durable.claim_key}" is malformed because ${describeClaimKeyNormalizationFailure(inspection.inspection.normalizationFailure ?? "missing_separator")}.` + (suggestionRecord.suggestion?.claimKey ? ` Claim extraction preview suggested "${suggestionRecord.suggestion.claimKey}" at confidence ${suggestionRecord.suggestion.confidence.toFixed(2)}.` : ""),
1743
+ confidence: suggestionRecord.suggestion?.confidence ?? 0.5,
1744
+ source,
1745
+ eligibleForApply: proposedClaimKeys.length > 0,
1746
+ lifecycle: {
1747
+ proposedClaimKeys,
1748
+ source,
1749
+ rawClaimKey: durable.claim_key ?? null
1750
+ }
1751
+ };
1752
+ }
1753
+ function buildTrustedGroupReusePersistInput(durable, trustedGroupReuse) {
1754
+ return {
1755
+ groupId: `claim-key-backfill:${durable.id}`,
1756
+ issueKind: "missing_claim_key",
1757
+ scope: "single_durable",
1758
+ durableIds: [durable.id],
1759
+ currentClaimKeys: [],
1760
+ proposedClaimKeys: [trustedGroupReuse.claimKey],
1761
+ rationale: `A matched subject/type group already uses trusted canonical key "${trustedGroupReuse.claimKey}" across ${trustedGroupReuse.supportingDurableIds.length} supporting durable${trustedGroupReuse.supportingDurableIds.length === 1 ? "" : "s"}, but that same key is already occupied by a different active durable type in the matched working set.`,
1762
+ confidence: 0.99,
1763
+ source: "trusted_group_reuse",
1764
+ eligibleForApply: true,
1765
+ lifecycle: {
1766
+ proposedClaimKeys: [trustedGroupReuse.claimKey],
1767
+ source: "trusted_group_reuse"
1768
+ }
1769
+ };
1770
+ }
1771
+ function buildMissingBackfillPersistInput(durable, resolved, input) {
1772
+ return {
1773
+ groupId: `claim-key-backfill:${durable.id}`,
1774
+ issueKind: "missing_claim_key",
1775
+ scope: "single_durable",
1776
+ durableIds: input.durableIds,
1777
+ currentClaimKeys: [],
1778
+ proposedClaimKeys: [resolved.targetClaimKey],
1779
+ rationale: input.rationale,
1780
+ confidence: resolved.suggestion.confidence,
1781
+ source: resolved.targetSource,
1782
+ eligibleForApply: true,
1783
+ lifecycle: {
1784
+ proposedClaimKeys: [resolved.targetClaimKey],
1785
+ source: resolved.targetSource,
1786
+ rawClaimKey: resolved.originalClaimKey,
1787
+ compactness: resolved.compactness,
1788
+ support: resolved.support
1789
+ },
1790
+ audit: {
1791
+ compactness: resolved.compactness,
1792
+ promotion: resolved.promotionPolicy,
1793
+ support: resolved.support,
1794
+ supportedCandidate: resolved.support.supportedProposal,
1795
+ ...input.audit
1796
+ }
1797
+ };
1798
+ }
1799
+ function buildEntityFamilyPersistInput(candidate, input) {
1800
+ return {
1801
+ groupId: `claim-key-entity-family:${candidate.entityPrefixes.join(",")}`,
1802
+ issueKind: "entity_family_convergence",
1803
+ scope: "cluster",
1804
+ durableIds: input.durableIds,
1805
+ currentClaimKeys: candidate.claimKeys,
1806
+ proposedClaimKeys: input.proposedClaimKeys,
1807
+ rationale: input.rationale,
1808
+ confidence: candidate.confidence,
1809
+ source: input.source,
1810
+ eligibleForApply: input.eligibleForApply,
1811
+ lifecycle: {
1812
+ proposedClaimKeys: input.proposedClaimKeys,
1813
+ source: input.source
1814
+ },
1815
+ audit: {
1816
+ entityFamilyAudit: input.audit,
1817
+ ...input.auditExtras
1818
+ }
1819
+ };
1820
+ }
1821
+
1822
+ // src/app/dreaming/reconcile/helpers/reconcile-proposal.ts
1823
+ async function persistReconcileProposal(ctx, input) {
1824
+ const proposalLifecycle = buildReconcileProposalClaimKeyLifecycle(input.lifecycle);
1825
+ const proposal = createProposal({
1826
+ runId: ctx.options.runId,
1827
+ groupId: input.groupId,
1828
+ issueKind: input.issueKind,
1829
+ scope: input.scope,
1830
+ durableIds: input.durableIds,
1831
+ currentClaimKeys: input.currentClaimKeys,
1832
+ proposedClaimKeys: input.proposedClaimKeys,
1833
+ rationale: buildReconcileProposalLifecycleRationale(input.rationale, proposalLifecycle),
1834
+ confidence: input.confidence,
1835
+ source: input.source,
1836
+ eligibleForApply: input.eligibleForApply,
1837
+ createdAt: ctx.options.now().toISOString()
1838
+ });
1839
+ await ctx.deps.port.logRunProposal(proposal);
1840
+ await ctx.deps.port.logRunAction({
1841
+ id: randomUUID5(),
1842
+ runId: ctx.options.runId,
1843
+ actionType: "flag_review",
1844
+ durableIds: proposal.durableIds,
1845
+ reasoning: proposal.rationale,
1846
+ details: buildProposalClaimKeyActionDetails(proposal, {
1847
+ proposalLifecycle,
1848
+ ...input.audit
1849
+ }),
1850
+ createdAt: proposal.createdAt
1851
+ });
1852
+ ctx.telemetry.actionsTaken += 1;
1853
+ ctx.telemetry.counts.proposalsEmitted += 1;
1854
+ if (resolveFlagAmbiguousProposal(input.issueKind, input.flagAmbiguousProposal)) {
1855
+ ctx.telemetry.counts.flaggedAmbiguousProposals += 1;
1856
+ }
1857
+ input.onPersisted?.(ctx);
1858
+ }
1859
+ async function persistCrossTypeCollisionProposal(ctx, input) {
1860
+ await persistReconcileProposal(ctx, {
1861
+ ...input,
1862
+ durableIds: normalizeStringArray(input.durableIds),
1863
+ audit: {
1864
+ autoApplyBlocker: "cross_type_collision",
1865
+ ...input.audit
1866
+ }
1867
+ });
1868
+ }
1869
+ function expandCrossTypeCollisionDurableIds(durableIds, activeSiblingIds) {
1870
+ return normalizeStringArray([...durableIds, ...activeSiblingIds]);
1871
+ }
1872
+ function appendCrossTypeCollisionRationaleSuffix(rationale, targetClaimKey) {
1873
+ return `${rationale} The same slot key is already used by a different durable type in the matched working set at "${targetClaimKey}".`;
1874
+ }
1875
+
1876
+ // src/app/dreaming/reconcile/helpers/cross-type-collision-outcome.ts
1877
+ function findCrossTypeActiveSiblings(projectedDurables, targetClaimKey, durable) {
1878
+ return findActiveClaimKeyOccupants(projectedDurables, targetClaimKey, durable.id).filter((sibling) => sibling.type !== durable.type);
1879
+ }
1880
+ function resolveCrossTypeCollisionSiblingIds(projectedDurables, targetClaimKey, durable) {
1881
+ return findCrossTypeActiveSiblings(projectedDurables, targetClaimKey, durable).map((sibling) => sibling.id);
1882
+ }
1883
+ async function persistProposalWhenCrossTypeCollision(ctx, targetClaimKey, durable, buildInput) {
1884
+ const activeSiblingIds = resolveCrossTypeCollisionSiblingIds(ctx.workingSet.projectedDurables, targetClaimKey, durable);
1885
+ if (activeSiblingIds.length === 0) {
1886
+ return false;
1887
+ }
1888
+ const input = buildInput(activeSiblingIds);
1889
+ await persistCrossTypeCollisionProposal(ctx, {
1890
+ ...input,
1891
+ durableIds: expandCrossTypeCollisionDurableIds(input.durableIds, activeSiblingIds)
1892
+ });
1893
+ return true;
1894
+ }
1895
+
1896
+ // src/app/dreaming/reconcile/helpers/entity-family.ts
1897
+ function buildEntityFamilyConvergenceAudit(candidate) {
1898
+ return {
1899
+ competingEntityPrefixes: [...candidate.entityPrefixes],
1900
+ canonicalEntityPrefix: candidate.canonicalEntityPrefix,
1901
+ canonicalSelectionReasons: [...candidate.canonicalSelectionReasons],
1902
+ unresolvedReason: candidate.unresolvedReason,
1903
+ evidence: flattenEntityFamilyEvidence(candidate.pairSupport),
1904
+ pairSupport: candidate.pairSupport.map((support) => ({
1905
+ entityPrefixes: [...support.entityPrefixes],
1906
+ supportingDurableIds: [...support.supportingDurableIds],
1907
+ sharedAttributes: [...support.sharedAttributes],
1908
+ confidence: support.confidence,
1909
+ autoSafe: support.autoSafe,
1910
+ preferredCanonicalEntityPrefix: support.preferredCanonicalEntityPrefix,
1911
+ evidence: support.evidence.map((evidence) => ({ ...evidence }))
1912
+ }))
1913
+ };
1914
+ }
1915
+ function flattenEntityFamilyEvidence(pairSupport) {
1916
+ const evidenceByKey = /* @__PURE__ */ new Map();
1917
+ for (const support of pairSupport) {
1918
+ for (const evidence of support.evidence) {
1919
+ const evidenceKey = `${evidence.kind}:${evidence.detail}`;
1920
+ if (!evidenceByKey.has(evidenceKey)) {
1921
+ evidenceByKey.set(evidenceKey, { ...evidence });
1922
+ }
1923
+ }
1924
+ }
1925
+ return [...evidenceByKey.values()];
1926
+ }
1927
+ function collectEntityFamilyDurablesToRewrite(projectedDurables, candidate) {
1928
+ const canonicalEntityPrefix = candidate.canonicalEntityPrefix;
1929
+ if (!canonicalEntityPrefix) {
1930
+ return [];
1931
+ }
1932
+ return projectedDurables.filter((durable) => {
1933
+ const claimKey = durable.claim_key?.trim();
1934
+ if (!claimKey) {
1935
+ return false;
1936
+ }
1937
+ const claimKeyInspection = inspectClaimKey(claimKey);
1938
+ if (!claimKeyInspection.normalized || claimKeyInspection.suspectReasons.length > 0 || !candidate.entityPrefixes.includes(claimKeyInspection.normalized.entity)) {
1939
+ return false;
1940
+ }
1941
+ return claimKeyInspection.normalized.entity !== canonicalEntityPrefix;
1942
+ });
1943
+ }
1944
+ function resolveEntityFamilyTargetClaimKey(durable, canonicalEntityPrefix) {
1945
+ const claimKey = durable.claim_key?.trim();
1946
+ if (!claimKey) {
1947
+ return null;
1948
+ }
1949
+ const claimKeyInspection = inspectClaimKey(claimKey);
1950
+ if (!claimKeyInspection.normalized) {
1951
+ return null;
1952
+ }
1953
+ return `${canonicalEntityPrefix}/${claimKeyInspection.normalized.attribute}`;
1954
+ }
1955
+ function mapEntityFamilyClaimKeys(claimKeys, entityPrefixes, canonicalEntityPrefix) {
1956
+ const entityPrefixSet = new Set(entityPrefixes);
1957
+ return normalizeStringArray(
1958
+ claimKeys.flatMap((claimKey) => {
1959
+ const inspection = inspectClaimKey(claimKey);
1960
+ if (!inspection.normalized || !entityPrefixSet.has(inspection.normalized.entity) || inspection.normalized.entity === canonicalEntityPrefix) {
1961
+ return [];
1962
+ }
1963
+ return [`${canonicalEntityPrefix}/${inspection.normalized.attribute}`];
1964
+ })
1965
+ );
1966
+ }
1967
+ function buildEntityFamilyConvergenceRationale(candidate) {
1968
+ const evidenceText = flattenEntityFamilyEvidence(candidate.pairSupport).map((evidence) => evidence.detail).join(" ");
1969
+ const canonicalText = candidate.canonicalEntityPrefix ? ` Canonical entity prefix candidate: "${candidate.canonicalEntityPrefix}".` : " No single canonical entity prefix is safe to choose automatically.";
1970
+ const reasonText = candidate.unresolvedReason ? ` ${candidate.unresolvedReason}` : "";
1971
+ const selectionText = candidate.canonicalSelectionReasons.length > 0 ? ` Canonical selection signals: ${candidate.canonicalSelectionReasons.join(", ")}.` : "";
1972
+ return (`Claim-key entity families ${candidate.entityPrefixes.join(", ")} show repeated same-slot overlap and grounding support. ` + evidenceText + canonicalText + selectionText + reasonText).trim();
1973
+ }
1974
+
1975
+ // src/app/dreaming/reconcile/pass-apply-handlers.ts
1976
+ import { randomUUID as randomUUID6 } from "crypto";
1977
+
1978
+ // src/app/dreaming/reconcile/helpers/circuit-breaker.ts
1979
+ function createCircuitBreakerState() {
1980
+ return {
1981
+ totalAutoMutations: 0,
1982
+ blockedCollisions: 0,
1983
+ appliedByClaimKey: /* @__PURE__ */ new Map(),
1984
+ appliedByEntity: /* @__PURE__ */ new Map()
1985
+ };
1986
+ }
1987
+ function recordCollision(state) {
1988
+ state.blockedCollisions += 1;
1989
+ }
1990
+ function recordAppliedRepair(state, claimKey) {
1991
+ state.totalAutoMutations += 1;
1992
+ state.appliedByClaimKey.set(claimKey, (state.appliedByClaimKey.get(claimKey) ?? 0) + 1);
1993
+ const entity = claimKey.split("/", 1)[0] ?? claimKey;
1994
+ state.appliedByEntity.set(entity, (state.appliedByEntity.get(entity) ?? 0) + 1);
1995
+ return evaluateCircuitBreaker(state);
1996
+ }
1997
+ function evaluateCircuitBreaker(state) {
1998
+ const largestClaimKeyCluster = maxCounterValue(state.appliedByClaimKey);
1999
+ if (state.totalAutoMutations >= CLAIM_KEY_CONCENTRATION_THRESHOLD && largestClaimKeyCluster >= CLAIM_KEY_CONCENTRATION_THRESHOLD && largestClaimKeyCluster / state.totalAutoMutations >= CLAIM_KEY_CONCENTRATION_RATIO) {
2000
+ const target = maxCounterKey(state.appliedByClaimKey) ?? "unknown";
2001
+ return {
2002
+ kind: "claim_key_concentration",
2003
+ message: `Reconcile circuit breaker tripped: ${largestClaimKeyCluster}/${state.totalAutoMutations} auto-repairs converged onto "${target}".`
2004
+ };
2005
+ }
2006
+ const largestEntityCluster = maxCounterValue(state.appliedByEntity);
2007
+ if (state.totalAutoMutations >= ENTITY_CONCENTRATION_THRESHOLD && largestEntityCluster >= ENTITY_CONCENTRATION_THRESHOLD && largestEntityCluster / state.totalAutoMutations >= ENTITY_CONCENTRATION_RATIO) {
2008
+ const target = maxCounterKey(state.appliedByEntity) ?? "unknown";
2009
+ return {
2010
+ kind: "entity_prefix_concentration",
2011
+ message: `Reconcile circuit breaker tripped: ${largestEntityCluster}/${state.totalAutoMutations} auto-repairs converged onto entity prefix "${target}".`
2012
+ };
2013
+ }
2014
+ if (state.blockedCollisions >= COLLISION_SPIKE_THRESHOLD && state.totalAutoMutations + state.blockedCollisions > 0 && state.blockedCollisions / (state.totalAutoMutations + state.blockedCollisions) >= COLLISION_SPIKE_RATIO) {
2015
+ return {
2016
+ kind: "collision_spike",
2017
+ message: `Reconcile circuit breaker tripped: ${state.blockedCollisions} proposed repairs were blocked by collisions, suggesting non-convergent claim-key cleanup.`
2018
+ };
2019
+ }
2020
+ return null;
2021
+ }
2022
+ function maxCounterValue(counter) {
2023
+ let max = 0;
2024
+ for (const value of counter.values()) {
2025
+ max = Math.max(max, value);
2026
+ }
2027
+ return max;
2028
+ }
2029
+ function maxCounterKey(counter) {
2030
+ let bestKey = null;
2031
+ let bestValue = -1;
2032
+ for (const [key, value] of counter.entries()) {
2033
+ if (value > bestValue) {
2034
+ bestKey = key;
2035
+ bestValue = value;
2036
+ }
2037
+ }
2038
+ return bestKey;
2039
+ }
2040
+
2041
+ // src/app/dreaming/reconcile/helpers/effects.ts
2042
+ function markAbortedIfSignalled(ctx) {
2043
+ if (ctx.options.signal?.aborted === true) {
2044
+ ctx.telemetry.terminalStatus = "aborted";
2045
+ ctx.telemetry.terminalError = USER_ABORT_ERROR;
2046
+ return true;
2047
+ }
2048
+ return false;
2049
+ }
2050
+ function tripCircuitBreaker(ctx, breaker) {
2051
+ if (!breaker) {
2052
+ return;
2053
+ }
2054
+ ctx.telemetry.circuitBreaker = breaker;
2055
+ ctx.telemetry.terminalStatus = "failed";
2056
+ ctx.telemetry.terminalError = breaker.message;
2057
+ }
2058
+ function recordRepairOutcome(ctx, claimKey, projected) {
2059
+ if (!projected) {
2060
+ return;
2061
+ }
2062
+ tripCircuitBreaker(ctx, recordAppliedRepair(ctx.telemetry.circuitBreakerState, claimKey));
2063
+ }
2064
+ function recordCollisionOutcome(ctx) {
2065
+ recordCollision(ctx.telemetry.circuitBreakerState);
2066
+ if (ctx.telemetry.circuitBreaker) {
2067
+ return;
2068
+ }
2069
+ tripCircuitBreaker(ctx, evaluateCircuitBreaker(ctx.telemetry.circuitBreakerState));
2070
+ }
2071
+
2072
+ // src/app/dreaming/reconcile/pass-apply-handlers.ts
2073
+ async function maybeApplyClaimKeyUpdate(ctx, durableId, claimKey, input) {
2074
+ const projected = ctx.workingSet.durablesById.get(durableId);
2075
+ const actual = ctx.workingSet.actualDurablesById.get(durableId);
2076
+ if (!projected || !actual) {
2077
+ return { projected: false, applied: false };
2078
+ }
2079
+ const previousProjected = snapshotClaimKeyLifecycle(projected);
2080
+ const lifecycle = buildReconcileAppliedClaimKeyLifecycleBundle({
2081
+ targetClaimKey: claimKey,
2082
+ priorClaimKey: input.oldClaimKey,
2083
+ priorClaimKeyRaw: projected.claim_key_raw ?? actual.claim_key_raw,
2084
+ rawClaimKey: input.rawClaimKey,
2085
+ source: input.source,
2086
+ confidence: input.confidence,
2087
+ rationale: input.rationale,
2088
+ support: input.support,
2089
+ compactness: input.compactness
2090
+ });
2091
+ applyClaimKeyLifecycle(projected, lifecycle);
2092
+ if (!ctx.options.apply) {
2093
+ refreshClaimKeyInspectionState(ctx, durableId, "projected");
2094
+ return { projected: true, applied: false };
2095
+ }
2096
+ const updated = await ctx.deps.port.updateDurable(durableId, buildClaimKeyLifecycleUpdateFields(lifecycle), {
2097
+ includeInactive: ctx.workingSet.selection.includeInactive
2098
+ });
2099
+ if (!updated) {
2100
+ restoreClaimKeyLifecycle(projected, previousProjected);
2101
+ return { projected: false, applied: false };
2102
+ }
2103
+ refreshClaimKeyInspectionState(ctx, durableId, "projected");
2104
+ applyClaimKeyLifecycle(actual, lifecycle);
2105
+ refreshClaimKeyInspectionState(ctx, durableId, "actual");
2106
+ await ctx.deps.port.logRunAction({
2107
+ id: randomUUID6(),
2108
+ runId: ctx.options.runId,
2109
+ actionType: "update_durable",
2110
+ durableIds: [durableId],
2111
+ reasoning: input.rationale,
2112
+ details: buildAppliedClaimKeyActionDetails({
2113
+ issueKind: input.issueKind,
2114
+ oldClaimKey: input.oldClaimKey,
2115
+ newClaimKey: claimKey,
2116
+ proposalSource: input.source,
2117
+ confidence: input.confidence,
2118
+ lifecycle,
2119
+ promotion: input.promotion,
2120
+ support: input.support,
2121
+ shadow: input.shadow,
2122
+ compactness: input.compactness,
2123
+ entityFamilyAudit: input.entityFamilyAudit
2124
+ }),
2125
+ createdAt: ctx.options.now().toISOString()
2126
+ });
2127
+ ctx.telemetry.actionsTaken += 1;
2128
+ return { projected: true, applied: true };
2129
+ }
2130
+ async function applyClaimKeyRepair(ctx, durableId, claimKey, input, options = {}) {
2131
+ options.onIdentified?.();
2132
+ const updateResult = await maybeApplyClaimKeyUpdate(ctx, durableId, claimKey, input);
2133
+ if (updateResult.applied) {
2134
+ options.onApplied?.();
2135
+ }
2136
+ if (updateResult.projected) {
2137
+ options.onProjected?.();
2138
+ if (options.registerTrustedReuse && isTrustedClaimKeyForCleanup(claimKey)) {
2139
+ ctx.workingSet.trustedReusableDurableIds.add(durableId);
2140
+ }
2141
+ recordRepairOutcome(ctx, claimKey, updateResult.projected);
2142
+ }
2143
+ return updateResult;
2144
+ }
2145
+ function refreshClaimKeyInspectionState(ctx, durableId, scope) {
2146
+ const durable = scope === "projected" ? ctx.workingSet.durablesById.get(durableId) : ctx.workingSet.actualDurablesById.get(durableId);
2147
+ const inspectionById = scope === "projected" ? ctx.workingSet.projectedInspectionById : ctx.workingSet.actualInspectionById;
2148
+ const tally = scope === "projected" ? ctx.workingSet.projectedInspectionTally : ctx.workingSet.actualInspectionTally;
2149
+ const previous = inspectionById.get(durableId);
2150
+ if (!durable || !previous) {
2151
+ return;
2152
+ }
2153
+ inspectionById.set(durableId, refreshDurableInspectionTally(durable, previous, ctx.extraction.claimExtractionConfig.eligibleTypes, tally));
2154
+ }
2155
+
2156
+ // src/app/dreaming/reconcile/handlers/entity-family.ts
2157
+ async function processEntityFamilyConvergenceCandidate(ctx, candidate) {
2158
+ const decision = evaluateEntityFamilyDecision(ctx, candidate);
2159
+ await executeEntityFamilyDecision(ctx, candidate, decision);
2160
+ }
2161
+ function evaluateEntityFamilyDecision(ctx, candidate) {
2162
+ const audit = buildEntityFamilyConvergenceAudit(candidate);
2163
+ const canonicalEntityPrefix = candidate.canonicalEntityPrefix;
2164
+ const durablesToRewrite = collectEntityFamilyDurablesToRewrite(ctx.workingSet.projectedDurables, candidate);
2165
+ if (!canonicalEntityPrefix || !candidate.autoConverge || durablesToRewrite.length === 0) {
2166
+ const proposedClaimKeys = canonicalEntityPrefix ? mapEntityFamilyClaimKeys(candidate.claimKeys, candidate.entityPrefixes, canonicalEntityPrefix) : [];
2167
+ return {
2168
+ kind: "propose_unresolved",
2169
+ audit,
2170
+ proposedClaimKeys,
2171
+ source: canonicalEntityPrefix ? "entity_family_canonical_candidate" : "entity_family_ambiguous",
2172
+ eligibleForApply: proposedClaimKeys.length === 1
2173
+ };
2174
+ }
2175
+ const rewrites = [];
2176
+ for (const durable of durablesToRewrite) {
2177
+ const targetClaimKey = resolveEntityFamilyTargetClaimKey(durable, canonicalEntityPrefix);
2178
+ if (!targetClaimKey) {
2179
+ continue;
2180
+ }
2181
+ const activeSiblingIds = resolveCrossTypeCollisionSiblingIds(ctx.workingSet.projectedDurables, targetClaimKey, durable);
2182
+ if (activeSiblingIds.length > 0) {
2183
+ return {
2184
+ kind: "propose_cross_type_collision",
2185
+ audit,
2186
+ targetClaimKey,
2187
+ activeSiblingIds,
2188
+ proposedClaimKeys: mapEntityFamilyClaimKeys(candidate.claimKeys, candidate.entityPrefixes, canonicalEntityPrefix)
2189
+ };
2190
+ }
2191
+ const oldClaimKey = durable.claim_key?.trim();
2192
+ if (!oldClaimKey) {
2193
+ continue;
2194
+ }
2195
+ rewrites.push({
2196
+ durable,
2197
+ targetClaimKey,
2198
+ oldClaimKey,
2199
+ attribute: targetClaimKey.slice(canonicalEntityPrefix.length + 1)
2200
+ });
2201
+ }
2202
+ return {
2203
+ kind: "auto_apply",
2204
+ audit,
2205
+ rewrites
2206
+ };
2207
+ }
2208
+ async function executeEntityFamilyDecision(ctx, candidate, decision) {
2209
+ switch (decision.kind) {
2210
+ case "propose_unresolved": {
2211
+ await persistReconcileProposal(ctx, {
2212
+ ...buildEntityFamilyPersistInput(candidate, {
2213
+ audit: decision.audit,
2214
+ proposedClaimKeys: decision.proposedClaimKeys,
2215
+ durableIds: candidate.durableIds,
2216
+ rationale: buildEntityFamilyConvergenceRationale(candidate),
2217
+ source: decision.source,
2218
+ eligibleForApply: decision.eligibleForApply
2219
+ }),
2220
+ onPersisted: (passCtx) => {
2221
+ passCtx.telemetry.entityFamilyDecisionStats.proposedClusters += 1;
2222
+ }
2223
+ });
2224
+ return;
2225
+ }
2226
+ case "propose_cross_type_collision": {
2227
+ await persistCrossTypeCollisionProposal(ctx, {
2228
+ ...buildEntityFamilyPersistInput(candidate, {
2229
+ audit: decision.audit,
2230
+ proposedClaimKeys: decision.proposedClaimKeys,
2231
+ durableIds: expandCrossTypeCollisionDurableIds(candidate.durableIds, decision.activeSiblingIds),
2232
+ rationale: `${buildEntityFamilyConvergenceRationale(candidate)} Auto-convergence would collide with an active durable of a different type at "${decision.targetClaimKey}".`,
2233
+ source: "entity_family_collision",
2234
+ eligibleForApply: false
2235
+ }),
2236
+ onPersisted: (passCtx) => {
2237
+ passCtx.telemetry.entityFamilyDecisionStats.proposedClusters += 1;
2238
+ }
2239
+ });
2240
+ return;
2241
+ }
2242
+ case "auto_apply": {
2243
+ let appliedDurables = 0;
2244
+ for (const rewrite of decision.rewrites) {
2245
+ await applyClaimKeyRepair(
2246
+ ctx,
2247
+ rewrite.durable.id,
2248
+ rewrite.targetClaimKey,
2249
+ {
2250
+ issueKind: "entity_family_convergence",
2251
+ oldClaimKey: rewrite.oldClaimKey,
2252
+ source: "entity_family_auto_convergence",
2253
+ confidence: candidate.confidence,
2254
+ rationale: `${buildEntityFamilyConvergenceRationale(candidate)} This durable keeps attribute "${rewrite.attribute}" while converging its entity prefix onto "${candidate.canonicalEntityPrefix}".`,
2255
+ entityFamilyAudit: decision.audit
2256
+ },
2257
+ {
2258
+ onIdentified: () => {
2259
+ ctx.telemetry.counts.identifiedEntityFamilyConvergences += 1;
2260
+ },
2261
+ onApplied: () => {
2262
+ ctx.telemetry.counts.appliedEntityFamilyConvergences += 1;
2263
+ appliedDurables += 1;
2264
+ }
2265
+ }
2266
+ );
2267
+ if (ctx.telemetry.circuitBreaker) {
2268
+ return;
2269
+ }
2270
+ }
2271
+ if (appliedDurables > 0) {
2272
+ ctx.telemetry.entityFamilyDecisionStats.appliedClusters += 1;
2273
+ ctx.telemetry.entityFamilyDecisionStats.appliedDurables += appliedDurables;
2274
+ }
2275
+ }
2276
+ }
2277
+ }
2278
+
2279
+ // src/app/dreaming/reconcile/helpers/claim-extraction.ts
2280
+ function claimExtractionUsage(llms) {
2281
+ return llms.reduce(
2282
+ (total, llm) => {
2283
+ const usage = llm.metadata?.usage;
2284
+ total.inputTokens += usage?.inputTokens ?? 0;
2285
+ total.outputTokens += usage?.outputTokens ?? 0;
2286
+ total.estimatedCostUsd += usage?.totalCost ?? 0;
2287
+ return total;
2288
+ },
2289
+ {
2290
+ inputTokens: 0,
2291
+ outputTokens: 0,
2292
+ estimatedCostUsd: 0
2293
+ }
2294
+ );
2295
+ }
2296
+ function resolveClaimExtractionConcurrency(config) {
2297
+ const concurrency = config.concurrency;
2298
+ const normalized = typeof concurrency === "number" ? Math.trunc(concurrency) : Number.NaN;
2299
+ if (!Number.isFinite(normalized) || normalized <= 0) {
2300
+ return DEFAULT_CLAIM_EXTRACTION_CONCURRENCY;
2301
+ }
2302
+ return normalized;
2303
+ }
2304
+
2305
+ // src/app/dreaming/reconcile/helpers/stats.ts
2306
+ function createEmptyMissingBackfillDecisionStats() {
2307
+ return {
2308
+ autoAppliedTrustedGroupReuse: 0,
2309
+ autoAppliedDeterministicRepair: 0,
2310
+ autoAppliedMetadataRepair: 0,
2311
+ autoAppliedSupportedPreview: 0,
2312
+ autoAppliedGroundedFamilyPromotion: 0,
2313
+ autoAppliedRelaxedStableSlotPromotion: 0,
2314
+ autoAppliedPreviewModel: 0,
2315
+ autoAppliedCompactedCandidate: 0,
2316
+ proposedTrustedGroupReuse: 0,
2317
+ proposedSupportedCandidate: 0,
2318
+ proposedGroundedFamilyPromotion: 0,
2319
+ proposedRelaxedStableSlotPromotion: 0,
2320
+ proposedPreviewCandidate: 0,
2321
+ proposedCompactedCandidate: 0,
2322
+ noClaimWithWarnings: 0
2323
+ };
2324
+ }
2325
+ function createEmptySiblingSlotResonanceShadowStats() {
2326
+ return {
2327
+ thresholdOnlyCandidateCount: 0,
2328
+ resonanceApplicableCount: 0,
2329
+ resonanceFiredCount: 0,
2330
+ shadowQualifiedCount: 0,
2331
+ resonanceFiredClaimKeys: [],
2332
+ shadowQualifiedClaimKeys: [],
2333
+ buckets: new Map(
2334
+ SHADOW_BUCKET_ORDER.map((bucket) => [
2335
+ bucket,
2336
+ {
2337
+ candidateCount: 0,
2338
+ resonanceApplicableCount: 0,
2339
+ resonanceFiredCount: 0,
2340
+ shadowQualifiedCount: 0
2341
+ }
2342
+ ])
2343
+ )
2344
+ };
2345
+ }
2346
+ function createEmptyEntityFamilyConvergenceDecisionStats() {
2347
+ return {
2348
+ appliedClusters: 0,
2349
+ appliedDurables: 0,
2350
+ proposedClusters: 0
2351
+ };
2352
+ }
2353
+ function createEmptyRepairCounts() {
2354
+ return {
2355
+ identifiedNormalizations: 0,
2356
+ appliedNormalizations: 0,
2357
+ identifiedBackfills: 0,
2358
+ appliedBackfills: 0,
2359
+ identifiedMetadataRewrites: 0,
2360
+ appliedMetadataRewrites: 0,
2361
+ identifiedEntityFamilyConvergences: 0,
2362
+ appliedEntityFamilyConvergences: 0,
2363
+ proposalsEmitted: 0,
2364
+ skippedNoClaim: 0,
2365
+ skippedLowConfidence: 0,
2366
+ skippedCollision: 0,
2367
+ flaggedAmbiguousProposals: 0
2368
+ };
2369
+ }
2370
+ function createEmptySuggestionRecord() {
2371
+ return {
2372
+ suggestion: null,
2373
+ warnings: [],
2374
+ previewOutcome: null
2375
+ };
2376
+ }
2377
+ function cloneRepairCounts(counts) {
2378
+ return {
2379
+ identifiedNormalizations: counts.identifiedNormalizations,
2380
+ appliedNormalizations: counts.appliedNormalizations,
2381
+ identifiedBackfills: counts.identifiedBackfills,
2382
+ appliedBackfills: counts.appliedBackfills,
2383
+ identifiedMetadataRewrites: counts.identifiedMetadataRewrites,
2384
+ appliedMetadataRewrites: counts.appliedMetadataRewrites,
2385
+ identifiedEntityFamilyConvergences: counts.identifiedEntityFamilyConvergences,
2386
+ appliedEntityFamilyConvergences: counts.appliedEntityFamilyConvergences,
2387
+ proposalsEmitted: counts.proposalsEmitted,
2388
+ skippedNoClaim: counts.skippedNoClaim,
2389
+ skippedLowConfidence: counts.skippedLowConfidence,
2390
+ skippedCollision: counts.skippedCollision,
2391
+ flaggedAmbiguousProposals: counts.flaggedAmbiguousProposals
2392
+ };
2393
+ }
2394
+
2395
+ // src/app/dreaming/reconcile/helpers/trusted-hints.ts
2396
+ function buildTrustedCleanupHintSeed(durables) {
2397
+ const sharedSeed = buildTrustedClaimKeySupportSeed(durables);
2398
+ const claimKeyExamples = normalizeStringArray(sharedSeed.entries.map((durable) => durable.claimKey)).slice(0, MAX_CLEANUP_CLAIM_KEY_HINTS);
2399
+ const entityHints = normalizeStringArray(claimKeyExamples.map((claimKey) => claimKey.split("/", 1)[0] ?? "").filter((entity) => entity.length > 0)).slice(
2400
+ 0,
2401
+ MAX_CLEANUP_ENTITY_HINTS
2402
+ );
2403
+ return {
2404
+ globalEntityHints: entityHints,
2405
+ globalClaimKeyExamples: claimKeyExamples,
2406
+ durables: sharedSeed.entries
2407
+ };
2408
+ }
2409
+ function buildCleanupHintsForDurable(baseHints, durable) {
2410
+ const rankedDurables = baseHints.durables.map((trustedDurable) => ({
2411
+ trustedDurable,
2412
+ score: scoreTrustedHintRelevance(durable, trustedDurable)
2413
+ })).filter((candidate) => candidate.score > 0).sort((left, right) => {
2414
+ const scoreDelta = right.score - left.score;
2415
+ if (scoreDelta !== 0) {
2416
+ return scoreDelta;
2417
+ }
2418
+ const createdAtDelta = right.trustedDurable.createdAt.localeCompare(left.trustedDurable.createdAt);
2419
+ if (createdAtDelta !== 0) {
2420
+ return createdAtDelta;
2421
+ }
2422
+ return left.trustedDurable.claimKey.localeCompare(right.trustedDurable.claimKey);
2423
+ });
2424
+ const relevantClaimKeyExamples = rankedDurables.map((candidate) => candidate.trustedDurable.claimKey);
2425
+ const claimKeyExamples = normalizeStringArray([...relevantClaimKeyExamples, ...baseHints.globalClaimKeyExamples]).slice(0, MAX_CLEANUP_CLAIM_KEY_HINTS);
2426
+ const entityHints = normalizeStringArray([
2427
+ ...rankedDurables.map((candidate) => candidate.trustedDurable.entity),
2428
+ ...claimKeyExamples.map((claimKey) => claimKey.split("/", 1)[0] ?? ""),
2429
+ ...baseHints.globalEntityHints
2430
+ ]).slice(0, MAX_CLEANUP_ENTITY_HINTS);
2431
+ return {
2432
+ entityHints,
2433
+ claimKeyExamples,
2434
+ project: durable.project,
2435
+ userId: durable.user_id,
2436
+ tags: normalizeGroundingTags(durable.tags),
2437
+ sourceContext: durable.source_context
2438
+ };
2439
+ }
2440
+ function scoreTrustedHintRelevance(durable, trustedDurable) {
2441
+ const durableTagSet = new Set(normalizeGroundingTags(durable.tags));
2442
+ const durableSourceTokens = new Set(tokenizeGroundingText(durable.source_context));
2443
+ const durableSubjectTokens = new Set(tokenizeGroundingText(durable.subject));
2444
+ const tagOverlap = countSetOverlap(durableTagSet, trustedDurable.tags);
2445
+ const sourceOverlap = countSetOverlap(durableSourceTokens, trustedDurable.sourceContextTokens);
2446
+ const subjectOverlap = countSetOverlap(durableSubjectTokens, trustedDurable.subjectTokens);
2447
+ return tagOverlap * 6 + sourceOverlap * 5 + subjectOverlap * 2 + (durable.type === trustedDurable.type ? 1 : 0);
2448
+ }
2449
+
2450
+ // src/app/dreaming/reconcile/pass-suggestion-handlers.ts
2451
+ function shouldPreloadSuggestions(ctx) {
2452
+ return ctx.extraction.claimExtractionConfig.enabled && typeof ctx.deps.createClaimExtractionLlm === "function";
2453
+ }
2454
+ function shouldPreviewMissingDurable(ctx, item) {
2455
+ return findTrustedGroupReuseCandidate(ctx.workingSet.projectedDurables, ctx.workingSet.trustedReusableDurableIds, item.durable) === null;
2456
+ }
2457
+ function shouldPreloadSuspectSuggestion(ctx, item) {
2458
+ if (!ctx.extraction.claimExtractionConfig.eligibleTypes.includes(item.durable.type)) {
2459
+ return false;
2460
+ }
2461
+ if (item.inspection.kind !== "suspect") {
2462
+ return false;
2463
+ }
2464
+ const metadataRepair = resolveExplicitMetadataRepair(item.durable, item.inspection.inspection);
2465
+ return metadataRepair === null || findClaimKeyOccupants(ctx.workingSet.projectedDurables, metadataRepair, item.durable.id).length > 0;
2466
+ }
2467
+ async function preloadSuggestionsForStage(ctx, durables) {
2468
+ if (durables.length === 0 || !shouldPreloadSuggestions(ctx)) {
2469
+ return;
2470
+ }
2471
+ if (ctx.options.costCapUsd <= 0) {
2472
+ ctx.telemetry.terminalStatus = "cost_capped";
2473
+ ctx.telemetry.terminalError = "Cost cap exhausted before previewing claim-key repairs.";
2474
+ return;
2475
+ }
2476
+ const workerCount = Math.min(ctx.extraction.previewConcurrency, durables.length);
2477
+ let nextIndex = 0;
2478
+ await Promise.all(
2479
+ Array.from({ length: workerCount }, async () => {
2480
+ const llm = createTrackedClaimExtractionLlm(ctx);
2481
+ while (true) {
2482
+ if (ctx.telemetry.terminalStatus !== "completed") {
2483
+ return;
2484
+ }
2485
+ if (markAbortedIfSignalled(ctx)) {
2486
+ return;
2487
+ }
2488
+ const currentIndex = nextIndex;
2489
+ nextIndex += 1;
2490
+ if (currentIndex >= durables.length) {
2491
+ return;
2492
+ }
2493
+ const durable = durables[currentIndex];
2494
+ if (!durable) {
2495
+ return;
2496
+ }
2497
+ await loadSuggestion(ctx, durable, llm);
2498
+ ctx.progressTracker.advancePreview();
2499
+ }
2500
+ })
2501
+ );
2502
+ }
2503
+ async function loadSuggestion(ctx, durable, llmOverride) {
2504
+ const cached = ctx.extraction.suggestionCache.get(durable.id);
2505
+ if (cached) {
2506
+ return cached;
2507
+ }
2508
+ if (!ctx.extraction.claimExtractionConfig.enabled || !ctx.extraction.claimExtractionConfig.eligibleTypes.includes(durable.type)) {
2509
+ const empty = createEmptySuggestionRecord();
2510
+ ctx.extraction.suggestionCache.set(durable.id, empty);
2511
+ return empty;
2512
+ }
2513
+ if (ctx.options.costCapUsd <= 0) {
2514
+ ctx.telemetry.terminalStatus = "cost_capped";
2515
+ ctx.telemetry.terminalError = "Cost cap exhausted before previewing claim-key repairs.";
2516
+ const empty = createEmptySuggestionRecord();
2517
+ ctx.extraction.suggestionCache.set(durable.id, empty);
2518
+ return empty;
2519
+ }
2520
+ const llm = llmOverride ?? getFallbackClaimExtractionLlm(ctx);
2521
+ if (!llm) {
2522
+ const empty = createEmptySuggestionRecord();
2523
+ ctx.extraction.suggestionCache.set(durable.id, empty);
2524
+ return empty;
2525
+ }
2526
+ const warnings = [];
2527
+ let previewOutcome = null;
2528
+ let suggestion;
2529
+ try {
2530
+ suggestion = await previewClaimKeyExtraction(
2531
+ {
2532
+ type: durable.type,
2533
+ subject: durable.subject,
2534
+ content: durable.content
2535
+ },
2536
+ llm,
2537
+ ctx.extraction.claimExtractionConfig,
2538
+ {
2539
+ hints: buildCleanupHintsForDurable(ctx.workingSet.trustedHints, durable),
2540
+ onWarning: (warning) => warnings.push(warning),
2541
+ onPreviewOutcome: (outcome) => {
2542
+ previewOutcome = outcome;
2543
+ }
2544
+ }
2545
+ );
2546
+ } catch (error) {
2547
+ warnings.push(error instanceof Error ? error.message : String(error));
2548
+ suggestion = null;
2549
+ }
2550
+ const record = { suggestion, warnings, previewOutcome };
2551
+ ctx.extraction.suggestionCache.set(durable.id, record);
2552
+ const usage = claimExtractionUsage(ctx.extraction.claimExtractionLlms);
2553
+ if (ctx.telemetry.terminalStatus === "completed" && usage.estimatedCostUsd >= ctx.options.costCapUsd) {
2554
+ ctx.telemetry.terminalStatus = "cost_capped";
2555
+ ctx.telemetry.terminalError = `Cost cap exceeded while previewing claim-key repairs at ${usage.estimatedCostUsd.toFixed(4)} USD.`;
2556
+ }
2557
+ return record;
2558
+ }
2559
+ async function loadSuggestionIfContinuing(ctx, durable, llmOverride) {
2560
+ const record = await loadSuggestion(ctx, durable, llmOverride);
2561
+ if (ctx.telemetry.terminalStatus !== "completed") {
2562
+ return null;
2563
+ }
2564
+ return record;
2565
+ }
2566
+ function getFallbackClaimExtractionLlm(ctx) {
2567
+ if (ctx.extraction.fallbackClaimExtractionLlm !== void 0) {
2568
+ return ctx.extraction.fallbackClaimExtractionLlm;
2569
+ }
2570
+ ctx.extraction.fallbackClaimExtractionLlm = createTrackedClaimExtractionLlm(ctx);
2571
+ return ctx.extraction.fallbackClaimExtractionLlm;
2572
+ }
2573
+ function createTrackedClaimExtractionLlm(ctx) {
2574
+ const llm = ctx.deps.createClaimExtractionLlm ? ctx.deps.createClaimExtractionLlm() : null;
2575
+ if (llm && !ctx.extraction.claimExtractionLlms.includes(llm)) {
2576
+ ctx.extraction.claimExtractionLlms.push(llm);
2577
+ }
2578
+ return llm;
2579
+ }
2580
+
2581
+ // src/app/dreaming/reconcile/handlers/invalid-durable.ts
2582
+ async function processInvalidOrNoncanonicalDurable(ctx, item) {
2583
+ const { durable, inspection } = item;
2584
+ if (inspection.kind !== "malformed" && inspection.kind !== "noncanonical") {
2585
+ return;
2586
+ }
2587
+ if (inspection.kind === "malformed") {
2588
+ const suggestionRecord = await loadSuggestion(ctx, durable);
2589
+ await persistReconcileProposal(ctx, buildMalformedClaimKeyPersistInput(durable, inspection, suggestionRecord));
2590
+ return;
2591
+ }
2592
+ const targetClaimKey = inspection.normalized.claimKey;
2593
+ const collision = findClaimKeyOccupants(ctx.workingSet.projectedDurables, targetClaimKey, durable.id);
2594
+ if (collision.length > 0) {
2595
+ ctx.telemetry.counts.skippedCollision += 1;
2596
+ recordCollisionOutcome(ctx);
2597
+ await persistReconcileProposal(ctx, {
2598
+ groupId: `claim-key-normalize:${durable.id}`,
2599
+ issueKind: "noncanonical_claim_key",
2600
+ scope: "single_durable",
2601
+ durableIds: [durable.id],
2602
+ currentClaimKeys: [durable.claim_key ?? ""],
2603
+ proposedClaimKeys: [targetClaimKey],
2604
+ rationale: `Canonical normalization would change "${durable.claim_key}" to "${targetClaimKey}", but that canonical key is already occupied by ${collision.length} other matched durable${collision.length === 1 ? "" : "s"}.`,
2605
+ confidence: 0.99,
2606
+ source: "normalize",
2607
+ eligibleForApply: true,
2608
+ lifecycle: {
2609
+ proposedClaimKeys: [targetClaimKey],
2610
+ source: "normalize",
2611
+ rawClaimKey: durable.claim_key ?? null
2612
+ }
2613
+ });
2614
+ return;
2615
+ }
2616
+ await applyClaimKeyRepair(
2617
+ ctx,
2618
+ durable.id,
2619
+ targetClaimKey,
2620
+ {
2621
+ issueKind: "noncanonical_claim_key",
2622
+ oldClaimKey: durable.claim_key ?? null,
2623
+ source: "normalize",
2624
+ confidence: 0.99,
2625
+ rationale: `Canonical normalization preserves the slot while rewriting "${durable.claim_key}" to "${targetClaimKey}".`
2626
+ },
2627
+ {
2628
+ onIdentified: () => {
2629
+ ctx.telemetry.counts.identifiedNormalizations += 1;
2630
+ },
2631
+ onApplied: () => {
2632
+ ctx.telemetry.counts.appliedNormalizations += 1;
2633
+ },
2634
+ registerTrustedReuse: true
2635
+ }
2636
+ );
2637
+ }
2638
+
2639
+ // src/core/claim-key-slot-resonance.ts
2640
+ var FAMILY_GENERIC_TOKEN_MIN_COUNT = 3;
2641
+ var FAMILY_GENERIC_TOKEN_RATIO = 0.5;
2642
+ var MIN_RESONANT_SHARED_TOKENS = 2;
2643
+ var MIN_RESONANT_SHAPE_COUNT = 2;
2644
+ function evaluateSiblingSlotResonance(input) {
2645
+ const candidateTokens = extractAttributeTokens(input.candidateClaimKey);
2646
+ const groundedSiblingAttributes = input.groundedSiblings.map((sibling) => ({
2647
+ entryId: sibling.entryId,
2648
+ claimKey: sibling.claimKey,
2649
+ tokens: extractAttributeTokens(sibling.claimKey)
2650
+ })).filter((sibling) => sibling.tokens.length > 0);
2651
+ if (candidateTokens.length < MIN_RESONANT_SHARED_TOKENS || groundedSiblingAttributes.length === 0) {
2652
+ return createEmptySiblingSlotResonanceEvaluation(groundedSiblingAttributes.length);
2653
+ }
2654
+ const familyTokenFrequency = countFamilyTokenFrequency(groundedSiblingAttributes.map((sibling) => sibling.tokens));
2655
+ const familyGenericCutoff = Math.max(FAMILY_GENERIC_TOKEN_MIN_COUNT, Math.ceil(groundedSiblingAttributes.length * FAMILY_GENERIC_TOKEN_RATIO));
2656
+ const familyGenericTokens = candidateTokens.filter((token) => (familyTokenFrequency.get(token) ?? 0) >= familyGenericCutoff);
2657
+ const discriminativeCandidateTokens = candidateTokens.filter((token) => !familyGenericTokens.includes(token));
2658
+ const localLexicalTokenSet = new Set(input.localLexicalTokens);
2659
+ const resonantShapes = /* @__PURE__ */ new Map();
2660
+ let resonantSiblingCount = 0;
2661
+ for (const sibling of groundedSiblingAttributes) {
2662
+ const resonanceShapeTokens = resolveResonanceShapeTokens(candidateTokens, discriminativeCandidateTokens, sibling.tokens);
2663
+ if (resonanceShapeTokens.length < MIN_RESONANT_SHARED_TOKENS) {
2664
+ continue;
2665
+ }
2666
+ resonantSiblingCount += 1;
2667
+ const resonanceShape = resonanceShapeTokens.join("_");
2668
+ const existing = resonantShapes.get(resonanceShape);
2669
+ if (existing) {
2670
+ existing.siblingEntryIds.push(sibling.entryId);
2671
+ existing.siblingClaimKeys.push(sibling.claimKey);
2672
+ continue;
2673
+ }
2674
+ resonantShapes.set(resonanceShape, {
2675
+ siblingEntryIds: [sibling.entryId],
2676
+ siblingClaimKeys: [sibling.claimKey]
2677
+ });
2678
+ }
2679
+ const dominantShapeEntry = [...resonantShapes.entries()].sort((left, right) => {
2680
+ const countDelta = right[1].siblingEntryIds.length - left[1].siblingEntryIds.length;
2681
+ if (countDelta !== 0) {
2682
+ return countDelta;
2683
+ }
2684
+ const tokenCountDelta = right[0].split("_").length - left[0].split("_").length;
2685
+ if (tokenCountDelta !== 0) {
2686
+ return tokenCountDelta;
2687
+ }
2688
+ return left[0].localeCompare(right[0]);
2689
+ })[0];
2690
+ const dominantShape = dominantShapeEntry?.[0] ?? null;
2691
+ const dominantShapeCount = dominantShapeEntry?.[1].siblingEntryIds.length ?? 0;
2692
+ const dominantShapeTokens = dominantShape ? dominantShape.split("_").filter((token) => token.length > 0) : [];
2693
+ const localShapeTokenCoverage = dominantShapeTokens.length > 0 ? dominantShapeTokens.filter((token) => localLexicalTokenSet.has(token)).length / dominantShapeTokens.length : 0;
2694
+ const fired = dominantShapeTokens.length >= MIN_RESONANT_SHARED_TOKENS && dominantShapeCount >= MIN_RESONANT_SHAPE_COUNT && localShapeTokenCoverage === 1;
2695
+ return {
2696
+ applicable: true,
2697
+ fired,
2698
+ groundedSiblingCount: groundedSiblingAttributes.length,
2699
+ resonantSiblingCount,
2700
+ dominantShape,
2701
+ dominantShapeCount,
2702
+ dominantShapeGroundedShare: groundedSiblingAttributes.length > 0 ? dominantShapeCount / groundedSiblingAttributes.length : 0,
2703
+ localShapeTokenCoverage,
2704
+ discriminativeCandidateTokens,
2705
+ familyGenericTokens,
2706
+ dominantSiblingEntryIds: dominantShapeEntry ? [...dominantShapeEntry[1].siblingEntryIds] : [],
2707
+ dominantSiblingClaimKeys: dominantShapeEntry ? [...dominantShapeEntry[1].siblingClaimKeys] : []
2708
+ };
2709
+ }
2710
+ function createEmptySiblingSlotResonanceEvaluation(groundedSiblingCount) {
2711
+ return {
2712
+ applicable: false,
2713
+ fired: false,
2714
+ groundedSiblingCount,
2715
+ resonantSiblingCount: 0,
2716
+ dominantShape: null,
2717
+ dominantShapeCount: 0,
2718
+ dominantShapeGroundedShare: 0,
2719
+ localShapeTokenCoverage: 0,
2720
+ discriminativeCandidateTokens: [],
2721
+ familyGenericTokens: [],
2722
+ dominantSiblingEntryIds: [],
2723
+ dominantSiblingClaimKeys: []
2724
+ };
2725
+ }
2726
+ function extractAttributeTokens(claimKey) {
2727
+ const compacted = compactClaimKey(claimKey);
2728
+ if (compacted) {
2729
+ return compacted.attribute.split("_").filter((token) => token.length > 0);
2730
+ }
2731
+ const normalized = normalizeClaimKey(claimKey);
2732
+ if (!normalized.ok) {
2733
+ return [];
2734
+ }
2735
+ return normalized.value.attribute.split("_").filter((token) => token.length > 0);
2736
+ }
2737
+ function countFamilyTokenFrequency(attributes) {
2738
+ const counts = /* @__PURE__ */ new Map();
2739
+ for (const tokens of attributes) {
2740
+ for (const token of new Set(tokens)) {
2741
+ counts.set(token, (counts.get(token) ?? 0) + 1);
2742
+ }
2743
+ }
2744
+ return counts;
2745
+ }
2746
+ function resolveResonanceShapeTokens(candidateTokens, discriminativeCandidateTokens, siblingTokens) {
2747
+ const siblingTokenSet = new Set(siblingTokens);
2748
+ const sharedTokens = candidateTokens.filter((token) => siblingTokenSet.has(token));
2749
+ if (sharedTokens.length < MIN_RESONANT_SHARED_TOKENS) {
2750
+ return [];
2751
+ }
2752
+ const sharedDiscriminativeTokens = discriminativeCandidateTokens.filter((token) => siblingTokenSet.has(token));
2753
+ const sharedSpan = findLongestSharedContiguousSpan(candidateTokens, siblingTokens);
2754
+ if (sharedSpan.length >= MIN_RESONANT_SHARED_TOKENS) {
2755
+ return sharedSpan;
2756
+ }
2757
+ const candidateHead = candidateTokens[candidateTokens.length - 1];
2758
+ const siblingHead = siblingTokens[siblingTokens.length - 1];
2759
+ if (candidateHead && siblingHead && candidateHead === siblingHead && sharedDiscriminativeTokens.length >= 1) {
2760
+ return dedupeOrderedTokens([...sharedDiscriminativeTokens, candidateHead]);
2761
+ }
2762
+ if (sharedDiscriminativeTokens.length >= MIN_RESONANT_SHARED_TOKENS) {
2763
+ return sharedDiscriminativeTokens;
2764
+ }
2765
+ return [];
2766
+ }
2767
+ function findLongestSharedContiguousSpan(candidateTokens, siblingTokens) {
2768
+ let best = [];
2769
+ for (let start = 0; start < candidateTokens.length; start += 1) {
2770
+ for (let end = start + MIN_RESONANT_SHARED_TOKENS; end <= candidateTokens.length; end += 1) {
2771
+ const span = candidateTokens.slice(start, end);
2772
+ if (span.length < best.length) {
2773
+ continue;
2774
+ }
2775
+ if (includesContiguousSpan(siblingTokens, span)) {
2776
+ if (span.length > best.length || span.join("_").localeCompare(best.join("_")) < 0) {
2777
+ best = span;
2778
+ }
2779
+ }
2780
+ }
2781
+ }
2782
+ return best;
2783
+ }
2784
+ function includesContiguousSpan(tokens, span) {
2785
+ if (span.length === 0 || span.length > tokens.length) {
2786
+ return false;
2787
+ }
2788
+ for (let start = 0; start <= tokens.length - span.length; start += 1) {
2789
+ let matched = true;
2790
+ for (let index = 0; index < span.length; index += 1) {
2791
+ if (tokens[start + index] !== span[index]) {
2792
+ matched = false;
2793
+ break;
2794
+ }
2795
+ }
2796
+ if (matched) {
2797
+ return true;
2798
+ }
2799
+ }
2800
+ return false;
2801
+ }
2802
+ function dedupeOrderedTokens(tokens) {
2803
+ const seen = /* @__PURE__ */ new Set();
2804
+ const deduped = [];
2805
+ for (const token of tokens) {
2806
+ if (seen.has(token)) {
2807
+ continue;
2808
+ }
2809
+ seen.add(token);
2810
+ deduped.push(token);
2811
+ }
2812
+ return deduped;
2813
+ }
2814
+
2815
+ // src/app/dreaming/reconcile/helpers/suggestions.ts
2816
+ function evaluateMissingBackfillSupport(durable, targetClaimKey, trustedHints) {
2817
+ const support = evaluateClaimKeySupport(durable, targetClaimKey, { entries: trustedHints.durables });
2818
+ const inspection = inspectClaimKey(targetClaimKey);
2819
+ const normalized = inspection.normalized;
2820
+ if (!normalized) {
2821
+ return {
2822
+ ...support,
2823
+ siblingSlotResonance: createEmptySiblingSlotResonanceEvaluation(0)
2824
+ };
2825
+ }
2826
+ return {
2827
+ ...support,
2828
+ siblingSlotResonance: evaluateSiblingSlotResonance({
2829
+ candidateClaimKey: normalized.claimKey,
2830
+ localLexicalTokens: buildDurableLocalLexicalTokens(durable),
2831
+ groundedSiblings: support.supportingDurableIds.map((durableId) => trustedHints.durables.find((trustedDurable) => trustedDurable.id === durableId)).filter((trustedDurable) => Boolean(trustedDurable)).map((trustedDurable) => ({
2832
+ entryId: trustedDurable.id,
2833
+ claimKey: trustedDurable.claimKey
2834
+ }))
2835
+ })
2836
+ };
2837
+ }
2838
+ function resolveMissingBackfillNullOutcome(suggestionRecord) {
2839
+ if (suggestionRecord.previewOutcome?.outcome === "no_claim") {
2840
+ return "no_claim";
2841
+ }
2842
+ if (suggestionRecord.previewOutcome?.outcome === "rejected_candidate") {
2843
+ return "rejected_candidate";
2844
+ }
2845
+ if (suggestionRecord.warnings.some((warning) => /json|unexpected token|unterminated|position \d+/iu.test(warning))) {
2846
+ return "malformed_output";
2847
+ }
2848
+ return "rejected_candidate";
2849
+ }
2850
+
2851
+ // src/app/dreaming/reconcile/helpers/missing-backfill-stats.ts
2852
+ function recordMissingBackfillNoClaimOutcome(ctx, durable, suggestionRecord) {
2853
+ if (suggestionRecord.warnings.length > 0) {
2854
+ ctx.telemetry.missingDecisionStats.noClaimWithWarnings += 1;
2855
+ }
2856
+ ctx.telemetry.counts.skippedNoClaim += 1;
2857
+ ctx.telemetry.skippedDiagnostics.push(
2858
+ buildMissingBackfillSkipDiagnostic(durable, suggestionRecord, {
2859
+ outcomeOverride: resolveMissingBackfillNullOutcome(suggestionRecord)
2860
+ })
2861
+ );
2862
+ }
2863
+ function recordMissingBackfillLowConfidenceSkip(ctx, durable, suggestionRecord, targetClaimKey) {
2864
+ ctx.telemetry.counts.skippedLowConfidence += 1;
2865
+ ctx.telemetry.skippedDiagnostics.push(
2866
+ buildMissingBackfillSkipDiagnostic(durable, suggestionRecord, {
2867
+ outcomeOverride: "low_confidence_candidate",
2868
+ suggestedClaimKey: targetClaimKey
2869
+ })
2870
+ );
2871
+ }
2872
+ function recordMissingBackfillProposalLaneOutcome(ctx, resolved) {
2873
+ if (resolved.metadataBackfillClaimKey !== null || resolved.suggestion.path === "deterministic_repair" || resolved.support.supportedProposal) {
2874
+ ctx.telemetry.missingDecisionStats.proposedSupportedCandidate += 1;
2875
+ } else {
2876
+ ctx.telemetry.missingDecisionStats.proposedPreviewCandidate += 1;
2877
+ }
2878
+ }
2879
+ function recordMissingBackfillProposalPersisted(ctx, resolved) {
2880
+ recordGroundedFamilyPromotionDecision(ctx.telemetry.missingDecisionStats, resolved.support, "proposal");
2881
+ if (resolved.compactness.compactedFrom) {
2882
+ ctx.telemetry.missingDecisionStats.proposedCompactedCandidate += 1;
2883
+ }
2884
+ }
2885
+ function recordMissingBackfillAutoApplyIdentified(ctx) {
2886
+ ctx.telemetry.counts.identifiedBackfills += 1;
2887
+ }
2888
+ function recordMissingBackfillAutoApplyApplied(ctx) {
2889
+ ctx.telemetry.counts.appliedBackfills += 1;
2890
+ }
2891
+ function recordMissingBackfillAutoApplyProjected(ctx, resolved) {
2892
+ if (resolved.metadataBackfillClaimKey !== null) {
2893
+ ctx.telemetry.missingDecisionStats.autoAppliedMetadataRepair += 1;
2894
+ } else if (resolved.suggestion.path === "deterministic_repair") {
2895
+ ctx.telemetry.missingDecisionStats.autoAppliedDeterministicRepair += 1;
2896
+ } else if (resolved.support.autoApplyClass !== null && resolved.suggestion.confidence < HIGH_CONFIDENCE_BACKFILL_THRESHOLD) {
2897
+ ctx.telemetry.missingDecisionStats.autoAppliedSupportedPreview += 1;
2898
+ } else {
2899
+ ctx.telemetry.missingDecisionStats.autoAppliedPreviewModel += 1;
2900
+ }
2901
+ recordGroundedFamilyPromotionDecision(ctx.telemetry.missingDecisionStats, resolved.support, "auto_apply");
2902
+ if (resolved.compactness.compactedFrom) {
2903
+ ctx.telemetry.missingDecisionStats.autoAppliedCompactedCandidate += 1;
2904
+ }
2905
+ }
2906
+ function recordMissingBackfillTrustedGroupReuseProposed(ctx) {
2907
+ ctx.telemetry.missingDecisionStats.proposedTrustedGroupReuse += 1;
2908
+ }
2909
+ function recordMissingBackfillTrustedGroupReuseAutoApplied(ctx) {
2910
+ ctx.telemetry.missingDecisionStats.autoAppliedTrustedGroupReuse += 1;
2911
+ }
2912
+
2913
+ // src/app/dreaming/reconcile/helpers/shadow-resonance.ts
2914
+ function buildSiblingSlotResonanceShadowSummary(stats) {
2915
+ if (stats.thresholdOnlyCandidateCount === 0) {
2916
+ return null;
2917
+ }
2918
+ const buckets = SHADOW_BUCKET_ORDER.map((bucket) => {
2919
+ const bucketStats = stats.buckets.get(bucket);
2920
+ return {
2921
+ bucket,
2922
+ candidateCount: bucketStats?.candidateCount ?? 0,
2923
+ resonanceApplicableCount: bucketStats?.resonanceApplicableCount ?? 0,
2924
+ resonanceFiredCount: bucketStats?.resonanceFiredCount ?? 0,
2925
+ shadowQualifiedCount: bucketStats?.shadowQualifiedCount ?? 0
2926
+ };
2927
+ });
2928
+ return {
2929
+ rule: {
2930
+ supportClass: "trusted_family_grounded_alignment",
2931
+ minFamilyReuseCount: SHADOW_RESONANCE_MIN_FAMILY_REUSE_COUNT,
2932
+ minGroundedRatio: SHADOW_RESONANCE_MIN_GROUNDED_RATIO,
2933
+ minConfidence: SHADOW_RESONANCE_MIN_CONFIDENCE,
2934
+ requiresSiblingSlotResonance: true
2935
+ },
2936
+ thresholdOnlyCandidateCount: stats.thresholdOnlyCandidateCount,
2937
+ resonanceApplicableCount: stats.resonanceApplicableCount,
2938
+ resonanceFiredCount: stats.resonanceFiredCount,
2939
+ shadowQualifiedCount: stats.shadowQualifiedCount,
2940
+ resonanceFiredClaimKeys: [...stats.resonanceFiredClaimKeys],
2941
+ shadowQualifiedClaimKeys: [...stats.shadowQualifiedClaimKeys],
2942
+ buckets
2943
+ };
2944
+ }
2945
+ function resolveThresholdOnlyShadowBucket(support) {
2946
+ if (support.relaxedStableSlotFamilyGate) {
2947
+ return "relaxed_one_sibling_stable_slot";
2948
+ }
2949
+ if (support.autoApplyClass !== "trusted_family_grounded_alignment") {
2950
+ return null;
2951
+ }
2952
+ const groundedRatio = support.familyReuseCount > 0 ? support.groundedFamilyReuseCount / support.familyReuseCount : 0;
2953
+ if (support.familyReuseCount >= SHADOW_RESONANCE_MIN_FAMILY_REUSE_COUNT) {
2954
+ return groundedRatio >= SHADOW_RESONANCE_MIN_GROUNDED_RATIO ? "high_density_grounded_family" : "large_grounding_diluted_grounded_family";
2955
+ }
2956
+ if (support.familyReuseCount <= 5) {
2957
+ return "thin_grounded_family_tail";
2958
+ }
2959
+ return "other_grounded_family_alignment";
2960
+ }
2961
+ function buildMissingBackfillShadowAudit(input) {
2962
+ if (input.autoApplyBlocker !== "below_auto_apply_threshold") {
2963
+ return null;
2964
+ }
2965
+ const bucket = resolveThresholdOnlyShadowBucket(input.support);
2966
+ if (!bucket) {
2967
+ return null;
2968
+ }
2969
+ const groundedRatio = input.support.familyReuseCount > 0 ? input.support.groundedFamilyReuseCount / input.support.familyReuseCount : 0;
2970
+ return {
2971
+ thresholdOnlyBucket: bucket,
2972
+ shadowWouldQualify: input.support.autoApplyClass === "trusted_family_grounded_alignment" && input.support.familyReuseCount >= SHADOW_RESONANCE_MIN_FAMILY_REUSE_COUNT && groundedRatio >= SHADOW_RESONANCE_MIN_GROUNDED_RATIO && input.confidence >= SHADOW_RESONANCE_MIN_CONFIDENCE && input.support.siblingSlotResonance.fired
2973
+ };
2974
+ }
2975
+ function recordSiblingSlotResonanceShadowCandidate(stats, claimKey, support, shadow) {
2976
+ stats.thresholdOnlyCandidateCount += 1;
2977
+ if (support.siblingSlotResonance.applicable) {
2978
+ stats.resonanceApplicableCount += 1;
2979
+ }
2980
+ if (support.siblingSlotResonance.fired) {
2981
+ stats.resonanceFiredCount += 1;
2982
+ stats.resonanceFiredClaimKeys = normalizeStringArray([...stats.resonanceFiredClaimKeys, claimKey]);
2983
+ }
2984
+ if (shadow.shadowWouldQualify) {
2985
+ stats.shadowQualifiedCount += 1;
2986
+ stats.shadowQualifiedClaimKeys = normalizeStringArray([...stats.shadowQualifiedClaimKeys, claimKey]);
2987
+ }
2988
+ const bucketStats = stats.buckets.get(shadow.thresholdOnlyBucket);
2989
+ if (!bucketStats) {
2990
+ return;
2991
+ }
2992
+ bucketStats.candidateCount += 1;
2993
+ if (support.siblingSlotResonance.applicable) {
2994
+ bucketStats.resonanceApplicableCount += 1;
2995
+ }
2996
+ if (support.siblingSlotResonance.fired) {
2997
+ bucketStats.resonanceFiredCount += 1;
2998
+ }
2999
+ if (shadow.shadowWouldQualify) {
3000
+ bucketStats.shadowQualifiedCount += 1;
3001
+ }
3002
+ }
3003
+
3004
+ // src/app/dreaming/reconcile/helpers/missing-backfill-rationale.ts
3005
+ function formatConfidence(confidence) {
3006
+ return confidence.toFixed(2);
3007
+ }
3008
+ function hasMetadataGrounding(input) {
3009
+ return input.metadataBackfillClaimKey !== null && input.originalClaimKey !== input.targetClaimKey;
3010
+ }
3011
+ function hasCompactionRewrite(input) {
3012
+ return Boolean(input.compactness.compactedFrom && input.compactness.compactedFrom !== input.targetClaimKey);
3013
+ }
3014
+ function formatCompactionSuffix(input) {
3015
+ if (!hasCompactionRewrite(input)) {
3016
+ return "";
3017
+ }
3018
+ return ` The candidate was compacted from "${input.compactness.compactedFrom}" to "${input.targetClaimKey}" because ${input.compactness.compactionReason}.`;
3019
+ }
3020
+ function formatInlineCompactionClause(input) {
3021
+ if (!hasCompactionRewrite(input)) {
3022
+ return "";
3023
+ }
3024
+ return ` after safely compacting "${input.compactness.compactedFrom}" to "${input.targetClaimKey}" because ${input.compactness.compactionReason}`;
3025
+ }
3026
+ function formatApplyCompactionSuffix(input) {
3027
+ if (!hasCompactionRewrite(input)) {
3028
+ return "";
3029
+ }
3030
+ return ` The candidate was compacted from "${input.compactness.compactedFrom}" because ${input.compactness.compactionReason}.`;
3031
+ }
3032
+ function formatPreviewLead(input) {
3033
+ if (hasMetadataGrounding(input)) {
3034
+ return `Backfill preview suggested "${input.originalClaimKey}" at confidence ${formatConfidence(input.confidence)}, and explicit metadata safely grounds that candidate to "${input.targetClaimKey}".`;
3035
+ }
3036
+ if (hasCompactionRewrite(input)) {
3037
+ return `Backfill preview suggested "${input.compactness.compactedFrom}" at confidence ${formatConfidence(input.confidence)}, and compact canonicalization safely shortened it to "${input.targetClaimKey}" because ${input.compactness.compactionReason}.`;
3038
+ }
3039
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}.`;
3040
+ }
3041
+ function buildMissingBackfillConflictRationale(input) {
3042
+ return `${formatPreviewLead(input)}${formatCompactionSuffix(input)}`;
3043
+ }
3044
+ function buildMissingBackfillProposalRationale(input) {
3045
+ if (!input.trusted) {
3046
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}, but the proposed key is still structurally suspect.`;
3047
+ }
3048
+ if (!input.compactness.compactEnoughForAutoApply) {
3049
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}${formatInlineCompactionClause(input)}, but the resulting slot name is still too verbose or awkward to auto-apply safely.`;
3050
+ }
3051
+ if (hasMetadataGrounding(input)) {
3052
+ return `Backfill preview suggested "${input.originalClaimKey}" at confidence ${formatConfidence(input.confidence)}, and explicit metadata resolves that candidate to likely canonical key "${input.targetClaimKey}", but that supported repair stays below the auto-apply threshold of ${formatConfidence(input.autoApplyThreshold)}.`;
3053
+ }
3054
+ if (input.support.autoApplyClass !== null) {
3055
+ const promotionLead = input.promotionLane === "compacted_supported" ? "Supported evidence remained strong after compact canonicalization" : `Supported evidence from ${describeMissingBackfillPromotionClass(input.support.autoApplyClass)} exists`;
3056
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}. ${promotionLead} via ${input.support.rationaleFragments.join(", ")}, but the candidate stays below the auto-apply threshold of ${formatConfidence(input.autoApplyThreshold)}.`;
3057
+ }
3058
+ if (input.support.rationaleFragments.length > 0) {
3059
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}. Structural support exists from ${input.support.rationaleFragments.join(", ")}, but the candidate stays below the auto-apply threshold of ${formatConfidence(input.autoApplyThreshold)}.`;
3060
+ }
3061
+ return `Backfill preview suggested "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)}, below the auto-apply threshold of ${formatConfidence(input.autoApplyThreshold)}.`;
3062
+ }
3063
+ function buildMissingBackfillApplyRationale(input) {
3064
+ if (hasMetadataGrounding(input)) {
3065
+ return `Metadata-grounded claim-key backfill rewrote preview candidate "${input.originalClaimKey}" to "${input.targetClaimKey}" at confidence ${formatConfidence(input.confidence)} from ${input.source}.` + (hasCompactionRewrite(input) ? ` Compact canonicalization also shortened "${input.compactness.compactedFrom}" because ${input.compactness.compactionReason}.` : "");
3066
+ }
3067
+ if (input.support.autoApplyClass !== null && input.confidence < HIGH_CONFIDENCE_BACKFILL_THRESHOLD) {
3068
+ const promotionPrefix = input.promotionLane === "compacted_supported" ? "Post-compaction supported claim-key backfill" : "Supported claim-key backfill";
3069
+ return `${promotionPrefix} assigned "${input.targetClaimKey}" from ${input.source} at confidence ${formatConfidence(input.confidence)} through ${describeMissingBackfillPromotionClass(input.support.autoApplyClass)} using ${input.support.rationaleFragments.join(", ")}.` + formatApplyCompactionSuffix(input);
3070
+ }
3071
+ return `High-confidence claim-key backfill assigned "${input.targetClaimKey}" from ${input.source} at confidence ${formatConfidence(input.confidence)}.` + formatApplyCompactionSuffix(input);
3072
+ }
3073
+ function describeMissingBackfillPromotionClass(promotionClass) {
3074
+ switch (promotionClass) {
3075
+ case "trusted_exact_reuse_grounded":
3076
+ return "trusted exact-key reuse with local grounding";
3077
+ case "trusted_family_template_grounded":
3078
+ return "trusted family reuse plus grounded template support";
3079
+ case "trusted_family_stable_slot":
3080
+ return "trusted family reuse plus a stable compact slot";
3081
+ case "trusted_family_grounded_alignment":
3082
+ return "trusted family reuse plus grounded dual lexical alignment";
3083
+ }
3084
+ }
3085
+
3086
+ // src/app/dreaming/reconcile/helpers/missing-backfill.ts
3087
+ function buildMissingBackfillSkipDiagnostic(durable, suggestionRecord, options) {
3088
+ const previewPath = suggestionRecord.suggestion?.path ?? suggestionRecord.previewOutcome?.path ?? null;
3089
+ const previewConfidence = suggestionRecord.suggestion?.confidence ?? (typeof suggestionRecord.previewOutcome?.confidence === "number" ? suggestionRecord.previewOutcome.confidence : null);
3090
+ return {
3091
+ durableId: durable.id,
3092
+ outcome: options.outcomeOverride,
3093
+ confidence: previewConfidence,
3094
+ path: previewPath,
3095
+ warning: suggestionRecord.warnings[0] ?? null,
3096
+ suggestedClaimKey: options.suggestedClaimKey ?? suggestionRecord.suggestion?.claimKey ?? null
3097
+ };
3098
+ }
3099
+ function formatMissingBackfillSkipDiagnostic(diagnostic) {
3100
+ const parts = [
3101
+ `missing_claim_key:${diagnostic.outcome}`,
3102
+ diagnostic.path ? `path=${diagnostic.path}` : null,
3103
+ typeof diagnostic.confidence === "number" ? `confidence=${diagnostic.confidence.toFixed(2)}` : null,
3104
+ diagnostic.suggestedClaimKey ? `suggested=${diagnostic.suggestedClaimKey}` : null,
3105
+ diagnostic.warning ? `warning=${diagnostic.warning}` : null
3106
+ ].filter((value) => value !== null);
3107
+ return parts.join(" ");
3108
+ }
3109
+ function resolveMissingBackfillPromotionPolicy(input) {
3110
+ if (input.metadataRepaired) {
3111
+ return {
3112
+ lane: "metadata_rewrite",
3113
+ autoApplyThreshold: STRUCTURED_AUTO_APPLY_BACKFILL_THRESHOLD
3114
+ };
3115
+ }
3116
+ if (input.previewPath === "deterministic_repair") {
3117
+ return {
3118
+ lane: "deterministic_repair",
3119
+ autoApplyThreshold: STRUCTURED_AUTO_APPLY_BACKFILL_THRESHOLD
3120
+ };
3121
+ }
3122
+ if (input.support.autoApplyClass !== null && input.compactness.compactedFrom) {
3123
+ return {
3124
+ lane: "compacted_supported",
3125
+ autoApplyThreshold: COMPACTED_SUPPORTED_AUTO_APPLY_BACKFILL_THRESHOLD
3126
+ };
3127
+ }
3128
+ if (input.support.autoApplyClass !== null) {
3129
+ return {
3130
+ lane: "structured_supported",
3131
+ autoApplyThreshold: STRUCTURED_AUTO_APPLY_BACKFILL_THRESHOLD
3132
+ };
3133
+ }
3134
+ return {
3135
+ lane: "high_confidence_preview",
3136
+ autoApplyThreshold: HIGH_CONFIDENCE_BACKFILL_THRESHOLD
3137
+ };
3138
+ }
3139
+ function resolveMissingBackfillProposalThreshold(input) {
3140
+ if (input.metadataRepaired || input.previewPath === "deterministic_repair" || input.support.supportedProposal) {
3141
+ return SUPPORTED_PROPOSAL_CONFIDENCE_THRESHOLD;
3142
+ }
3143
+ return PROPOSAL_CONFIDENCE_THRESHOLD;
3144
+ }
3145
+ function resolveMissingBackfillAutoApplyBlocker(input) {
3146
+ if (!input.trusted) {
3147
+ return "structurally_suspect_claim_key";
3148
+ }
3149
+ if (!input.compactness.compactEnoughForAutoApply) {
3150
+ return input.compactness.blockerReason;
3151
+ }
3152
+ if (input.confidence < input.autoApplyThreshold) {
3153
+ return "below_auto_apply_threshold";
3154
+ }
3155
+ return null;
3156
+ }
3157
+ function recordGroundedFamilyPromotionDecision(stats, support, decision) {
3158
+ if (support.autoApplyClass === "trusted_family_grounded_alignment") {
3159
+ if (decision === "auto_apply") {
3160
+ stats.autoAppliedGroundedFamilyPromotion += 1;
3161
+ } else {
3162
+ stats.proposedGroundedFamilyPromotion += 1;
3163
+ }
3164
+ }
3165
+ if (support.relaxedStableSlotFamilyGate) {
3166
+ if (decision === "auto_apply") {
3167
+ stats.autoAppliedRelaxedStableSlotPromotion += 1;
3168
+ } else {
3169
+ stats.proposedRelaxedStableSlotPromotion += 1;
3170
+ }
3171
+ }
3172
+ }
3173
+ function buildMissingBackfillResolvedPreview(durable, suggestion, ctx) {
3174
+ const metadataBackfillClaimKey = resolveMetadataBackfillClaimKey(durable, suggestion.claimKey);
3175
+ const originalClaimKey = suggestion.compactedFrom ?? suggestion.claimKey;
3176
+ const compactness = evaluateClaimKeyCompactness(metadataBackfillClaimKey ?? suggestion.claimKey, {
3177
+ priorCompactedFrom: suggestion.compactedFrom ?? null,
3178
+ priorCompactionReason: suggestion.compactionReason ?? null
3179
+ });
3180
+ const targetClaimKey = compactness.claimKey;
3181
+ const targetSource = metadataBackfillClaimKey ? "metadata_backfill_rewrite" : suggestion.path;
3182
+ const support = evaluateMissingBackfillSupport(durable, targetClaimKey, ctx.workingSet.trustedHints);
3183
+ const promotionPolicy = resolveMissingBackfillPromotionPolicy({
3184
+ metadataRepaired: metadataBackfillClaimKey !== null,
3185
+ previewPath: suggestion.path,
3186
+ support,
3187
+ compactness
3188
+ });
3189
+ return {
3190
+ targetClaimKey,
3191
+ targetSource,
3192
+ originalClaimKey,
3193
+ compactness,
3194
+ promotionPolicy,
3195
+ support,
3196
+ suggestion,
3197
+ metadataBackfillClaimKey
3198
+ };
3199
+ }
3200
+ function evaluateMissingDurablePreview(durable, suggestionRecord, ctx) {
3201
+ const suggestion = suggestionRecord.suggestion;
3202
+ if (!suggestion?.claimKey) {
3203
+ return { kind: "skip_no_claim", suggestionRecord };
3204
+ }
3205
+ const resolved = buildMissingBackfillResolvedPreview(durable, suggestion, ctx);
3206
+ const targetInspection = inspectClaimKey(resolved.targetClaimKey);
3207
+ const targetIsTrusted = targetInspection.suspectReasons.length === 0;
3208
+ const autoApplyThreshold = resolved.promotionPolicy.autoApplyThreshold;
3209
+ const proposalThreshold = resolveMissingBackfillProposalThreshold({
3210
+ metadataRepaired: resolved.metadataBackfillClaimKey !== null,
3211
+ previewPath: suggestion.path,
3212
+ support: resolved.support
3213
+ });
3214
+ const activeSiblingIds = resolveCrossTypeCollisionSiblingIds(ctx.workingSet.projectedDurables, resolved.targetClaimKey, durable);
3215
+ if (activeSiblingIds.length > 0) {
3216
+ return {
3217
+ kind: "propose_cross_type_collision",
3218
+ activeSiblingIds,
3219
+ ...resolved
3220
+ };
3221
+ }
3222
+ if (!targetIsTrusted || !resolved.compactness.compactEnoughForAutoApply || suggestion.confidence < autoApplyThreshold) {
3223
+ if (suggestion.confidence >= proposalThreshold) {
3224
+ return {
3225
+ kind: "propose_below_auto_apply",
3226
+ autoApplyThreshold,
3227
+ proposalThreshold,
3228
+ targetIsTrusted,
3229
+ ...resolved
3230
+ };
3231
+ }
3232
+ return { kind: "skip_low_confidence", suggestionRecord, targetClaimKey: resolved.targetClaimKey };
3233
+ }
3234
+ return {
3235
+ kind: "auto_apply",
3236
+ ...resolved
3237
+ };
3238
+ }
3239
+ async function executeMissingDurablePreviewDecision(ctx, durable, decision) {
3240
+ switch (decision.kind) {
3241
+ case "skip_no_claim": {
3242
+ recordMissingBackfillNoClaimOutcome(ctx, durable, decision.suggestionRecord);
3243
+ return;
3244
+ }
3245
+ case "propose_cross_type_collision": {
3246
+ await persistMissingBackfillProposal(ctx, durable, decision, {
3247
+ durableIds: [.../* @__PURE__ */ new Set([durable.id, ...decision.activeSiblingIds])],
3248
+ rationale: appendCrossTypeCollisionRationaleSuffix(
3249
+ buildMissingBackfillConflictRationale({
3250
+ originalClaimKey: decision.originalClaimKey,
3251
+ targetClaimKey: decision.targetClaimKey,
3252
+ confidence: decision.suggestion.confidence,
3253
+ metadataBackfillClaimKey: decision.metadataBackfillClaimKey,
3254
+ compactness: decision.compactness
3255
+ }),
3256
+ decision.targetClaimKey
3257
+ ),
3258
+ audit: {}
3259
+ });
3260
+ return;
3261
+ }
3262
+ case "propose_below_auto_apply": {
3263
+ const autoApplyBlocker = resolveMissingBackfillAutoApplyBlocker({
3264
+ trusted: decision.targetIsTrusted,
3265
+ compactness: decision.compactness,
3266
+ confidence: decision.suggestion.confidence,
3267
+ autoApplyThreshold: decision.autoApplyThreshold
3268
+ });
3269
+ const shadowAudit = buildMissingBackfillShadowAudit({
3270
+ support: decision.support,
3271
+ confidence: decision.suggestion.confidence,
3272
+ autoApplyBlocker
3273
+ });
3274
+ await persistMissingBackfillProposal(ctx, durable, decision, {
3275
+ durableIds: [durable.id],
3276
+ rationale: buildMissingBackfillProposalRationale({
3277
+ originalClaimKey: decision.originalClaimKey,
3278
+ targetClaimKey: decision.targetClaimKey,
3279
+ confidence: decision.suggestion.confidence,
3280
+ autoApplyThreshold: decision.autoApplyThreshold,
3281
+ promotionLane: decision.promotionPolicy.lane,
3282
+ trusted: decision.targetIsTrusted,
3283
+ metadataBackfillClaimKey: decision.metadataBackfillClaimKey,
3284
+ compactness: decision.compactness,
3285
+ support: decision.support
3286
+ }),
3287
+ audit: {
3288
+ autoApplyBlocker,
3289
+ shadow: shadowAudit ?? void 0
3290
+ }
3291
+ });
3292
+ if (shadowAudit && ctx.options.includeShadowTelemetry === true) {
3293
+ recordSiblingSlotResonanceShadowCandidate(ctx.telemetry.siblingSlotResonanceShadowStats, decision.targetClaimKey, decision.support, shadowAudit);
3294
+ }
3295
+ recordMissingBackfillProposalLaneOutcome(ctx, decision);
3296
+ return;
3297
+ }
3298
+ case "skip_low_confidence": {
3299
+ recordMissingBackfillLowConfidenceSkip(ctx, durable, decision.suggestionRecord, decision.targetClaimKey);
3300
+ return;
3301
+ }
3302
+ case "auto_apply": {
3303
+ await applyClaimKeyRepair(
3304
+ ctx,
3305
+ durable.id,
3306
+ decision.targetClaimKey,
3307
+ {
3308
+ issueKind: "missing_claim_key",
3309
+ oldClaimKey: null,
3310
+ source: decision.targetSource,
3311
+ confidence: decision.suggestion.confidence,
3312
+ compactness: decision.compactness,
3313
+ promotion: decision.promotionPolicy,
3314
+ support: decision.support,
3315
+ rawClaimKey: decision.originalClaimKey,
3316
+ rationale: buildMissingBackfillApplyRationale({
3317
+ originalClaimKey: decision.originalClaimKey,
3318
+ targetClaimKey: decision.targetClaimKey,
3319
+ confidence: decision.suggestion.confidence,
3320
+ promotionLane: decision.promotionPolicy.lane,
3321
+ source: decision.targetSource,
3322
+ metadataBackfillClaimKey: decision.metadataBackfillClaimKey,
3323
+ compactness: decision.compactness,
3324
+ support: decision.support
3325
+ })
3326
+ },
3327
+ {
3328
+ onIdentified: () => {
3329
+ recordMissingBackfillAutoApplyIdentified(ctx);
3330
+ },
3331
+ onApplied: () => {
3332
+ recordMissingBackfillAutoApplyApplied(ctx);
3333
+ },
3334
+ onProjected: () => {
3335
+ recordMissingBackfillAutoApplyProjected(ctx, decision);
3336
+ }
3337
+ }
3338
+ );
3339
+ }
3340
+ }
3341
+ }
3342
+ async function persistMissingBackfillProposal(ctx, durable, resolved, input) {
3343
+ await persistReconcileProposal(ctx, {
3344
+ ...buildMissingBackfillPersistInput(durable, resolved, input),
3345
+ onPersisted: (passCtx) => {
3346
+ recordMissingBackfillProposalPersisted(passCtx, resolved);
3347
+ }
3348
+ });
3349
+ }
3350
+
3351
+ // src/app/dreaming/reconcile/handlers/missing-durable.ts
3352
+ async function processMissingDurable(ctx, item) {
3353
+ const { durable, inspection } = item;
3354
+ if (inspection.kind !== "missing") {
3355
+ return;
3356
+ }
3357
+ const trustedGroupReuse = findTrustedGroupReuseCandidate(ctx.workingSet.projectedDurables, ctx.workingSet.trustedReusableDurableIds, durable);
3358
+ if (trustedGroupReuse) {
3359
+ await executeTrustedGroupReuse(ctx, durable, trustedGroupReuse);
3360
+ return;
3361
+ }
3362
+ const suggestionRecord = await loadSuggestionIfContinuing(ctx, durable);
3363
+ if (!suggestionRecord) {
3364
+ return;
3365
+ }
3366
+ const decision = evaluateMissingDurablePreview(durable, suggestionRecord, ctx);
3367
+ await executeMissingDurablePreviewDecision(ctx, durable, decision);
3368
+ }
3369
+ async function executeTrustedGroupReuse(ctx, durable, trustedGroupReuse) {
3370
+ const persistedCollision = await persistProposalWhenCrossTypeCollision(ctx, trustedGroupReuse.claimKey, durable, () => ({
3371
+ ...buildTrustedGroupReusePersistInput(durable, trustedGroupReuse),
3372
+ onPersisted: (passCtx) => {
3373
+ recordMissingBackfillTrustedGroupReuseProposed(passCtx);
3374
+ }
3375
+ }));
3376
+ if (persistedCollision) {
3377
+ return;
3378
+ }
3379
+ await applyClaimKeyRepair(
3380
+ ctx,
3381
+ durable.id,
3382
+ trustedGroupReuse.claimKey,
3383
+ {
3384
+ issueKind: "missing_claim_key",
3385
+ oldClaimKey: null,
3386
+ source: "trusted_group_reuse",
3387
+ confidence: 0.99,
3388
+ rationale: `Matched subject/type durables already use trusted canonical key "${trustedGroupReuse.claimKey}", so the missing key can safely reuse that established family from ${trustedGroupReuse.supportingDurableIds.length} supporting durable${trustedGroupReuse.supportingDurableIds.length === 1 ? "" : "s"}.`
3389
+ },
3390
+ {
3391
+ onIdentified: () => {
3392
+ recordMissingBackfillAutoApplyIdentified(ctx);
3393
+ },
3394
+ onApplied: () => {
3395
+ recordMissingBackfillAutoApplyApplied(ctx);
3396
+ },
3397
+ onProjected: () => {
3398
+ recordMissingBackfillTrustedGroupReuseAutoApplied(ctx);
3399
+ }
3400
+ }
3401
+ );
3402
+ }
3403
+
3404
+ // src/app/dreaming/reconcile/handlers/mixed-group.ts
3405
+ async function processMixedKeyGroup(ctx, group) {
3406
+ const source = group.proposedClaimKey ? "mixed_group_consensus" : "mixed_group";
3407
+ await persistReconcileProposal(ctx, {
3408
+ groupId: `claim-key-mixed:${group.groupKey}`,
3409
+ issueKind: "mixed_claim_key_group",
3410
+ scope: "cluster",
3411
+ durableIds: group.durables.map((durable) => durable.id),
3412
+ currentClaimKeys: group.durables.flatMap((durable) => durable.claim_key ? [durable.claim_key] : []),
3413
+ proposedClaimKeys: group.proposedClaimKey ? [group.proposedClaimKey] : [],
3414
+ rationale: buildMixedGroupRationale(group),
3415
+ confidence: group.proposedClaimKey ? 0.8 : 0.55,
3416
+ source,
3417
+ eligibleForApply: group.proposedClaimKey !== null,
3418
+ lifecycle: {
3419
+ proposedClaimKeys: group.proposedClaimKey ? [group.proposedClaimKey] : [],
3420
+ source
3421
+ }
3422
+ });
3423
+ }
3424
+
3425
+ // src/app/dreaming/reconcile/handlers/suspect-durable.ts
3426
+ async function processSuspectDurable(ctx, item) {
3427
+ const { durable, inspection } = item;
3428
+ if (inspection.kind !== "suspect") {
3429
+ return;
3430
+ }
3431
+ const metadataRepair = resolveExplicitMetadataRepair(durable, inspection.inspection);
3432
+ if (metadataRepair && findClaimKeyOccupants(ctx.workingSet.projectedDurables, metadataRepair, durable.id).length === 0) {
3433
+ await applyClaimKeyRepair(
3434
+ ctx,
3435
+ durable.id,
3436
+ metadataRepair,
3437
+ {
3438
+ issueKind: "suspect_canonical_claim_key",
3439
+ oldClaimKey: durable.claim_key ?? null,
3440
+ source: "metadata_rewrite",
3441
+ confidence: 0.98,
3442
+ rationale: `Explicit durable metadata resolves ${describeSuspicionList(inspection.inspection)} to "${metadataRepair}".`
3443
+ },
3444
+ {
3445
+ onIdentified: () => {
3446
+ ctx.telemetry.counts.identifiedMetadataRewrites += 1;
3447
+ },
3448
+ onApplied: () => {
3449
+ ctx.telemetry.counts.appliedMetadataRewrites += 1;
3450
+ }
3451
+ }
3452
+ );
3453
+ return;
3454
+ }
3455
+ const suggestionRecord = ctx.extraction.claimExtractionConfig.eligibleTypes.includes(durable.type) ? await loadSuggestionIfContinuing(ctx, durable) : { suggestion: null, warnings: [], previewOutcome: null };
3456
+ if (!suggestionRecord) {
3457
+ return;
3458
+ }
3459
+ const proposedClaimKeys = [
3460
+ metadataRepair,
3461
+ suggestionRecord.suggestion?.claimKey && suggestionRecord.suggestion.claimKey !== durable.claim_key ? suggestionRecord.suggestion.claimKey : null
3462
+ ].filter((value) => value !== null);
3463
+ const source = metadataRepair ? "metadata_rewrite" : suggestionRecord.suggestion?.path ?? "heuristic";
3464
+ await persistReconcileProposal(ctx, {
3465
+ groupId: `claim-key-suspect:${durable.id}`,
3466
+ issueKind: "suspect_canonical_claim_key",
3467
+ scope: "single_durable",
3468
+ durableIds: [durable.id],
3469
+ currentClaimKeys: durable.claim_key ? [durable.claim_key] : [],
3470
+ proposedClaimKeys,
3471
+ rationale: buildSuspectProposalRationale(durable, inspection.inspection, metadataRepair, suggestionRecord.suggestion),
3472
+ confidence: metadataRepair ? 0.98 : suggestionRecord.suggestion?.confidence ?? 0.5,
3473
+ source,
3474
+ eligibleForApply: proposedClaimKeys.length > 0,
3475
+ lifecycle: {
3476
+ proposedClaimKeys,
3477
+ source,
3478
+ rawClaimKey: durable.claim_key_raw ?? durable.claim_key ?? null
3479
+ }
3480
+ });
3481
+ }
3482
+
3483
+ // src/app/dreaming/reconcile/helpers/observations.ts
3484
+ function buildEntityFamilyConvergenceObservation(stats) {
3485
+ if (stats.appliedClusters === 0 && stats.proposedClusters === 0) {
3486
+ return null;
3487
+ }
3488
+ return `Entity-family convergence auto-applied ${stats.appliedDurables} durable rewrite${stats.appliedDurables === 1 ? "" : "s"} across ${stats.appliedClusters} family cluster${stats.appliedClusters === 1 ? "" : "s"} and staged ${stats.proposedClusters} unresolved family proposal${stats.proposedClusters === 1 ? "" : "s"}.`;
3489
+ }
3490
+ function buildMissingDecisionObservation(stats) {
3491
+ const autoAppliedParts = [
3492
+ stats.autoAppliedTrustedGroupReuse > 0 ? `${stats.autoAppliedTrustedGroupReuse} trusted-group reuses` : null,
3493
+ stats.autoAppliedMetadataRepair > 0 ? `${stats.autoAppliedMetadataRepair} metadata-grounded backfills` : null,
3494
+ stats.autoAppliedDeterministicRepair > 0 ? `${stats.autoAppliedDeterministicRepair} deterministic repairs` : null,
3495
+ stats.autoAppliedSupportedPreview > 0 ? `${stats.autoAppliedSupportedPreview} supported preview auto-applies` : null,
3496
+ stats.autoAppliedPreviewModel > 0 ? `${stats.autoAppliedPreviewModel} high-confidence preview suggestions` : null
3497
+ ].filter((value) => value !== null);
3498
+ const proposalParts = [
3499
+ stats.proposedTrustedGroupReuse > 0 ? `${stats.proposedTrustedGroupReuse} trusted-group reuse proposals` : null,
3500
+ stats.proposedSupportedCandidate > 0 ? `${stats.proposedSupportedCandidate} supported preview proposals` : null,
3501
+ stats.proposedPreviewCandidate > 0 ? `${stats.proposedPreviewCandidate} plain preview proposals` : null
3502
+ ].filter((value) => value !== null);
3503
+ if (autoAppliedParts.length === 0 && proposalParts.length === 0) {
3504
+ return null;
3505
+ }
3506
+ return `Missing-key decisions used ${autoAppliedParts.join(", ") || "no auto-applies"} and ${proposalParts.join(", ") || "no proposals"} after structural reuse checks.`;
3507
+ }
3508
+ function buildGroundedFamilyPromotionObservation(stats) {
3509
+ const observations = [
3510
+ stats.autoAppliedGroundedFamilyPromotion > 0 || stats.proposedGroundedFamilyPromotion > 0 ? `Grounded-family promotion auto-applied ${stats.autoAppliedGroundedFamilyPromotion} candidate${stats.autoAppliedGroundedFamilyPromotion === 1 ? "" : "s"} and staged ${stats.proposedGroundedFamilyPromotion} proposal${stats.proposedGroundedFamilyPromotion === 1 ? "" : "s"}.` : null,
3511
+ stats.autoAppliedRelaxedStableSlotPromotion > 0 || stats.proposedRelaxedStableSlotPromotion > 0 ? `Relaxed stable-slot promotion auto-applied ${stats.autoAppliedRelaxedStableSlotPromotion} candidate${stats.autoAppliedRelaxedStableSlotPromotion === 1 ? "" : "s"} and staged ${stats.proposedRelaxedStableSlotPromotion} proposal${stats.proposedRelaxedStableSlotPromotion === 1 ? "" : "s"} after accepting one grounded family sibling.` : null
3512
+ ].filter((value) => value !== null);
3513
+ if (observations.length === 0) {
3514
+ return null;
3515
+ }
3516
+ return observations.join(" ");
3517
+ }
3518
+ function buildSiblingSlotResonanceObservation(stats) {
3519
+ if (stats.thresholdOnlyCandidateCount === 0) {
3520
+ return null;
3521
+ }
3522
+ const bucketSummary = SHADOW_BUCKET_ORDER.map((bucket) => {
3523
+ const bucketStats = stats.buckets.get(bucket);
3524
+ const label = describeShadowBucket(bucket);
3525
+ return `${label} ${bucketStats?.resonanceFiredCount ?? 0}/${bucketStats?.candidateCount ?? 0}`;
3526
+ }).join(", ");
3527
+ return `Shadow sibling-slot resonance fired for ${stats.resonanceFiredCount}/${stats.thresholdOnlyCandidateCount} threshold-only candidates (${bucketSummary}).`;
3528
+ }
3529
+ function buildSiblingSlotResonanceShadowRuleObservation(stats) {
3530
+ if (stats.thresholdOnlyCandidateCount === 0) {
3531
+ return null;
3532
+ }
3533
+ if (stats.shadowQualifiedCount === 0) {
3534
+ return `Shadow sibling-slot-resonance rule would have qualified 0 candidates under grounded-family counts >= ${SHADOW_RESONANCE_MIN_FAMILY_REUSE_COUNT}, grounded ratio >= ${SHADOW_RESONANCE_MIN_GROUNDED_RATIO.toFixed(2)}, confidence >= ${SHADOW_RESONANCE_MIN_CONFIDENCE.toFixed(2)}, and sibling-slot resonance.`;
3535
+ }
3536
+ return `Shadow sibling-slot-resonance rule would have qualified ${stats.shadowQualifiedCount} candidate${stats.shadowQualifiedCount === 1 ? "" : "s"}: ${stats.shadowQualifiedClaimKeys.join(", ")}.`;
3537
+ }
3538
+ function buildMissingCompactionObservation(stats) {
3539
+ if (stats.autoAppliedCompactedCandidate === 0 && stats.proposedCompactedCandidate === 0) {
3540
+ return null;
3541
+ }
3542
+ return `Compact canonicalization rewrote ${stats.autoAppliedCompactedCandidate} missing-key candidate${stats.autoAppliedCompactedCandidate === 1 ? "" : "s"} before auto-apply and ${stats.proposedCompactedCandidate} before unresolved proposal logging.`;
3543
+ }
3544
+ function buildReconcilePassObservations(ctx, input) {
3545
+ const { counts, missingDecisionStats, siblingSlotResonanceShadowStats, entityFamilyDecisionStats } = ctx.telemetry;
3546
+ const observations = [
3547
+ `Reconcile reviewed ${input.before.totalDurables} durable${input.before.totalDurables === 1 ? "" : "s"} in ${input.executionStyle} mode.`,
3548
+ `Identified ${counts.identifiedNormalizations} normalizations, ${counts.identifiedBackfills} backfills, ${counts.identifiedMetadataRewrites} metadata-backed suspect-key rewrites, and ${counts.identifiedEntityFamilyConvergences} entity-family convergence rewrites.`,
3549
+ `Emitted ${counts.proposalsEmitted} unresolved proposal${counts.proposalsEmitted === 1 ? "" : "s"}.`
3550
+ ];
3551
+ if (counts.skippedNoClaim > 0 || counts.skippedLowConfidence > 0 || counts.skippedCollision > 0) {
3552
+ observations.push(
3553
+ `Skipped ${counts.skippedNoClaim} no-claim cases, ${counts.skippedLowConfidence} low-confidence cases, and ${counts.skippedCollision} collision cases.`
3554
+ );
3555
+ }
3556
+ if (counts.flaggedAmbiguousProposals > 0) {
3557
+ observations.push(`Flagged ${counts.flaggedAmbiguousProposals} ambiguous proposal${counts.flaggedAmbiguousProposals === 1 ? "" : "s"} for later review.`);
3558
+ }
3559
+ const missingDecisionObservation = buildMissingDecisionObservation(missingDecisionStats);
3560
+ if (missingDecisionObservation) {
3561
+ observations.push(missingDecisionObservation);
3562
+ }
3563
+ const groundedFamilyObservation = buildGroundedFamilyPromotionObservation(missingDecisionStats);
3564
+ if (groundedFamilyObservation) {
3565
+ observations.push(groundedFamilyObservation);
3566
+ }
3567
+ if (ctx.options.includeShadowTelemetry === true) {
3568
+ const siblingSlotResonanceObservation = buildSiblingSlotResonanceObservation(siblingSlotResonanceShadowStats);
3569
+ if (siblingSlotResonanceObservation) {
3570
+ observations.push(siblingSlotResonanceObservation);
3571
+ }
3572
+ const siblingSlotResonanceShadowRuleObservation = buildSiblingSlotResonanceShadowRuleObservation(siblingSlotResonanceShadowStats);
3573
+ if (siblingSlotResonanceShadowRuleObservation) {
3574
+ observations.push(siblingSlotResonanceShadowRuleObservation);
3575
+ }
3576
+ }
3577
+ const missingCompactionObservation = buildMissingCompactionObservation(missingDecisionStats);
3578
+ if (missingCompactionObservation) {
3579
+ observations.push(missingCompactionObservation);
3580
+ }
3581
+ if (missingDecisionStats.noClaimWithWarnings > 0) {
3582
+ observations.push(
3583
+ `${missingDecisionStats.noClaimWithWarnings} missing-key previews ended without a safe claim after deterministic validation warnings or malformed output.`
3584
+ );
3585
+ }
3586
+ const entityFamilyObservation = buildEntityFamilyConvergenceObservation(entityFamilyDecisionStats);
3587
+ if (entityFamilyObservation) {
3588
+ observations.push(entityFamilyObservation);
3589
+ }
3590
+ return observations;
3591
+ }
3592
+ function buildReconcilePassRecommendations(ctx, actualAfter) {
3593
+ const recommendations = [];
3594
+ const circuitBreakerMessage = ctx.telemetry.circuitBreaker?.message;
3595
+ if (circuitBreakerMessage) {
3596
+ recommendations.push(circuitBreakerMessage);
3597
+ }
3598
+ if (actualAfter.exactKeyMultiActiveClusterCount > 0) {
3599
+ recommendations.push(
3600
+ `Exact-key multi-active clusters remain at ${actualAfter.exactKeyMultiActiveClusterCount}. Run supersession after reconcile to adjudicate lineage separately.`
3601
+ );
3602
+ }
3603
+ return recommendations;
3604
+ }
3605
+
3606
+ // src/app/dreaming/reconcile/helpers/partition.ts
3607
+ function partitionReconcileDurables(durables, eligibleTypes) {
3608
+ const invalidOrNoncanonical = [];
3609
+ const missing = [];
3610
+ const suspect = [];
3611
+ const inspectionTally = createEmptyClaimKeyInspectionTally();
3612
+ const inspectionById = /* @__PURE__ */ new Map();
3613
+ for (const durable of durables) {
3614
+ const inspection = inspectExistingClaimKey(durable);
3615
+ inspectionById.set(durable.id, inspection);
3616
+ tallyExistingClaimKeyInspection(inspection, durable.type, eligibleTypes, inspectionTally);
3617
+ const item = { durable, inspection };
3618
+ switch (inspection.kind) {
3619
+ case "malformed":
3620
+ case "noncanonical":
3621
+ invalidOrNoncanonical.push(item);
3622
+ break;
3623
+ case "missing":
3624
+ if (eligibleTypes.includes(durable.type)) {
3625
+ missing.push(item);
3626
+ }
3627
+ break;
3628
+ case "suspect":
3629
+ suspect.push(item);
3630
+ break;
3631
+ default:
3632
+ break;
3633
+ }
3634
+ }
3635
+ return {
3636
+ partitions: { invalidOrNoncanonical, missing, suspect },
3637
+ inspectionTally,
3638
+ inspectionById
3639
+ };
3640
+ }
3641
+
3642
+ // src/app/dreaming/reconcile/progress-tracker.ts
3643
+ function createReconcileProgressTracker(input) {
3644
+ const startedAtMs = Date.now();
3645
+ const progressIntervalMs = input.verbose ? CLAIM_KEY_PROGRESS_VERBOSE_INTERVAL_MS : CLAIM_KEY_PROGRESS_INTERVAL_MS;
3646
+ const progressEvery = input.verbose ? CLAIM_KEY_PROGRESS_EVERY_VERBOSE_DURABLES : CLAIM_KEY_PROGRESS_EVERY_DURABLES;
3647
+ let processedDurables = 0;
3648
+ let activeStage = null;
3649
+ return {
3650
+ emitHealthSnapshot(snapshot) {
3651
+ emitDreamProgress(input.reportProgress, {
3652
+ kind: "reconcile_progress",
3653
+ tier: input.tier,
3654
+ apply: input.apply,
3655
+ stage: "health",
3656
+ status: "snapshot",
3657
+ completed: 0,
3658
+ total: snapshot.totalDurables,
3659
+ unitLabel: "durables",
3660
+ processedDurables,
3661
+ totalDurables: input.totalDurables,
3662
+ counts: cloneRepairCounts(input.counts),
3663
+ elapsedMs: elapsedMs(startedAtMs),
3664
+ health: snapshot
3665
+ });
3666
+ },
3667
+ startStage(stage, total, unitLabel, options) {
3668
+ const previewTotal = normalizeOptionalNonNegativeCount(options?.previewTotal);
3669
+ activeStage = total > 0 ? {
3670
+ stage,
3671
+ total,
3672
+ completed: 0,
3673
+ unitLabel,
3674
+ previewQueued: previewTotal,
3675
+ previewCompleted: 0,
3676
+ previewTotal,
3677
+ previewConcurrency: previewTotal > 0 ? options?.previewConcurrency ?? null : null,
3678
+ lastReportedCompleted: 0,
3679
+ lastReportedPreviewCompleted: 0,
3680
+ lastReportedAtMs: Date.now()
3681
+ } : null;
3682
+ if (!activeStage) {
3683
+ return;
3684
+ }
3685
+ emitStageEvent("started");
3686
+ },
3687
+ advancePreview(count = 1) {
3688
+ if (!activeStage || activeStage.previewTotal === 0) {
3689
+ return;
3690
+ }
3691
+ activeStage.previewCompleted += count;
3692
+ if (activeStage.previewCompleted > activeStage.previewTotal) {
3693
+ activeStage.previewCompleted = activeStage.previewTotal;
3694
+ }
3695
+ const nowMs = Date.now();
3696
+ if (activeStage.previewCompleted >= activeStage.previewTotal || activeStage.previewCompleted - activeStage.lastReportedPreviewCompleted >= progressEvery || nowMs - activeStage.lastReportedAtMs >= progressIntervalMs) {
3697
+ emitStageEvent("preview_progress", nowMs);
3698
+ }
3699
+ },
3700
+ advanceStage(count = 1) {
3701
+ if (!activeStage) {
3702
+ return;
3703
+ }
3704
+ activeStage.completed += count;
3705
+ if (activeStage.unitLabel === "durables") {
3706
+ processedDurables += count;
3707
+ }
3708
+ if (activeStage.completed >= activeStage.total) {
3709
+ emitStageEvent("completed");
3710
+ activeStage = null;
3711
+ return;
3712
+ }
3713
+ const nowMs = Date.now();
3714
+ if (activeStage.completed - activeStage.lastReportedCompleted >= progressEvery || nowMs - activeStage.lastReportedAtMs >= progressIntervalMs) {
3715
+ emitStageEvent("progress", nowMs);
3716
+ }
3717
+ }
3718
+ };
3719
+ function emitStageEvent(status, nowMs = Date.now()) {
3720
+ if (!activeStage) {
3721
+ return;
3722
+ }
3723
+ activeStage.lastReportedCompleted = activeStage.completed;
3724
+ activeStage.lastReportedPreviewCompleted = activeStage.previewCompleted;
3725
+ activeStage.lastReportedAtMs = nowMs;
3726
+ emitDreamProgress(input.reportProgress, {
3727
+ kind: "reconcile_progress",
3728
+ tier: input.tier,
3729
+ apply: input.apply,
3730
+ stage: activeStage.stage,
3731
+ status,
3732
+ completed: activeStage.completed,
3733
+ total: activeStage.total,
3734
+ unitLabel: activeStage.unitLabel,
3735
+ ...activeStage.previewTotal > 0 ? {
3736
+ previewQueued: activeStage.previewQueued,
3737
+ previewCompleted: activeStage.previewCompleted,
3738
+ previewTotal: activeStage.previewTotal,
3739
+ ...activeStage.previewConcurrency !== null ? { previewConcurrency: activeStage.previewConcurrency } : {}
3740
+ } : {},
3741
+ processedDurables,
3742
+ totalDurables: input.totalDurables,
3743
+ counts: cloneRepairCounts(input.counts),
3744
+ elapsedMs: elapsedMs(startedAtMs, nowMs)
3745
+ });
3746
+ }
3747
+ }
3748
+
3749
+ // src/app/dreaming/reconcile/pass-context.ts
3750
+ async function createReconcilePassContext(options, deps) {
3751
+ const selection = {
3752
+ includeInactive: options.includeInactive === true,
3753
+ project: normalizeOptionalString(options.project) ?? null,
3754
+ type: normalizeOptionalString(options.type) ?? null,
3755
+ claimKeyPrefix: normalizeOptionalString(options.claimKeyPrefix) ?? null,
3756
+ durableIds: normalizeStringArray(options.durableIds ?? [])
3757
+ };
3758
+ const sourceDurables = await deps.port.listReconcileDurables({
3759
+ project: selection.project ?? void 0,
3760
+ type: selection.type ?? void 0,
3761
+ claimKeyPrefix: selection.claimKeyPrefix ?? void 0,
3762
+ durableIds: selection.durableIds,
3763
+ includeInactive: selection.includeInactive
3764
+ });
3765
+ const actualDurables = sourceDurables.map((durable) => cloneDurable(durable));
3766
+ const projectedDurables = sourceDurables.map((durable) => cloneDurable(durable));
3767
+ const durablesById = new Map(projectedDurables.map((durable) => [durable.id, durable]));
3768
+ const actualDurablesById = new Map(actualDurables.map((durable) => [durable.id, durable]));
3769
+ const counts = createEmptyRepairCounts();
3770
+ const claimExtractionConfig = resolveClaimExtractionConfig(deps.config ?? void 0);
3771
+ const previewConcurrency = resolveClaimExtractionConcurrency(claimExtractionConfig);
3772
+ const trustedHints = buildTrustedCleanupHintSeed(actualDurables);
3773
+ const trustedReusableDurableIds = new Set(
3774
+ sourceDurables.flatMap((durable) => {
3775
+ const claimKey = durable.claim_key?.trim();
3776
+ return claimKey && isTrustedClaimKeyForCleanup(claimKey) ? [durable.id] : [];
3777
+ })
3778
+ );
3779
+ const progressTracker = createReconcileProgressTracker({
3780
+ tier: options.tier,
3781
+ apply: options.apply,
3782
+ verbose: options.verbose,
3783
+ totalDurables: actualDurables.length,
3784
+ counts,
3785
+ reportProgress: options.reportProgress
3786
+ });
3787
+ return {
3788
+ options,
3789
+ deps,
3790
+ workingSet: {
3791
+ selection,
3792
+ projectedDurables,
3793
+ actualDurables,
3794
+ durablesById,
3795
+ actualDurablesById,
3796
+ trustedHints,
3797
+ trustedReusableDurableIds,
3798
+ handledEntityFamilyClaimKeys: /* @__PURE__ */ new Set(),
3799
+ projectedInspectionById: /* @__PURE__ */ new Map(),
3800
+ actualInspectionById: /* @__PURE__ */ new Map(),
3801
+ projectedInspectionTally: createEmptyClaimKeyInspectionTally(),
3802
+ actualInspectionTally: createEmptyClaimKeyInspectionTally()
3803
+ },
3804
+ extraction: {
3805
+ claimExtractionConfig,
3806
+ previewConcurrency,
3807
+ suggestionCache: /* @__PURE__ */ new Map(),
3808
+ claimExtractionLlms: [],
3809
+ fallbackClaimExtractionLlm: void 0
3810
+ },
3811
+ telemetry: {
3812
+ counts,
3813
+ observations: [],
3814
+ recommendations: [],
3815
+ missingDecisionStats: createEmptyMissingBackfillDecisionStats(),
3816
+ siblingSlotResonanceShadowStats: createEmptySiblingSlotResonanceShadowStats(),
3817
+ entityFamilyDecisionStats: createEmptyEntityFamilyConvergenceDecisionStats(),
3818
+ skippedDiagnostics: [],
3819
+ circuitBreakerState: createCircuitBreakerState(),
3820
+ circuitBreaker: null,
3821
+ terminalStatus: "completed",
3822
+ terminalError: null,
3823
+ actionsTaken: 0
3824
+ },
3825
+ progressTracker
3826
+ };
3827
+ }
3828
+ function getClaimExtractionUsage(ctx) {
3829
+ return claimExtractionUsage(ctx.extraction.claimExtractionLlms);
3830
+ }
3831
+
3832
+ // src/app/dreaming/reconcile/pass-stage-runner.ts
3833
+ async function runReconcileStage(ctx, options) {
3834
+ ctx.progressTracker.startStage(options.stage, options.items.length, options.unitLabel, {
3835
+ previewTotal: options.preview?.items.length ?? 0,
3836
+ previewConcurrency: options.preview?.concurrency
3837
+ });
3838
+ if (options.preload && options.preview) {
3839
+ await options.preload(ctx, options.preview.items);
3840
+ }
3841
+ for (const item of options.items) {
3842
+ if (markAbortedIfSignalled(ctx)) {
3843
+ break;
3844
+ }
3845
+ await options.process(ctx, item);
3846
+ options.afterItem?.(ctx, item);
3847
+ ctx.progressTracker.advanceStage();
3848
+ if (ctx.telemetry.terminalStatus !== "completed" || ctx.telemetry.circuitBreaker) {
3849
+ break;
3850
+ }
3851
+ }
3852
+ }
3853
+ function canContinueReconcilePass(ctx) {
3854
+ return !ctx.telemetry.circuitBreaker && ctx.telemetry.terminalStatus === "completed";
3855
+ }
3856
+
3857
+ // src/app/dreaming/reconcile/pass.ts
3858
+ async function runReconcilePass(options, deps) {
3859
+ const ctx = await createReconcilePassContext(options, deps);
3860
+ const { selection, projectedDurables, actualDurables } = ctx.workingSet;
3861
+ const executionStyle = selection.includeInactive || selection.type !== null || selection.claimKeyPrefix !== null || selection.durableIds.length > 0 ? "targeted" : "autonomous";
3862
+ const eligibleTypes = ctx.extraction.claimExtractionConfig.eligibleTypes;
3863
+ const { partitions, inspectionTally, inspectionById } = partitionReconcileDurables(projectedDurables, eligibleTypes);
3864
+ const before = buildClaimKeyHealthSnapshot(actualDurables, eligibleTypes, inspectionTally);
3865
+ ctx.workingSet.projectedInspectionById = new Map(inspectionById);
3866
+ ctx.workingSet.actualInspectionById = new Map(inspectionById);
3867
+ ctx.workingSet.projectedInspectionTally = cloneClaimKeyInspectionTally(inspectionTally);
3868
+ ctx.workingSet.actualInspectionTally = cloneClaimKeyInspectionTally(inspectionTally);
3869
+ emitDreamProgress(options.reportProgress, {
3870
+ kind: "phase",
3871
+ phase: "load_working_set_complete",
3872
+ tier: options.tier,
3873
+ apply: options.apply,
3874
+ workingSetSize: before.totalDurables
3875
+ });
3876
+ emitDreamProgress(options.reportProgress, {
3877
+ kind: "phase",
3878
+ phase: "pass_start",
3879
+ tier: options.tier,
3880
+ apply: options.apply
3881
+ });
3882
+ ctx.progressTracker.emitHealthSnapshot(before);
3883
+ const stageRunners = buildReconcileStageRunners(partitions);
3884
+ try {
3885
+ for (const runStage of stageRunners) {
3886
+ if (!canContinueReconcilePass(ctx)) {
3887
+ break;
3888
+ }
3889
+ await runStage(ctx);
3890
+ }
3891
+ } catch (error) {
3892
+ ctx.telemetry.terminalStatus = "failed";
3893
+ ctx.telemetry.terminalError = error instanceof Error ? error.message : String(error);
3894
+ }
3895
+ const actualAfter = buildClaimKeyHealthSnapshot(actualDurables, ctx.extraction.claimExtractionConfig.eligibleTypes, ctx.workingSet.actualInspectionTally);
3896
+ const projectedAfter = buildClaimKeyHealthSnapshot(
3897
+ projectedDurables,
3898
+ ctx.extraction.claimExtractionConfig.eligibleTypes,
3899
+ ctx.workingSet.projectedInspectionTally
3900
+ );
3901
+ ctx.telemetry.observations.push(...buildReconcilePassObservations(ctx, { before, executionStyle }));
3902
+ ctx.telemetry.recommendations.push(...buildReconcilePassRecommendations(ctx, actualAfter));
3903
+ const passSummary = {
3904
+ executionStyle,
3905
+ workingSet: selection,
3906
+ before,
3907
+ after: options.apply ? actualAfter : before,
3908
+ projectedAfter: options.apply ? void 0 : projectedAfter,
3909
+ counts: ctx.telemetry.counts,
3910
+ shadowSiblingSlotResonance: ctx.options.includeShadowTelemetry ? buildSiblingSlotResonanceShadowSummary(ctx.telemetry.siblingSlotResonanceShadowStats) : void 0,
3911
+ circuitBreaker: ctx.telemetry.circuitBreaker
3912
+ };
3913
+ const completion = {
3914
+ actions_taken: ctx.telemetry.actionsTaken,
3915
+ durables_skipped: ctx.telemetry.skippedDiagnostics.map((diagnostic) => ({
3916
+ durable_id: diagnostic.durableId,
3917
+ reason: formatMissingBackfillSkipDiagnostic(diagnostic)
3918
+ })),
3919
+ observations: ctx.telemetry.observations,
3920
+ recommendations: ctx.telemetry.recommendations,
3921
+ reconcile: passSummary
3922
+ };
3923
+ return {
3924
+ status: ctx.telemetry.terminalStatus,
3925
+ error: ctx.telemetry.terminalError,
3926
+ completion,
3927
+ durablesStaled: 0,
3928
+ usage: getClaimExtractionUsage(ctx)
3929
+ };
3930
+ }
3931
+ function buildReconcileStageRunners(partitions) {
3932
+ const durableStageDefs = [
3933
+ {
3934
+ stage: "invalid_noncanonical",
3935
+ items: partitions.invalidOrNoncanonical,
3936
+ process: processInvalidOrNoncanonicalDurable
3937
+ },
3938
+ {
3939
+ stage: "missing",
3940
+ items: partitions.missing,
3941
+ process: processMissingDurable,
3942
+ previewFilter: shouldPreviewMissingDurable
3943
+ },
3944
+ {
3945
+ stage: "suspect_canonical",
3946
+ items: partitions.suspect,
3947
+ process: processSuspectDurable,
3948
+ previewFilter: shouldPreloadSuspectSuggestion
3949
+ }
3950
+ ];
3951
+ return [
3952
+ ...durableStageDefs.map(buildInspectedDurableStageRunner),
3953
+ (ctx) => runReconcileStage(ctx, {
3954
+ stage: "entity_family_convergence",
3955
+ items: detectClaimKeyEntityFamilyCandidates(ctx.workingSet.projectedDurables),
3956
+ unitLabel: "groups",
3957
+ process: processEntityFamilyConvergenceCandidate,
3958
+ afterItem: (passCtx, candidate) => {
3959
+ for (const claimKey of candidate.claimKeys) {
3960
+ passCtx.workingSet.handledEntityFamilyClaimKeys.add(claimKey);
3961
+ }
3962
+ }
3963
+ }),
3964
+ (ctx) => runReconcileStage(ctx, {
3965
+ stage: "mixed_key_groups",
3966
+ items: findMixedKeyGroups(ctx.workingSet.projectedDurables, ctx.workingSet.handledEntityFamilyClaimKeys),
3967
+ unitLabel: "groups",
3968
+ process: processMixedKeyGroup
3969
+ })
3970
+ ];
3971
+ }
3972
+ function buildInspectedDurableStageRunner(def) {
3973
+ return (ctx) => {
3974
+ const preloadEnabled = shouldPreloadSuggestions(ctx);
3975
+ const previewItems = def.previewFilter && preloadEnabled ? def.items.filter((item) => def.previewFilter(ctx, item)) : [];
3976
+ return runReconcileStage(ctx, {
3977
+ stage: def.stage,
3978
+ items: def.items,
3979
+ unitLabel: "durables",
3980
+ preview: def.previewFilter && preloadEnabled ? {
3981
+ items: previewItems,
3982
+ concurrency: previewItems.length > 0 ? ctx.extraction.previewConcurrency : void 0
3983
+ } : void 0,
3984
+ preload: def.previewFilter ? (ctx2, items) => preloadSuggestionsForStage(
3985
+ ctx2,
3986
+ items.map((item) => item.durable)
3987
+ ) : void 0,
3988
+ process: def.process
3989
+ });
3990
+ };
3991
+ }
3992
+
3993
+ // src/app/dreaming/temporalize.ts
3994
+ import { randomUUID as randomUUID7 } from "crypto";
3995
+ async function runTemporalizeStage(options, deps) {
3996
+ const refineCandidates = options.candidates.filter(
3997
+ (candidate) => candidate.disposition === "refines" && candidate.refinesDurableId !== null
3998
+ );
3999
+ let revisionsIdentified = 0;
4000
+ let revisionsApplied = 0;
4001
+ let revisionsSkipped = 0;
4002
+ for (const candidate of refineCandidates) {
4003
+ throwIfAborted(options.signal);
4004
+ const predecessor = await deps.port.getDurable(candidate.refinesDurableId);
4005
+ if (!predecessor) {
4006
+ revisionsSkipped += 1;
4007
+ continue;
4008
+ }
4009
+ const rules = validateSupersessionRules({ type: predecessor.type, expiry: predecessor.expiry }, { type: candidate.type, expiry: candidate.expiry });
4010
+ if (!rules.ok) {
4011
+ revisionsSkipped += 1;
4012
+ continue;
4013
+ }
4014
+ revisionsIdentified += 1;
4015
+ if (!options.apply) {
4016
+ continue;
4017
+ }
4018
+ await applyRevision(candidate, predecessor, options, deps);
4019
+ revisionsApplied += 1;
4020
+ }
4021
+ return {
4022
+ summary: {
4023
+ revisionsIdentified,
4024
+ revisionsApplied,
4025
+ revisionsSkipped
4026
+ }
4027
+ };
4028
+ }
4029
+ async function applyRevision(candidate, predecessor, options, deps) {
4030
+ const nowIso = options.now().toISOString();
4031
+ const successorId = randomUUID7();
4032
+ const successor = buildDurableFromCandidate(candidate, {
4033
+ id: successorId,
4034
+ claimKeySource: "dreaming_temporalize",
4035
+ claimKeyStatus: predecessor.claim_key_status ?? "tentative",
4036
+ claimKeyConfidence: predecessor.claim_key_confidence ?? 0.6,
4037
+ claimKeyRationale: `Temporal revision superseding durable ${predecessor.id} from episode evidence.`,
4038
+ claimKeyOverride: predecessor.claim_key ?? candidate.claimKey,
4039
+ createdAt: nowIso,
4040
+ validFrom: nowIso
4041
+ });
4042
+ successor.expiry = predecessor.expiry;
4043
+ const contentHash = successor.content_hash ?? computeContentHash(successor.content, successor.source_file);
4044
+ const [successorEmbedding = []] = await deps.embedding.embed([composeEmbeddingText(successor)]);
4045
+ await deps.port.withTransaction(async (tx) => {
4046
+ await tx.insertDurable(successor, successorEmbedding, contentHash);
4047
+ await tx.supersedeDurable(predecessor.id, successorId, "update", "Temporal revision from dreaming evidence.");
4048
+ if (canCloseValidityWindow(predecessor.valid_from, nowIso)) {
4049
+ await tx.updateDurable(predecessor.id, { valid_to: nowIso }, { includeInactive: true });
4050
+ }
4051
+ await tx.logRunAction({
4052
+ id: randomUUID7(),
4053
+ runId: options.runId,
4054
+ actionType: "supersede_durable",
4055
+ durableIds: [predecessor.id, successorId],
4056
+ reasoning: `Superseded durable ${predecessor.id} with temporal revision ${successorId}.`,
4057
+ details: {
4058
+ claim_key: successor.claim_key ?? null,
4059
+ predecessor_id: predecessor.id,
4060
+ successor_id: successorId,
4061
+ valid_to: nowIso
4062
+ },
4063
+ createdAt: nowIso
4064
+ });
4065
+ });
4066
+ }
4067
+ function canCloseValidityWindow(validFrom, nowIso) {
4068
+ if (!validFrom) {
4069
+ return true;
4070
+ }
4071
+ const fromMs = Date.parse(validFrom);
4072
+ const nowMs = Date.parse(nowIso);
4073
+ if (Number.isNaN(fromMs) || Number.isNaN(nowMs)) {
4074
+ return true;
4075
+ }
4076
+ return fromMs < nowMs;
4077
+ }
4078
+
4079
+ // src/app/dreaming/service.ts
4080
+ async function runDream(options, deps) {
4081
+ throwIfAborted(options.signal);
4082
+ return withDreamingRunLock(deps.port, deps.dbPath, (lease) => runDreamWithHeldLock(options, deps, lease));
4083
+ }
4084
+ async function runDreamWithHeldLock(options, deps, lease) {
4085
+ throwIfAborted(options.signal);
4086
+ await lease.heartbeat();
4087
+ return executeDreamRun(options, deps);
4088
+ }
4089
+ async function executeDreamRun(options, deps) {
4090
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
4091
+ assertDreamTierEnabled(options.tier, deps.config);
4092
+ const dailyCost = await deps.port.getDailyCost(now());
4093
+ const dailyCap = deps.config?.dreaming?.dailyCostCap ?? DEFAULT_DREAMING_DAILY_COST_CAP;
4094
+ if (dailyCost >= dailyCap) {
4095
+ throw new Error(`Daily dreaming cost cap reached (${dailyCost.toFixed(2)} / ${dailyCap.toFixed(2)} USD).`);
4096
+ }
4097
+ const remainingDailyBudgetUsd = Math.max(0, dailyCap - dailyCost);
4098
+ const runId = await deps.port.createRun({
4099
+ tier: options.tier,
4100
+ project: options.project,
4101
+ dryRun: !options.apply,
4102
+ config: {
4103
+ tier: options.tier,
4104
+ project: options.project ?? null,
4105
+ type: options.type ?? null,
4106
+ claimKeyPrefix: options.claimKeyPrefix ?? null,
4107
+ durableIds: options.durableIds ?? [],
4108
+ includeInactive: options.includeInactive ?? false
4109
+ }
4110
+ });
4111
+ emitDreamProgress(deps.reportProgress, {
4112
+ kind: "phase",
4113
+ phase: "start",
4114
+ tier: options.tier,
4115
+ apply: options.apply
4116
+ });
4117
+ if (options.apply && !options.skipBackup && deps.dbPath && deps.dbPath !== ":memory:" && deps.backupDb) {
4118
+ emitDreamProgress(deps.reportProgress, { kind: "phase", phase: "backup_start", tier: options.tier, apply: options.apply });
4119
+ const backupPath = await deps.backupDb(deps.dbPath);
4120
+ emitDreamProgress(deps.reportProgress, {
4121
+ kind: "phase",
4122
+ phase: "backup_complete",
4123
+ tier: options.tier,
4124
+ apply: options.apply,
4125
+ backupPath
4126
+ });
4127
+ }
4128
+ const extractStages = resolveExtractStageConfig(deps.config, options.tier);
4129
+ const stagePlan = resolveTierStages(options.tier);
4130
+ const createExtractLlm = deps.createExtractLlm ?? deps.createClaimExtractionLlm;
4131
+ const embedding = resolveDreamEmbedding(deps.embedding);
4132
+ let scanSummary;
4133
+ let extractSummary;
4134
+ let reconcileSummary;
4135
+ let temporalizeSummary;
4136
+ let projectSummary;
4137
+ let pruneSummary;
4138
+ let efficiencySummary;
4139
+ const stagesSkipped = [];
4140
+ let durablesSkipped = [];
4141
+ let observations = [];
4142
+ let recommendations = [];
4143
+ let inputTokens = 0;
4144
+ let outputTokens = 0;
4145
+ let estimatedCostUsd = 0;
4146
+ let actionsTaken = 0;
4147
+ let actionsSkipped = 0;
4148
+ let durablesStaled = 0;
4149
+ let reconcileStatus;
4150
+ let reconcileError;
4151
+ try {
4152
+ const fullBacklog = options.tier === "deep";
4153
+ const scan = await runDreamScan({ project: options.project, fullBacklog, now }, { port: deps.port });
4154
+ scanSummary = scan;
4155
+ const extract = await runExtractStage(
4156
+ {
4157
+ now,
4158
+ ...options.project ? { project: options.project } : {},
4159
+ fullBacklog,
4160
+ maxEpisodes: extractStages.maxEpisodes,
4161
+ contextLookupEnabled: extractStages.contextLookupEnabled,
4162
+ costCapUsd: remainingDailyBudgetUsd,
4163
+ ...options.signal ? { signal: options.signal } : {}
4164
+ },
4165
+ {
4166
+ port: deps.port,
4167
+ ...createExtractLlm ? { createExtractLlm } : {}
4168
+ }
4169
+ );
4170
+ let durablesInserted = 0;
4171
+ if (options.apply) {
4172
+ const applied = await applyExtractedDurables({ runId, candidates: extract.candidates, now }, { port: deps.port, embedding });
4173
+ durablesInserted = applied.inserted;
4174
+ }
4175
+ extractSummary = { ...extract.summary, durablesInserted };
4176
+ actionsTaken = durablesInserted;
4177
+ inputTokens = extract.usage.inputTokens;
4178
+ outputTokens = extract.usage.outputTokens;
4179
+ estimatedCostUsd = extract.usage.estimatedCostUsd;
4180
+ if (stagePlan.runReconcile) {
4181
+ const reconcile = await runReconcilePass(
4182
+ {
4183
+ runId,
4184
+ tier: options.tier,
4185
+ apply: options.apply,
4186
+ project: options.project,
4187
+ type: options.type,
4188
+ claimKeyPrefix: options.claimKeyPrefix,
4189
+ durableIds: options.durableIds,
4190
+ includeInactive: options.includeInactive,
4191
+ signal: options.signal,
4192
+ now,
4193
+ costCapUsd: Math.max(0, remainingDailyBudgetUsd - extract.usage.estimatedCostUsd),
4194
+ verbose: options.verbose,
4195
+ includeShadowTelemetry: options.includeShadowTelemetry === true,
4196
+ reportProgress: deps.reportProgress
4197
+ },
4198
+ {
4199
+ port: deps.port,
4200
+ config: deps.config,
4201
+ ...deps.createClaimExtractionLlm ? { createClaimExtractionLlm: deps.createClaimExtractionLlm } : {}
4202
+ }
4203
+ );
4204
+ observations = [...reconcile.completion.observations];
4205
+ actionsTaken = reconcile.completion.actions_taken + durablesInserted;
4206
+ actionsSkipped = reconcile.completion.durables_skipped.length;
4207
+ durablesSkipped = reconcile.completion.durables_skipped;
4208
+ recommendations = reconcile.completion.recommendations;
4209
+ reconcileSummary = reconcile.completion.reconcile;
4210
+ durablesStaled = reconcile.durablesStaled;
4211
+ inputTokens = reconcile.usage.inputTokens + extract.usage.inputTokens;
4212
+ outputTokens = reconcile.usage.outputTokens + extract.usage.outputTokens;
4213
+ estimatedCostUsd = reconcile.usage.estimatedCostUsd + extract.usage.estimatedCostUsd;
4214
+ reconcileStatus = reconcile.status;
4215
+ reconcileError = reconcile.error ?? void 0;
4216
+ } else {
4217
+ stagesSkipped.push({ stage: "reconcile", reason: "light_tier" });
4218
+ observations.push("Reconcile stage skipped for light tier.");
4219
+ actionsTaken = durablesInserted;
4220
+ reconcileStatus = "completed";
4221
+ }
4222
+ if (extract.status === "cost_capped") {
4223
+ observations.push("Extract stage stopped early after reaching the daily dreaming cost cap.");
4224
+ }
4225
+ const temporalize = await runTemporalizeStage(
4226
+ {
4227
+ runId,
4228
+ candidates: extract.candidates,
4229
+ apply: options.apply,
4230
+ now,
4231
+ ...options.signal ? { signal: options.signal } : {}
4232
+ },
4233
+ { port: deps.port, embedding }
4234
+ );
4235
+ temporalizeSummary = temporalize.summary;
4236
+ actionsTaken += temporalize.summary.revisionsApplied;
4237
+ const projectStage = await runProjectStage(
4238
+ {
4239
+ runId,
4240
+ now,
4241
+ ...options.project ? { project: options.project } : {},
4242
+ maxProfileDurables: resolveProjectStageConfig(deps.config).maxProfileDurables
4243
+ },
4244
+ { port: deps.port }
4245
+ );
4246
+ const status = extract.status === "cost_capped" && reconcileStatus === "completed" ? "cost_capped" : reconcileStatus;
4247
+ const activeProfileSnapshot = status === "completed" && options.apply && !options.project ? projectStage.snapshot : null;
4248
+ projectSummary = {
4249
+ ...projectStage.summary,
4250
+ snapshotId: activeProfileSnapshot?.id ?? null,
4251
+ applied: activeProfileSnapshot !== null
4252
+ };
4253
+ if (stagePlan.runPrune) {
4254
+ const pruneConfig = resolvePruneStageConfig(deps.config);
4255
+ pruneSummary = await runPruneStage(
4256
+ {
4257
+ runId,
4258
+ apply: options.apply,
4259
+ now,
4260
+ ...options.project ? { project: options.project } : {},
4261
+ protectedDurableIds: projectStage.snapshot ? [...projectStage.snapshot.durableIds, ...projectStage.snapshot.directiveIds] : [],
4262
+ protectRecalledDays: pruneConfig.protectRecalledDays,
4263
+ protectMinImportance: pruneConfig.protectMinImportance
4264
+ },
4265
+ { port: deps.port }
4266
+ );
4267
+ actionsTaken += pruneSummary.durablesStaled;
4268
+ actionsSkipped += pruneSummary.candidatesProtected + (options.apply ? 0 : pruneSummary.candidatesRetirable);
4269
+ durablesStaled += pruneSummary.durablesStaled;
4270
+ } else {
4271
+ stagesSkipped.push({ stage: "prune", reason: "light_tier" });
4272
+ }
4273
+ efficiencySummary = buildDreamEfficiencySummary({
4274
+ scan,
4275
+ estimatedCostUsd,
4276
+ synthesizedDurableMutations: extractSummary.durablesInserted + temporalizeSummary.revisionsApplied + (pruneSummary?.durablesStaled ?? 0),
4277
+ profileDurableCount: projectSummary.profileDurableCount,
4278
+ directiveCount: projectSummary.directiveCount
4279
+ });
4280
+ const completionSummary = {
4281
+ actions_taken: actionsTaken,
4282
+ ...resolveBackupSkipped(options) ? { backupSkipped: true } : {},
4283
+ ...stagesSkipped.length > 0 ? { stages_skipped: stagesSkipped } : {},
4284
+ durables_skipped: durablesSkipped,
4285
+ observations,
4286
+ recommendations,
4287
+ scan,
4288
+ extract: extractSummary,
4289
+ ...reconcileSummary ? { reconcile: reconcileSummary } : {},
4290
+ temporalize: temporalizeSummary,
4291
+ project: projectSummary,
4292
+ ...pruneSummary ? { prune: pruneSummary } : {},
4293
+ efficiency: efficiencySummary
4294
+ };
4295
+ const runCompletion = {
4296
+ status,
4297
+ inputTokens,
4298
+ outputTokens,
4299
+ estimatedCostUsd,
4300
+ actionsTaken,
4301
+ actionsSkipped,
4302
+ durablesStaled,
4303
+ summaryJson: completionSummary,
4304
+ error: reconcileError
4305
+ };
4306
+ const dreamStateUpdate = {
4307
+ unsynthesizedImportanceSum: status === "completed" ? 0 : scan.unsynthesizedImportanceSum,
4308
+ ...status === "completed" ? { lastSuccessfulRunAt: now().toISOString() } : {},
4309
+ ...activeProfileSnapshot ? { activeProfileSnapshotId: activeProfileSnapshot.id } : {},
4310
+ updatedAt: now().toISOString()
4311
+ };
4312
+ if (activeProfileSnapshot) {
4313
+ await deps.port.withTransaction(async (tx) => {
4314
+ await tx.createProfileSnapshot(activeProfileSnapshot);
4315
+ await tx.completeRun(runId, runCompletion);
4316
+ await tx.updateDreamState(dreamStateUpdate);
4317
+ });
4318
+ } else {
4319
+ await deps.port.completeRun(runId, runCompletion);
4320
+ await deps.port.updateDreamState(dreamStateUpdate);
4321
+ }
4322
+ return {
4323
+ runId,
4324
+ status,
4325
+ tier: options.tier,
4326
+ actionsTaken,
4327
+ actionsSkipped,
4328
+ durablesStaled,
4329
+ inputTokens,
4330
+ outputTokens,
4331
+ estimatedCostUsd,
4332
+ summary: reconcileError ?? (status === "completed" ? "Dreaming run completed." : null),
4333
+ completionSummary
4334
+ };
4335
+ } catch (error) {
4336
+ const message = error instanceof Error ? error.message : String(error);
4337
+ const status = message === USER_ABORT_ERROR ? "aborted" : "failed";
4338
+ const partialActions = await loadPartialRunActions(deps.port, runId);
4339
+ actionsTaken = Math.max(actionsTaken, partialActions);
4340
+ const failureObservations = [...observations, `Dreaming run stopped before completion: ${message}`];
4341
+ const failureSummary = {
4342
+ actions_taken: actionsTaken,
4343
+ ...resolveBackupSkipped(options) ? { backupSkipped: true } : {},
4344
+ ...stagesSkipped.length > 0 ? { stages_skipped: stagesSkipped } : {},
4345
+ durables_skipped: durablesSkipped,
4346
+ observations: failureObservations,
4347
+ recommendations,
4348
+ ...scanSummary ? { scan: scanSummary } : {},
4349
+ ...extractSummary ? { extract: extractSummary } : {},
4350
+ ...reconcileSummary ? { reconcile: reconcileSummary } : {},
4351
+ ...temporalizeSummary ? { temporalize: temporalizeSummary } : {},
4352
+ ...projectSummary ? { project: projectSummary } : {},
4353
+ ...pruneSummary ? { prune: pruneSummary } : {},
4354
+ ...efficiencySummary ? { efficiency: efficiencySummary } : {}
4355
+ };
4356
+ await deps.port.completeRun(runId, {
4357
+ status,
4358
+ inputTokens,
4359
+ outputTokens,
4360
+ estimatedCostUsd,
4361
+ actionsTaken,
4362
+ actionsSkipped,
4363
+ durablesStaled,
4364
+ summaryJson: failureSummary,
4365
+ error: message
4366
+ });
4367
+ throw error;
4368
+ }
4369
+ }
4370
+ function assertDreamTierEnabled(tier, config) {
4371
+ if (config?.dreaming?.tiers?.[tier]?.enabled === false) {
4372
+ throw new Error(`Dreaming tier "${tier}" is disabled in config.`);
4373
+ }
4374
+ }
4375
+ function resolveBackupSkipped(options) {
4376
+ return options.apply === true && options.skipBackup === true;
4377
+ }
4378
+ function resolveProjectStageConfig(config) {
4379
+ return {
4380
+ maxProfileDurables: config?.dreaming?.stages?.project?.maxProfileDurables
4381
+ };
4382
+ }
4383
+ function resolvePruneStageConfig(config) {
4384
+ const prune = config?.dreaming?.stages?.prune;
4385
+ return {
4386
+ protectRecalledDays: typeof prune?.protectRecalledDays === "number" && prune.protectRecalledDays >= 0 ? prune.protectRecalledDays : DEFAULT_DREAMING_PRUNE_PROTECT_RECALLED_DAYS,
4387
+ protectMinImportance: typeof prune?.protectMinImportance === "number" && prune.protectMinImportance >= 0 ? prune.protectMinImportance : DEFAULT_DREAMING_PRUNE_PROTECT_MIN_IMPORTANCE
4388
+ };
4389
+ }
4390
+ async function backupDatabaseFile(dbPath) {
4391
+ const sourcePath = resolveLocalFilesystemPath(dbPath);
4392
+ if (!sourcePath) {
4393
+ throw new Error(`Cannot back up non-file database path: ${dbPath}.`);
4394
+ }
4395
+ const backupDir = path.join(path.dirname(sourcePath), "backups");
4396
+ await mkdir(backupDir, { recursive: true });
4397
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4398
+ const backupPath = path.join(backupDir, `knowledge-${stamp}.db`);
4399
+ await copyFile(sourcePath, backupPath);
4400
+ for (const suffix of ["-wal", "-shm"]) {
4401
+ await copySidecarIfPresent(`${sourcePath}${suffix}`, `${backupPath}${suffix}`);
4402
+ }
4403
+ return backupPath;
4404
+ }
4405
+ function resolveExtractStageConfig(config, tier) {
4406
+ const extract = config?.dreaming?.stages?.extract;
4407
+ const defaultMaxSessions = tier === "light" ? DEFAULT_DREAMING_LIGHT_MAX_SESSIONS : DEFAULT_DREAMING_EXTRACT_MAX_SESSIONS;
4408
+ const configuredMaxSessions = tier === "light" ? extract?.lightMaxSessionsPerRun : extract?.maxSessionsPerRun;
4409
+ const maxEpisodes = typeof configuredMaxSessions === "number" && configuredMaxSessions > 0 ? configuredMaxSessions : defaultMaxSessions;
4410
+ const contextLookupEnabled = extract?.contextLookup?.enabled ?? true;
4411
+ return { maxEpisodes, contextLookupEnabled };
4412
+ }
4413
+ function resolveDreamEmbedding(embedding) {
4414
+ if (embedding) {
4415
+ return embedding;
4416
+ }
4417
+ return {
4418
+ embed: async (texts) => {
4419
+ if (texts.length === 0) {
4420
+ return [];
4421
+ }
4422
+ throw new Error("Dreaming apply requires an embedding provider, but none was configured.");
4423
+ }
4424
+ };
4425
+ }
4426
+ async function copySidecarIfPresent(sourcePath, targetPath) {
4427
+ try {
4428
+ await copyFile(sourcePath, targetPath);
4429
+ } catch (error) {
4430
+ if (isMissingFileError(error)) {
4431
+ return;
4432
+ }
4433
+ throw error;
4434
+ }
4435
+ }
4436
+ async function loadPartialRunActions(port, runId) {
4437
+ try {
4438
+ return (await port.getRunActions(runId)).length;
4439
+ } catch {
4440
+ return 0;
4441
+ }
4442
+ }
4443
+ function isMissingFileError(error) {
4444
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
4445
+ }
4446
+
4447
+ export {
4448
+ parseExtractionResponse,
4449
+ runDream,
4450
+ runDreamWithHeldLock,
4451
+ backupDatabaseFile
4452
+ };