@xopcai/xopc 0.0.24 → 0.0.26

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 (109) hide show
  1. package/dist/extensions/feishu/xopc.extension.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/agents-Clv9i1Kb.js +216 -0
  4. package/dist/gateway/static/root/assets/agents-Clv9i1Kb.js.map +1 -0
  5. package/dist/gateway/static/root/assets/{apps-page-tZz69XM3.js → apps-page-DqclV-PP.js} +2 -2
  6. package/dist/gateway/static/root/assets/{apps-page-tZz69XM3.js.map → apps-page-DqclV-PP.js.map} +1 -1
  7. package/dist/gateway/static/root/assets/{channels-settings-BAvk9-aK.js → channels-settings-CLyTYjrz.js} +3 -3
  8. package/dist/gateway/static/root/assets/{channels-settings-BAvk9-aK.js.map → channels-settings-CLyTYjrz.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/cron-page-CU8lutMt.js +2 -0
  10. package/dist/gateway/static/root/assets/{cron-page-CANqvhK7.js.map → cron-page-CU8lutMt.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{cron-utils-DyOO6TdN.js → cron-utils-_UjiWax6.js} +2 -2
  12. package/dist/gateway/static/root/assets/{cron-utils-DyOO6TdN.js.map → cron-utils-_UjiWax6.js.map} +1 -1
  13. package/dist/gateway/static/root/assets/dist-Xqb4IGWC.js +2 -0
  14. package/dist/gateway/static/root/assets/{dist-Brod9LF3.js.map → dist-Xqb4IGWC.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{extension-debug-page-CDD7ozsC.js → extension-debug-page-CtTUkAmw.js} +2 -2
  16. package/dist/gateway/static/root/assets/{extension-debug-page-CDD7ozsC.js.map → extension-debug-page-CtTUkAmw.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{extension-page-UUFMjoWf.js → extension-page-C-aQU8qR.js} +2 -2
  18. package/dist/gateway/static/root/assets/{extension-page-UUFMjoWf.js.map → extension-page-C-aQU8qR.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-settings-page-CP9JNc4m.js → extension-settings-page-b0y9aY-q.js} +2 -2
  20. package/dist/gateway/static/root/assets/extension-settings-page-b0y9aY-q.js.map +1 -0
  21. package/dist/gateway/static/root/assets/index-DhSFfSNN.css +1 -0
  22. package/dist/gateway/static/root/assets/index-Gr2HWo-G.js +4734 -0
  23. package/dist/gateway/static/root/assets/index-Gr2HWo-G.js.map +1 -0
  24. package/dist/gateway/static/root/assets/logs-page-DRI33XK4.js +2 -0
  25. package/dist/gateway/static/root/assets/{logs-page-Cr0eCGb4.js.map → logs-page-DRI33XK4.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/sessions-page-Cryg-36Z.js +2 -0
  27. package/dist/gateway/static/root/assets/{sessions-page-DwLHN5GJ.js.map → sessions-page-Cryg-36Z.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/settings-page-DFNKT9yg.js +2 -0
  29. package/dist/gateway/static/root/assets/settings-page-DFNKT9yg.js.map +1 -0
  30. package/dist/gateway/static/root/assets/skills-page-D4gfh0Ih.js +3 -0
  31. package/dist/gateway/static/root/assets/{skills-page-DgBYvH6B.js.map → skills-page-D4gfh0Ih.js.map} +1 -1
  32. package/dist/gateway/static/root/index.html +2 -2
  33. package/dist/package.js +1 -1
  34. package/dist/src/agent/memory/dreaming/config.d.ts +48 -0
  35. package/dist/src/agent/memory/dreaming/config.js +86 -0
  36. package/dist/src/agent/memory/dreaming/config.js.map +1 -0
  37. package/dist/src/agent/memory/dreaming/constants.d.ts +29 -0
  38. package/dist/src/agent/memory/dreaming/constants.js +38 -0
  39. package/dist/src/agent/memory/dreaming/constants.js.map +1 -0
  40. package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +21 -0
  41. package/dist/src/agent/memory/dreaming/deep-promotion.js +271 -0
  42. package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -0
  43. package/dist/src/agent/memory/dreaming/events.d.ts +36 -0
  44. package/dist/src/agent/memory/dreaming/events.js +44 -0
  45. package/dist/src/agent/memory/dreaming/events.js.map +1 -0
  46. package/dist/src/agent/memory/dreaming/last-run.d.ts +80 -0
  47. package/dist/src/agent/memory/dreaming/last-run.js +98 -0
  48. package/dist/src/agent/memory/dreaming/last-run.js.map +1 -0
  49. package/dist/src/agent/memory/dreaming/light-sweep.d.ts +19 -0
  50. package/dist/src/agent/memory/dreaming/light-sweep.js +328 -0
  51. package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -0
  52. package/dist/src/agent/memory/dreaming/preview.d.ts +28 -0
  53. package/dist/src/agent/memory/dreaming/preview.js +97 -0
  54. package/dist/src/agent/memory/dreaming/preview.js.map +1 -0
  55. package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +21 -0
  56. package/dist/src/agent/memory/dreaming/rem-patterns.js +286 -0
  57. package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -0
  58. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +65 -0
  59. package/dist/src/agent/memory/dreaming/short-term-store.js +197 -0
  60. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -0
  61. package/dist/src/agent/memory/dreaming/utils.d.ts +42 -0
  62. package/dist/src/agent/memory/dreaming/utils.js +141 -0
  63. package/dist/src/agent/memory/dreaming/utils.js.map +1 -0
  64. package/dist/src/agent/orchestration/agent-orchestrator.js +59 -0
  65. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  66. package/dist/src/agent/service.d.ts +6 -0
  67. package/dist/src/agent/service.js +78 -0
  68. package/dist/src/agent/service.js.map +1 -1
  69. package/dist/src/agent/tools/dreaming-tool.d.ts +7 -0
  70. package/dist/src/agent/tools/dreaming-tool.js +102 -0
  71. package/dist/src/agent/tools/dreaming-tool.js.map +1 -0
  72. package/dist/src/agent/tools/factory.js +5 -0
  73. package/dist/src/agent/tools/factory.js.map +1 -1
  74. package/dist/src/agent/tools/index.d.ts +1 -0
  75. package/dist/src/agent/tools/index.js +2 -1
  76. package/dist/src/agent/tools/memory-tool.js +9 -2
  77. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  78. package/dist/src/config/schema.d.ts +93 -0
  79. package/dist/src/config/schema.js +42 -1
  80. package/dist/src/config/schema.js.map +1 -1
  81. package/dist/src/gateway/hono/lib/config-payload.d.ts +31 -0
  82. package/dist/src/gateway/hono/routes/config.js +48 -0
  83. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  84. package/dist/src/gateway/hono/routes/dreaming.d.ts +3 -0
  85. package/dist/src/gateway/hono/routes/dreaming.js +288 -0
  86. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -0
  87. package/dist/src/gateway/hono/routes/index.js +2 -0
  88. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  89. package/dist/src/gateway/hono/routes/models.js +26 -1
  90. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  91. package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
  92. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  93. package/dist/src/gateway/lock.js +1 -1
  94. package/dist/src/gateway/service.js +7 -0
  95. package/dist/src/gateway/service.js.map +1 -1
  96. package/package.json +1 -1
  97. package/dist/gateway/static/root/assets/agents-CiZMJZRp.js +0 -216
  98. package/dist/gateway/static/root/assets/agents-CiZMJZRp.js.map +0 -1
  99. package/dist/gateway/static/root/assets/cron-page-CANqvhK7.js +0 -2
  100. package/dist/gateway/static/root/assets/dist-Brod9LF3.js +0 -2
  101. package/dist/gateway/static/root/assets/extension-settings-page-CP9JNc4m.js.map +0 -1
  102. package/dist/gateway/static/root/assets/index-BZvlG48D.js +0 -150
  103. package/dist/gateway/static/root/assets/index-BZvlG48D.js.map +0 -1
  104. package/dist/gateway/static/root/assets/index-DxkgyT8R.css +0 -1
  105. package/dist/gateway/static/root/assets/logs-page-Cr0eCGb4.js +0 -2
  106. package/dist/gateway/static/root/assets/sessions-page-DwLHN5GJ.js +0 -2
  107. package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js +0 -2
  108. package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js.map +0 -1
  109. package/dist/gateway/static/root/assets/skills-page-DgBYvH6B.js +0 -3
@@ -0,0 +1,97 @@
1
+ import { MEMORY_MD_FILENAME } from "./constants.js";
2
+ import { clamp01, compareCandidatesByScore, computeCandidateScore, extractPromotionMarkers, isContaminatedSnippet, isExpiredEntry, readFileLines, resolveDeepDefaults, sliceRange, snippetHash } from "./utils.js";
3
+ import { loadDreamingStore } from "./short-term-store.js";
4
+ import path from "node:path";
5
+ import fs from "node:fs/promises";
6
+ //#region src/agent/memory/dreaming/preview.ts
7
+ async function previewDreamingDeepPromotion(params) {
8
+ const cfg = resolveDeepDefaults(params.config);
9
+ const memoryPath = path.join(params.workspaceDir, MEMORY_MD_FILENAME);
10
+ if (!cfg.enabled) return {
11
+ ok: true,
12
+ reason: "dreaming disabled",
13
+ items: [],
14
+ memoryPath
15
+ };
16
+ const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });
17
+ const nowMs = (params.now ?? /* @__PURE__ */ new Date()).getTime();
18
+ const ranked = Object.values(store.entries ?? {}).filter((e) => {
19
+ if (!e || typeof e !== "object") return false;
20
+ if (e.promotedAt) return false;
21
+ if (!e.path || !e.path.startsWith("memory/")) return false;
22
+ if (e.recallCount < cfg.minRecallCount) return false;
23
+ if ((e.queryHashes?.length ?? 0) < cfg.minUniqueQueries) return false;
24
+ if (isExpiredEntry(e.lastRecalledAt, nowMs, cfg.maxAgeDays)) return false;
25
+ return clamp01(e.recallCount > 0 ? e.totalScore / e.recallCount : 0) >= cfg.minScore;
26
+ }).map((e) => {
27
+ const { avgScore, score, recencyDecay } = computeCandidateScore(e, nowMs, cfg.recencyHalfLifeDays);
28
+ return {
29
+ ...e,
30
+ avgScore,
31
+ score,
32
+ recencyDecay
33
+ };
34
+ }).sort(compareCandidatesByScore);
35
+ const markers = extractPromotionMarkers(await fs.readFile(memoryPath, "utf-8").catch((err) => {
36
+ if (err?.code === "ENOENT") return "";
37
+ throw err;
38
+ }));
39
+ const limit = Math.min(Math.max(params.limit ?? 20, 1), 50);
40
+ const out = [];
41
+ const scanCap = Math.min(ranked.length, Math.max(limit * 3, limit));
42
+ for (const candidate of ranked.slice(0, scanCap)) {
43
+ if (markers.keys.has(candidate.key)) {
44
+ out.push({
45
+ key: candidate.key,
46
+ hash: "",
47
+ snippet: "",
48
+ path: candidate.path,
49
+ startLine: candidate.startLine,
50
+ endLine: candidate.endLine,
51
+ score: candidate.score,
52
+ avgScore: candidate.avgScore,
53
+ recallCount: candidate.recallCount,
54
+ recencyDecay: candidate.recencyDecay,
55
+ alreadyPromotedByKey: true,
56
+ alreadyPromotedByHash: false,
57
+ skippedReason: "already promoted (key)"
58
+ });
59
+ continue;
60
+ }
61
+ const lines = await readFileLines(path.join(params.workspaceDir, candidate.path));
62
+ if (!lines) continue;
63
+ const startLine = Math.max(1, Math.floor(candidate.startLine));
64
+ const endLine = Math.max(startLine, Math.floor(candidate.endLine));
65
+ const snippet = sliceRange(lines, startLine, endLine);
66
+ if (!snippet) continue;
67
+ if (isContaminatedSnippet(snippet)) continue;
68
+ const hash = snippetHash(snippet);
69
+ const alreadyPromotedByHash = markers.hashes.has(hash);
70
+ out.push({
71
+ key: candidate.key,
72
+ hash,
73
+ snippet,
74
+ path: candidate.path,
75
+ startLine,
76
+ endLine,
77
+ score: candidate.score,
78
+ avgScore: candidate.avgScore,
79
+ recallCount: candidate.recallCount,
80
+ recencyDecay: candidate.recencyDecay,
81
+ alreadyPromotedByKey: false,
82
+ alreadyPromotedByHash,
83
+ skippedReason: alreadyPromotedByHash ? "already promoted (hash)" : null
84
+ });
85
+ if (out.filter((x) => !x.skippedReason).length >= limit) break;
86
+ }
87
+ return {
88
+ ok: true,
89
+ reason: "ok",
90
+ items: out,
91
+ memoryPath
92
+ };
93
+ }
94
+ //#endregion
95
+ export { previewDreamingDeepPromotion };
96
+
97
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/preview.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { DreamingDeepConfig } from './config.js';\nimport { MEMORY_MD_FILENAME } from './constants.js';\nimport { loadDreamingStore, type DreamingStoreEntry } from './short-term-store.js';\nimport {\n clamp01,\n compareCandidatesByScore,\n computeCandidateScore,\n extractPromotionMarkers,\n isContaminatedSnippet,\n isExpiredEntry,\n readFileLines,\n resolveDeepDefaults,\n sliceRange,\n snippetHash,\n} from './utils.js';\n\ntype PreviewItem = {\n key: string;\n hash: string;\n snippet: string;\n path: string;\n startLine: number;\n endLine: number;\n score: number;\n avgScore: number;\n recallCount: number;\n recencyDecay: number;\n alreadyPromotedByKey: boolean;\n alreadyPromotedByHash: boolean;\n skippedReason: string | null;\n};\n\nexport async function previewDreamingDeepPromotion(params: {\n workspaceDir: string;\n config?: Partial<DreamingDeepConfig>;\n limit?: number;\n now?: Date;\n}): Promise<{ ok: boolean; reason: string; items: PreviewItem[]; memoryPath: string }> {\n const cfg = resolveDeepDefaults(params.config);\n const memoryPath = path.join(params.workspaceDir, MEMORY_MD_FILENAME);\n if (!cfg.enabled) return { ok: true, reason: 'dreaming disabled', items: [], memoryPath };\n\n const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });\n const nowMs = (params.now ?? new Date()).getTime();\n\n const all = Object.values(store.entries ?? {}).filter((e): e is DreamingStoreEntry => {\n if (!e || typeof e !== 'object') return false;\n if (e.promotedAt) return false;\n if (!e.path || !e.path.startsWith('memory/')) return false;\n if (e.recallCount < cfg.minRecallCount) return false;\n if ((e.queryHashes?.length ?? 0) < cfg.minUniqueQueries) return false;\n if (isExpiredEntry(e.lastRecalledAt, nowMs, cfg.maxAgeDays)) return false;\n const avg = e.recallCount > 0 ? e.totalScore / e.recallCount : 0;\n return clamp01(avg) >= cfg.minScore;\n });\n\n const ranked = all\n .map((e) => {\n const { avgScore, score, recencyDecay } = computeCandidateScore(e, nowMs, cfg.recencyHalfLifeDays);\n return { ...e, avgScore, score, recencyDecay };\n })\n .sort(compareCandidatesByScore);\n\n const existing = await fs.readFile(memoryPath, 'utf-8').catch((err: unknown) => {\n if ((err as NodeJS.ErrnoException | undefined)?.code === 'ENOENT') return '';\n throw err;\n });\n const markers = extractPromotionMarkers(existing);\n\n const limit = Math.min(Math.max(params.limit ?? 20, 1), 50);\n const out: PreviewItem[] = [];\n\n // Scan more than limit so filtering doesn't yield empty previews.\n const scanCap = Math.min(ranked.length, Math.max(limit * 3, limit));\n for (const candidate of ranked.slice(0, scanCap)) {\n const alreadyPromotedByKey = markers.keys.has(candidate.key);\n if (alreadyPromotedByKey) {\n out.push({\n key: candidate.key,\n hash: '',\n snippet: '',\n path: candidate.path,\n startLine: candidate.startLine,\n endLine: candidate.endLine,\n score: candidate.score,\n avgScore: candidate.avgScore,\n recallCount: candidate.recallCount,\n recencyDecay: candidate.recencyDecay,\n alreadyPromotedByKey: true,\n alreadyPromotedByHash: false,\n skippedReason: 'already promoted (key)',\n });\n continue;\n }\n\n const fullPath = path.join(params.workspaceDir, candidate.path);\n const lines = await readFileLines(fullPath);\n if (!lines) continue;\n const startLine = Math.max(1, Math.floor(candidate.startLine));\n const endLine = Math.max(startLine, Math.floor(candidate.endLine));\n const snippet = sliceRange(lines, startLine, endLine);\n if (!snippet) continue;\n if (isContaminatedSnippet(snippet)) continue;\n const hash = snippetHash(snippet);\n const alreadyPromotedByHash = markers.hashes.has(hash);\n out.push({\n key: candidate.key,\n hash,\n snippet,\n path: candidate.path,\n startLine,\n endLine,\n score: candidate.score,\n avgScore: candidate.avgScore,\n recallCount: candidate.recallCount,\n recencyDecay: candidate.recencyDecay,\n alreadyPromotedByKey: false,\n alreadyPromotedByHash,\n skippedReason: alreadyPromotedByHash ? 'already promoted (hash)' : null,\n });\n if (out.filter((x) => !x.skippedReason).length >= limit) break;\n }\n\n return { ok: true, reason: 'ok', items: out, memoryPath };\n}\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { DreamingRemConfig } from './config.js';
2
+ /**
3
+ * REM phase: cross-session pattern discovery.
4
+ *
5
+ * Scans the short-term store for entries that share query hashes or
6
+ * appear across multiple daily files, then clusters them to identify
7
+ * recurring themes/patterns. Bumps `remHits` on touched entries and
8
+ * optionally writes a pattern summary to DREAMS.md.
9
+ *
10
+ * Runs weekly; expensive but insightful.
11
+ */
12
+ export declare function runRemPatterns(params: {
13
+ workspaceDir: string;
14
+ config?: Partial<DreamingRemConfig>;
15
+ now?: Date;
16
+ }): Promise<{
17
+ ok: boolean;
18
+ reason: string;
19
+ patternsDiscovered: number;
20
+ entriesAnalyzed: number;
21
+ }>;
@@ -0,0 +1,286 @@
1
+ import { createLogger } from "../../../utils/logger/index.js";
2
+ import { init_logger } from "../../../utils/logger.js";
3
+ import { DREAMING_DIR_RELATIVE, DREAMS_MD_FILENAME, MS_PER_DAY } from "./constants.js";
4
+ import { isoDay } from "./utils.js";
5
+ import { bumpEntryPhaseSignal, loadDreamingStore, saveDreamingStore } from "./short-term-store.js";
6
+ import "./last-run.js";
7
+ import path from "node:path";
8
+ import fs from "node:fs/promises";
9
+ //#region src/agent/memory/dreaming/rem-patterns.ts
10
+ init_logger();
11
+ const log = createLogger("Dreaming:REM");
12
+ function resolveConfig(overrides) {
13
+ return {
14
+ enabled: overrides?.enabled === true,
15
+ cron: typeof overrides?.cron === "string" ? overrides.cron : "0 5 * * 0",
16
+ lookbackDays: Math.max(1, Math.floor(Number(overrides?.lookbackDays) || 7)),
17
+ limit: Math.max(0, Math.floor(Number(overrides?.limit) || 10)),
18
+ minPatternStrength: Math.max(0, Math.min(1, Number(overrides?.minPatternStrength) || .75))
19
+ };
20
+ }
21
+ /**
22
+ * REM phase: cross-session pattern discovery.
23
+ *
24
+ * Scans the short-term store for entries that share query hashes or
25
+ * appear across multiple daily files, then clusters them to identify
26
+ * recurring themes/patterns. Bumps `remHits` on touched entries and
27
+ * optionally writes a pattern summary to DREAMS.md.
28
+ *
29
+ * Runs weekly; expensive but insightful.
30
+ */
31
+ async function runRemPatterns(params) {
32
+ const cfg = resolveConfig(params.config);
33
+ const now = params.now ?? /* @__PURE__ */ new Date();
34
+ const startedAt = now.toISOString();
35
+ const runId = `rem:${startedAt}:${process.pid}`;
36
+ const startMs = Date.now();
37
+ const nowMs = now.getTime();
38
+ if (!cfg.enabled) {
39
+ await writeLastRun(params.workspaceDir, {
40
+ runId,
41
+ startedAt,
42
+ cfg,
43
+ ok: true,
44
+ reason: "REM patterns disabled",
45
+ startMs,
46
+ rem: {
47
+ patternsDiscovered: 0,
48
+ entriesAnalyzed: 0
49
+ }
50
+ });
51
+ return {
52
+ ok: true,
53
+ reason: "REM patterns disabled",
54
+ patternsDiscovered: 0,
55
+ entriesAnalyzed: 0
56
+ };
57
+ }
58
+ try {
59
+ const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });
60
+ const cutoffMs = nowMs - cfg.lookbackDays * MS_PER_DAY;
61
+ const recentEntries = Object.values(store.entries ?? {}).filter((entry) => {
62
+ if (!entry || typeof entry !== "object") return false;
63
+ if (!entry.lastRecalledAt) return false;
64
+ const lastMs = Date.parse(entry.lastRecalledAt);
65
+ return Number.isFinite(lastMs) && lastMs >= cutoffMs;
66
+ });
67
+ if (recentEntries.length < 2) {
68
+ await writeLastRun(params.workspaceDir, {
69
+ runId,
70
+ startedAt,
71
+ cfg,
72
+ ok: true,
73
+ reason: "not enough recent entries for pattern analysis",
74
+ startMs,
75
+ rem: {
76
+ patternsDiscovered: 0,
77
+ entriesAnalyzed: recentEntries.length
78
+ }
79
+ });
80
+ return {
81
+ ok: true,
82
+ reason: "not enough recent entries for pattern analysis",
83
+ patternsDiscovered: 0,
84
+ entriesAnalyzed: recentEntries.length
85
+ };
86
+ }
87
+ const topClusters = discoverPatternClusters(recentEntries, cfg.minPatternStrength).slice(0, cfg.limit);
88
+ const touchedKeys = /* @__PURE__ */ new Set();
89
+ for (const cluster of topClusters) for (const member of cluster.members) if (!touchedKeys.has(member.key)) {
90
+ touchedKeys.add(member.key);
91
+ const storeEntry = store.entries[member.key];
92
+ if (storeEntry) bumpEntryPhaseSignal(storeEntry, "remHits");
93
+ }
94
+ store.updatedAt = now.toISOString();
95
+ await saveDreamingStore({
96
+ workspaceDir: params.workspaceDir,
97
+ store
98
+ });
99
+ if (topClusters.length > 0) await appendPatternSummary(params.workspaceDir, topClusters, now);
100
+ log.info({
101
+ workspaceDir: params.workspaceDir,
102
+ patterns: topClusters.length,
103
+ entriesAnalyzed: recentEntries.length,
104
+ touched: touchedKeys.size
105
+ }, "REM pattern discovery complete");
106
+ await writeLastRun(params.workspaceDir, {
107
+ runId,
108
+ startedAt,
109
+ cfg,
110
+ ok: true,
111
+ reason: "REM patterns complete",
112
+ startMs,
113
+ rem: {
114
+ patternsDiscovered: topClusters.length,
115
+ entriesAnalyzed: recentEntries.length
116
+ }
117
+ });
118
+ return {
119
+ ok: true,
120
+ reason: "REM patterns complete",
121
+ patternsDiscovered: topClusters.length,
122
+ entriesAnalyzed: recentEntries.length
123
+ };
124
+ } catch (err) {
125
+ const errorMessage = err instanceof Error ? err.message : String(err);
126
+ log.error({
127
+ err,
128
+ errorMessage,
129
+ workspaceDir: params.workspaceDir
130
+ }, `REM pattern discovery failed: ${errorMessage}`);
131
+ await writeLastRun(params.workspaceDir, {
132
+ runId,
133
+ startedAt,
134
+ cfg,
135
+ ok: false,
136
+ reason: `REM error: ${errorMessage}`,
137
+ startMs,
138
+ rem: {
139
+ patternsDiscovered: 0,
140
+ entriesAnalyzed: 0
141
+ },
142
+ errorMessage
143
+ }).catch(() => void 0);
144
+ return {
145
+ ok: false,
146
+ reason: errorMessage,
147
+ patternsDiscovered: 0,
148
+ entriesAnalyzed: 0
149
+ };
150
+ }
151
+ }
152
+ /**
153
+ * Build an inverted index of queryHash → entries, then form clusters
154
+ * where multiple entries share overlapping query hashes. Each cluster's
155
+ * "strength" is the ratio of shared queries to total unique queries.
156
+ */
157
+ function discoverPatternClusters(entries, minStrength) {
158
+ const hashToEntries = /* @__PURE__ */ new Map();
159
+ for (const entry of entries) for (const queryHash of entry.queryHashes ?? []) {
160
+ const group = hashToEntries.get(queryHash);
161
+ if (group) group.push(entry);
162
+ else hashToEntries.set(queryHash, [entry]);
163
+ }
164
+ const significantHashes = [];
165
+ for (const [hash, group] of hashToEntries) {
166
+ const uniquePaths = new Set(group.map((e) => e.path));
167
+ if (group.length >= 2 && uniquePaths.size >= 2) significantHashes.push({
168
+ hash,
169
+ entries: group
170
+ });
171
+ }
172
+ if (significantHashes.length === 0) return [];
173
+ const keyToCluster = /* @__PURE__ */ new Map();
174
+ const keyToEntry = /* @__PURE__ */ new Map();
175
+ for (const entry of entries) keyToEntry.set(entry.key, entry);
176
+ for (const { entries: groupEntries } of significantHashes) {
177
+ const keys = groupEntries.map((e) => e.key);
178
+ let mergedCluster = keyToCluster.get(keys[0]);
179
+ if (!mergedCluster) {
180
+ mergedCluster = /* @__PURE__ */ new Set();
181
+ mergedCluster.add(keys[0]);
182
+ keyToCluster.set(keys[0], mergedCluster);
183
+ }
184
+ for (const key of keys.slice(1)) {
185
+ const existingCluster = keyToCluster.get(key);
186
+ if (existingCluster && existingCluster !== mergedCluster) for (const existingKey of existingCluster) {
187
+ mergedCluster.add(existingKey);
188
+ keyToCluster.set(existingKey, mergedCluster);
189
+ }
190
+ else {
191
+ mergedCluster.add(key);
192
+ keyToCluster.set(key, mergedCluster);
193
+ }
194
+ }
195
+ }
196
+ const seenClusters = /* @__PURE__ */ new Set();
197
+ const rawClusters = [];
198
+ for (const cluster of keyToCluster.values()) if (!seenClusters.has(cluster) && cluster.size >= 2) {
199
+ seenClusters.add(cluster);
200
+ rawClusters.push(cluster);
201
+ }
202
+ const scoredClusters = [];
203
+ for (const clusterKeys of rawClusters) {
204
+ const members = [];
205
+ for (const key of clusterKeys) {
206
+ const entry = keyToEntry.get(key);
207
+ if (entry) members.push(entry);
208
+ }
209
+ if (members.length < 2) continue;
210
+ const hashCounts = /* @__PURE__ */ new Map();
211
+ for (const member of members) for (const hash of member.queryHashes ?? []) hashCounts.set(hash, (hashCounts.get(hash) ?? 0) + 1);
212
+ const sharedQueries = [...hashCounts.entries()].filter(([, count]) => count >= 2).map(([hash]) => hash);
213
+ const allUniqueHashes = /* @__PURE__ */ new Set();
214
+ for (const member of members) for (const hash of member.queryHashes ?? []) allUniqueHashes.add(hash);
215
+ const strength = allUniqueHashes.size > 0 ? sharedQueries.length / allUniqueHashes.size : 0;
216
+ if (strength < minStrength) continue;
217
+ const distinctPaths = [...new Set(members.map((m) => m.path))];
218
+ const representative = members.reduce((best, current) => (current.totalSignalCount ?? 0) > (best.totalSignalCount ?? 0) ? current : best);
219
+ scoredClusters.push({
220
+ representative,
221
+ members,
222
+ strength,
223
+ sharedQueries,
224
+ distinctPaths
225
+ });
226
+ }
227
+ scoredClusters.sort((a, b) => {
228
+ if (b.strength !== a.strength) return b.strength - a.strength;
229
+ return b.members.length - a.members.length;
230
+ });
231
+ return scoredClusters;
232
+ }
233
+ async function appendPatternSummary(workspaceDir, clusters, now) {
234
+ const dreamsPath = path.join(workspaceDir, DREAMS_MD_FILENAME);
235
+ const existing = await fs.readFile(dreamsPath, "utf-8").catch((err) => {
236
+ if (err?.code === "ENOENT") return "";
237
+ throw err;
238
+ });
239
+ const day = isoDay(now);
240
+ const lines = [];
241
+ if (existing.trim().length === 0) lines.push("# Dream Diary", "");
242
+ lines.push(`## REM Pattern Discovery — ${day}`, "");
243
+ lines.push(`*${now.toISOString()}*`, "");
244
+ for (let i = 0; i < clusters.length; i++) {
245
+ const cluster = clusters[i];
246
+ lines.push(`### Pattern ${i + 1}: ${cluster.distinctPaths.length} files, strength=${cluster.strength.toFixed(2)}`);
247
+ lines.push("");
248
+ lines.push(`**Files involved:** ${cluster.distinctPaths.join(", ")}`);
249
+ lines.push(`**Shared query themes:** ${cluster.sharedQueries.length} overlapping queries`);
250
+ lines.push(`**Members:** ${cluster.members.length} snippets`);
251
+ lines.push("");
252
+ const rep = cluster.representative;
253
+ lines.push(`> ${rep.snippet?.slice(0, 200) ?? "(no snippet)"}`);
254
+ lines.push(`> — ${rep.path}:${rep.startLine}-${rep.endLine}`);
255
+ lines.push("");
256
+ }
257
+ lines.push("---", "");
258
+ const next = `${existing}${existing.trim().length > 0 && !existing.endsWith("\n") ? "\n" : ""}${lines.join("\n")}`;
259
+ await fs.writeFile(dreamsPath, next, "utf-8");
260
+ }
261
+ async function writeLastRun(workspaceDir, params) {
262
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
263
+ const durationMs = Math.max(0, Date.now() - params.startMs);
264
+ const lastRun = {
265
+ version: 2,
266
+ phase: "rem",
267
+ runId: params.runId,
268
+ startedAt: params.startedAt,
269
+ finishedAt,
270
+ durationMs,
271
+ ok: params.ok,
272
+ reason: params.reason,
273
+ config: params.cfg,
274
+ rem: params.rem,
275
+ ...params.errorMessage ? { errorMessage: params.errorMessage } : {}
276
+ };
277
+ const lastRunPath = path.join(workspaceDir, DREAMING_DIR_RELATIVE, "last-run-rem.json");
278
+ await fs.mkdir(path.dirname(lastRunPath), { recursive: true });
279
+ const tmp = `${lastRunPath}.${process.pid}.${Date.now()}.tmp`;
280
+ await fs.writeFile(tmp, `${JSON.stringify(lastRun, null, 2)}\n`, "utf-8");
281
+ await fs.rename(tmp, lastRunPath);
282
+ }
283
+ //#endregion
284
+ export { runRemPatterns };
285
+
286
+ //# sourceMappingURL=rem-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rem-patterns.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/rem-patterns.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { DREAMING_DIR_RELATIVE, DREAMS_MD_FILENAME, MS_PER_DAY } from './constants.js';\nimport type { DreamingRemConfig } from './config.js';\nimport {\n bumpEntryPhaseSignal,\n loadDreamingStore,\n saveDreamingStore,\n type DreamingStoreEntry,\n} from './short-term-store.js';\nimport { isoDay } from './utils.js';\nimport {\n DREAMING_LAST_RUN_FORMAT_VERSION,\n type DreamingRemLastRun,\n} from './last-run.js';\n\nconst log = createLogger('Dreaming:REM');\n\n// ── Pattern types ──────────────────────────────────────────────────────\n\ntype PatternCluster = {\n representative: DreamingStoreEntry;\n members: DreamingStoreEntry[];\n strength: number;\n /** Shared query hashes across members. */\n sharedQueries: string[];\n /** Distinct source files involved. */\n distinctPaths: string[];\n};\n\n// ── Config defaults ────────────────────────────────────────────────────\n\nfunction resolveConfig(overrides?: Partial<DreamingRemConfig>): DreamingRemConfig {\n return {\n enabled: overrides?.enabled === true,\n cron: typeof overrides?.cron === 'string' ? overrides.cron : '0 5 * * 0',\n lookbackDays: Math.max(1, Math.floor(Number(overrides?.lookbackDays) || 7)),\n limit: Math.max(0, Math.floor(Number(overrides?.limit) || 10)),\n minPatternStrength: Math.max(0, Math.min(1, Number(overrides?.minPatternStrength) || 0.75)),\n };\n}\n\n// ── Core REM pattern discovery ─────────────────────────────────────────\n\n/**\n * REM phase: cross-session pattern discovery.\n *\n * Scans the short-term store for entries that share query hashes or\n * appear across multiple daily files, then clusters them to identify\n * recurring themes/patterns. Bumps `remHits` on touched entries and\n * optionally writes a pattern summary to DREAMS.md.\n *\n * Runs weekly; expensive but insightful.\n */\nexport async function runRemPatterns(params: {\n workspaceDir: string;\n config?: Partial<DreamingRemConfig>;\n now?: Date;\n}): Promise<{\n ok: boolean;\n reason: string;\n patternsDiscovered: number;\n entriesAnalyzed: number;\n}> {\n const cfg = resolveConfig(params.config);\n const now = params.now ?? new Date();\n const startedAt = now.toISOString();\n const runId = `rem:${startedAt}:${process.pid}`;\n const startMs = Date.now();\n const nowMs = now.getTime();\n\n if (!cfg.enabled) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns disabled', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 },\n });\n return { ok: true, reason: 'REM patterns disabled', patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n\n try {\n const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });\n\n // Filter entries within the lookback window.\n const cutoffMs = nowMs - cfg.lookbackDays * MS_PER_DAY;\n const recentEntries = Object.values(store.entries ?? {}).filter(\n (entry): entry is DreamingStoreEntry => {\n if (!entry || typeof entry !== 'object') return false;\n if (!entry.lastRecalledAt) return false;\n const lastMs = Date.parse(entry.lastRecalledAt);\n return Number.isFinite(lastMs) && lastMs >= cutoffMs;\n },\n );\n\n if (recentEntries.length < 2) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'not enough recent entries for pattern analysis', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: recentEntries.length },\n });\n return {\n ok: true,\n reason: 'not enough recent entries for pattern analysis',\n patternsDiscovered: 0,\n entriesAnalyzed: recentEntries.length,\n };\n }\n\n // Discover patterns by clustering entries that share query hashes.\n const clusters = discoverPatternClusters(recentEntries, cfg.minPatternStrength);\n const topClusters = clusters.slice(0, cfg.limit);\n\n // Bump remHits on all entries that belong to a discovered pattern.\n const touchedKeys = new Set<string>();\n for (const cluster of topClusters) {\n for (const member of cluster.members) {\n if (!touchedKeys.has(member.key)) {\n touchedKeys.add(member.key);\n const storeEntry = store.entries[member.key];\n if (storeEntry) {\n bumpEntryPhaseSignal(storeEntry, 'remHits');\n }\n }\n }\n }\n\n store.updatedAt = now.toISOString();\n await saveDreamingStore({ workspaceDir: params.workspaceDir, store });\n\n // Write pattern summary to DREAMS.md (append).\n if (topClusters.length > 0) {\n await appendPatternSummary(params.workspaceDir, topClusters, now);\n }\n\n log.info(\n {\n workspaceDir: params.workspaceDir,\n patterns: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n touched: touchedKeys.size,\n },\n 'REM pattern discovery complete',\n );\n\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns complete', startMs,\n rem: { patternsDiscovered: topClusters.length, entriesAnalyzed: recentEntries.length },\n });\n\n return {\n ok: true,\n reason: 'REM patterns complete',\n patternsDiscovered: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage, workspaceDir: params.workspaceDir }, `REM pattern discovery failed: ${errorMessage}`);\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: false, reason: `REM error: ${errorMessage}`, startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 }, errorMessage,\n }).catch(() => undefined);\n return { ok: false, reason: errorMessage, patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n}\n\n// ── Pattern clustering ─────────────────────────────────────────────────\n\n/**\n * Build an inverted index of queryHash → entries, then form clusters\n * where multiple entries share overlapping query hashes. Each cluster's\n * \"strength\" is the ratio of shared queries to total unique queries.\n */\nfunction discoverPatternClusters(\n entries: DreamingStoreEntry[],\n minStrength: number,\n): PatternCluster[] {\n // Build inverted index: queryHash → entry keys.\n const hashToEntries = new Map<string, DreamingStoreEntry[]>();\n for (const entry of entries) {\n for (const queryHash of entry.queryHashes ?? []) {\n const group = hashToEntries.get(queryHash);\n if (group) {\n group.push(entry);\n } else {\n hashToEntries.set(queryHash, [entry]);\n }\n }\n }\n\n // Find query hashes that appear in 2+ distinct entries from different paths.\n const significantHashes: Array<{ hash: string; entries: DreamingStoreEntry[] }> = [];\n for (const [hash, group] of hashToEntries) {\n const uniquePaths = new Set(group.map((e) => e.path));\n if (group.length >= 2 && uniquePaths.size >= 2) {\n significantHashes.push({ hash, entries: group });\n }\n }\n\n if (significantHashes.length === 0) return [];\n\n // Merge overlapping groups into clusters using union-find.\n const keyToCluster = new Map<string, Set<string>>();\n const keyToEntry = new Map<string, DreamingStoreEntry>();\n\n for (const entry of entries) {\n keyToEntry.set(entry.key, entry);\n }\n\n for (const { entries: groupEntries } of significantHashes) {\n const keys = groupEntries.map((e) => e.key);\n // Find or create the cluster for the first key.\n let mergedCluster = keyToCluster.get(keys[0]!);\n if (!mergedCluster) {\n mergedCluster = new Set<string>();\n mergedCluster.add(keys[0]!);\n keyToCluster.set(keys[0]!, mergedCluster);\n }\n // Merge all other keys into this cluster.\n for (const key of keys.slice(1)) {\n const existingCluster = keyToCluster.get(key);\n if (existingCluster && existingCluster !== mergedCluster) {\n for (const existingKey of existingCluster) {\n mergedCluster.add(existingKey);\n keyToCluster.set(existingKey, mergedCluster);\n }\n } else {\n mergedCluster.add(key);\n keyToCluster.set(key, mergedCluster);\n }\n }\n }\n\n // Deduplicate cluster sets.\n const seenClusters = new Set<Set<string>>();\n const rawClusters: Set<string>[] = [];\n for (const cluster of keyToCluster.values()) {\n if (!seenClusters.has(cluster) && cluster.size >= 2) {\n seenClusters.add(cluster);\n rawClusters.push(cluster);\n }\n }\n\n // Score each cluster.\n const scoredClusters: PatternCluster[] = [];\n for (const clusterKeys of rawClusters) {\n const members: DreamingStoreEntry[] = [];\n for (const key of clusterKeys) {\n const entry = keyToEntry.get(key);\n if (entry) members.push(entry);\n }\n if (members.length < 2) continue;\n\n // Shared queries: hashes that appear in 2+ members.\n const hashCounts = new Map<string, number>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) {\n hashCounts.set(hash, (hashCounts.get(hash) ?? 0) + 1);\n }\n }\n const sharedQueries = [...hashCounts.entries()]\n .filter(([, count]) => count >= 2)\n .map(([hash]) => hash);\n\n const allUniqueHashes = new Set<string>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) allUniqueHashes.add(hash);\n }\n\n const strength = allUniqueHashes.size > 0 ? sharedQueries.length / allUniqueHashes.size : 0;\n if (strength < minStrength) continue;\n\n const distinctPaths = [...new Set(members.map((m) => m.path))];\n\n // Representative: the member with the highest totalSignalCount.\n const representative = members.reduce((best, current) =>\n (current.totalSignalCount ?? 0) > (best.totalSignalCount ?? 0) ? current : best,\n );\n\n scoredClusters.push({\n representative,\n members,\n strength,\n sharedQueries,\n distinctPaths,\n });\n }\n\n // Sort by strength descending, then by member count.\n scoredClusters.sort((a, b) => {\n if (b.strength !== a.strength) return b.strength - a.strength;\n return b.members.length - a.members.length;\n });\n\n return scoredClusters;\n}\n\n// ── DREAMS.md writer ───────────────────────────────────────────────────\n\nasync function appendPatternSummary(\n workspaceDir: string,\n clusters: PatternCluster[],\n now: Date,\n): Promise<void> {\n const dreamsPath = path.join(workspaceDir, DREAMS_MD_FILENAME);\n const existing = await fs.readFile(dreamsPath, 'utf-8').catch((err: unknown) => {\n if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') return '';\n throw err;\n });\n\n const day = isoDay(now);\n const lines: string[] = [];\n\n if (existing.trim().length === 0) {\n lines.push('# Dream Diary', '');\n }\n\n lines.push(`## REM Pattern Discovery — ${day}`, '');\n lines.push(`*${now.toISOString()}*`, '');\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i]!;\n lines.push(\n `### Pattern ${i + 1}: ${cluster.distinctPaths.length} files, strength=${cluster.strength.toFixed(2)}`,\n );\n lines.push('');\n lines.push(`**Files involved:** ${cluster.distinctPaths.join(', ')}`);\n lines.push(`**Shared query themes:** ${cluster.sharedQueries.length} overlapping queries`);\n lines.push(`**Members:** ${cluster.members.length} snippets`);\n lines.push('');\n // Include the representative snippet.\n const rep = cluster.representative;\n lines.push(`> ${rep.snippet?.slice(0, 200) ?? '(no snippet)'}`);\n lines.push(`> — ${rep.path}:${rep.startLine}-${rep.endLine}`);\n lines.push('');\n }\n\n lines.push('---', '');\n\n const separator = existing.trim().length > 0 && !existing.endsWith('\\n') ? '\\n' : '';\n const next = `${existing}${separator}${lines.join('\\n')}`;\n await fs.writeFile(dreamsPath, next, 'utf-8');\n}\n\n// ── Last-run writer ────────────────────────────────────────────────────\n\nasync function writeLastRun(\n workspaceDir: string,\n params: {\n runId: string;\n startedAt: string;\n cfg: DreamingRemConfig;\n ok: boolean;\n reason: string;\n startMs: number;\n rem: DreamingRemLastRun['rem'];\n errorMessage?: string;\n },\n): Promise<void> {\n const finishedAt = new Date().toISOString();\n const durationMs = Math.max(0, Date.now() - params.startMs);\n\n const lastRun: DreamingRemLastRun = {\n version: DREAMING_LAST_RUN_FORMAT_VERSION,\n phase: 'rem',\n runId: params.runId,\n startedAt: params.startedAt,\n finishedAt,\n durationMs,\n ok: params.ok,\n reason: params.reason,\n config: params.cfg,\n rem: params.rem,\n ...(params.errorMessage ? { errorMessage: params.errorMessage } : {}),\n };\n\n const lastRunPath = path.join(workspaceDir, DREAMING_DIR_RELATIVE, 'last-run-rem.json');\n await fs.mkdir(path.dirname(lastRunPath), { recursive: true });\n const tmp = `${lastRunPath}.${process.pid}.${Date.now()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(lastRun, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, lastRunPath);\n}\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { MemorySearchOptions } from '../../prompt/memory/index.js';
2
+ export type DreamingStoreEntry = {
3
+ key: string;
4
+ path: string;
5
+ startLine: number;
6
+ endLine: number;
7
+ snippet: string;
8
+ /** Number of times this snippet was returned by a memory recall query. */
9
+ recallCount: number;
10
+ /** Number of times recorded from daily log scanning (light sweep). */
11
+ dailyCount: number;
12
+ /** Number of times replayed from grounded context (agent-initiated). */
13
+ groundedCount: number;
14
+ /** Number of times the light phase touched this entry. */
15
+ lightHits: number;
16
+ /** Number of times the REM phase touched this entry. */
17
+ remHits: number;
18
+ /** Cross-phase hit count (light + deep + rem combined touches). */
19
+ phaseHitCount: number;
20
+ /** Weighted aggregate of all signal dimensions. */
21
+ totalSignalCount: number;
22
+ totalScore: number;
23
+ maxScore: number;
24
+ queryHashes: string[];
25
+ recallDays: string[];
26
+ firstRecalledAt: string;
27
+ lastRecalledAt: string;
28
+ promotedAt?: string;
29
+ };
30
+ export type DreamingStore = {
31
+ version: 1;
32
+ updatedAt: string;
33
+ entries: Record<string, DreamingStoreEntry>;
34
+ };
35
+ type MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];
36
+ export declare function recordDreamingRecalls(params: {
37
+ workspaceDir: string;
38
+ query: string;
39
+ matches: MemoryMatch[];
40
+ options?: MemorySearchOptions;
41
+ now?: Date;
42
+ }): Promise<{
43
+ recorded: number;
44
+ skipped: number;
45
+ storePath: string;
46
+ }>;
47
+ export declare function withDreamingPromotionLock<T>(workspaceDir: string, task: () => Promise<T>): Promise<T>;
48
+ export declare function loadDreamingStore(params: {
49
+ workspaceDir: string;
50
+ }): Promise<{
51
+ store: DreamingStore;
52
+ storePath: string;
53
+ }>;
54
+ export declare function saveDreamingStore(params: {
55
+ workspaceDir: string;
56
+ store: DreamingStore;
57
+ }): Promise<void>;
58
+ type PhaseSignalField = 'dailyCount' | 'groundedCount' | 'lightHits' | 'remHits';
59
+ /**
60
+ * Increment a phase-specific signal counter on an existing store entry.
61
+ * Also bumps `phaseHitCount` and `totalSignalCount`.
62
+ * Returns `true` if the entry existed and was updated.
63
+ */
64
+ export declare function bumpEntryPhaseSignal(entry: DreamingStoreEntry, field: PhaseSignalField, increment?: number): void;
65
+ export {};