abmind 0.1.2 → 0.1.4

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 (145) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/abmind-backup.js +10 -10
  3. package/dist/cli/abmind-backup.js.map +1 -1
  4. package/dist/cli/abmind-hook-postToolUse.d.ts +10 -0
  5. package/dist/cli/abmind-hook-postToolUse.d.ts.map +1 -0
  6. package/dist/cli/abmind-hook-postToolUse.js +85 -0
  7. package/dist/cli/abmind-hook-postToolUse.js.map +1 -0
  8. package/dist/cli/abmind-hook-preToolUse.d.ts +10 -0
  9. package/dist/cli/abmind-hook-preToolUse.d.ts.map +1 -0
  10. package/dist/cli/abmind-hook-preToolUse.js +61 -0
  11. package/dist/cli/abmind-hook-preToolUse.js.map +1 -0
  12. package/dist/cli/abmind-hook-store.js +26 -3
  13. package/dist/cli/abmind-hook-store.js.map +1 -1
  14. package/dist/cli/abmind-hook-wakeup.js +46 -0
  15. package/dist/cli/abmind-hook-wakeup.js.map +1 -1
  16. package/dist/cli/abmind-key.d.ts +5 -0
  17. package/dist/cli/abmind-key.d.ts.map +1 -0
  18. package/dist/cli/abmind-key.js +214 -0
  19. package/dist/cli/abmind-key.js.map +1 -0
  20. package/dist/cli/abmind-retro-extract.js +41 -5
  21. package/dist/cli/abmind-retro-extract.js.map +1 -1
  22. package/dist/cli/abmind-sleep-state.js +1 -1
  23. package/dist/cli/abmind-sleep-state.js.map +1 -1
  24. package/dist/cli/abmind-update.js +17 -1
  25. package/dist/cli/abmind-update.js.map +1 -1
  26. package/dist/cli/abmind.js +2 -0
  27. package/dist/cli/abmind.js.map +1 -1
  28. package/dist/src/backup.d.ts +3 -1
  29. package/dist/src/backup.d.ts.map +1 -1
  30. package/dist/src/backup.js +11 -4
  31. package/dist/src/backup.js.map +1 -1
  32. package/dist/src/context-engine.d.ts +1 -1
  33. package/dist/src/context-engine.d.ts.map +1 -1
  34. package/dist/src/context-engine.js +1 -1
  35. package/dist/src/context-engine.js.map +1 -1
  36. package/dist/src/context-orchestrator.js +3 -3
  37. package/dist/src/context-orchestrator.js.map +1 -1
  38. package/dist/src/crypto.d.ts +6 -0
  39. package/dist/src/crypto.d.ts.map +1 -1
  40. package/dist/src/crypto.js +103 -17
  41. package/dist/src/crypto.js.map +1 -1
  42. package/dist/src/ensure-initialized.d.ts +1 -1
  43. package/dist/src/ensure-initialized.js +1 -1
  44. package/dist/src/env-schema.d.ts +2 -0
  45. package/dist/src/env-schema.d.ts.map +1 -1
  46. package/dist/src/env-schema.js +3 -1
  47. package/dist/src/env-schema.js.map +1 -1
  48. package/dist/src/index.d.ts +5 -2
  49. package/dist/src/index.d.ts.map +1 -1
  50. package/dist/src/index.js +5 -2
  51. package/dist/src/index.js.map +1 -1
  52. package/dist/src/keyring.d.ts +9 -0
  53. package/dist/src/keyring.d.ts.map +1 -0
  54. package/dist/src/keyring.js +60 -0
  55. package/dist/src/keyring.js.map +1 -0
  56. package/dist/src/mem-types.d.ts +1 -1
  57. package/dist/src/mem-types.d.ts.map +1 -1
  58. package/dist/src/memory-backend.d.ts +3 -0
  59. package/dist/src/memory-backend.d.ts.map +1 -1
  60. package/dist/src/memory-config.js +1 -1
  61. package/dist/src/memory-config.js.map +1 -1
  62. package/dist/src/memory-db.d.ts +1 -6
  63. package/dist/src/memory-db.d.ts.map +1 -1
  64. package/dist/src/memory-db.js +7 -1
  65. package/dist/src/memory-db.js.map +1 -1
  66. package/dist/src/memory-editor.d.ts +1 -0
  67. package/dist/src/memory-editor.d.ts.map +1 -1
  68. package/dist/src/memory-editor.js +17 -16
  69. package/dist/src/memory-editor.js.map +1 -1
  70. package/dist/src/memory-ipc-client.d.ts +3 -0
  71. package/dist/src/memory-ipc-client.d.ts.map +1 -1
  72. package/dist/src/memory-ipc-client.js +4 -0
  73. package/dist/src/memory-ipc-client.js.map +1 -1
  74. package/dist/src/message-store.js +1 -1
  75. package/dist/src/message-store.js.map +1 -1
  76. package/dist/src/openclaw-plugin/index.d.ts.map +1 -1
  77. package/dist/src/openclaw-plugin/index.js +30 -0
  78. package/dist/src/openclaw-plugin/index.js.map +1 -1
  79. package/dist/src/query-tokenizer.d.ts.map +1 -1
  80. package/dist/src/query-tokenizer.js +3 -2
  81. package/dist/src/query-tokenizer.js.map +1 -1
  82. package/dist/src/recall-benchmark.js +4 -2
  83. package/dist/src/recall-benchmark.js.map +1 -1
  84. package/dist/src/redact-secrets.d.ts.map +1 -1
  85. package/dist/src/redact-secrets.js +3 -0
  86. package/dist/src/redact-secrets.js.map +1 -1
  87. package/dist/src/session-context.d.ts +6 -5
  88. package/dist/src/session-context.d.ts.map +1 -1
  89. package/dist/src/session-context.js +134 -66
  90. package/dist/src/session-context.js.map +1 -1
  91. package/dist/src/sleep/levels.d.ts +7 -6
  92. package/dist/src/sleep/levels.d.ts.map +1 -1
  93. package/dist/src/sleep/levels.js.map +1 -1
  94. package/dist/src/sleep/orchestrator.d.ts +1 -1
  95. package/dist/src/sleep/orchestrator.d.ts.map +1 -1
  96. package/dist/src/sleep/orchestrator.js +96 -38
  97. package/dist/src/sleep/orchestrator.js.map +1 -1
  98. package/dist/src/sleep/sleep-daily-summary.d.ts +6 -2
  99. package/dist/src/sleep/sleep-daily-summary.d.ts.map +1 -1
  100. package/dist/src/sleep/sleep-daily-summary.js +34 -12
  101. package/dist/src/sleep/sleep-daily-summary.js.map +1 -1
  102. package/dist/src/sleep/sleep-extract-daily.d.ts.map +1 -1
  103. package/dist/src/sleep/sleep-extract-daily.js +3 -0
  104. package/dist/src/sleep/sleep-extract-daily.js.map +1 -1
  105. package/dist/src/sleep/sleep-prompt-loader.d.ts +3 -3
  106. package/dist/src/sleep/sleep-prompt-loader.d.ts.map +1 -1
  107. package/dist/src/sleep/sleep-prompt-loader.js +20 -9
  108. package/dist/src/sleep/sleep-prompt-loader.js.map +1 -1
  109. package/dist/src/sleep/test-harness.d.ts.map +1 -1
  110. package/dist/src/sleep/test-harness.js +3 -2
  111. package/dist/src/sleep/test-harness.js.map +1 -1
  112. package/dist/src/sleep-data-access.d.ts +4 -2
  113. package/dist/src/sleep-data-access.d.ts.map +1 -1
  114. package/dist/src/sleep-data-access.js +49 -13
  115. package/dist/src/sleep-data-access.js.map +1 -1
  116. package/dist/src/sqlite-backend.d.ts +3 -0
  117. package/dist/src/sqlite-backend.d.ts.map +1 -1
  118. package/dist/src/sqlite-backend.js +3 -0
  119. package/dist/src/sqlite-backend.js.map +1 -1
  120. package/dist/src/status-block.d.ts +7 -0
  121. package/dist/src/status-block.d.ts.map +1 -0
  122. package/dist/src/status-block.js +67 -0
  123. package/dist/src/status-block.js.map +1 -0
  124. package/dist/src/wake-up-renderer.d.ts +0 -5
  125. package/dist/src/wake-up-renderer.d.ts.map +1 -1
  126. package/dist/src/wake-up-renderer.js +0 -34
  127. package/dist/src/wake-up-renderer.js.map +1 -1
  128. package/package.json +4 -1
  129. package/prompts/sleep/03-retrospective.md +23 -0
  130. package/prompts/sleep/04-extract-memories.md +3 -0
  131. package/prompts/sleep/06-retro-derive.md +39 -0
  132. package/prompts/sleep/08-memory-maintenance.md +39 -0
  133. package/prompts/sleep/{13-consolidation.md → 11-consolidation.md} +4 -1
  134. package/prompts/sleep/03-extract-from-daily.md +0 -3
  135. package/prompts/sleep/04-retrospective.md +0 -30
  136. package/prompts/sleep/07-topic-assignment.md +0 -23
  137. package/prompts/sleep/08-core-promotion.md +0 -26
  138. package/prompts/sleep/09-merge.md +0 -19
  139. package/prompts/sleep/12-core-knowledge.md +0 -14
  140. package/prompts/sleep/14-emotion-context.md +0 -17
  141. /package/prompts/sleep/{15-contradiction-and-graph.md → 05-contradiction-and-graph.md} +0 -0
  142. /package/prompts/sleep/{06-feedback.md → 07-feedback.md} +0 -0
  143. /package/prompts/sleep/{10-translation.md → 09-translation.md} +0 -0
  144. /package/prompts/sleep/{11-skill-review.md → 10-skill-review.md} +0 -0
  145. /package/prompts/sleep/{16-rem-synthesis.md → 12-rem-synthesis.md} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"redact-secrets.js","sourceRoot":"","sources":["../../src/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAoC;IAC9D,CAAC,wBAAwB,EAAE,mBAAmB,CAAC;IAC/C,CAAC,uBAAuB,EAAE,oBAAoB,CAAC;IAC/C,CAAC,+BAA+B,EAAE,2BAA2B,CAAC;IAC9D,CAAC,+BAA+B,EAAE,qBAAqB,CAAC;IACxD,CAAC,yBAAyB,EAAE,oBAAoB,CAAC;IACjD,CAAC,mBAAmB,EAAE,oBAAoB,CAAC;IAC3C,CAAC,8BAA8B,EAAE,iBAAiB,CAAC;IACnD,CAAC,6BAA6B,EAAE,uBAAuB,CAAC;IACxD,CAAC,sBAAsB,EAAE,mBAAmB,CAAC;IAC7C,CAAC,uBAAuB,EAAE,oBAAoB,CAAC;IAC/C,CAAC,2BAA2B,EAAE,wBAAwB,CAAC;IACvD,CAAC,2BAA2B,EAAE,wBAAwB,CAAC;IACvD,CAAC,yBAAyB,EAAE,mBAAmB,CAAC;IAChD,CAAC,uFAAuF,EAAE,mBAAmB,CAAC;IAC9G,CAAC,mDAAmD,EAAE,kBAAkB,CAAC;CAC1E,CAAC;AAEF,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,eAAe,EAAE,CAAC;QACrD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"redact-secrets.js","sourceRoot":"","sources":["../../src/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAoC;IAC9D,CAAC,wBAAwB,EAAE,mBAAmB,CAAC;IAC/C,CAAC,2BAA2B,EAAE,sBAAsB,CAAC;IACrD,CAAC,uBAAuB,EAAE,oBAAoB,CAAC;IAC/C,CAAC,uBAAuB,EAAE,oBAAoB,CAAC;IAC/C,CAAC,+BAA+B,EAAE,2BAA2B,CAAC;IAC9D,CAAC,+BAA+B,EAAE,qBAAqB,CAAC;IACxD,CAAC,yBAAyB,EAAE,oBAAoB,CAAC;IACjD,CAAC,mBAAmB,EAAE,oBAAoB,CAAC;IAC3C,CAAC,8BAA8B,EAAE,iBAAiB,CAAC;IACnD,CAAC,6BAA6B,EAAE,uBAAuB,CAAC;IACxD,CAAC,mEAAmE,EAAE,oBAAoB,CAAC;IAC3F,CAAC,sBAAsB,EAAE,mBAAmB,CAAC;IAC7C,CAAC,uBAAuB,EAAE,oBAAoB,CAAC;IAC/C,CAAC,2BAA2B,EAAE,wBAAwB,CAAC;IACvD,CAAC,2BAA2B,EAAE,wBAAwB,CAAC;IACvD,CAAC,yBAAyB,EAAE,mBAAmB,CAAC;IAChD,CAAC,uFAAuF,EAAE,mBAAmB,CAAC;IAC9G,CAAC,mDAAmD,EAAE,kBAAkB,CAAC;CAC1E,CAAC;AAEF,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,eAAe,EAAE,CAAC;QACrD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import type { MemoryManager } from "./memory-manager.js";
2
- export declare const RECENT_MSG_LIMIT = 8;
3
- export declare const RECENT_MSG_CAP = 2500;
2
+ export declare const SESSION_HISTORY_MIN_MSGS = 8;
4
3
  /**
5
4
  * Build session-start context for injection after /new, /reset, or restart.
6
- * Returns the latest daily summary, or recent messages if no daily covers the gap.
7
- * Wrapped in REQ-4 temporal markers.
5
+ * Budget-based interleaved fill: dailies + recent messages (#615).
8
6
  */
9
- export declare function buildSessionStartContext(memory: MemoryManager, userId: string): string | null;
7
+ export declare function buildSessionStartContext(memory: MemoryManager, userId: string, maxContext?: number, opts?: {
8
+ skipDailies?: boolean;
9
+ maxAgeMs?: number;
10
+ }): string | null;
10
11
  //# sourceMappingURL=session-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../src/session-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,cAAc,OAAO,CAAC;AA0BnC;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuD7F"}
1
+ {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../src/session-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAQzD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAI1C;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CA0FvK"}
@@ -1,83 +1,151 @@
1
1
  import { localTime, localDateTime } from "./local-time.js";
2
- export const RECENT_MSG_LIMIT = 8;
3
- export const RECENT_MSG_CAP = 2500;
2
+ import { getAbmindEnv } from "./env-schema.js";
3
+ import { join } from "node:path";
4
+ import { readdirSync, readFileSync } from "node:fs";
5
+ export const SESSION_HISTORY_MIN_MSGS = 8;
4
6
  /**
5
- * Format recent messages for injection. Keeps newest messages intact
6
- * drops oldest messages first if over the soft cap (never truncates mid-message).
7
+ * Build session-start context for injection after /new, /reset, or restart.
8
+ * Budget-based interleaved fill: dailies + recent messages (#615).
7
9
  */
8
- function formatRecentMessages(rows) {
9
- // rows come DESC from DB — reverse to chronological, skip empty
10
- const chronological = [...rows].reverse().filter(r => r.content.trim());
11
- const lines = chronological.map(r => {
12
- const time = localTime(new Date(r.timestamp));
13
- return `[${time}] ${r.role}: ${r.content}`;
14
- });
15
- // Drop oldest lines until under cap
16
- while (lines.length > 1) {
17
- const total = lines.join("\n").length;
18
- if (total <= RECENT_MSG_CAP)
10
+ export function buildSessionStartContext(memory, userId, maxContext, opts) {
11
+ const env = getAbmindEnv();
12
+ const ctxWindow = maxContext ?? 128000;
13
+ const pct = parseFloat(process.env["SESSION_HISTORY_PCT"] ?? "3");
14
+ const minMsgs = parseInt(process.env["SESSION_HISTORY_MIN_MSGS"] ?? "8", 10);
15
+ const cap = parseInt(process.env["SESSION_HISTORY_CAP"] ?? "25000", 10);
16
+ const budget = Math.min(Math.floor(ctxWindow * pct / 100), cap);
17
+ // --- Load sources ---
18
+ let recentRows = loadRecentUserMessages(memory, minMsgs + 50); // fetch extra for enrichment
19
+ // Age filter (Code sessions — discard messages older than maxAgeMs)
20
+ if (opts?.maxAgeMs) {
21
+ const cutoff = Date.now() - opts.maxAgeMs;
22
+ recentRows = recentRows.filter(r => r.timestamp >= cutoff);
23
+ }
24
+ const dailies = opts?.skipDailies ? [] : loadDailySummaries(memory.getConfig().memoryDir, 14);
25
+ // --- Floor: 1 daily + min messages (always included) ---
26
+ const recentBucket = [];
27
+ const dailyBucket = [];
28
+ // Floor messages — take NEWEST (last N elements, since recentRows is oldest-first after reverse+filter)
29
+ const floorStart = Math.max(0, recentRows.length - minMsgs);
30
+ for (let i = floorStart; i < recentRows.length; i++) {
31
+ recentBucket.push(formatMessage(recentRows[i]));
32
+ }
33
+ // Floor daily
34
+ if (dailies.length > 0) {
35
+ dailyBucket.push(dailies[0].content);
36
+ }
37
+ let used = recentBucket.join("\n").length + dailyBucket.join("\n").length;
38
+ // --- Enrichment cycle: fill BACKWARD (older messages) + forward (older dailies) within budget ---
39
+ let msgCursor = floorStart - 1;
40
+ let dailyCursor = 1;
41
+ while (used < budget) {
42
+ let added = false;
43
+ if (msgCursor >= 0) {
44
+ const line = formatMessage(recentRows[msgCursor]);
45
+ if (used + line.length <= budget) {
46
+ recentBucket.unshift(line); // prepend to maintain chronological order
47
+ used += line.length;
48
+ msgCursor--;
49
+ added = true;
50
+ }
51
+ }
52
+ if (dailyCursor < dailies.length) {
53
+ const entry = dailies[dailyCursor].content;
54
+ if (used + entry.length <= budget) {
55
+ dailyBucket.push(entry);
56
+ used += entry.length;
57
+ dailyCursor++;
58
+ added = true;
59
+ }
60
+ }
61
+ if (!added)
19
62
  break;
20
- lines.shift();
21
63
  }
22
- return lines.join("\n");
23
- }
24
- /**
25
- * Build session-start context for injection after /new, /reset, or restart.
26
- * Returns the latest daily summary, or recent messages if no daily covers the gap.
27
- * Wrapped in REQ-4 temporal markers.
28
- */
29
- export function buildSessionStartContext(memory, userId) {
30
- const daily = memory.getLatestCompaction(userId);
31
- const dailyTs = daily?.timestamp ?? 0;
32
- const lastMsgTs = memory.store?.getLastMessageTimestamp() ?? 0;
64
+ // --- Assemble: clean separation, temporal order ---
65
+ if (recentBucket.length === 0 && dailyBucket.length === 0)
66
+ return null;
33
67
  const now = localDateTime(new Date());
34
- let body;
35
- let endedAt;
36
- if (lastMsgTs > dailyTs && dailyTs > 0) {
37
- const rows = memory.store.getMessagesSince(dailyTs, RECENT_MSG_LIMIT);
38
- body = formatRecentMessages(rows);
39
- endedAt = localDateTime(new Date(lastMsgTs));
68
+ const lastMsgTs = recentRows.length > 0 ? recentRows[recentRows.length - 1].timestamp : Date.now();
69
+ const endedAt = localDateTime(new Date(lastMsgTs));
70
+ const parts = [];
71
+ if (dailyBucket.length > 0) {
72
+ parts.push("[PAST DAYS]\n" + dailyBucket.join("\n\n"));
40
73
  }
41
- else if (daily) {
42
- body = daily.summary;
43
- endedAt = localDateTime(new Date(dailyTs));
74
+ if (recentBucket.length > 0) {
75
+ parts.push(`[RECENT last session, ended ${endedAt}]\n` + recentBucket.join("\n"));
44
76
  }
45
- else if (lastMsgTs > 0) {
46
- const rows = memory.store.getMessagesSince(0, RECENT_MSG_LIMIT);
47
- body = formatRecentMessages(rows);
48
- endedAt = localDateTime(new Date(lastMsgTs));
77
+ // Emotional tone
78
+ const tone = getEmotionalTone(memory, userId);
79
+ if (tone)
80
+ parts.push(tone);
81
+ parts.push(`[SESSION START — ${now}]`);
82
+ return parts.join("\n\n");
83
+ }
84
+ function formatMessage(row) {
85
+ const time = localTime(new Date(row.timestamp));
86
+ return `[${time}] ${row.content}`;
87
+ }
88
+ function loadRecentUserMessages(memory, limit) {
89
+ if (!memory.store)
90
+ return [];
91
+ try {
92
+ const rows = memory.store.getMessagesSince(0, limit);
93
+ // getMessagesSince returns oldest first — reverse for newest first, filter to user only
94
+ return [...rows].reverse().filter(r => r.role === "user" && r.content.trim());
49
95
  }
50
- else {
51
- return null;
96
+ catch {
97
+ return [];
52
98
  }
53
- if (!body)
54
- return null;
55
- // Emotional tone of last session
56
- let emotionalTone = "";
99
+ }
100
+ function loadDailySummaries(memoryDir, days) {
101
+ const dir = join(memoryDir, "daily");
102
+ try {
103
+ const files = readdirSync(dir).filter(f => f.endsWith(".md")).sort().reverse(); // newest first
104
+ const cutoff = Date.now() - days * 86_400_000;
105
+ const results = [];
106
+ for (const file of files) {
107
+ const m = file.match(/daily_(\d{4})-(\d{2})-(\d{2})\.md/);
108
+ if (!m)
109
+ continue;
110
+ const ts = new Date(`${m[1]}-${m[2]}-${m[3]}T00:00:00Z`).getTime();
111
+ if (ts < cutoff)
112
+ break;
113
+ const content = readFileSync(join(dir, file), "utf-8").trim();
114
+ if (content)
115
+ results.push({ timestamp: ts, content });
116
+ }
117
+ return results;
118
+ }
119
+ catch {
120
+ return [];
121
+ }
122
+ }
123
+ function getEmotionalTone(memory, userId) {
57
124
  try {
58
125
  const db = memory.getDatabase();
59
- if (db) {
60
- const rows = db.prepare(`SELECT emotion_tags, emotion_context FROM extracted_memories
61
- WHERE user_id = ? AND emotion_tags IS NOT NULL AND emotion_tags != ''
62
- ORDER BY created_at DESC LIMIT 5`).all(userId);
63
- if (rows.length > 0) {
64
- const tagCounts = new Map();
65
- for (const r of rows) {
66
- for (const t of r.emotion_tags.split(",")) {
67
- const tag = t.trim();
68
- if (tag)
69
- tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
70
- }
71
- }
72
- const top = [...tagCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([t]) => t);
73
- const contexts = rows.map(r => r.emotion_context).filter(Boolean).slice(0, 2);
74
- if (top.length > 0) {
75
- emotionalTone = `\n[Last session tone: ${top.join(", ")}${contexts.length > 0 ? ` (${contexts.join("; ")})` : ""}]`;
76
- }
126
+ if (!db)
127
+ return null;
128
+ const rows = db.prepare(`SELECT emotion_tags, emotion_context FROM extracted_memories
129
+ WHERE user_id = ? AND emotion_tags IS NOT NULL AND emotion_tags != ''
130
+ ORDER BY created_at DESC LIMIT 5`).all(userId);
131
+ if (rows.length === 0)
132
+ return null;
133
+ const tagCounts = new Map();
134
+ for (const r of rows) {
135
+ for (const t of r.emotion_tags.split(",")) {
136
+ const tag = t.trim();
137
+ if (tag)
138
+ tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
77
139
  }
78
140
  }
141
+ const top = [...tagCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([t]) => t);
142
+ const contexts = rows.map(r => r.emotion_context).filter(Boolean).slice(0, 2);
143
+ if (top.length === 0)
144
+ return null;
145
+ return `[Last session tone: ${top.join(", ")}${contexts.length > 0 ? ` (${contexts.join("; ")})` : ""}]`;
146
+ }
147
+ catch {
148
+ return null;
79
149
  }
80
- catch { /* */ }
81
- return `[LAST SESSION SUMMARY — ended ${endedAt}]\n${body}${emotionalTone}\n[SESSION START — ${now}]`;
82
150
  }
83
151
  //# sourceMappingURL=session-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.js","sourceRoot":"","sources":["../../src/session-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-context.js","sourceRoot":"","sources":["../../src/session-context.ts"],"names":[],"mappings":"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"}
@@ -3,12 +3,13 @@
3
3
  *
4
4
  * - basic: 1 LLM call. Single-shot combined prompt. Frontier-model-only.
5
5
  * Prompt: prompts/sleep/basic.md. Emits ===DAILY=== + ===MEMORIES===.
6
- * - budget: ~3 LLM calls. gc-noise + daily-summary + extract-from-daily only.
7
- * - normal: ~10-14 LLM calls. All eligible prompts except weekly-only
8
- * (skill-review, core-knowledge, consolidation) on non-curation days;
9
- * full set on curation day. Default.
10
- * - ultimate: ~14 LLM calls. All eligible prompts every day; candidate-driven
11
- * skips still apply.
6
+ * - budget: ~3 calls daily, ~5 on curation day (adds retro + derive).
7
+ * - normal: ~7 calls daily, ~15 on curation day. Default.
8
+ * Daily: gc-noise, daily-summary, retrospective, extract-memories,
9
+ * retro-derive, feedback, contradiction+graph.
10
+ * Curation adds: topic-assignment, core-promotion, merge, translation,
11
+ * skill-review, consolidation, emotion-context, rem-synthesis.
12
+ * - ultimate: ~15 calls every night. All steps, no weekly gating.
12
13
  *
13
14
  * See docs/plans/163-sleep-to-abmind.md for audience + tradeoff notes.
14
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"levels.d.ts","sourceRoot":"","sources":["../../../src/sleep/levels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAI1E,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAI3C;AAED,kFAAkF;AAClF,eAAO,MAAM,aAAa,EAAE,KAAgB,CAAC"}
1
+ {"version":3,"file":"levels.d.ts","sourceRoot":"","sources":["../../../src/sleep/levels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAI1E,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAI3C;AAED,kFAAkF;AAClF,eAAO,MAAM,aAAa,EAAE,KAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"levels.js","sourceRoot":"","sources":["../../../src/sleep/levels.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,GAAqB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC;AAE9F,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9B,IAAK,MAA4B,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAc,CAAC;IACzE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAU,QAAQ,CAAC"}
1
+ {"version":3,"file":"levels.js","sourceRoot":"","sources":["../../../src/sleep/levels.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,GAAqB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC;AAE9F,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9B,IAAK,MAA4B,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAc,CAAC;IACzE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAU,QAAQ,CAAC"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Called via runSleepCycle({ runtime, level, ... }). Gathers system state,
6
6
  * runs through a pipeline of prompt-driven steps (gc-noise, daily-summary,
7
- * extract-from-daily, retrospective, etc.), persists audit log, returns result.
7
+ * extract-memories, retrospective, retro-derive, etc.), persists audit log, returns result.
8
8
  *
9
9
  * Library-only — no CLI entry point here. Standalone entry lives in
10
10
  * cli/abmind-sleep.ts.
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../../src/sleep/orchestrator.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAoBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,aAAa,CAAC;AAgBpE,+FAA+F;AAC/F,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAAqE,CAAC;AAGtH,qEAAqE;AACrE,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAC5B;AAED,qFAAqF;AACrF,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAC5B;AAED,2FAA2F;AAC3F,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iHAAiH;IACjH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iGAAiG;IACjG,SAAS,CAAC,EAAE,CAAC,mBAAmB,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,OAAO,CAAC,OAAO,qBAAqB,EAAE,YAAY,CAAC,CAAC;CAC5E;AAED,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAmBjD;AAmdD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAqiBrE"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../../src/sleep/orchestrator.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAoBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,aAAa,CAAC;AAgBpE,+FAA+F;AAC/F,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAAmE,CAAC;AAGpH,qEAAqE;AACrE,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAC5B;AAED,qFAAqF;AACrF,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAC5B;AAED,2FAA2F;AAC3F,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iHAAiH;IACjH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iGAAiG;IACjG,SAAS,CAAC,EAAE,CAAC,mBAAmB,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,OAAO,CAAC,OAAO,qBAAqB,EAAE,YAAY,CAAC,CAAC;CAC5E;AAED,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAmBjD;AAmdD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAomBrE"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Called via runSleepCycle({ runtime, level, ... }). Gathers system state,
6
6
  * runs through a pipeline of prompt-driven steps (gc-noise, daily-summary,
7
- * extract-from-daily, retrospective, etc.), persists audit log, returns result.
7
+ * extract-memories, retrospective, retro-derive, etc.), persists audit log, returns result.
8
8
  *
9
9
  * Library-only — no CLI entry point here. Standalone entry lives in
10
10
  * cli/abmind-sleep.ts.
@@ -42,7 +42,7 @@ function toIsoDate(ts) {
42
42
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
43
43
  }
44
44
  /** Steps whose failure blocks watermark advance. Public so tests can derive reject targets. */
45
- export const ESSENTIAL_STEPS = new Set(["daily-summary", "extract-from-daily", "retrospective"]);
45
+ export const ESSENTIAL_STEPS = new Set(["daily-summary", "extract-memories", "retrospective"]);
46
46
  const CATCHUP_MAX_AGE_DAYS = 3;
47
47
  /** Thrown by runSleepCycle when memory layer fails to initialize. */
48
48
  export class SleepInitError extends Error {
@@ -401,12 +401,12 @@ async function runCatchUp(locks, sleepData, memoryConfig, steps, flags, runtime,
401
401
  }
402
402
  writeStateFile(lock.path, lock.state);
403
403
  }
404
- // 04b — extract from daily (needs daily file to exist)
405
- if (needed.includes("extract-from-daily")) {
404
+ // 04b — extract memories from daily (needs daily file to exist)
405
+ if (needed.includes("extract-memories")) {
406
406
  const dailyPath = join(memoryConfig.memoryDir, "daily", `daily_${dateStrToFormatted(lock.dateStr)}.md`);
407
407
  if (!existsSync(dailyPath)) {
408
408
  logInfo(TAG, `[CATCH-UP] ⏭ 04b — no daily file for ${lock.dateStr}`);
409
- lock.state.steps["extract-from-daily"] = { status: "skipped" };
409
+ lock.state.steps["extract-memories"] = { status: "skipped" };
410
410
  }
411
411
  else {
412
412
  const start = Date.now();
@@ -414,12 +414,12 @@ async function runCatchUp(locks, sleepData, memoryConfig, steps, flags, runtime,
414
414
  const userId = sleepData.getPrimaryUserId();
415
415
  const result = await extractFromDaily(dailyPath, userId, (p) => sendWithRetry(runtime, p, "catch-up-04b", flags.verbose, budget).then(r => { if (r === null)
416
416
  throw new LLMUnavailableError(); return r; }));
417
- lock.state.steps["extract-from-daily"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
418
- logInfo(TAG, `[CATCH-UP] ✓ 04b-extract-from-daily for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
417
+ lock.state.steps["extract-memories"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
418
+ logInfo(TAG, `[CATCH-UP] ✓ 04b-extract-memories for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
419
419
  }
420
420
  catch (err) {
421
421
  logWarn(TAG, `[CATCH-UP] ✗ 04b for ${lock.dateStr}: ${err instanceof Error ? err.message : String(err)}`);
422
- lock.state.steps["extract-from-daily"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
422
+ lock.state.steps["extract-memories"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
423
423
  }
424
424
  }
425
425
  writeStateFile(lock.path, lock.state);
@@ -485,6 +485,11 @@ export async function runSleepCycle(opts) {
485
485
  try {
486
486
  const sleepData = memory.getSleepData();
487
487
  const db = memory.db; // access DB for meta writes
488
+ // TTL: clean ephemeral system/agent messages older than 24h
489
+ try {
490
+ db.prepare("DELETE FROM messages WHERE user_id IN ('system', 'agent') AND timestamp < ?").run(Date.now() - 86_400_000);
491
+ }
492
+ catch { /* */ }
488
493
  const { metaSet, metaIncrement, metaGetInt } = await import("../meta-store.js");
489
494
  // Record attempt
490
495
  metaSet(db, "sleep_last_attempt_ts", Date.now());
@@ -532,7 +537,7 @@ export async function runSleepCycle(opts) {
532
537
  const wiredResults = await runWiredPreTasks(sleepData, memoryConfig.memoryDir, memory);
533
538
  logInfo(TAG, `[SLEEP] Wired: ${formatWiredResults(wiredResults)}`);
534
539
  // Build candidate lists for conditional prompts
535
- const candidates = sleepData.buildSleepCandidates();
540
+ const candidates = sleepData.buildSleepCandidates(getAbmindEnv().sleepModelName ?? "unknown");
536
541
  logInfo(TAG, `[SLEEP] Candidates: topics=${candidates.untaggedMemories ? "yes" : "none"}, promote=${candidates.promotionCandidates ? "yes" : "none"}, contradict=${candidates.contradictions ? "yes" : "none"}, merge=${candidates.mergeCandidates ? "yes" : "none"}, translate=${candidates.translationIssues ? "yes" : "none"}, emotion-ctx=${candidates.emotionContextGaps ? "yes" : "none"}, feedback=${candidates.recallFeedback ? "yes" : "none"}`);
537
542
  // Load step files + build vars
538
543
  const vars = buildSleepVars(snapshot);
@@ -563,7 +568,7 @@ export async function runSleepCycle(opts) {
563
568
  }
564
569
  }
565
570
  catch { /* no garbage file */ }
566
- const msgs = sleepData.getMessagesAfter(lastSleepTs);
571
+ const msgs = sleepData.getMessagesAfter(lastSleepTs, sleepData.getPrimaryUserId());
567
572
  const lines = msgs
568
573
  .filter(m => !garbageIds.has(m.id) && !m.content.startsWith("[SYSTEM"))
569
574
  .map(m => `[${m.role}]${m.emotion_score ? ` (emotion:${m.emotion_score})` : ""} ${m.content.slice(0, 500)}`);
@@ -578,6 +583,7 @@ export async function runSleepCycle(opts) {
578
583
  // Set remaining missing vars
579
584
  vars.MESSAGES_SINCE_WATERMARK = vars.CLEAN_MESSAGES; // same data, different name for gc-noise
580
585
  vars.RETRO_PATH = join(memoryConfig.memoryDir, "daily", `daily_${toIsoDate(now())}.md`);
586
+ vars.DAILY_PATH = vars.RETRO_PATH; // step 03 appends retro to the daily file
581
587
  try {
582
588
  const { getLatestConsolidationFile } = await import("../consolidation-search.js");
583
589
  const latest = getLatestConsolidationFile(memoryConfig.memoryDir, "weekly");
@@ -586,6 +592,20 @@ export async function runSleepCycle(opts) {
586
592
  catch {
587
593
  vars.CONSOLIDATION_PATH = "No consolidation files yet.";
588
594
  }
595
+ // Output path for consolidation — weekly or quarterly
596
+ const todayIso = new Date(now()).toISOString().slice(0, 10); // YYYY-MM-DD
597
+ const weeklyDir = join(memoryConfig.memoryDir, "weekly");
598
+ const quarterlyDir = join(memoryConfig.memoryDir, "quarterly");
599
+ mkdirSync(weeklyDir, { recursive: true });
600
+ mkdirSync(quarterlyDir, { recursive: true });
601
+ const month = new Date(now()).getMonth(); // 0-based
602
+ const isQuarterBoundary = month % 3 === 0 && new Date(now()).getDate() <= 7;
603
+ if (isQuarterBoundary) {
604
+ vars.CONSOLIDATION_OUTPUT_PATH = join(quarterlyDir, `quarterly_${todayIso}.md`);
605
+ }
606
+ else {
607
+ vars.CONSOLIDATION_OUTPUT_PATH = join(weeklyDir, `weekly_${todayIso}.md`);
608
+ }
589
609
  const steps = loadSleepSteps();
590
610
  // Merge snapshot vars + bridge vars into one map for JIT substitution
591
611
  const snapshotVars = buildSleepVars(snapshot);
@@ -611,56 +631,50 @@ export async function runSleepCycle(opts) {
611
631
  const curationDay = getAbmindEnv().sleepCurationDay;
612
632
  const today = new Date(now()).toLocaleDateString("en", { weekday: "long" }).toLowerCase();
613
633
  const isCurationDay = today === curationDay;
614
- const BUDGET_ONLY = new Set(["gc-noise", "daily-summary", "extract-from-daily"]);
615
- const WEEKLY_ONLY = new Set(["skill-review", "core-knowledge", "consolidation"]);
616
- const ULTIMATE_ONLY = new Set(["rem-synthesis"]);
617
- if (quality === "budget") {
634
+ const BUDGET_ONLY = new Set(["gc-noise", "daily-summary", "extract-memories"]);
635
+ const BUDGET_CURATION = new Set([...BUDGET_ONLY, "retrospective", "retro-derive"]);
636
+ const WEEKLY_ONLY = new Set(["memory-maintenance", "translation",
637
+ "skill-review", "consolidation", "rem-synthesis"]);
638
+ if (quality === "budget" && !isCurationDay) {
618
639
  for (const step of steps) {
619
640
  if (!BUDGET_ONLY.has(step.name))
620
641
  skipSet.add(step.name);
621
642
  }
622
643
  logInfo(TAG, `[SLEEP] Quality=budget — only essential extraction`);
623
644
  }
645
+ else if (quality === "budget" && isCurationDay) {
646
+ for (const step of steps) {
647
+ if (!BUDGET_CURATION.has(step.name))
648
+ skipSet.add(step.name);
649
+ }
650
+ logInfo(TAG, `[SLEEP] Quality=budget (curation day) — adds retro + derive`);
651
+ }
624
652
  else if (quality === "normal" && !isCurationDay) {
625
653
  for (const name of WEEKLY_ONLY)
626
654
  skipSet.add(name);
627
- for (const name of ULTIMATE_ONLY)
628
- skipSet.add(name);
629
- logInfo(TAG, `[SLEEP] Quality=normal — weekly + ultimate prompts skipped (curation day: ${curationDay})`);
655
+ logInfo(TAG, `[SLEEP] Quality=normal — weekly prompts skipped (curation day: ${curationDay})`);
630
656
  }
631
657
  else if (quality === "normal" && isCurationDay) {
632
- for (const name of ULTIMATE_ONLY)
633
- skipSet.add(name);
634
- logInfo(TAG, `[SLEEP] Quality=normal (curation day) — ultimate prompts skipped`);
658
+ logInfo(TAG, `[SLEEP] Quality=normal (curation day) all steps`);
635
659
  }
636
660
  else {
637
- // ultimate: only skip REM on non-curation days
638
- if (!isCurationDay)
639
- skipSet.add("rem-synthesis");
661
+ // ultimate: all steps every night
640
662
  logInfo(TAG, `[SLEEP] Quality=${quality}${isCurationDay ? " (curation day)" : ""} — all eligible`);
641
663
  }
642
664
  // Candidate-driven skips (empty = nothing to do)
643
665
  if (!candidates.recallFeedback)
644
666
  skipSet.add("feedback");
645
- if (!candidates.untaggedMemories)
646
- skipSet.add("topic-assignment");
647
- if (!candidates.promotionCandidates)
648
- skipSet.add("core-promotion");
649
- if (!candidates.mergeCandidates)
650
- skipSet.add("merge");
651
- if (!candidates.translationIssues)
652
- skipSet.add("translation-check");
667
+ // memory-maintenance: skip if ALL three inputs are empty
668
+ if (!candidates.untaggedMemories && !candidates.mergeCandidates && !candidates.emotionContextGaps)
669
+ skipSet.add("memory-maintenance");
670
+ // promotion candidates are optional input to retro-derive — don't skip the step for it
653
671
  if (!candidates.translationIssues)
654
672
  skipSet.add("translation");
655
- if (!candidates.emotionContextGaps)
656
- skipSet.add("emotion-context");
657
- if (!candidates.emotionContextGaps)
658
- skipSet.add("emotion-context-backfill");
659
673
  // Legacy skip names (old prompt files)
660
674
  if (snapshot.topicFiles.length === 0)
661
675
  skipSet.add("topic-reorg");
662
676
  if (snapshot.dbStats.extractedMemoryCount < 10) {
663
- skipSet.add("merge");
677
+ skipSet.add("memory-maintenance");
664
678
  skipSet.add("darwinism");
665
679
  }
666
680
  if (snapshot.dbStats.extractedMemoryCount < 20)
@@ -699,6 +713,11 @@ export async function runSleepCycle(opts) {
699
713
  let dailySummaryPath = null;
700
714
  try {
701
715
  // ── LLM call budget (hard safety limit) ──
716
+ // On resume: reset llmCalls to completed step count (don't carry stale counter)
717
+ if (isResume) {
718
+ const completedCount = Object.values(state.steps).filter(s => s.status === "ok").length;
719
+ state.llmCalls = completedCount;
720
+ }
702
721
  const budget = new LlmBudget(state, statePath);
703
722
  // ── Catch-up: recover failed essentials from previous days ──
704
723
  const sleepDir = join(memoryConfig.memoryDir, "sleep");
@@ -707,6 +726,29 @@ export async function runSleepCycle(opts) {
707
726
  logInfo(TAG, `[CATCH-UP] Found ${previousLocks.length} previous lock(s)`);
708
727
  await runCatchUp(previousLocks, sleepData, memoryConfig, steps, flags, runtime, budget);
709
728
  }
729
+ // Housekeeping: move misplaced daily/consolidation_* to weekly/ (#640)
730
+ try {
731
+ const dailyDir = join(memoryConfig.memoryDir, "daily");
732
+ if (existsSync(dailyDir)) {
733
+ for (const f of readdirSync(dailyDir).filter(fn => fn.startsWith("consolidation_"))) {
734
+ const m = f.match(/consolidation_(\d{4})-(\d{2})-week(\d)/);
735
+ if (m) {
736
+ const [, year, month, week] = m;
737
+ const day = (parseInt(week) - 1) * 7 + 1;
738
+ const approxDate = `${year}-${month}-${String(Math.min(day, 28)).padStart(2, "0")}`;
739
+ const dest = join(weeklyDir, `weekly_${approxDate}.md`);
740
+ if (!existsSync(dest)) {
741
+ const { renameSync } = await import("node:fs");
742
+ renameSync(join(dailyDir, f), dest);
743
+ logInfo(TAG, `[HOUSEKEEPING] Moved ${f} → weekly_${approxDate}.md`);
744
+ }
745
+ }
746
+ }
747
+ }
748
+ }
749
+ catch (err) {
750
+ logWarn(TAG, `[HOUSEKEEPING] consolidation migration failed: ${err}`);
751
+ }
710
752
  emitProgress("starting");
711
753
  let consecutiveFailures = 0;
712
754
  // Create day directory for per-step logs
@@ -779,10 +821,10 @@ export async function runSleepCycle(opts) {
779
821
  logInfo(TAG, `[SLEEP] ${state.steps[step.name]?.status === "ok" ? "✓" : "✗"} ${step.name} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
780
822
  continue;
781
823
  }
782
- if (step.name === "extract-from-daily") {
824
+ if (step.name === "extract-memories") {
783
825
  // Resume path: if daily-summary already completed in a prior run, the
784
826
  // in-memory dailySummaryPath is null. Recover it from the lock's
785
- // recorded path so extract-from-daily can still run. #181.
827
+ // recorded path so extract-memories can still run. #181.
786
828
  if (!dailySummaryPath) {
787
829
  const priorPath = state.steps["daily-summary"]?.path;
788
830
  if (priorPath && existsSync(priorPath)) {
@@ -909,6 +951,22 @@ export async function runSleepCycle(opts) {
909
951
  const [, a, b, rel] = rm;
910
952
  memDb.prepare(`INSERT INTO entity_graph (entity_a, entity_b, relation, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(entity_a, entity_b, relation) DO UPDATE SET last_seen_at = ?`).run(a, b, rel, Date.now(), Date.now(), Date.now());
911
953
  }
954
+ // #529: age out stale event memories via decay (recall_count / age_days < threshold)
955
+ const EVENT_MIN_AGE_DAYS = 7;
956
+ const DECAY_THRESHOLD = 0.1; // below this score → expire
957
+ const now = Date.now();
958
+ const candidates = memDb.prepare(`SELECT id, recall_count, created_at FROM extracted_memories WHERE memory_type = 'event' AND valid_to IS NULL AND created_at < ?`).all(now - EVENT_MIN_AGE_DAYS * 86400_000);
959
+ let agedCount = 0;
960
+ for (const m of candidates) {
961
+ const ageDays = (now - m.created_at) / 86400_000;
962
+ const score = m.recall_count / ageDays;
963
+ if (score < DECAY_THRESHOLD) {
964
+ memDb.prepare("UPDATE extracted_memories SET valid_to = ? WHERE id = ?").run(now, m.id);
965
+ agedCount++;
966
+ }
967
+ }
968
+ if (agedCount > 0)
969
+ logInfo(TAG, `[SLEEP] Aged out ${agedCount} faded event memories (score < ${DECAY_THRESHOLD})`);
912
970
  }
913
971
  }
914
972
  }