@xopcai/xopc 0.0.23 → 0.0.25
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.
- package/dist/extensions/feishu/xopc.extension.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-C_bPhtBs.js +216 -0
- package/dist/gateway/static/root/assets/agents-C_bPhtBs.js.map +1 -0
- package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js +2 -0
- package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js.map +1 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js → attachment-preview-renderer-CxMJMbD2.js} +4 -4
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js.map → attachment-preview-renderer-CxMJMbD2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js → attachment-process-heavy-EFXPGfWk.js} +6 -6
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js.map → attachment-process-heavy-EFXPGfWk.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-utils-core-Dt6UxMPV.js → attachment-utils-core-ECbeoa9H.js} +1 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-ECbeoa9H.js.map +1 -0
- package/dist/gateway/static/root/assets/channels-settings-CeGoU9v8.js +9 -0
- package/dist/gateway/static/root/assets/{channels-settings-CGzrrBlT.js.map → channels-settings-CeGoU9v8.js.map} +1 -1
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js +2 -0
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-DpEYUvxB.js +2 -0
- package/dist/gateway/static/root/assets/{cron-page-BGCdDf2w.js.map → cron-page-DpEYUvxB.js.map} +1 -1
- package/dist/gateway/static/root/assets/cron-utils-Cvv0F3pa.js +3 -0
- package/dist/gateway/static/root/assets/{cron-utils-Dnks4wWv.js.map → cron-utils-Cvv0F3pa.js.map} +1 -1
- package/dist/gateway/static/root/assets/dist-C41N3YrO.js +2 -0
- package/dist/gateway/static/root/assets/{dist-BG1ChbY9.js.map → dist-C41N3YrO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js → docx-preview-F-jKDMNv.js} +2 -2
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js.map → docx-preview-F-jKDMNv.js.map} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js → excel-worksheet-utils-DPfAinZn.js} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js.map → excel-worksheet-utils-DPfAinZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-debug-page-CkkYZjNP.js +2 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CRC16AbL.js.map → extension-debug-page-CkkYZjNP.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-page-BjUIPVNG.js +2 -0
- package/dist/gateway/static/root/assets/{extension-page-BagrJnbm.js.map → extension-page-BjUIPVNG.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js +2 -0
- package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js.map +1 -0
- package/dist/gateway/static/root/assets/index-DwzwDCjW.js +150 -0
- package/dist/gateway/static/root/assets/index-DwzwDCjW.js.map +1 -0
- package/dist/gateway/static/root/assets/index-dhtHG1nU.css +1 -0
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js → jszip.min-CL3dfyxs.js} +1 -1
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js.map → jszip.min-CL3dfyxs.js.map} +1 -1
- package/dist/gateway/static/root/assets/logs-page-BtwGPuw2.js +2 -0
- package/dist/gateway/static/root/assets/{logs-page-Bo9EsE_D.js.map → logs-page-BtwGPuw2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js → pdf-CX6ji-QC.js} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js.map → pdf-CX6ji-QC.js.map} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-4rKFDn2k.js +2 -0
- package/dist/gateway/static/root/assets/{sessions-page-CDgXq8qp.js.map → sessions-page-4rKFDn2k.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-_siDuHeF.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-BRS5qYTw.js.map → skills-page-_siDuHeF.js.map} +1 -1
- package/dist/gateway/static/root/assets/vendor-swr-B5fPo7KK.js +2 -0
- package/dist/gateway/static/root/assets/{vendor-swr-Dp4nzp5h.js.map → vendor-swr-B5fPo7KK.js.map} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js → xlsx-CPtvfmVF.js} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js.map → xlsx-CPtvfmVF.js.map} +1 -1
- package/dist/gateway/static/root/index.html +5 -4
- package/dist/package.js +1 -1
- package/dist/src/agent/image/tool-model-config.js +2 -2
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.d.ts +20 -0
- package/dist/src/agent/memory/dreaming/config.js +44 -0
- package/dist/src/agent/memory/dreaming/config.js.map +1 -0
- package/dist/src/agent/memory/dreaming/constants.d.ts +8 -0
- package/dist/src/agent/memory/dreaming/constants.js +14 -0
- package/dist/src/agent/memory/dreaming/constants.js.map +1 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +22 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js +337 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -0
- package/dist/src/agent/memory/dreaming/preview.d.ts +26 -0
- package/dist/src/agent/memory/dreaming/preview.js +176 -0
- package/dist/src/agent/memory/dreaming/preview.js.map +1 -0
- package/dist/src/agent/memory/dreaming/short-term-store.d.ts +45 -0
- package/dist/src/agent/memory/dreaming/short-term-store.js +187 -0
- package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -0
- package/dist/src/agent/orchestration/agent-orchestrator.js +17 -0
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/service.d.ts +6 -0
- package/dist/src/agent/service.js +52 -0
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/dreaming-tool.d.ts +7 -0
- package/dist/src/agent/tools/dreaming-tool.js +102 -0
- package/dist/src/agent/tools/dreaming-tool.js.map +1 -0
- package/dist/src/agent/tools/execute-code-tool.js +1 -1
- package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.js +5 -0
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -0
- package/dist/src/agent/tools/index.js +2 -1
- package/dist/src/agent/tools/memory-tool.js +9 -2
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/cli/commands/extension-dev.d.ts +2 -0
- package/dist/src/cli/commands/extension-dev.js +196 -0
- package/dist/src/cli/commands/extension-dev.js.map +1 -0
- package/dist/src/cli/commands/extension-marketplace.d.ts +4 -0
- package/dist/src/cli/commands/extension-marketplace.js +145 -0
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -0
- package/dist/src/cli/commands/extension-pack.d.ts +2 -0
- package/dist/src/cli/commands/extension-pack.js +242 -0
- package/dist/src/cli/commands/extension-pack.js.map +1 -0
- package/dist/src/cli/commands/extension.js +13 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/index.js +5 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.d.ts +39 -0
- package/dist/src/config/schema.js +17 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/api.d.ts +6 -1
- package/dist/src/extensions/api.js +30 -0
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/engine-check.d.ts +14 -0
- package/dist/src/extensions/engine-check.js +148 -0
- package/dist/src/extensions/engine-check.js.map +1 -0
- package/dist/src/extensions/loader.js +23 -0
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/marketplace.d.ts +24 -0
- package/dist/src/extensions/marketplace.js +98 -0
- package/dist/src/extensions/marketplace.js.map +1 -0
- package/dist/src/extensions/normalize-manifest.js +16 -4
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +2 -0
- package/dist/src/extensions/sdk/index.js +2 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/sdk/testing.d.ts +49 -3
- package/dist/src/extensions/sdk/testing.js +174 -5
- package/dist/src/extensions/sdk/testing.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +5 -0
- package/dist/src/extensions/types/manifest.d.ts +17 -0
- package/dist/src/extensions/when-context.d.ts +6 -0
- package/dist/src/extensions/when-context.js +28 -0
- package/dist/src/extensions/when-context.js.map +1 -0
- package/dist/src/extensions/when-expression.d.ts +11 -0
- package/dist/src/extensions/when-expression.js +215 -0
- package/dist/src/extensions/when-expression.js.map +1 -0
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +13 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +28 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +48 -0
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.d.ts +3 -0
- package/dist/src/gateway/hono/routes/dreaming.js +198 -0
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -0
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/service.js +7 -0
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/providers/index.d.ts +6 -3
- package/dist/src/providers/index.js +12 -23
- package/dist/src/providers/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BY_DaknM.js +0 -216
- package/dist/gateway/static/root/assets/agents-BY_DaknM.js.map +0 -1
- package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-Dt6UxMPV.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CGzrrBlT.js +0 -9
- package/dist/gateway/static/root/assets/cron-page-BGCdDf2w.js +0 -2
- package/dist/gateway/static/root/assets/cron-utils-Dnks4wWv.js +0 -3
- package/dist/gateway/static/root/assets/dist-BG1ChbY9.js +0 -2
- package/dist/gateway/static/root/assets/extension-debug-page-CRC16AbL.js +0 -2
- package/dist/gateway/static/root/assets/extension-page-BagrJnbm.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js.map +0 -1
- package/dist/gateway/static/root/assets/index-CYVs9x8D.css +0 -1
- package/dist/gateway/static/root/assets/index-KNzRh6gu.js +0 -150
- package/dist/gateway/static/root/assets/index-KNzRh6gu.js.map +0 -1
- package/dist/gateway/static/root/assets/logs-page-Bo9EsE_D.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-CDgXq8qp.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-BRS5qYTw.js +0 -3
- package/dist/gateway/static/root/assets/vendor-swr-Dp4nzp5h.js +0 -2
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { MEMORY_MD_FILENAME } from "./constants.js";
|
|
2
|
+
import { loadDreamingStore } from "./short-term-store.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
//#region src/agent/memory/dreaming/preview.ts
|
|
7
|
+
function clamp01(value) {
|
|
8
|
+
if (!Number.isFinite(value)) return 0;
|
|
9
|
+
return Math.max(0, Math.min(1, value));
|
|
10
|
+
}
|
|
11
|
+
function compare(a, b) {
|
|
12
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
13
|
+
if (b.recallCount !== a.recallCount) return b.recallCount - a.recallCount;
|
|
14
|
+
const aMs = Date.parse(a.lastRecalledAt);
|
|
15
|
+
const bMs = Date.parse(b.lastRecalledAt);
|
|
16
|
+
if (Number.isFinite(aMs) || Number.isFinite(bMs)) {
|
|
17
|
+
if (bMs !== aMs) return bMs - aMs;
|
|
18
|
+
}
|
|
19
|
+
return a.path.localeCompare(b.path);
|
|
20
|
+
}
|
|
21
|
+
async function readFileLines(fullPath) {
|
|
22
|
+
try {
|
|
23
|
+
return (await fs.readFile(fullPath, "utf-8")).split(/\r?\n/);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err?.code === "ENOENT") return null;
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function sliceRange(lines, startLine, endLine) {
|
|
30
|
+
const startIdx = Math.max(0, startLine - 1);
|
|
31
|
+
const endIdx = Math.min(lines.length, endLine);
|
|
32
|
+
if (startIdx >= endIdx) return "";
|
|
33
|
+
return lines.slice(startIdx, endIdx).map((l) => l.trim()).filter(Boolean).join(" ").replace(/\s+/g, " ").trim().slice(0, 360);
|
|
34
|
+
}
|
|
35
|
+
function normalizeSnippetForHash(snippet) {
|
|
36
|
+
return snippet.trim().replace(/\s+/g, " ").replace(/[“”]/g, "\"").replace(/[‘’]/g, "'").toLowerCase().slice(0, 512);
|
|
37
|
+
}
|
|
38
|
+
function snippetHash(snippet) {
|
|
39
|
+
return createHash("sha1").update(normalizeSnippetForHash(snippet)).digest("hex").slice(0, 12);
|
|
40
|
+
}
|
|
41
|
+
function extractPromotionMarkers(memoryText) {
|
|
42
|
+
const keys = /* @__PURE__ */ new Set();
|
|
43
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
44
|
+
for (const match of memoryText.matchAll(/<!--\s*xopc-memory-promotion\b([\s\S]*?)-->/gi)) {
|
|
45
|
+
const body = match[1] ?? "";
|
|
46
|
+
const k = body.match(/\bkey\s*=\s*"([^"]+)"/i)?.[1]?.trim();
|
|
47
|
+
const h = body.match(/\bhash\s*=\s*"([^"]+)"/i)?.[1]?.trim();
|
|
48
|
+
if (k) keys.add(k);
|
|
49
|
+
if (h) hashes.add(h);
|
|
50
|
+
}
|
|
51
|
+
for (const match of memoryText.matchAll(/<!--\s*xopc-memory-promotion:([^\n]+?)\s*-->/gi)) {
|
|
52
|
+
const key = match[1]?.trim();
|
|
53
|
+
if (key) keys.add(key);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
keys,
|
|
57
|
+
hashes
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function isContaminatedSnippet(snippet) {
|
|
61
|
+
const s = snippet.trim();
|
|
62
|
+
if (!s) return true;
|
|
63
|
+
const lower = s.toLowerCase();
|
|
64
|
+
if ([
|
|
65
|
+
/\b(system|assistant|tool)\s*:/i,
|
|
66
|
+
/<\s*(system|assistant|tool)\b/i,
|
|
67
|
+
/<!--\s*xopc-memory-promotion\b/i,
|
|
68
|
+
/tool_call_id|toolcallid|function_call|arguments\s*:\s*\{/i,
|
|
69
|
+
/"tool"\s*:\s*|\btool_name\b|\btool\b\s*results?/i,
|
|
70
|
+
/you are (an|a)\s+(ai|assistant)|follow these instructions/i,
|
|
71
|
+
/begin\s+(system prompt|instructions)|end\s+(system prompt|instructions)/i,
|
|
72
|
+
/```/i,
|
|
73
|
+
/\b__xopc_/i
|
|
74
|
+
].some((p) => p.test(s))) return true;
|
|
75
|
+
if ((s.match(/[{}[\]]/g) ?? []).length >= 12) return true;
|
|
76
|
+
if ((lower.match(/https?:\/\//g) ?? []).length >= 3) return true;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
function resolveDefaultConfig(overrides) {
|
|
80
|
+
const enabled = overrides?.enabled === true;
|
|
81
|
+
const minScore = typeof overrides?.minScore === "number" ? overrides.minScore : .8;
|
|
82
|
+
const minRecallCount = typeof overrides?.minRecallCount === "number" ? overrides.minRecallCount : 3;
|
|
83
|
+
const limit = typeof overrides?.limit === "number" ? overrides.limit : 10;
|
|
84
|
+
return {
|
|
85
|
+
enabled,
|
|
86
|
+
minScore: clamp01(minScore),
|
|
87
|
+
minRecallCount: Math.max(1, Math.floor(minRecallCount)),
|
|
88
|
+
limit: Math.max(0, Math.floor(limit))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function previewDreamingDeepPromotion(params) {
|
|
92
|
+
const cfg = resolveDefaultConfig(params.config);
|
|
93
|
+
const memoryPath = path.join(params.workspaceDir, MEMORY_MD_FILENAME);
|
|
94
|
+
if (!cfg.enabled) return {
|
|
95
|
+
ok: true,
|
|
96
|
+
reason: "dreaming disabled",
|
|
97
|
+
items: [],
|
|
98
|
+
memoryPath
|
|
99
|
+
};
|
|
100
|
+
const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });
|
|
101
|
+
const ranked = Object.values(store.entries ?? {}).filter((e) => {
|
|
102
|
+
if (!e || typeof e !== "object") return false;
|
|
103
|
+
if (e.promotedAt) return false;
|
|
104
|
+
if (!e.path || !e.path.startsWith("memory/")) return false;
|
|
105
|
+
if (e.recallCount < cfg.minRecallCount) return false;
|
|
106
|
+
return clamp01(e.recallCount > 0 ? e.totalScore / e.recallCount : 0) >= cfg.minScore;
|
|
107
|
+
}).map((e) => {
|
|
108
|
+
const avgScore = e.recallCount > 0 ? clamp01(e.totalScore / e.recallCount) : 0;
|
|
109
|
+
const score = clamp01(avgScore + clamp01(Math.log1p(e.recallCount) / Math.log1p(10)) * .12);
|
|
110
|
+
return {
|
|
111
|
+
...e,
|
|
112
|
+
avgScore,
|
|
113
|
+
score
|
|
114
|
+
};
|
|
115
|
+
}).sort(compare);
|
|
116
|
+
const markers = extractPromotionMarkers(await fs.readFile(memoryPath, "utf-8").catch((err) => {
|
|
117
|
+
if (err?.code === "ENOENT") return "";
|
|
118
|
+
throw err;
|
|
119
|
+
}));
|
|
120
|
+
const limit = Math.min(Math.max(params.limit ?? 20, 1), 50);
|
|
121
|
+
const out = [];
|
|
122
|
+
const scanCap = Math.min(ranked.length, Math.max(limit * 3, limit));
|
|
123
|
+
for (const candidate of ranked.slice(0, scanCap)) {
|
|
124
|
+
if (markers.keys.has(candidate.key)) {
|
|
125
|
+
out.push({
|
|
126
|
+
key: candidate.key,
|
|
127
|
+
hash: "",
|
|
128
|
+
snippet: "",
|
|
129
|
+
path: candidate.path,
|
|
130
|
+
startLine: candidate.startLine,
|
|
131
|
+
endLine: candidate.endLine,
|
|
132
|
+
score: candidate.score,
|
|
133
|
+
avgScore: candidate.avgScore,
|
|
134
|
+
recallCount: candidate.recallCount,
|
|
135
|
+
alreadyPromotedByKey: true,
|
|
136
|
+
alreadyPromotedByHash: false,
|
|
137
|
+
skippedReason: "already promoted (key)"
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const lines = await readFileLines(path.join(params.workspaceDir, candidate.path));
|
|
142
|
+
if (!lines) continue;
|
|
143
|
+
const startLine = Math.max(1, Math.floor(candidate.startLine));
|
|
144
|
+
const endLine = Math.max(startLine, Math.floor(candidate.endLine));
|
|
145
|
+
const snippet = sliceRange(lines, startLine, endLine);
|
|
146
|
+
if (!snippet) continue;
|
|
147
|
+
if (isContaminatedSnippet(snippet)) continue;
|
|
148
|
+
const hash = snippetHash(snippet);
|
|
149
|
+
const alreadyPromotedByHash = markers.hashes.has(hash);
|
|
150
|
+
out.push({
|
|
151
|
+
key: candidate.key,
|
|
152
|
+
hash,
|
|
153
|
+
snippet,
|
|
154
|
+
path: candidate.path,
|
|
155
|
+
startLine,
|
|
156
|
+
endLine,
|
|
157
|
+
score: candidate.score,
|
|
158
|
+
avgScore: candidate.avgScore,
|
|
159
|
+
recallCount: candidate.recallCount,
|
|
160
|
+
alreadyPromotedByKey: false,
|
|
161
|
+
alreadyPromotedByHash,
|
|
162
|
+
skippedReason: alreadyPromotedByHash ? "already promoted (hash)" : null
|
|
163
|
+
});
|
|
164
|
+
if (out.filter((x) => !x.skippedReason).length >= limit) break;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
ok: true,
|
|
168
|
+
reason: "ok",
|
|
169
|
+
items: out,
|
|
170
|
+
memoryPath
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { previewDreamingDeepPromotion };
|
|
175
|
+
|
|
176
|
+
//# 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';\nimport { createHash } from 'node:crypto';\n\nimport type { DreamingDeepConfig } from './deep-promotion.js';\nimport { loadDreamingStore, type DreamingStoreEntry } from './short-term-store.js';\nimport { MEMORY_MD_FILENAME } from './constants.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 alreadyPromotedByKey: boolean;\n alreadyPromotedByHash: boolean;\n skippedReason: string | null;\n};\n\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction compare(a: { score: number; recallCount: number; lastRecalledAt: string; path: string }, b: { score: number; recallCount: number; lastRecalledAt: string; path: string }): number {\n if (b.score !== a.score) return b.score - a.score;\n if (b.recallCount !== a.recallCount) return b.recallCount - a.recallCount;\n const aMs = Date.parse(a.lastRecalledAt);\n const bMs = Date.parse(b.lastRecalledAt);\n if (Number.isFinite(aMs) || Number.isFinite(bMs)) {\n if (bMs !== aMs) return bMs - aMs;\n }\n return a.path.localeCompare(b.path);\n}\n\nasync function readFileLines(fullPath: string): Promise<string[] | null> {\n try {\n const raw = await fs.readFile(fullPath, 'utf-8');\n return raw.split(/\\r?\\n/);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return null;\n throw err;\n }\n}\n\nfunction sliceRange(lines: string[], startLine: number, endLine: number): string {\n const startIdx = Math.max(0, startLine - 1);\n const endIdx = Math.min(lines.length, endLine);\n if (startIdx >= endIdx) return '';\n return lines\n .slice(startIdx, endIdx)\n .map((l) => l.trim())\n .filter(Boolean)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 360);\n}\n\nfunction normalizeSnippetForHash(snippet: string): string {\n return snippet\n .trim()\n .replace(/\\s+/g, ' ')\n .replace(/[“”]/g, '\"')\n .replace(/[‘’]/g, \"'\")\n .toLowerCase()\n .slice(0, 512);\n}\n\nfunction snippetHash(snippet: string): string {\n return createHash('sha1').update(normalizeSnippetForHash(snippet)).digest('hex').slice(0, 12);\n}\n\nfunction extractPromotionMarkers(memoryText: string): { keys: Set<string>; hashes: Set<string> } {\n const keys = new Set<string>();\n const hashes = new Set<string>();\n for (const match of memoryText.matchAll(/<!--\\s*xopc-memory-promotion\\b([\\s\\S]*?)-->/gi)) {\n const body = match[1] ?? '';\n const k = body.match(/\\bkey\\s*=\\s*\"([^\"]+)\"/i)?.[1]?.trim();\n const h = body.match(/\\bhash\\s*=\\s*\"([^\"]+)\"/i)?.[1]?.trim();\n if (k) keys.add(k);\n if (h) hashes.add(h);\n }\n for (const match of memoryText.matchAll(/<!--\\s*xopc-memory-promotion:([^\\n]+?)\\s*-->/gi)) {\n const key = match[1]?.trim();\n if (key) keys.add(key);\n }\n return { keys, hashes };\n}\n\nfunction isContaminatedSnippet(snippet: string): boolean {\n const s = snippet.trim();\n if (!s) return true;\n const lower = s.toLowerCase();\n const patterns: RegExp[] = [\n /\\b(system|assistant|tool)\\s*:/i,\n /<\\s*(system|assistant|tool)\\b/i,\n /<!--\\s*xopc-memory-promotion\\b/i,\n /tool_call_id|toolcallid|function_call|arguments\\s*:\\s*\\{/i,\n /\"tool\"\\s*:\\s*|\\btool_name\\b|\\btool\\b\\s*results?/i,\n /you are (an|a)\\s+(ai|assistant)|follow these instructions/i,\n /begin\\s+(system prompt|instructions)|end\\s+(system prompt|instructions)/i,\n /```/i,\n /\\b__xopc_/i,\n ];\n if (patterns.some((p) => p.test(s))) return true;\n const braceCount = (s.match(/[{}[\\]]/g) ?? []).length;\n if (braceCount >= 12) return true;\n const urlCount = (lower.match(/https?:\\/\\//g) ?? []).length;\n if (urlCount >= 3) return true;\n return false;\n}\n\nfunction resolveDefaultConfig(overrides?: Partial<DreamingDeepConfig>): DreamingDeepConfig {\n const enabled = overrides?.enabled === true;\n const minScore = typeof overrides?.minScore === 'number' ? overrides.minScore : 0.8;\n const minRecallCount = typeof overrides?.minRecallCount === 'number' ? overrides.minRecallCount : 3;\n const limit = typeof overrides?.limit === 'number' ? overrides.limit : 10;\n return {\n enabled,\n minScore: clamp01(minScore),\n minRecallCount: Math.max(1, Math.floor(minRecallCount)),\n limit: Math.max(0, Math.floor(limit)),\n };\n}\n\nexport async function previewDreamingDeepPromotion(params: {\n workspaceDir: string;\n config?: Partial<DreamingDeepConfig>;\n limit?: number;\n}): Promise<{ ok: boolean; reason: string; items: PreviewItem[]; memoryPath: string }> {\n const cfg = resolveDefaultConfig(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 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 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 = e.recallCount > 0 ? clamp01(e.totalScore / e.recallCount) : 0;\n const reinforcement = clamp01(Math.log1p(e.recallCount) / Math.log1p(10)) * 0.12;\n const score = clamp01(avgScore + reinforcement);\n return { ...e, avgScore, score };\n })\n .sort(compare);\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 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 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\n"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
recallCount: number;
|
|
9
|
+
totalScore: number;
|
|
10
|
+
maxScore: number;
|
|
11
|
+
queryHashes: string[];
|
|
12
|
+
recallDays: string[];
|
|
13
|
+
firstRecalledAt: string;
|
|
14
|
+
lastRecalledAt: string;
|
|
15
|
+
promotedAt?: string;
|
|
16
|
+
};
|
|
17
|
+
export type DreamingStore = {
|
|
18
|
+
version: 1;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
entries: Record<string, DreamingStoreEntry>;
|
|
21
|
+
};
|
|
22
|
+
type MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];
|
|
23
|
+
export declare function recordDreamingRecalls(params: {
|
|
24
|
+
workspaceDir: string;
|
|
25
|
+
query: string;
|
|
26
|
+
matches: MemoryMatch[];
|
|
27
|
+
options?: MemorySearchOptions;
|
|
28
|
+
now?: Date;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
recorded: number;
|
|
31
|
+
skipped: number;
|
|
32
|
+
storePath: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function withDreamingPromotionLock<T>(workspaceDir: string, task: () => Promise<T>): Promise<T>;
|
|
35
|
+
export declare function loadDreamingStore(params: {
|
|
36
|
+
workspaceDir: string;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
store: DreamingStore;
|
|
39
|
+
storePath: string;
|
|
40
|
+
}>;
|
|
41
|
+
export declare function saveDreamingStore(params: {
|
|
42
|
+
workspaceDir: string;
|
|
43
|
+
store: DreamingStore;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createLogger } from "../../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../../utils/logger.js";
|
|
3
|
+
import { SHORT_TERM_PROMOTION_LOCK_RELATIVE, SHORT_TERM_RECALL_STORE_RELATIVE } from "./constants.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
+
//#region src/agent/memory/dreaming/short-term-store.ts
|
|
8
|
+
init_logger();
|
|
9
|
+
const log = createLogger("Dreaming:Store");
|
|
10
|
+
function normalizeMemoryPath(raw) {
|
|
11
|
+
return raw.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
12
|
+
}
|
|
13
|
+
function isDailyWorkspaceMemoryPath(rel) {
|
|
14
|
+
const p = normalizeMemoryPath(rel);
|
|
15
|
+
return /^memory\/\d{4}-\d{2}-\d{2}\.md$/i.test(p);
|
|
16
|
+
}
|
|
17
|
+
function clampScore(value) {
|
|
18
|
+
if (!Number.isFinite(value)) return 0;
|
|
19
|
+
return Math.max(0, Math.min(1, value));
|
|
20
|
+
}
|
|
21
|
+
function isoDay(now) {
|
|
22
|
+
return now.toISOString().slice(0, 10);
|
|
23
|
+
}
|
|
24
|
+
function hashQuery(query) {
|
|
25
|
+
return createHash("sha1").update(query.trim().toLowerCase()).digest("hex").slice(0, 12);
|
|
26
|
+
}
|
|
27
|
+
function buildEntryKey(params) {
|
|
28
|
+
return `memory:${normalizeMemoryPath(params.path)}:${params.startLine}:${params.endLine}`;
|
|
29
|
+
}
|
|
30
|
+
function mergeRecentDistinct(existing, nextValue, limit) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
const normalized = existing.filter((v) => typeof v === "string").map((v) => v.trim()).filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));
|
|
33
|
+
if (nextValue && !normalized.includes(nextValue)) normalized.push(nextValue);
|
|
34
|
+
return normalized.length <= limit ? normalized : normalized.slice(-limit);
|
|
35
|
+
}
|
|
36
|
+
function mergeQueryHashes(existing, nextHash) {
|
|
37
|
+
return mergeRecentDistinct(existing, nextHash, 32);
|
|
38
|
+
}
|
|
39
|
+
function emptyStore(nowIso) {
|
|
40
|
+
return {
|
|
41
|
+
version: 1,
|
|
42
|
+
updatedAt: nowIso,
|
|
43
|
+
entries: {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function ensureDreamDir(workspaceDir) {
|
|
47
|
+
const dir = path.join(workspaceDir, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));
|
|
48
|
+
await fs.mkdir(dir, { recursive: true });
|
|
49
|
+
return dir;
|
|
50
|
+
}
|
|
51
|
+
async function readStore(workspaceDir, nowIso) {
|
|
52
|
+
const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);
|
|
53
|
+
try {
|
|
54
|
+
const raw = await fs.readFile(storePath, "utf-8");
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
if (!parsed || typeof parsed !== "object") return emptyStore(nowIso);
|
|
57
|
+
const rec = parsed;
|
|
58
|
+
if (rec.version !== 1 || !rec.entries || typeof rec.entries !== "object") return emptyStore(nowIso);
|
|
59
|
+
return {
|
|
60
|
+
version: 1,
|
|
61
|
+
updatedAt: typeof rec.updatedAt === "string" ? rec.updatedAt : nowIso,
|
|
62
|
+
entries: rec.entries
|
|
63
|
+
};
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err?.code === "ENOENT") return emptyStore(nowIso);
|
|
66
|
+
log.warn({
|
|
67
|
+
err,
|
|
68
|
+
workspaceDir
|
|
69
|
+
}, "Failed to read dreaming store; resetting");
|
|
70
|
+
return emptyStore(nowIso);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function writeStore(workspaceDir, store) {
|
|
74
|
+
await ensureDreamDir(workspaceDir);
|
|
75
|
+
const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);
|
|
76
|
+
const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
77
|
+
await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\n`, "utf-8");
|
|
78
|
+
await fs.rename(tmp, storePath);
|
|
79
|
+
}
|
|
80
|
+
async function recordDreamingRecalls(params) {
|
|
81
|
+
const workspaceDir = params.workspaceDir.trim();
|
|
82
|
+
const query = params.query.trim();
|
|
83
|
+
if (!workspaceDir || !query) return {
|
|
84
|
+
recorded: 0,
|
|
85
|
+
skipped: params.matches.length,
|
|
86
|
+
storePath: SHORT_TERM_RECALL_STORE_RELATIVE
|
|
87
|
+
};
|
|
88
|
+
const now = params.now ?? /* @__PURE__ */ new Date();
|
|
89
|
+
const nowIso = now.toISOString();
|
|
90
|
+
const dayBucket = isoDay(now);
|
|
91
|
+
const qHash = hashQuery(query);
|
|
92
|
+
const store = await readStore(workspaceDir, nowIso);
|
|
93
|
+
let recorded = 0;
|
|
94
|
+
let skipped = 0;
|
|
95
|
+
for (const match of params.matches) {
|
|
96
|
+
const file = typeof match?.file === "string" ? match.file : "";
|
|
97
|
+
if (!file || !isDailyWorkspaceMemoryPath(file)) {
|
|
98
|
+
skipped += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const lines = typeof match?.lines === "string" ? match.lines.trim() : "";
|
|
102
|
+
const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];
|
|
103
|
+
const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));
|
|
104
|
+
const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));
|
|
105
|
+
if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {
|
|
106
|
+
skipped += 1;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const score = clampScore(Number(match.score));
|
|
110
|
+
const key = buildEntryKey({
|
|
111
|
+
path: file,
|
|
112
|
+
startLine,
|
|
113
|
+
endLine
|
|
114
|
+
});
|
|
115
|
+
const existing = store.entries[key];
|
|
116
|
+
const snippet = lines.length > 0 ? lines.slice(0, 360) : file;
|
|
117
|
+
const next = existing ? {
|
|
118
|
+
...existing,
|
|
119
|
+
snippet,
|
|
120
|
+
recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),
|
|
121
|
+
totalScore: Math.max(0, existing.totalScore + score),
|
|
122
|
+
maxScore: Math.max(existing.maxScore, score),
|
|
123
|
+
queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),
|
|
124
|
+
recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),
|
|
125
|
+
lastRecalledAt: nowIso
|
|
126
|
+
} : {
|
|
127
|
+
key,
|
|
128
|
+
path: normalizeMemoryPath(file),
|
|
129
|
+
startLine,
|
|
130
|
+
endLine,
|
|
131
|
+
snippet,
|
|
132
|
+
recallCount: 1,
|
|
133
|
+
totalScore: score,
|
|
134
|
+
maxScore: score,
|
|
135
|
+
queryHashes: [qHash],
|
|
136
|
+
recallDays: [dayBucket],
|
|
137
|
+
firstRecalledAt: nowIso,
|
|
138
|
+
lastRecalledAt: nowIso
|
|
139
|
+
};
|
|
140
|
+
store.entries[key] = next;
|
|
141
|
+
recorded += 1;
|
|
142
|
+
}
|
|
143
|
+
if (recorded > 0) {
|
|
144
|
+
store.updatedAt = nowIso;
|
|
145
|
+
await writeStore(workspaceDir, store);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
recorded,
|
|
149
|
+
skipped,
|
|
150
|
+
storePath: SHORT_TERM_RECALL_STORE_RELATIVE
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async function withDreamingPromotionLock(workspaceDir, task) {
|
|
154
|
+
const lockPath = path.join(workspaceDir, SHORT_TERM_PROMOTION_LOCK_RELATIVE);
|
|
155
|
+
await ensureDreamDir(workspaceDir);
|
|
156
|
+
const startedAt = Date.now();
|
|
157
|
+
const timeoutMs = 1e4;
|
|
158
|
+
const retryDelayMs = 50;
|
|
159
|
+
while (true) try {
|
|
160
|
+
const handle = await fs.open(lockPath, "wx");
|
|
161
|
+
await handle.writeFile(`${process.pid}:${Date.now()}\n`, "utf-8").catch(() => void 0);
|
|
162
|
+
try {
|
|
163
|
+
return await task();
|
|
164
|
+
} finally {
|
|
165
|
+
await handle.close().catch(() => void 0);
|
|
166
|
+
await fs.unlink(lockPath).catch(() => void 0);
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err?.code !== "EEXIST") throw err;
|
|
170
|
+
if (Date.now() - startedAt >= timeoutMs) throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);
|
|
171
|
+
await new Promise((r) => setTimeout(r, retryDelayMs));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function loadDreamingStore(params) {
|
|
175
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
176
|
+
return {
|
|
177
|
+
store: await readStore(params.workspaceDir, nowIso),
|
|
178
|
+
storePath: SHORT_TERM_RECALL_STORE_RELATIVE
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async function saveDreamingStore(params) {
|
|
182
|
+
await writeStore(params.workspaceDir, params.store);
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { loadDreamingStore, recordDreamingRecalls, saveDreamingStore, withDreamingPromotionLock };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=short-term-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"short-term-store.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/short-term-store.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash, randomUUID } from 'node:crypto';\n\nimport type { MemorySearchOptions } from '../../prompt/memory/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from './constants.js';\n\nconst log = createLogger('Dreaming:Store');\n\nexport type DreamingStoreEntry = {\n key: string;\n path: string; // workspace-relative: memory/YYYY-MM-DD.md\n startLine: number;\n endLine: number;\n snippet: string;\n recallCount: number;\n totalScore: number;\n maxScore: number;\n queryHashes: string[];\n recallDays: string[];\n firstRecalledAt: string;\n lastRecalledAt: string;\n promotedAt?: string;\n};\n\nexport type DreamingStore = {\n version: 1;\n updatedAt: string;\n entries: Record<string, DreamingStoreEntry>;\n};\n\ntype MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];\n\nfunction normalizeMemoryPath(raw: string): string {\n return raw.replaceAll('\\\\', '/').replace(/^\\.\\//, '');\n}\n\nfunction isDailyWorkspaceMemoryPath(rel: string): boolean {\n const p = normalizeMemoryPath(rel);\n return /^memory\\/\\d{4}-\\d{2}-\\d{2}\\.md$/i.test(p);\n}\n\nfunction clampScore(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction isoDay(now: Date): string {\n return now.toISOString().slice(0, 10);\n}\n\nfunction hashQuery(query: string): string {\n return createHash('sha1').update(query.trim().toLowerCase()).digest('hex').slice(0, 12);\n}\n\nfunction buildEntryKey(params: { path: string; startLine: number; endLine: number }): string {\n return `memory:${normalizeMemoryPath(params.path)}:${params.startLine}:${params.endLine}`;\n}\n\nfunction mergeRecentDistinct(existing: string[], nextValue: string, limit: number): string[] {\n const seen = new Set<string>();\n const normalized = existing\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.trim())\n .filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));\n if (nextValue && !normalized.includes(nextValue)) {\n normalized.push(nextValue);\n }\n return normalized.length <= limit ? normalized : normalized.slice(-limit);\n}\n\nfunction mergeQueryHashes(existing: string[], nextHash: string): string[] {\n const out = mergeRecentDistinct(existing, nextHash, 32);\n return out;\n}\n\nfunction emptyStore(nowIso: string): DreamingStore {\n return { version: 1, updatedAt: nowIso, entries: {} };\n}\n\nasync function ensureDreamDir(workspaceDir: string): Promise<string> {\n const dir = path.join(workspaceDir, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\nasync function readStore(workspaceDir: string, nowIso: string): Promise<DreamingStore> {\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n try {\n const raw = await fs.readFile(storePath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return emptyStore(nowIso);\n const rec = parsed as Partial<DreamingStore>;\n if (rec.version !== 1 || !rec.entries || typeof rec.entries !== 'object') {\n return emptyStore(nowIso);\n }\n return {\n version: 1,\n updatedAt: typeof rec.updatedAt === 'string' ? rec.updatedAt : nowIso,\n entries: rec.entries as Record<string, DreamingStoreEntry>,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return emptyStore(nowIso);\n log.warn({ err, workspaceDir }, 'Failed to read dreaming store; resetting');\n return emptyStore(nowIso);\n }\n}\n\nasync function writeStore(workspaceDir: string, store: DreamingStore): Promise<void> {\n await ensureDreamDir(workspaceDir);\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, storePath);\n}\n\nexport async function recordDreamingRecalls(params: {\n workspaceDir: string;\n query: string;\n matches: MemoryMatch[];\n options?: MemorySearchOptions;\n now?: Date;\n}): Promise<{ recorded: number; skipped: number; storePath: string }> {\n const workspaceDir = params.workspaceDir.trim();\n const query = params.query.trim();\n if (!workspaceDir || !query) {\n return { recorded: 0, skipped: params.matches.length, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n }\n const now = params.now ?? new Date();\n const nowIso = now.toISOString();\n const dayBucket = isoDay(now);\n const qHash = hashQuery(query);\n\n const store = await readStore(workspaceDir, nowIso);\n\n let recorded = 0;\n let skipped = 0;\n for (const match of params.matches) {\n const file = typeof match?.file === \"string\" ? match.file : \"\";\n if (!file || !isDailyWorkspaceMemoryPath(file)) {\n skipped += 1;\n continue;\n }\n const lines = typeof match?.lines === \"string\" ? match.lines.trim() : \"\";\n const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];\n const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {\n skipped += 1;\n continue;\n }\n const score = clampScore(Number(match.score));\n const key = buildEntryKey({ path: file, startLine, endLine });\n const existing = store.entries[key];\n const snippet = lines.length > 0 ? lines.slice(0, 360) : file;\n\n const next: DreamingStoreEntry = existing\n ? {\n ...existing,\n snippet,\n recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),\n totalScore: Math.max(0, existing.totalScore + score),\n maxScore: Math.max(existing.maxScore, score),\n queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),\n recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),\n lastRecalledAt: nowIso,\n }\n : {\n key,\n path: normalizeMemoryPath(file),\n startLine,\n endLine,\n snippet,\n recallCount: 1,\n totalScore: score,\n maxScore: score,\n queryHashes: [qHash],\n recallDays: [dayBucket],\n firstRecalledAt: nowIso,\n lastRecalledAt: nowIso,\n };\n\n store.entries[key] = next;\n recorded += 1;\n }\n\n if (recorded > 0) {\n store.updatedAt = nowIso;\n await writeStore(workspaceDir, store);\n }\n\n return {\n recorded,\n skipped,\n storePath: SHORT_TERM_RECALL_STORE_RELATIVE,\n };\n}\n\nexport async function withDreamingPromotionLock<T>(\n workspaceDir: string,\n task: () => Promise<T>,\n): Promise<T> {\n const lockPath = path.join(workspaceDir, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n await ensureDreamDir(workspaceDir);\n\n const startedAt = Date.now();\n const timeoutMs = 10_000;\n const retryDelayMs = 50;\n\n while (true) {\n try {\n const handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}\\n`, 'utf-8').catch(() => undefined);\n try {\n return await task();\n } finally {\n await handle.close().catch(() => undefined);\n await fs.unlink(lockPath).catch(() => undefined);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);\n }\n await new Promise((r) => setTimeout(r, retryDelayMs));\n }\n }\n}\n\nexport async function loadDreamingStore(params: {\n workspaceDir: string;\n}): Promise<{ store: DreamingStore; storePath: string }> {\n const nowIso = new Date().toISOString();\n const store = await readStore(params.workspaceDir, nowIso);\n return { store, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n}\n\nexport async function saveDreamingStore(params: {\n workspaceDir: string;\n store: DreamingStore;\n}): Promise<void> {\n await writeStore(params.workspaceDir, params.store);\n}\n\n"],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import "../memory/dreaming/constants.js";
|
|
4
|
+
import { resolveDreamingConfig } from "../memory/dreaming/config.js";
|
|
3
5
|
import { extractAgentUserPlainText } from "../memory/user-message-text.js";
|
|
4
6
|
import { formatInboundFileTextBlock, persistInboundAttachmentsToWorkspace } from "../../channels/attachments/inbound-persist.js";
|
|
5
7
|
import { cleanTrailingErrors, sanitizeMessages } from "../memory/message-sanitizer.js";
|
|
@@ -8,6 +10,7 @@ import { tryApplySessionTranscriptHygiene, tryApplySessionTranscriptHygieneForPe
|
|
|
8
10
|
import { runAgentTurnWithModelFallbacks } from "./run-agent-turn-with-fallbacks.js";
|
|
9
11
|
import { expandAtFileMentionsInPlainText } from "../context/expand-at-file-mentions.js";
|
|
10
12
|
import { resolveInboundImageContentParts } from "../image/inbound-image-handling.js";
|
|
13
|
+
import { runDreamingDeepPromotion } from "../memory/dreaming/deep-promotion.js";
|
|
11
14
|
//#region src/agent/orchestration/agent-orchestrator.ts
|
|
12
15
|
init_logger();
|
|
13
16
|
const log = createLogger("AgentOrchestrator");
|
|
@@ -53,6 +56,20 @@ var AgentOrchestrator = class {
|
|
|
53
56
|
const { sessionKey } = context;
|
|
54
57
|
log.debug({ sessionKey }, "Processing message through agent orchestrator");
|
|
55
58
|
await this.hydrateSessionWorkspaceFromStore?.(sessionKey);
|
|
59
|
+
if (typeof msg.content === "string" && msg.content.includes("__xopc_memory_dreaming_sweep__") && (sessionKey.startsWith("cron:") || context.channel === "cron")) {
|
|
60
|
+
const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);
|
|
61
|
+
const resolved = resolveDreamingConfig(this.getConfig?.());
|
|
62
|
+
await runDreamingDeepPromotion({
|
|
63
|
+
workspaceDir,
|
|
64
|
+
config: {
|
|
65
|
+
enabled: resolved.deep.enabled,
|
|
66
|
+
minScore: resolved.deep.minScore,
|
|
67
|
+
minRecallCount: resolved.deep.minRecallCount,
|
|
68
|
+
limit: resolved.deep.limit
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
56
73
|
const agent = this.agentManager.getOrCreateAgent(sessionKey);
|
|
57
74
|
try {
|
|
58
75
|
await this.hydrateSessionModelFromStore(sessionKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Agent, AgentMessage } from '@mariozechner/pi-agent-core';\nimport type { Config } from '../../config/schema.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { sanitizeMessages, cleanTrailingErrors } from '../memory/message-sanitizer.js';\nimport {\n tryApplySessionTranscriptHygiene,\n tryApplySessionTranscriptHygieneForPersistence,\n} from '../transcript/transcript-hygiene.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { runAgentTurnWithModelFallbacks } from './run-agent-turn-with-fallbacks.js';\nimport {\n persistInboundAttachmentsToWorkspace,\n formatInboundFileTextBlock,\n} from '../../channels/attachments/inbound-persist.js';\nimport { expandAtFileMentionsInPlainText } from '../context/expand-at-file-mentions.js';\nimport { resolveInboundImageContentParts } from '../image/inbound-image-handling.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Load per-session workspace override and mkdir before creating the agent. */\n hydrateSessionWorkspaceFromStore?: (sessionKey: string) => Promise<void>;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout via `agents.defaults.maxTaskDurationMs`. */\n getConfig?: () => Config | undefined;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private eventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private hydrateSessionWorkspaceFromStore?: (sessionKey: string) => Promise<void>;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private workspaceRoot: string;\n private getWorkspaceRootForSession?: (sessionKey: string) => string;\n private getAgentInternalStorageRootForSession: (sessionKey: string) => string;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.eventHandler = config.eventHandler;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.hydrateSessionWorkspaceFromStore = config.hydrateSessionWorkspaceFromStore;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.workspaceRoot = config.workspaceRoot;\n this.getWorkspaceRootForSession = config.getWorkspaceRootForSession;\n this.getAgentInternalStorageRootForSession =\n config.getAgentInternalStorageRootForSession ??\n ((sk) => this.getWorkspaceRootForSession?.(sk) ?? this.workspaceRoot);\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n }\n\n private async hydrateSessionModelFromStore(sessionKey: string): Promise<void> {\n const cfg = await this.sessionConfigStore.get(sessionKey);\n if (cfg?.modelOverride) {\n await this.modelManager.switchModelForSession(sessionKey, cfg.modelOverride);\n }\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.hydrateSessionWorkspaceFromStore?.(sessionKey);\n\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n\n try {\n await this.hydrateSessionModelFromStore(sessionKey);\n\n // 1. Load session history\n let messages = await this.sessionStore.load(sessionKey);\n\n // Clean any trailing errors from previous sessions (defensive)\n messages = cleanTrailingErrors(messages);\n\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n messages = tryApplySessionTranscriptHygiene(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene skipped (model resolve failed)');\n }\n\n agent.state.messages = messages;\n\n // 2. Apply model configuration for session\n await this.modelManager.applyModelForSession(agent, sessionKey);\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n // 3. Persist inbound files (Telegram, etc.) under agent home, then build user message\n const storageRoot = this.getAgentInternalStorageRootForSession(sessionKey);\n const persistedAttachments = await persistInboundAttachmentsToWorkspace(\n storageRoot,\n sessionKey,\n msg.attachments,\n );\n const userMessage = await this.buildUserMessage(\n {\n ...msg,\n attachments: persistedAttachments ?? msg.attachments,\n },\n sessionKey,\n );\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n // 4. Start task feedback\n this.feedbackCoordinator.startTask();\n\n // 5. Execute agent\n await this.executeAgent(agent, userMessageForModel, context);\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n // 6. Sanitize messages before saving (remove error messages, empty content)\n const rawMessages = agent.state.messages;\n const { messages: sanitizedMessages, removed } = sanitizeMessages(rawMessages);\n\n if (removed > 0) {\n log.info({ sessionKey, removed }, 'Removed problematic messages before saving');\n }\n\n // 7. Save session messages (transcript hygiene)\n await this.saveSessionSnapshot(sessionKey, sanitizedMessages);\n\n this.enqueueAutoTitle?.(sessionKey);\n\n // 8. End task feedback\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Transcript hygiene + persist. Expects messages already passed through {@link sanitizeMessages}.\n * Keeps thinking blocks on disk for UI; agent load path applies full hygiene including dropThinking.\n */\n private async saveSessionSnapshot(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n let toPersist = messages;\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n toPersist = tryApplySessionTranscriptHygieneForPersistence(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on save skipped');\n }\n await this.sessionStore.save(sessionKey, toPersist);\n }\n\n /**\n * Execute the agent with a user message (primary model, then `agents.defaults.model.fallbacks` on failure).\n */\n private async executeAgent(\n agent: Agent,\n userMessage: AgentMessage,\n context: SessionContext\n ): Promise<void> {\n const sessionKey = context.sessionKey;\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager: this.modelManager,\n userMessage,\n log,\n getConfig: this.getConfig,\n beforeUserPrompt: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n afterUserPrompt: async () => {\n try {\n const { messages: sanitizedTurn } = sanitizeMessages(agent.state.messages);\n await this.saveSessionSnapshot(sessionKey, sanitizedTurn);\n log.debug({ sessionKey }, 'User message saved immediately after prompt');\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to save user message immediately');\n }\n },\n });\n }\n\n /**\n * Build an agent message from an inbound message\n */\n private async buildUserMessage(msg: InboundMessage, sessionKey: string): Promise<AgentMessage> {\n const storageRootAbs = this.getAgentInternalStorageRootForSession(sessionKey);\n let textBody = msg.content.trimStart().startsWith('/skill:')\n ? this.agentManager.expandSkillUserText(msg.content)\n : msg.content;\n\n if (/@file:/.test(textBody)) {\n const root = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n textBody = await expandAtFileMentionsInPlainText(textBody, root);\n }\n\n if (!msg.attachments || msg.attachments.length === 0) {\n return {\n role: 'user',\n content: textBody,\n timestamp: Date.now(),\n };\n }\n\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const cfg = this.getConfig?.();\n\n const messageContent: Array<\n { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }\n > = [];\n\n if (msg.content.trim()) {\n messageContent.push({ type: 'text', text: textBody });\n }\n\n const attachments = msg.attachments;\n let i = 0;\n while (i < attachments.length) {\n const att = attachments[i]!;\n const isImage =\n att.type === 'image' || att.type === 'photo' || Boolean(att.mimeType?.startsWith('image/'));\n\n if (isImage) {\n const group: Array<{ data: string; mimeType: string }> = [];\n while (i < attachments.length) {\n const a = attachments[i]!;\n const img =\n a.type === 'image' || a.type === 'photo' || Boolean(a.mimeType?.startsWith('image/'));\n if (!img) {\n break;\n }\n if (!a.data || a.data.length === 0) {\n log.warn({ type: a.type, name: a.name }, 'Empty image data, skipping');\n i += 1;\n continue;\n }\n group.push({ data: a.data, mimeType: a.mimeType || 'image/jpeg' });\n i += 1;\n }\n if (group.length > 0) {\n const parts = await resolveInboundImageContentParts({\n modelRef,\n cfg,\n userTextForContext: msg.content.trim() ? textBody : '',\n images: group,\n });\n messageContent.push(...parts);\n }\n } else {\n const fileBlock = formatInboundFileTextBlock(\n {\n type: att.type,\n mimeType: att.mimeType,\n name: att.name,\n size: att.size,\n workspaceRelativePath: att.workspaceRelativePath,\n },\n storageRootAbs,\n );\n messageContent.push({ type: 'text', text: fileBlock });\n i += 1;\n }\n }\n\n const hasText = messageContent.some((item) => item.type === 'text');\n const hasImage = messageContent.some((item) => item.type === 'image');\n if (hasImage && !hasText) {\n messageContent.unshift({ type: 'text', text: 'Please analyze the image(s) I sent.' });\n }\n\n if (messageContent.length === 0) {\n log.warn(\n { attachmentCount: msg.attachments.length },\n 'All attachments were skipped, falling back to text message',\n );\n return {\n role: 'user',\n content: textBody || '[Image attachment could not be processed]',\n timestamp: Date.now(),\n };\n }\n\n return {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n const agent = this.agentManager.getAgent(sessionKey);\n if (agent) {\n agent.abort();\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Agent, AgentMessage } from '@mariozechner/pi-agent-core';\nimport type { Config } from '../../config/schema.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { sanitizeMessages, cleanTrailingErrors } from '../memory/message-sanitizer.js';\nimport {\n tryApplySessionTranscriptHygiene,\n tryApplySessionTranscriptHygieneForPersistence,\n} from '../transcript/transcript-hygiene.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { runAgentTurnWithModelFallbacks } from './run-agent-turn-with-fallbacks.js';\nimport {\n persistInboundAttachmentsToWorkspace,\n formatInboundFileTextBlock,\n} from '../../channels/attachments/inbound-persist.js';\nimport { expandAtFileMentionsInPlainText } from '../context/expand-at-file-mentions.js';\nimport { resolveInboundImageContentParts } from '../image/inbound-image-handling.js';\nimport { DREAMING_SWEEP_TOKEN } from '../memory/dreaming/constants.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Load per-session workspace override and mkdir before creating the agent. */\n hydrateSessionWorkspaceFromStore?: (sessionKey: string) => Promise<void>;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout via `agents.defaults.maxTaskDurationMs`. */\n getConfig?: () => Config | undefined;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private eventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private hydrateSessionWorkspaceFromStore?: (sessionKey: string) => Promise<void>;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private workspaceRoot: string;\n private getWorkspaceRootForSession?: (sessionKey: string) => string;\n private getAgentInternalStorageRootForSession: (sessionKey: string) => string;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.eventHandler = config.eventHandler;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.hydrateSessionWorkspaceFromStore = config.hydrateSessionWorkspaceFromStore;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.workspaceRoot = config.workspaceRoot;\n this.getWorkspaceRootForSession = config.getWorkspaceRootForSession;\n this.getAgentInternalStorageRootForSession =\n config.getAgentInternalStorageRootForSession ??\n ((sk) => this.getWorkspaceRootForSession?.(sk) ?? this.workspaceRoot);\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n }\n\n private async hydrateSessionModelFromStore(sessionKey: string): Promise<void> {\n const cfg = await this.sessionConfigStore.get(sessionKey);\n if (cfg?.modelOverride) {\n await this.modelManager.switchModelForSession(sessionKey, cfg.modelOverride);\n }\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.hydrateSessionWorkspaceFromStore?.(sessionKey);\n\n // Dreaming: short-circuit cron-triggered sweep token into a maintenance run.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n msg.content.includes(DREAMING_SWEEP_TOKEN) &&\n (sessionKey.startsWith('cron:') || context.channel === 'cron')\n ) {\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const resolved = resolveDreamingConfig(this.getConfig?.());\n await runDreamingDeepPromotion({\n workspaceDir,\n config: {\n enabled: resolved.deep.enabled,\n minScore: resolved.deep.minScore,\n minRecallCount: resolved.deep.minRecallCount,\n limit: resolved.deep.limit,\n },\n });\n return;\n }\n\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n\n try {\n await this.hydrateSessionModelFromStore(sessionKey);\n\n // 1. Load session history\n let messages = await this.sessionStore.load(sessionKey);\n\n // Clean any trailing errors from previous sessions (defensive)\n messages = cleanTrailingErrors(messages);\n\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n messages = tryApplySessionTranscriptHygiene(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene skipped (model resolve failed)');\n }\n\n agent.state.messages = messages;\n\n // 2. Apply model configuration for session\n await this.modelManager.applyModelForSession(agent, sessionKey);\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n // 3. Persist inbound files (Telegram, etc.) under agent home, then build user message\n const storageRoot = this.getAgentInternalStorageRootForSession(sessionKey);\n const persistedAttachments = await persistInboundAttachmentsToWorkspace(\n storageRoot,\n sessionKey,\n msg.attachments,\n );\n const userMessage = await this.buildUserMessage(\n {\n ...msg,\n attachments: persistedAttachments ?? msg.attachments,\n },\n sessionKey,\n );\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n // 4. Start task feedback\n this.feedbackCoordinator.startTask();\n\n // 5. Execute agent\n await this.executeAgent(agent, userMessageForModel, context);\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n // 6. Sanitize messages before saving (remove error messages, empty content)\n const rawMessages = agent.state.messages;\n const { messages: sanitizedMessages, removed } = sanitizeMessages(rawMessages);\n\n if (removed > 0) {\n log.info({ sessionKey, removed }, 'Removed problematic messages before saving');\n }\n\n // 7. Save session messages (transcript hygiene)\n await this.saveSessionSnapshot(sessionKey, sanitizedMessages);\n\n this.enqueueAutoTitle?.(sessionKey);\n\n // 8. End task feedback\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Transcript hygiene + persist. Expects messages already passed through {@link sanitizeMessages}.\n * Keeps thinking blocks on disk for UI; agent load path applies full hygiene including dropThinking.\n */\n private async saveSessionSnapshot(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n let toPersist = messages;\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n toPersist = tryApplySessionTranscriptHygieneForPersistence(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on save skipped');\n }\n await this.sessionStore.save(sessionKey, toPersist);\n }\n\n /**\n * Execute the agent with a user message (primary model, then `agents.defaults.model.fallbacks` on failure).\n */\n private async executeAgent(\n agent: Agent,\n userMessage: AgentMessage,\n context: SessionContext\n ): Promise<void> {\n const sessionKey = context.sessionKey;\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager: this.modelManager,\n userMessage,\n log,\n getConfig: this.getConfig,\n beforeUserPrompt: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n afterUserPrompt: async () => {\n try {\n const { messages: sanitizedTurn } = sanitizeMessages(agent.state.messages);\n await this.saveSessionSnapshot(sessionKey, sanitizedTurn);\n log.debug({ sessionKey }, 'User message saved immediately after prompt');\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to save user message immediately');\n }\n },\n });\n }\n\n /**\n * Build an agent message from an inbound message\n */\n private async buildUserMessage(msg: InboundMessage, sessionKey: string): Promise<AgentMessage> {\n const storageRootAbs = this.getAgentInternalStorageRootForSession(sessionKey);\n let textBody = msg.content.trimStart().startsWith('/skill:')\n ? this.agentManager.expandSkillUserText(msg.content)\n : msg.content;\n\n if (/@file:/.test(textBody)) {\n const root = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n textBody = await expandAtFileMentionsInPlainText(textBody, root);\n }\n\n if (!msg.attachments || msg.attachments.length === 0) {\n return {\n role: 'user',\n content: textBody,\n timestamp: Date.now(),\n };\n }\n\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const cfg = this.getConfig?.();\n\n const messageContent: Array<\n { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }\n > = [];\n\n if (msg.content.trim()) {\n messageContent.push({ type: 'text', text: textBody });\n }\n\n const attachments = msg.attachments;\n let i = 0;\n while (i < attachments.length) {\n const att = attachments[i]!;\n const isImage =\n att.type === 'image' || att.type === 'photo' || Boolean(att.mimeType?.startsWith('image/'));\n\n if (isImage) {\n const group: Array<{ data: string; mimeType: string }> = [];\n while (i < attachments.length) {\n const a = attachments[i]!;\n const img =\n a.type === 'image' || a.type === 'photo' || Boolean(a.mimeType?.startsWith('image/'));\n if (!img) {\n break;\n }\n if (!a.data || a.data.length === 0) {\n log.warn({ type: a.type, name: a.name }, 'Empty image data, skipping');\n i += 1;\n continue;\n }\n group.push({ data: a.data, mimeType: a.mimeType || 'image/jpeg' });\n i += 1;\n }\n if (group.length > 0) {\n const parts = await resolveInboundImageContentParts({\n modelRef,\n cfg,\n userTextForContext: msg.content.trim() ? textBody : '',\n images: group,\n });\n messageContent.push(...parts);\n }\n } else {\n const fileBlock = formatInboundFileTextBlock(\n {\n type: att.type,\n mimeType: att.mimeType,\n name: att.name,\n size: att.size,\n workspaceRelativePath: att.workspaceRelativePath,\n },\n storageRootAbs,\n );\n messageContent.push({ type: 'text', text: fileBlock });\n i += 1;\n }\n }\n\n const hasText = messageContent.some((item) => item.type === 'text');\n const hasImage = messageContent.some((item) => item.type === 'image');\n if (hasImage && !hasText) {\n messageContent.unshift({ type: 'text', text: 'Please analyze the image(s) I sent.' });\n }\n\n if (messageContent.length === 0) {\n log.warn(\n { attachmentCount: msg.attachments.length },\n 'All attachments were skipped, falling back to text message',\n );\n return {\n role: 'user',\n content: textBody || '[Image attachment could not be processed]',\n timestamp: Date.now(),\n };\n }\n\n return {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n const agent = this.agentManager.getAgent(sessionKey);\n if (agent) {\n agent.abort();\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -76,6 +76,12 @@ export declare class AgentService {
|
|
|
76
76
|
clearStreamHandle(): void;
|
|
77
77
|
start(): Promise<void>;
|
|
78
78
|
stop(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Reconcile managed Dreaming cron job against the current effective config.
|
|
81
|
+
* Safe to call after config saves to apply changes without restarting the process.
|
|
82
|
+
*/
|
|
83
|
+
reconcileDreamingNow(): Promise<void>;
|
|
84
|
+
private reconcileDreamingCronJob;
|
|
79
85
|
/**
|
|
80
86
|
* Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.
|
|
81
87
|
* Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).
|