@goondocks/myco 0.5.0 → 0.6.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +4 -0
- package/dist/{chunk-JJL6AMDA.js → chunk-24DOZEUJ.js} +255 -6
- package/dist/chunk-24DOZEUJ.js.map +1 -0
- package/dist/{chunk-ZWUFTOG3.js → chunk-2GSX3BK2.js} +4 -4
- package/dist/{chunk-FIRMTYFH.js → chunk-2YBUL3IL.js} +4 -37
- package/dist/chunk-2YBUL3IL.js.map +1 -0
- package/dist/{chunk-HL2S5QZG.js → chunk-2ZBB3MQT.js} +319 -40
- package/dist/chunk-2ZBB3MQT.js.map +1 -0
- package/dist/{chunk-HJG7Z6SJ.js → chunk-3EM23DMD.js} +2 -2
- package/dist/{chunk-XQXXF6MU.js → chunk-4RMSHZE4.js} +12 -1
- package/dist/{chunk-XQXXF6MU.js.map → chunk-4RMSHZE4.js.map} +1 -1
- package/dist/{chunk-T7OC6GH5.js → chunk-5FNZ7AMX.js} +2 -2
- package/dist/{chunk-X6TKHO22.js → chunk-5QWZT4AB.js} +2 -2
- package/dist/{chunk-B6WVNDA5.js → chunk-6BSDCZ5Q.js} +8 -2
- package/dist/{chunk-B6WVNDA5.js.map → chunk-6BSDCZ5Q.js.map} +1 -1
- package/dist/{chunk-R6LQT3U7.js → chunk-B5UZSHQV.js} +8 -12
- package/dist/{chunk-R6LQT3U7.js.map → chunk-B5UZSHQV.js.map} +1 -1
- package/dist/{chunk-7KQB22DP.js → chunk-E7OBRBCQ.js} +2 -2
- package/dist/{chunk-RCV2I4AI.js → chunk-GDYYJTTT.js} +5 -3
- package/dist/{chunk-RCV2I4AI.js.map → chunk-GDYYJTTT.js.map} +1 -1
- package/dist/{chunk-MIU3DKLN.js → chunk-GNR3QAER.js} +2 -2
- package/dist/{chunk-BMJX2IDQ.js → chunk-H7PRCVGQ.js} +2 -2
- package/dist/{chunk-6LTNFMXO.js → chunk-KC7ENQTN.js} +2 -2
- package/dist/chunk-KUMVJIJW.js +117 -0
- package/dist/chunk-KUMVJIJW.js.map +1 -0
- package/dist/{chunk-ND4VK6C7.js → chunk-L25U7PIG.js} +2 -2
- package/dist/{chunk-6UJWI4IW.js → chunk-MQSYSQ6T.js} +7 -5
- package/dist/{chunk-6UJWI4IW.js.map → chunk-MQSYSQ6T.js.map} +1 -1
- package/dist/{chunk-TBRZAJ7W.js → chunk-P3WO3N3I.js} +11 -3
- package/dist/chunk-P3WO3N3I.js.map +1 -0
- package/dist/{chunk-JI6M2L2W.js → chunk-QGJ2ZIUZ.js} +7 -4
- package/dist/chunk-QGJ2ZIUZ.js.map +1 -0
- package/dist/{chunk-5EZ7QF6J.js → chunk-QLUE3BUL.js} +66 -1
- package/dist/chunk-QLUE3BUL.js.map +1 -0
- package/dist/{chunk-AK6GNLPV.js → chunk-TWSTAVLO.js} +17 -1
- package/dist/{chunk-AK6GNLPV.js.map → chunk-TWSTAVLO.js.map} +1 -1
- package/dist/{chunk-FIA5NTRH.js → chunk-UVGAVYWZ.js} +11 -13
- package/dist/chunk-UVGAVYWZ.js.map +1 -0
- package/dist/{chunk-UKWO26VI.js → chunk-YTANWAGE.js} +2 -2
- package/dist/chunk-ZMYNRTTD.js +64 -0
- package/dist/chunk-ZMYNRTTD.js.map +1 -0
- package/dist/{cli-BLYNNKGJ.js → cli-K7SUTP7A.js} +22 -22
- package/dist/{client-5GB4WVXE.js → client-YJMNTITQ.js} +5 -5
- package/dist/{config-5FGLQGCW.js → config-G5GGT5A6.js} +3 -3
- package/dist/curate-6T5NKVXK.js +80 -0
- package/dist/curate-6T5NKVXK.js.map +1 -0
- package/dist/{detect-providers-BIHYFK5M.js → detect-providers-S3M5TAMW.js} +3 -3
- package/dist/{digest-7NKYXM6G.js → digest-O35VHYFP.js} +31 -40
- package/dist/digest-O35VHYFP.js.map +1 -0
- package/dist/{init-HPQ77WWF.js → init-TFLSATB3.js} +9 -11
- package/dist/init-TFLSATB3.js.map +1 -0
- package/dist/{logs-BSTBZHDR.js → logs-IENORIYR.js} +3 -3
- package/dist/{main-NFQ4II75.js → main-JEUQS3BY.js} +1218 -294
- package/dist/main-JEUQS3BY.js.map +1 -0
- package/dist/rebuild-7SH5GSNX.js +66 -0
- package/dist/rebuild-7SH5GSNX.js.map +1 -0
- package/dist/{reprocess-ZL4HKTSC.js → reprocess-Q4YH2ZBK.js} +20 -22
- package/dist/{reprocess-ZL4HKTSC.js.map → reprocess-Q4YH2ZBK.js.map} +1 -1
- package/dist/{restart-FYW662DR.js → restart-NLJLB52D.js} +7 -6
- package/dist/{restart-FYW662DR.js.map → restart-NLJLB52D.js.map} +1 -1
- package/dist/{search-E5JQMTXV.js → search-2BVRF54H.js} +10 -10
- package/dist/{server-TV3D35HZ.js → server-4AMZNP4F.js} +51 -97
- package/dist/{server-TV3D35HZ.js.map → server-4AMZNP4F.js.map} +1 -1
- package/dist/{session-QF6MILAC.js → session-F326AWCH.js} +2 -2
- package/dist/{session-start-5MFEOVQ5.js → session-start-AZAF3DTE.js} +10 -10
- package/dist/setup-digest-YLZZGSSR.js +15 -0
- package/dist/setup-llm-JOXBSLXC.js +15 -0
- package/dist/src/cli.js +4 -4
- package/dist/src/daemon/main.js +4 -4
- package/dist/src/hooks/post-tool-use.js +5 -5
- package/dist/src/hooks/session-end.js +5 -5
- package/dist/src/hooks/session-start.js +4 -4
- package/dist/src/hooks/stop.js +7 -7
- package/dist/src/hooks/user-prompt-submit.js +5 -5
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/prompts/consolidation.md +46 -0
- package/dist/src/templates/portal.md +5 -0
- package/dist/stats-MKDIZFIQ.js +58 -0
- package/dist/stats-MKDIZFIQ.js.map +1 -0
- package/dist/templates-XPRBOWCE.js +38 -0
- package/dist/templates-XPRBOWCE.js.map +1 -0
- package/dist/ui/assets/index-D37IoDXS.css +1 -0
- package/dist/ui/assets/index-DA61Ial2.js +289 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/fonts/GeistMono-LICENSE.txt +92 -0
- package/dist/ui/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/ui/index.html +14 -0
- package/dist/{verify-RACBFT2P.js → verify-7DW7LAND.js} +6 -6
- package/dist/{version-HJTVNPOO.js → version-RQLD7VBP.js} +4 -4
- package/package.json +3 -2
- package/skills/myco/SKILL.md +20 -1
- package/skills/myco/references/cli-usage.md +48 -0
- package/skills/myco/references/wisdom.md +11 -1
- package/dist/chunk-2AMAOSRF.js +0 -105
- package/dist/chunk-2AMAOSRF.js.map +0 -1
- package/dist/chunk-5EZ7QF6J.js.map +0 -1
- package/dist/chunk-FIA5NTRH.js.map +0 -1
- package/dist/chunk-FIRMTYFH.js.map +0 -1
- package/dist/chunk-HL2S5QZG.js.map +0 -1
- package/dist/chunk-IURC35BF.js +0 -49
- package/dist/chunk-IURC35BF.js.map +0 -1
- package/dist/chunk-JI6M2L2W.js.map +0 -1
- package/dist/chunk-JJL6AMDA.js.map +0 -1
- package/dist/chunk-KYL67SKZ.js +0 -150
- package/dist/chunk-KYL67SKZ.js.map +0 -1
- package/dist/chunk-TBRZAJ7W.js.map +0 -1
- package/dist/curate-S4HOYWXA.js +0 -231
- package/dist/curate-S4HOYWXA.js.map +0 -1
- package/dist/digest-7NKYXM6G.js.map +0 -1
- package/dist/init-HPQ77WWF.js.map +0 -1
- package/dist/main-NFQ4II75.js.map +0 -1
- package/dist/rebuild-KQ6G2GZM.js +0 -86
- package/dist/rebuild-KQ6G2GZM.js.map +0 -1
- package/dist/setup-digest-DZAFIBEF.js +0 -15
- package/dist/setup-llm-4BZM33YT.js +0 -15
- package/dist/stats-ZIIJ2GB3.js +0 -77
- package/dist/stats-ZIIJ2GB3.js.map +0 -1
- /package/dist/{chunk-ZWUFTOG3.js.map → chunk-2GSX3BK2.js.map} +0 -0
- /package/dist/{chunk-HJG7Z6SJ.js.map → chunk-3EM23DMD.js.map} +0 -0
- /package/dist/{chunk-T7OC6GH5.js.map → chunk-5FNZ7AMX.js.map} +0 -0
- /package/dist/{chunk-X6TKHO22.js.map → chunk-5QWZT4AB.js.map} +0 -0
- /package/dist/{chunk-7KQB22DP.js.map → chunk-E7OBRBCQ.js.map} +0 -0
- /package/dist/{chunk-MIU3DKLN.js.map → chunk-GNR3QAER.js.map} +0 -0
- /package/dist/{chunk-BMJX2IDQ.js.map → chunk-H7PRCVGQ.js.map} +0 -0
- /package/dist/{chunk-6LTNFMXO.js.map → chunk-KC7ENQTN.js.map} +0 -0
- /package/dist/{chunk-ND4VK6C7.js.map → chunk-L25U7PIG.js.map} +0 -0
- /package/dist/{chunk-UKWO26VI.js.map → chunk-YTANWAGE.js.map} +0 -0
- /package/dist/{cli-BLYNNKGJ.js.map → cli-K7SUTP7A.js.map} +0 -0
- /package/dist/{client-5GB4WVXE.js.map → client-YJMNTITQ.js.map} +0 -0
- /package/dist/{config-5FGLQGCW.js.map → config-G5GGT5A6.js.map} +0 -0
- /package/dist/{detect-providers-BIHYFK5M.js.map → detect-providers-S3M5TAMW.js.map} +0 -0
- /package/dist/{logs-BSTBZHDR.js.map → logs-IENORIYR.js.map} +0 -0
- /package/dist/{search-E5JQMTXV.js.map → search-2BVRF54H.js.map} +0 -0
- /package/dist/{session-QF6MILAC.js.map → session-F326AWCH.js.map} +0 -0
- /package/dist/{session-start-5MFEOVQ5.js.map → session-start-AZAF3DTE.js.map} +0 -0
- /package/dist/{setup-digest-DZAFIBEF.js.map → setup-digest-YLZZGSSR.js.map} +0 -0
- /package/dist/{setup-llm-4BZM33YT.js.map → setup-llm-JOXBSLXC.js.map} +0 -0
- /package/dist/{verify-RACBFT2P.js.map → verify-7DW7LAND.js.map} +0 -0
- /package/dist/{version-HJTVNPOO.js.map → version-RQLD7VBP.js.map} +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
getPluginVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E7OBRBCQ.js";
|
|
5
5
|
import {
|
|
6
6
|
AgentRegistry
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5QWZT4AB.js";
|
|
8
8
|
import {
|
|
9
9
|
DAEMON_CLIENT_TIMEOUT_MS,
|
|
10
10
|
DAEMON_HEALTH_CHECK_TIMEOUT_MS,
|
|
11
11
|
DAEMON_HEALTH_RETRY_DELAYS,
|
|
12
12
|
DAEMON_STALE_GRACE_PERIOD_MS
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-6BSDCZ5Q.js";
|
|
14
14
|
|
|
15
15
|
// src/hooks/client.ts
|
|
16
16
|
import fs from "fs";
|
|
@@ -178,4 +178,4 @@ var DaemonClient = class {
|
|
|
178
178
|
export {
|
|
179
179
|
DaemonClient
|
|
180
180
|
};
|
|
181
|
-
//# sourceMappingURL=chunk-
|
|
181
|
+
//# sourceMappingURL=chunk-2GSX3BK2.js.map
|
|
@@ -2,10 +2,10 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
|
|
|
2
2
|
import {
|
|
3
3
|
LmStudioBackend,
|
|
4
4
|
OllamaBackend
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QGJ2ZIUZ.js";
|
|
6
6
|
import {
|
|
7
7
|
AgentRegistry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5QWZT4AB.js";
|
|
9
9
|
|
|
10
10
|
// src/cli/shared.ts
|
|
11
11
|
import fs from "fs";
|
|
@@ -33,39 +33,6 @@ var PROVIDER_DEFAULTS = {
|
|
|
33
33
|
ollama: { base_url: OllamaBackend.DEFAULT_BASE_URL },
|
|
34
34
|
"lm-studio": { base_url: LmStudioBackend.DEFAULT_BASE_URL }
|
|
35
35
|
};
|
|
36
|
-
var DASHBOARD_CONTENT = `# Myco Vault
|
|
37
|
-
|
|
38
|
-
## Active Plans
|
|
39
|
-
\`\`\`dataview
|
|
40
|
-
TABLE status, tags FROM #type/plan
|
|
41
|
-
WHERE status = "active" OR status = "in_progress"
|
|
42
|
-
SORT created DESC
|
|
43
|
-
\`\`\`
|
|
44
|
-
|
|
45
|
-
## Recent Sessions
|
|
46
|
-
\`\`\`dataview
|
|
47
|
-
TABLE user, started, tools_used FROM #type/session
|
|
48
|
-
SORT started DESC LIMIT 10
|
|
49
|
-
\`\`\`
|
|
50
|
-
|
|
51
|
-
## Recent Spores
|
|
52
|
-
\`\`\`dataview
|
|
53
|
-
TABLE observation_type AS "Type", created FROM #type/spore
|
|
54
|
-
SORT created DESC LIMIT 15
|
|
55
|
-
\`\`\`
|
|
56
|
-
|
|
57
|
-
## Spores by Type
|
|
58
|
-
\`\`\`dataview
|
|
59
|
-
TABLE WITHOUT ID observation_type AS "Type", length(rows) AS "Count"
|
|
60
|
-
FROM #type/spore GROUP BY observation_type
|
|
61
|
-
SORT length(rows) DESC
|
|
62
|
-
\`\`\`
|
|
63
|
-
|
|
64
|
-
## Gotchas
|
|
65
|
-
\`\`\`dataview
|
|
66
|
-
LIST FROM #spore/gotcha SORT created DESC LIMIT 10
|
|
67
|
-
\`\`\`
|
|
68
|
-
`;
|
|
69
36
|
var VAULT_GITIGNORE = `# Runtime \u2014 rebuilt on daemon startup
|
|
70
37
|
index.db
|
|
71
38
|
index.db-wal
|
|
@@ -74,6 +41,7 @@ vectors.db
|
|
|
74
41
|
|
|
75
42
|
# Daemon state \u2014 per-machine, ephemeral
|
|
76
43
|
daemon.json
|
|
44
|
+
_portal.md
|
|
77
45
|
buffer/
|
|
78
46
|
logs/
|
|
79
47
|
|
|
@@ -112,8 +80,7 @@ For other agents, add to your shell profile:`);
|
|
|
112
80
|
export {
|
|
113
81
|
loadEnv,
|
|
114
82
|
isProcessAlive,
|
|
115
|
-
DASHBOARD_CONTENT,
|
|
116
83
|
VAULT_GITIGNORE,
|
|
117
84
|
configureVaultEnv
|
|
118
85
|
};
|
|
119
|
-
//# sourceMappingURL=chunk-
|
|
86
|
+
//# sourceMappingURL=chunk-2YBUL3IL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/shared.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { AgentRegistry } from '../agents/registry.js';\nimport { OllamaBackend } from '../intelligence/ollama.js';\nimport { LmStudioBackend } from '../intelligence/lm-studio.js';\n\nexport { parseStringFlag, parseIntFlag } from '../logs/format.js';\n\n/** Load .env from cwd (not script location — that's the plugin install dir). */\nexport function loadEnv(): void {\n const envPath = path.resolve(process.cwd(), '.env');\n if (!fs.existsSync(envPath)) return;\n for (const line of fs.readFileSync(envPath, 'utf-8').split('\\n')) {\n const match = line.match(/^\\s*([^#=]+?)\\s*=\\s*(.*?)\\s*$/);\n if (match && !process.env[match[1]]) {\n process.env[match[1]] = match[2];\n }\n }\n}\n\nexport function isProcessAlive(pid: number): boolean {\n try { process.kill(pid, 0); return true; } catch { return false; }\n}\n\n// --- Provider defaults (sourced from backend classes) ---\nexport const PROVIDER_DEFAULTS: Record<string, { base_url: string }> = {\n ollama: { base_url: OllamaBackend.DEFAULT_BASE_URL },\n 'lm-studio': { base_url: LmStudioBackend.DEFAULT_BASE_URL },\n};\n\n\nexport const VAULT_GITIGNORE = `# Runtime — rebuilt on daemon startup\nindex.db\nindex.db-wal\nindex.db-shm\nvectors.db\n\n# Daemon state — per-machine, ephemeral\ndaemon.json\n_portal.md\nbuffer/\nlogs/\n\n# Obsidian — per-user workspace config\n.obsidian/\n`;\n\n/** Collapse an absolute home-dir path to its `~/` form for portable config storage. */\nexport function collapseHomePath(absPath: string): string {\n const home = os.homedir();\n if (absPath.startsWith(home + path.sep) || absPath === home) {\n return '~' + absPath.slice(home.length);\n }\n return absPath;\n}\n\n/** Set MYCO_VAULT_DIR in the active agent's config, falling back to all known agents. */\nexport function configureVaultEnv(projectRoot: string, vaultDir: string): void {\n const registry = new AgentRegistry();\n const active = registry.detectActiveAgent();\n // Store the portable ~/... form so config files don't leak the username\n const portableDir = collapseHomePath(vaultDir);\n\n if (active) {\n if (active.configureVaultEnv(projectRoot, portableDir)) {\n console.log(`Set MYCO_VAULT_DIR for ${active.displayName}`);\n }\n } else {\n // No active agent detected — try all adapters\n for (const name of registry.adapterNames) {\n const adapter = registry.getAdapter(name);\n if (adapter?.configureVaultEnv(projectRoot, portableDir)) {\n console.log(`Set MYCO_VAULT_DIR for ${adapter.displayName}`);\n }\n }\n }\n\n console.log(`\\nFor other agents, add to your shell profile:`);\n console.log(` export MYCO_VAULT_DIR=\"${portableDir}\"\\n`);\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAQR,SAAS,UAAgB;AAC9B,QAAM,UAAU,KAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAClD,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG;AAC7B,aAAW,QAAQ,GAAG,aAAa,SAAS,OAAO,EAAE,MAAM,IAAI,GAAG;AAChE,UAAM,QAAQ,KAAK,MAAM,+BAA+B;AACxD,QAAI,SAAS,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,GAAG;AACnC,cAAQ,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC;AAAA,IACjC;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAAsB;AACnD,MAAI;AAAE,YAAQ,KAAK,KAAK,CAAC;AAAG,WAAO;AAAA,EAAM,QAAQ;AAAE,WAAO;AAAA,EAAO;AACnE;AAGO,IAAM,oBAA0D;AAAA,EACrE,QAAQ,EAAE,UAAU,cAAc,iBAAiB;AAAA,EACnD,aAAa,EAAE,UAAU,gBAAgB,iBAAiB;AAC5D;AAGO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBxB,SAAS,iBAAiB,SAAyB;AACxD,QAAM,OAAO,GAAG,QAAQ;AACxB,MAAI,QAAQ,WAAW,OAAO,KAAK,GAAG,KAAK,YAAY,MAAM;AAC3D,WAAO,MAAM,QAAQ,MAAM,KAAK,MAAM;AAAA,EACxC;AACA,SAAO;AACT;AAGO,SAAS,kBAAkB,aAAqB,UAAwB;AAC7E,QAAM,WAAW,IAAI,cAAc;AACnC,QAAM,SAAS,SAAS,kBAAkB;AAE1C,QAAM,cAAc,iBAAiB,QAAQ;AAE7C,MAAI,QAAQ;AACV,QAAI,OAAO,kBAAkB,aAAa,WAAW,GAAG;AACtD,cAAQ,IAAI,0BAA0B,OAAO,WAAW,EAAE;AAAA,IAC5D;AAAA,EACF,OAAO;AAEL,eAAW,QAAQ,SAAS,cAAc;AACxC,YAAM,UAAU,SAAS,WAAW,IAAI;AACxC,UAAI,SAAS,kBAAkB,aAAa,WAAW,GAAG;AACxD,gBAAQ,IAAI,0BAA0B,QAAQ,WAAW,EAAE;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,6CAAgD;AAC5D,UAAQ,IAAI,4BAA4B,WAAW;AAAA,CAAK;AAC1D;","names":[]}
|
|
@@ -1,31 +1,79 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
EMBEDDING_BATCH_CONCURRENCY,
|
|
4
|
+
batchExecute
|
|
5
|
+
} from "./chunk-3JCXYLHD.js";
|
|
6
|
+
import {
|
|
7
|
+
isActiveSpore,
|
|
8
|
+
supersedeSpore,
|
|
9
|
+
supersededIdsSchema
|
|
10
|
+
} from "./chunk-UVGAVYWZ.js";
|
|
11
|
+
import {
|
|
12
|
+
formatNoteForPrompt,
|
|
13
|
+
formatNotesForPrompt,
|
|
3
14
|
loadPrompt,
|
|
15
|
+
rebuildIndex,
|
|
4
16
|
stripReasoningTokens
|
|
5
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-24DOZEUJ.js";
|
|
18
|
+
import {
|
|
19
|
+
generateEmbedding
|
|
20
|
+
} from "./chunk-RGVBGTD6.js";
|
|
6
21
|
import {
|
|
7
22
|
stripFrontmatter
|
|
8
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-GNR3QAER.js";
|
|
24
|
+
import {
|
|
25
|
+
initFts
|
|
26
|
+
} from "./chunk-6FQISQNA.js";
|
|
9
27
|
import {
|
|
10
28
|
require_dist
|
|
11
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-MQSYSQ6T.js";
|
|
12
30
|
import {
|
|
13
31
|
CHARS_PER_TOKEN,
|
|
32
|
+
CURATION_CLUSTER_SIMILARITY,
|
|
14
33
|
DIGEST_LLM_REQUEST_TIMEOUT_MS,
|
|
15
34
|
DIGEST_SUBSTRATE_TYPE_WEIGHTS,
|
|
16
35
|
DIGEST_TIER_MIN_CONTEXT,
|
|
36
|
+
EMBEDDING_INPUT_LIMIT,
|
|
17
37
|
LLM_REASONING_MODE,
|
|
38
|
+
SUPERSESSION_MAX_TOKENS,
|
|
18
39
|
estimateTokens
|
|
19
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-6BSDCZ5Q.js";
|
|
20
41
|
import {
|
|
21
42
|
__toESM
|
|
22
43
|
} from "./chunk-PZUWP5VK.js";
|
|
23
44
|
|
|
24
45
|
// src/daemon/digest.ts
|
|
25
46
|
var import_yaml = __toESM(require_dist(), 1);
|
|
47
|
+
import fs2 from "fs";
|
|
48
|
+
import path2 from "path";
|
|
49
|
+
import crypto from "crypto";
|
|
50
|
+
|
|
51
|
+
// src/daemon/trace.ts
|
|
26
52
|
import fs from "fs";
|
|
27
53
|
import path from "path";
|
|
28
|
-
|
|
54
|
+
function readLastTimestamp(filePath) {
|
|
55
|
+
let content;
|
|
56
|
+
try {
|
|
57
|
+
content = fs.readFileSync(filePath, "utf-8").trim();
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!content) return null;
|
|
62
|
+
const lines = content.split("\n");
|
|
63
|
+
const lastLine = lines[lines.length - 1];
|
|
64
|
+
try {
|
|
65
|
+
const record = JSON.parse(lastLine);
|
|
66
|
+
return record.timestamp ?? null;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function appendTraceRecord(filePath, record) {
|
|
72
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
73
|
+
fs.appendFileSync(filePath, JSON.stringify(record) + "\n", "utf-8");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/daemon/digest.ts
|
|
29
77
|
var PREVIOUS_EXTRACT_OVERHEAD_TOKENS = 50;
|
|
30
78
|
var CONTEXT_SAFETY_MARGIN = 0.7;
|
|
31
79
|
var EXTRACT_TYPE = "extract";
|
|
@@ -37,6 +85,10 @@ var DigestEngine = class {
|
|
|
37
85
|
log;
|
|
38
86
|
lastCycleTimestampCache = void 0;
|
|
39
87
|
cycleInProgress = false;
|
|
88
|
+
/** Hooks that run before each digest cycle (e.g., consolidation). */
|
|
89
|
+
prePassHooks = [];
|
|
90
|
+
/** Hooks that run after each successful digest cycle. */
|
|
91
|
+
postPassHooks = [];
|
|
40
92
|
constructor(engineConfig) {
|
|
41
93
|
this.vaultDir = engineConfig.vaultDir;
|
|
42
94
|
this.index = engineConfig.index;
|
|
@@ -45,6 +97,14 @@ var DigestEngine = class {
|
|
|
45
97
|
this.log = engineConfig.log ?? (() => {
|
|
46
98
|
});
|
|
47
99
|
}
|
|
100
|
+
/** Register a hook that runs before each digest cycle. Best-effort — errors are logged, not thrown. */
|
|
101
|
+
registerPrePass(name, fn) {
|
|
102
|
+
this.prePassHooks.push({ name, fn });
|
|
103
|
+
}
|
|
104
|
+
/** Register a hook that runs after each successful digest cycle. Best-effort — errors are logged, not thrown. */
|
|
105
|
+
registerPostPass(name, fn) {
|
|
106
|
+
this.postPassHooks.push({ name, fn });
|
|
107
|
+
}
|
|
48
108
|
/**
|
|
49
109
|
* Query index for recent vault notes to feed into the digest.
|
|
50
110
|
* Filters out extract notes (our own output) and caps at max_notes_per_cycle.
|
|
@@ -98,10 +158,10 @@ ${note.content}`;
|
|
|
98
158
|
* Returns the body (stripped of YAML frontmatter), or null if not found.
|
|
99
159
|
*/
|
|
100
160
|
readPreviousExtract(tier) {
|
|
101
|
-
const extractPath =
|
|
161
|
+
const extractPath = path2.join(this.vaultDir, "digest", `extract-${tier}.md`);
|
|
102
162
|
let content;
|
|
103
163
|
try {
|
|
104
|
-
content =
|
|
164
|
+
content = fs2.readFileSync(extractPath, "utf-8");
|
|
105
165
|
} catch {
|
|
106
166
|
return null;
|
|
107
167
|
}
|
|
@@ -112,8 +172,8 @@ ${note.content}`;
|
|
|
112
172
|
* Uses atomic write pattern (temp file + rename).
|
|
113
173
|
*/
|
|
114
174
|
writeExtract(tier, body, cycleId, model, substrateCount) {
|
|
115
|
-
const digestDir =
|
|
116
|
-
|
|
175
|
+
const digestDir = path2.join(this.vaultDir, "digest");
|
|
176
|
+
fs2.mkdirSync(digestDir, { recursive: true });
|
|
117
177
|
const frontmatter = {
|
|
118
178
|
type: EXTRACT_TYPE,
|
|
119
179
|
tier,
|
|
@@ -132,19 +192,17 @@ ${fmYaml}
|
|
|
132
192
|
|
|
133
193
|
${body}
|
|
134
194
|
`;
|
|
135
|
-
const fullPath =
|
|
195
|
+
const fullPath = path2.join(digestDir, `extract-${tier}.md`);
|
|
136
196
|
const tmpPath = `${fullPath}.tmp`;
|
|
137
|
-
|
|
138
|
-
|
|
197
|
+
fs2.writeFileSync(tmpPath, file, "utf-8");
|
|
198
|
+
fs2.renameSync(tmpPath, fullPath);
|
|
139
199
|
}
|
|
140
200
|
/**
|
|
141
201
|
* Append a digest cycle result as a JSON line to trace.jsonl.
|
|
142
202
|
*/
|
|
143
203
|
appendTrace(record) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
const tracePath = path.join(digestDir, "trace.jsonl");
|
|
147
|
-
fs.appendFileSync(tracePath, JSON.stringify(record) + "\n", "utf-8");
|
|
204
|
+
const tracePath = path2.join(this.vaultDir, "digest", "trace.jsonl");
|
|
205
|
+
appendTraceRecord(tracePath, record);
|
|
148
206
|
this.lastCycleTimestampCache = record.timestamp;
|
|
149
207
|
}
|
|
150
208
|
/**
|
|
@@ -153,28 +211,9 @@ ${body}
|
|
|
153
211
|
*/
|
|
154
212
|
getLastCycleTimestamp() {
|
|
155
213
|
if (this.lastCycleTimestampCache !== void 0) return this.lastCycleTimestampCache;
|
|
156
|
-
const tracePath =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
content = fs.readFileSync(tracePath, "utf-8").trim();
|
|
160
|
-
} catch {
|
|
161
|
-
this.lastCycleTimestampCache = null;
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
if (!content) {
|
|
165
|
-
this.lastCycleTimestampCache = null;
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
const lines = content.split("\n");
|
|
169
|
-
const lastLine = lines[lines.length - 1];
|
|
170
|
-
try {
|
|
171
|
-
const record = JSON.parse(lastLine);
|
|
172
|
-
this.lastCycleTimestampCache = record.timestamp;
|
|
173
|
-
return record.timestamp;
|
|
174
|
-
} catch {
|
|
175
|
-
this.lastCycleTimestampCache = null;
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
214
|
+
const tracePath = path2.join(this.vaultDir, "digest", "trace.jsonl");
|
|
215
|
+
this.lastCycleTimestampCache = readLastTimestamp(tracePath);
|
|
216
|
+
return this.lastCycleTimestampCache;
|
|
178
217
|
}
|
|
179
218
|
/**
|
|
180
219
|
* Run a full digest cycle: discover substrate, generate extracts for each tier.
|
|
@@ -187,6 +226,13 @@ ${body}
|
|
|
187
226
|
}
|
|
188
227
|
this.cycleInProgress = true;
|
|
189
228
|
try {
|
|
229
|
+
for (const hook of this.prePassHooks) {
|
|
230
|
+
try {
|
|
231
|
+
await hook.fn();
|
|
232
|
+
} catch (err) {
|
|
233
|
+
this.log("warn", `Pre-pass hook "${hook.name}" failed`, { error: err.message });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
190
236
|
return await this.runCycleInternal(opts);
|
|
191
237
|
} finally {
|
|
192
238
|
this.cycleInProgress = false;
|
|
@@ -295,6 +341,13 @@ ${body}
|
|
|
295
341
|
tokensUsed: totalTokensUsed
|
|
296
342
|
};
|
|
297
343
|
this.appendTrace(result);
|
|
344
|
+
for (const hook of this.postPassHooks) {
|
|
345
|
+
try {
|
|
346
|
+
await hook.fn(result);
|
|
347
|
+
} catch (err) {
|
|
348
|
+
this.log("warn", `Post-pass hook "${hook.name}" failed`, { error: err.message });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
298
351
|
return result;
|
|
299
352
|
}
|
|
300
353
|
};
|
|
@@ -378,8 +431,234 @@ var Metabolism = class {
|
|
|
378
431
|
}
|
|
379
432
|
};
|
|
380
433
|
|
|
434
|
+
// src/services/vault-ops.ts
|
|
435
|
+
async function runRebuild(ctx, embeddingProvider, onProgress) {
|
|
436
|
+
const { index, vaultDir } = ctx;
|
|
437
|
+
initFts(index);
|
|
438
|
+
const ftsCount = rebuildIndex(index, vaultDir);
|
|
439
|
+
if (!ctx.vectorIndex) {
|
|
440
|
+
return { ftsCount, embeddedCount: 0, failedCount: 0, skippedCount: 0 };
|
|
441
|
+
}
|
|
442
|
+
const allNotes = index.query({});
|
|
443
|
+
const activeNotes = allNotes.filter((n) => {
|
|
444
|
+
const status = n.frontmatter?.status;
|
|
445
|
+
return status !== "superseded" && status !== "archived";
|
|
446
|
+
});
|
|
447
|
+
const skippedCount = allNotes.length - activeNotes.length;
|
|
448
|
+
const vec = ctx.vectorIndex;
|
|
449
|
+
const result = await batchExecute(
|
|
450
|
+
activeNotes,
|
|
451
|
+
async (note) => {
|
|
452
|
+
const text = `${note.title}
|
|
453
|
+
${note.content}`.slice(0, EMBEDDING_INPUT_LIMIT);
|
|
454
|
+
const emb = await generateEmbedding(embeddingProvider, text);
|
|
455
|
+
vec.upsert(note.id, emb.embedding, {
|
|
456
|
+
type: note.type,
|
|
457
|
+
session_id: note.frontmatter?.session ?? ""
|
|
458
|
+
});
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
concurrency: EMBEDDING_BATCH_CONCURRENCY,
|
|
462
|
+
onProgress
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
return {
|
|
466
|
+
ftsCount,
|
|
467
|
+
embeddedCount: result.succeeded,
|
|
468
|
+
failedCount: result.failed,
|
|
469
|
+
skippedCount
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
async function runDigest(ctx, llmProvider, options) {
|
|
473
|
+
const { config, vaultDir, index } = ctx;
|
|
474
|
+
const log = ctx.log ? (level, message, data) => ctx.log(level, message, data) : () => {
|
|
475
|
+
};
|
|
476
|
+
const engine = new DigestEngine({
|
|
477
|
+
vaultDir,
|
|
478
|
+
index,
|
|
479
|
+
llmProvider,
|
|
480
|
+
config,
|
|
481
|
+
log
|
|
482
|
+
});
|
|
483
|
+
const opts = {};
|
|
484
|
+
const isReprocess = options?.full || options?.tier !== void 0;
|
|
485
|
+
if (isReprocess) {
|
|
486
|
+
opts.fullReprocess = true;
|
|
487
|
+
opts.cleanSlate = true;
|
|
488
|
+
}
|
|
489
|
+
if (options?.tier !== void 0) {
|
|
490
|
+
const eligible = engine.getEligibleTiers();
|
|
491
|
+
if (!eligible.includes(options.tier)) {
|
|
492
|
+
throw new Error(`Tier ${options.tier} is not eligible. Eligible tiers: [${eligible.join(", ")}]`);
|
|
493
|
+
}
|
|
494
|
+
opts.tiers = [options.tier];
|
|
495
|
+
}
|
|
496
|
+
return engine.runCycle(opts);
|
|
497
|
+
}
|
|
498
|
+
var CURATION_EMBEDDING_BATCH_SIZE = 10;
|
|
499
|
+
function cosineSimilarity(a, b) {
|
|
500
|
+
let dot = 0, normA = 0, normB = 0;
|
|
501
|
+
for (let i = 0; i < a.length; i++) {
|
|
502
|
+
dot += a[i] * b[i];
|
|
503
|
+
normA += a[i] * a[i];
|
|
504
|
+
normB += b[i] * b[i];
|
|
505
|
+
}
|
|
506
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
507
|
+
}
|
|
508
|
+
function updateCentroid(spores) {
|
|
509
|
+
if (spores.length === 0) return [];
|
|
510
|
+
const dim = spores[0].embedding.length;
|
|
511
|
+
const centroid = new Array(dim).fill(0);
|
|
512
|
+
for (const s of spores) {
|
|
513
|
+
for (let i = 0; i < dim; i++) {
|
|
514
|
+
centroid[i] += s.embedding[i];
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (let i = 0; i < dim; i++) {
|
|
518
|
+
centroid[i] /= spores.length;
|
|
519
|
+
}
|
|
520
|
+
return centroid;
|
|
521
|
+
}
|
|
522
|
+
function clusterSpores(spores) {
|
|
523
|
+
const clusters = [];
|
|
524
|
+
for (const spore of spores) {
|
|
525
|
+
let bestCluster = null;
|
|
526
|
+
let bestSimilarity = -1;
|
|
527
|
+
for (const cluster of clusters) {
|
|
528
|
+
const sim = cosineSimilarity(spore.embedding, cluster.centroid);
|
|
529
|
+
if (sim > bestSimilarity) {
|
|
530
|
+
bestSimilarity = sim;
|
|
531
|
+
bestCluster = cluster;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (bestCluster !== null && bestSimilarity >= CURATION_CLUSTER_SIMILARITY) {
|
|
535
|
+
bestCluster.spores.push(spore);
|
|
536
|
+
bestCluster.centroid = updateCentroid(bestCluster.spores);
|
|
537
|
+
} else {
|
|
538
|
+
clusters.push({ spores: [spore], centroid: [...spore.embedding] });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return clusters;
|
|
542
|
+
}
|
|
543
|
+
async function runCuration(deps, dryRun) {
|
|
544
|
+
const { index, vectorIndex, llmProvider, embeddingProvider, vaultDir } = deps;
|
|
545
|
+
const log = deps.log ?? (() => {
|
|
546
|
+
});
|
|
547
|
+
const allSpores = index.query({ type: "spore" });
|
|
548
|
+
const activeSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));
|
|
549
|
+
if (activeSpores.length === 0) {
|
|
550
|
+
return { scanned: 0, clustersEvaluated: 0, superseded: 0 };
|
|
551
|
+
}
|
|
552
|
+
const sporesWithEmbeddings = [];
|
|
553
|
+
let embedFailures = 0;
|
|
554
|
+
for (let i = 0; i < activeSpores.length; i += CURATION_EMBEDDING_BATCH_SIZE) {
|
|
555
|
+
const batch = activeSpores.slice(i, i + CURATION_EMBEDDING_BATCH_SIZE);
|
|
556
|
+
const results = await Promise.allSettled(
|
|
557
|
+
batch.map(async (spore) => {
|
|
558
|
+
const text = spore.content.slice(0, EMBEDDING_INPUT_LIMIT);
|
|
559
|
+
const result = await generateEmbedding(embeddingProvider, text);
|
|
560
|
+
return { spore, embedding: result.embedding };
|
|
561
|
+
})
|
|
562
|
+
);
|
|
563
|
+
for (const result of results) {
|
|
564
|
+
if (result.status === "fulfilled") {
|
|
565
|
+
const { spore, embedding } = result.value;
|
|
566
|
+
sporesWithEmbeddings.push({
|
|
567
|
+
id: spore.id,
|
|
568
|
+
path: spore.path,
|
|
569
|
+
title: spore.title,
|
|
570
|
+
content: spore.content,
|
|
571
|
+
created: spore.created,
|
|
572
|
+
frontmatter: spore.frontmatter,
|
|
573
|
+
embedding
|
|
574
|
+
});
|
|
575
|
+
} else {
|
|
576
|
+
embedFailures++;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (embedFailures > 0) {
|
|
581
|
+
log("warn", `${embedFailures} spore(s) could not be embedded and were skipped`);
|
|
582
|
+
}
|
|
583
|
+
const byType = /* @__PURE__ */ new Map();
|
|
584
|
+
for (const spore of sporesWithEmbeddings) {
|
|
585
|
+
const obsType = spore.frontmatter["observation_type"] ?? "unknown";
|
|
586
|
+
if (!byType.has(obsType)) byType.set(obsType, []);
|
|
587
|
+
byType.get(obsType).push(spore);
|
|
588
|
+
}
|
|
589
|
+
const template = loadPrompt("supersession");
|
|
590
|
+
let totalClusters = 0;
|
|
591
|
+
let totalSuperseded = 0;
|
|
592
|
+
for (const [obsType, typeSpores] of byType) {
|
|
593
|
+
const clusters = clusterSpores(typeSpores);
|
|
594
|
+
const multiSpore = clusters.filter((c) => c.spores.length >= 2);
|
|
595
|
+
if (multiSpore.length === 0) continue;
|
|
596
|
+
log("info", `Type: ${obsType} \u2014 ${typeSpores.length} spores, ${multiSpore.length} cluster(s) to evaluate`);
|
|
597
|
+
totalClusters += multiSpore.length;
|
|
598
|
+
for (const cluster of multiSpore) {
|
|
599
|
+
const sorted = [...cluster.spores].sort((a, b) => a.created.localeCompare(b.created));
|
|
600
|
+
const newest = sorted[sorted.length - 1];
|
|
601
|
+
const candidates = sorted.slice(0, sorted.length - 1);
|
|
602
|
+
const newSporeText = formatNoteForPrompt(newest);
|
|
603
|
+
const candidatesText = formatNotesForPrompt(candidates);
|
|
604
|
+
const prompt = template.replace("{{new_spore}}", newSporeText).replace("{{candidates}}", candidatesText);
|
|
605
|
+
let responseText;
|
|
606
|
+
try {
|
|
607
|
+
const response = await llmProvider.summarize(prompt, {
|
|
608
|
+
maxTokens: SUPERSESSION_MAX_TOKENS,
|
|
609
|
+
reasoning: LLM_REASONING_MODE
|
|
610
|
+
});
|
|
611
|
+
responseText = stripReasoningTokens(response.text);
|
|
612
|
+
} catch (err) {
|
|
613
|
+
log("warn", `LLM call failed for cluster in ${obsType}: ${String(err)}`);
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
let rawIds;
|
|
617
|
+
try {
|
|
618
|
+
rawIds = JSON.parse(responseText);
|
|
619
|
+
} catch {
|
|
620
|
+
log("warn", `Could not parse LLM response for cluster in ${obsType}`);
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
const parsed = supersededIdsSchema.safeParse(rawIds);
|
|
624
|
+
if (!parsed.success) {
|
|
625
|
+
log("warn", `LLM response schema invalid for cluster in ${obsType}`);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const candidateMap = new Map(candidates.map((c) => [c.id, c]));
|
|
629
|
+
const validIds = parsed.data.filter((id) => candidateMap.has(id));
|
|
630
|
+
if (validIds.length === 0) continue;
|
|
631
|
+
for (const id of validIds) {
|
|
632
|
+
const candidate = candidateMap.get(id);
|
|
633
|
+
if (dryRun) {
|
|
634
|
+
log("info", `[dry-run] Would supersede: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);
|
|
635
|
+
totalSuperseded++;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const wrote = supersedeSpore(id, newest.id, candidate.path, { index, vectorIndex, vaultDir });
|
|
639
|
+
if (!wrote) {
|
|
640
|
+
log("warn", `File not found for ${id}, skipping write`);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
log("info", `Superseded: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);
|
|
644
|
+
totalSuperseded++;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
scanned: activeSpores.length,
|
|
650
|
+
clustersEvaluated: totalClusters,
|
|
651
|
+
superseded: totalSuperseded
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
381
655
|
export {
|
|
656
|
+
readLastTimestamp,
|
|
657
|
+
appendTraceRecord,
|
|
382
658
|
DigestEngine,
|
|
383
|
-
Metabolism
|
|
659
|
+
Metabolism,
|
|
660
|
+
runRebuild,
|
|
661
|
+
runDigest,
|
|
662
|
+
runCuration
|
|
384
663
|
};
|
|
385
|
-
//# sourceMappingURL=chunk-
|
|
664
|
+
//# sourceMappingURL=chunk-2ZBB3MQT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/digest.ts","../src/daemon/trace.ts","../src/services/vault-ops.ts"],"sourcesContent":["/**\n * DigestEngine — synthesizes vault knowledge into tiered context extracts.\n * Metabolism — adaptive timer that throttles digest cycles based on activity.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport YAML from 'yaml';\n\nimport type { MycoIndex, IndexedNote } from '@myco/index/sqlite.js';\nimport type { LlmProvider, LlmRequestOptions } from '@myco/intelligence/llm.js';\nimport type { MycoConfig } from '@myco/config/schema.js';\nimport { loadPrompt } from '@myco/prompts/index.js';\nimport { stripReasoningTokens } from '@myco/intelligence/response.js';\nimport { stripFrontmatter } from '@myco/vault/frontmatter.js';\nimport { readLastTimestamp, appendTraceRecord } from './trace.js';\nimport {\n estimateTokens,\n CHARS_PER_TOKEN,\n DIGEST_TIER_MIN_CONTEXT,\n DIGEST_SUBSTRATE_TYPE_WEIGHTS,\n DIGEST_LLM_REQUEST_TIMEOUT_MS,\n LLM_REASONING_MODE,\n} from '@myco/constants.js';\n\n// --- Interfaces ---\n\nexport interface DigestCycleResult {\n cycleId: string;\n timestamp: string;\n substrate: {\n sessions: string[];\n spores: string[];\n plans: string[];\n artifacts: string[];\n team: string[];\n };\n tiersGenerated: number[];\n model: string;\n durationMs: number;\n tokensUsed: number;\n}\n\n/** Simple log function signature for digest progress reporting. */\nexport type DigestLogFn = (level: 'debug' | 'info' | 'warn', message: string, data?: Record<string, unknown>) => void;\n\nexport interface DigestCycleOptions {\n /** Process all substrate regardless of last cycle timestamp. */\n fullReprocess?: boolean;\n /** Only generate these tiers (default: all eligible). */\n tiers?: number[];\n /** Skip previous extract — start from clean slate. */\n cleanSlate?: boolean;\n}\n\nexport interface DigestEngineConfig {\n vaultDir: string;\n index: MycoIndex;\n llmProvider: LlmProvider;\n config: MycoConfig;\n log?: DigestLogFn;\n}\n\n// --- Constants ---\n\n/** Token overhead estimate for previous extract section wrapper. */\nconst PREVIOUS_EXTRACT_OVERHEAD_TOKENS = 50;\n\n/** Safety margin for context window — our CHARS_PER_TOKEN=4 heuristic significantly\n * underestimates real token counts (observed ~3.2 chars/token for mixed content).\n * 0.70 provides a safe buffer: 32K * 0.70 = 22.4K usable tokens. */\nconst CONTEXT_SAFETY_MARGIN = 0.70;\n\n/** Types that are digest output — excluded from substrate to avoid self-digestion. */\nconst EXTRACT_TYPE = 'extract';\n\n// --- DigestEngine ---\n\nexport class DigestEngine {\n private vaultDir: string;\n private index: MycoIndex;\n private llm: LlmProvider;\n private config: MycoConfig;\n private log: DigestLogFn;\n private lastCycleTimestampCache: string | null | undefined = undefined;\n private cycleInProgress = false;\n\n /** Hooks that run before each digest cycle (e.g., consolidation). */\n private prePassHooks: Array<{ name: string; fn: () => Promise<void> }> = [];\n\n /** Hooks that run after each successful digest cycle. */\n private postPassHooks: Array<{ name: string; fn: (result: DigestCycleResult) => Promise<void> }> = [];\n\n constructor(engineConfig: DigestEngineConfig) {\n this.vaultDir = engineConfig.vaultDir;\n this.index = engineConfig.index;\n this.llm = engineConfig.llmProvider;\n this.config = engineConfig.config;\n this.log = engineConfig.log ?? (() => {});\n }\n\n /** Register a hook that runs before each digest cycle. Best-effort — errors are logged, not thrown. */\n registerPrePass(name: string, fn: () => Promise<void>): void {\n this.prePassHooks.push({ name, fn });\n }\n\n /** Register a hook that runs after each successful digest cycle. Best-effort — errors are logged, not thrown. */\n registerPostPass(name: string, fn: (result: DigestCycleResult) => Promise<void>): void {\n this.postPassHooks.push({ name, fn });\n }\n\n /**\n * Query index for recent vault notes to feed into the digest.\n * Filters out extract notes (our own output) and caps at max_notes_per_cycle.\n */\n discoverSubstrate(lastCycleTimestamp: string | null): IndexedNote[] {\n const maxNotes = this.config.digest.substrate.max_notes_per_cycle;\n\n const notes = lastCycleTimestamp\n ? this.index.query({ updatedSince: lastCycleTimestamp, limit: maxNotes })\n : this.index.query({ limit: maxNotes });\n\n // Guard against self-digestion: extract files are not currently indexed,\n // but this filter prevents feedback loops if they ever are (e.g., via rebuild)\n const filtered = notes\n .filter((n) => n.type !== EXTRACT_TYPE)\n .filter((n) => {\n if (n.type !== 'spore') return true;\n const status = n.frontmatter.status as string | undefined;\n return !status || status === 'active';\n });\n\n // Sort by type weight (descending) then by recency (descending)\n filtered.sort((a, b) => {\n const weightA = DIGEST_SUBSTRATE_TYPE_WEIGHTS[a.type] ?? 0;\n const weightB = DIGEST_SUBSTRATE_TYPE_WEIGHTS[b.type] ?? 0;\n if (weightB !== weightA) return weightB - weightA;\n // More recent first — created is ISO string, lexicographic sort works\n return b.created.localeCompare(a.created);\n });\n\n return filtered.slice(0, maxNotes);\n }\n\n /**\n * Filter configured tiers by the context window available.\n * Only tiers whose minimum context requirement is met are eligible.\n */\n getEligibleTiers(): number[] {\n const contextWindow = this.config.digest.intelligence.context_window;\n return this.config.digest.tiers.filter((tier) => {\n const minContext = DIGEST_TIER_MIN_CONTEXT[tier];\n return minContext !== undefined && minContext <= contextWindow;\n });\n }\n\n /**\n * Format notes compactly for inclusion in the digest prompt.\n * Stops adding notes once the token budget is exceeded.\n */\n formatSubstrate(notes: IndexedNote[], tokenBudget: number): string {\n const charBudget = tokenBudget * CHARS_PER_TOKEN;\n const parts: string[] = [];\n let usedChars = 0;\n\n for (const note of notes) {\n const entry = `### [${note.type}] ${note.id} — \"${note.title}\"\\n${note.content}`;\n if (usedChars + entry.length > charBudget && parts.length > 0) break;\n parts.push(entry);\n usedChars += entry.length;\n }\n\n return parts.join('\\n\\n');\n }\n\n /**\n * Read a previously generated extract for a given tier.\n * Returns the body (stripped of YAML frontmatter), or null if not found.\n */\n readPreviousExtract(tier: number): string | null {\n const extractPath = path.join(this.vaultDir, 'digest', `extract-${tier}.md`);\n let content: string;\n try {\n content = fs.readFileSync(extractPath, 'utf-8');\n } catch {\n return null;\n }\n\n return stripFrontmatter(content).body;\n }\n\n /**\n * Write a digest extract to the vault with YAML frontmatter.\n * Uses atomic write pattern (temp file + rename).\n */\n writeExtract(\n tier: number,\n body: string,\n cycleId: string,\n model: string,\n substrateCount: number,\n ): void {\n const digestDir = path.join(this.vaultDir, 'digest');\n fs.mkdirSync(digestDir, { recursive: true });\n\n const frontmatter: Record<string, unknown> = {\n type: EXTRACT_TYPE,\n tier,\n generated: new Date().toISOString(),\n cycle_id: cycleId,\n substrate_count: substrateCount,\n model,\n };\n\n const fmYaml = YAML.stringify(frontmatter, {\n defaultStringType: 'QUOTE_DOUBLE',\n defaultKeyType: 'PLAIN',\n }).trim();\n const file = `---\\n${fmYaml}\\n---\\n\\n${body}\\n`;\n\n const fullPath = path.join(digestDir, `extract-${tier}.md`);\n const tmpPath = `${fullPath}.tmp`;\n fs.writeFileSync(tmpPath, file, 'utf-8');\n fs.renameSync(tmpPath, fullPath);\n }\n\n /**\n * Append a digest cycle result as a JSON line to trace.jsonl.\n */\n appendTrace(record: DigestCycleResult): void {\n const tracePath = path.join(this.vaultDir, 'digest', 'trace.jsonl');\n appendTraceRecord(tracePath, record as unknown as Record<string, unknown>);\n this.lastCycleTimestampCache = record.timestamp;\n }\n\n /**\n * Read the last cycle timestamp from trace.jsonl.\n * Cached in memory after first read — subsequent calls are O(1).\n */\n getLastCycleTimestamp(): string | null {\n if (this.lastCycleTimestampCache !== undefined) return this.lastCycleTimestampCache;\n\n const tracePath = path.join(this.vaultDir, 'digest', 'trace.jsonl');\n this.lastCycleTimestampCache = readLastTimestamp(tracePath);\n return this.lastCycleTimestampCache;\n }\n\n /**\n * Run a full digest cycle: discover substrate, generate extracts for each tier.\n * Returns the cycle result, or null if no substrate was found.\n */\n async runCycle(opts?: DigestCycleOptions): Promise<DigestCycleResult | null> {\n if (this.cycleInProgress) {\n this.log('debug', 'Cycle already in progress — skipping');\n return null;\n }\n this.cycleInProgress = true;\n\n try {\n // Run pre-pass hooks (e.g., consolidation) before discovering substrate\n for (const hook of this.prePassHooks) {\n try {\n await hook.fn();\n } catch (err) {\n this.log('warn', `Pre-pass hook \"${hook.name}\" failed`, { error: (err as Error).message });\n }\n }\n\n return await this.runCycleInternal(opts);\n } finally {\n this.cycleInProgress = false;\n }\n }\n\n private async runCycleInternal(opts?: DigestCycleOptions): Promise<DigestCycleResult | null> {\n // Ensure model is loaded with correct settings every cycle.\n // LM Studio's idle TTL can evict our instance between cycles — without\n // re-running ensureLoaded, the auto-reloaded instance would use LM Studio's\n // UI defaults (wrong KV cache setting). This is fast (~26ms) when the\n // instance is still alive (just a getLoadedInstances check).\n if (this.llm.ensureLoaded) {\n const { context_window: contextWindow, gpu_kv_cache: gpuKvCache } = this.config.digest.intelligence;\n this.log('debug', 'Verifying digest model', { contextWindow, gpuKvCache });\n await this.llm.ensureLoaded(contextWindow, gpuKvCache);\n }\n\n const startTime = Date.now();\n const fullReprocess = opts?.fullReprocess ?? false;\n const lastTimestamp = fullReprocess ? null : this.getLastCycleTimestamp();\n const substrate = this.discoverSubstrate(lastTimestamp);\n\n this.log('debug', 'Discovering substrate', { lastTimestamp: lastTimestamp ?? 'full reprocess', substrateCount: substrate.length });\n if (substrate.length === 0) {\n this.log('debug', 'No substrate found — skipping cycle');\n return null;\n }\n\n this.log('info', `Starting digest cycle`, { substrateCount: substrate.length, fullReprocess });\n const cycleId = crypto.randomUUID();\n const allEligible = this.getEligibleTiers();\n const eligibleTiers = opts?.tiers\n ? allEligible.filter((t) => opts.tiers!.includes(t))\n : allEligible;\n this.log('debug', `Eligible tiers: [${eligibleTiers.join(', ')}]`);\n const tiersGenerated: number[] = [];\n let totalTokensUsed = 0;\n let model = '';\n\n // Categorize substrate by type for the result\n const typeToKey: Record<string, keyof DigestCycleResult['substrate']> = {\n session: 'sessions',\n spore: 'spores',\n plan: 'plans',\n artifact: 'artifacts',\n 'team-member': 'team',\n };\n const substrateIndex: DigestCycleResult['substrate'] = {\n sessions: [],\n spores: [],\n plans: [],\n artifacts: [],\n team: [],\n };\n for (const note of substrate) {\n const key = typeToKey[note.type];\n if (key) {\n substrateIndex[key].push(note.id);\n }\n }\n\n // Record the cycle timestamp NOW, before tier processing. This ensures the\n // timestamp advances even if LLM calls fail, preventing the same substrate\n // from being rediscovered on every subsequent timer fire.\n const cycleTimestamp = new Date().toISOString();\n\n const systemPrompt = loadPrompt('digest-system');\n\n for (const tier of eligibleTiers) {\n const tierPrompt = loadPrompt(`digest-${tier}`);\n const previousExtract = opts?.cleanSlate ? null : this.readPreviousExtract(tier);\n\n // Calculate token budget for substrate:\n // (context_window * safety_margin) - output - system_prompt - tier_prompt - previous_extract\n const contextWindow = this.config.digest.intelligence.context_window;\n const systemPromptTokens = estimateTokens(systemPrompt);\n const tierPromptTokens = estimateTokens(tierPrompt);\n const previousExtractTokens = previousExtract\n ? estimateTokens(previousExtract) + PREVIOUS_EXTRACT_OVERHEAD_TOKENS\n : 0;\n const availableTokens = Math.floor(contextWindow * CONTEXT_SAFETY_MARGIN);\n const substrateBudget = availableTokens - tier - systemPromptTokens - tierPromptTokens - previousExtractTokens;\n\n if (substrateBudget <= 0) continue;\n\n const formattedSubstrate = this.formatSubstrate(substrate, substrateBudget);\n\n // Build user prompt (system prompt sent separately via LlmRequestOptions)\n const promptParts = [tierPrompt];\n\n if (previousExtract) {\n promptParts.push('', '## Previous Synthesis', '', previousExtract);\n }\n\n promptParts.push('', '## New Substrate', '', formattedSubstrate);\n promptParts.push(\n '',\n '---',\n 'Produce your updated synthesis now. Stay within the token budget specified above.',\n );\n\n const userPrompt = promptParts.join('\\n');\n const promptTokens = estimateTokens(systemPrompt + userPrompt);\n this.log('debug', `Tier ${tier}: sending LLM request`, { promptTokens, maxTokens: tier, substrateBudget });\n\n try {\n const tierStart = Date.now();\n const digestConfig = this.config.digest.intelligence;\n const opts: LlmRequestOptions = {\n maxTokens: tier,\n timeoutMs: DIGEST_LLM_REQUEST_TIMEOUT_MS,\n contextLength: contextWindow,\n reasoning: LLM_REASONING_MODE,\n systemPrompt,\n keepAlive: digestConfig.keep_alive ?? undefined,\n };\n const response = await this.llm.summarize(userPrompt, opts);\n const tierDuration = Date.now() - tierStart;\n\n // Strip reasoning tokens if present (some models output chain-of-thought)\n const extractText = stripReasoningTokens(response.text);\n model = response.model;\n const responseTokens = estimateTokens(extractText);\n totalTokensUsed += promptTokens + responseTokens;\n\n this.log('info', `Tier ${tier}: completed`, { durationMs: tierDuration, responseTokens, model: response.model });\n this.writeExtract(tier, extractText, cycleId, response.model, substrate.length);\n tiersGenerated.push(tier);\n } catch (err) {\n this.log('warn', `Tier ${tier}: failed`, { error: (err as Error).message });\n }\n }\n\n const result: DigestCycleResult = {\n cycleId,\n timestamp: cycleTimestamp,\n substrate: substrateIndex,\n tiersGenerated,\n model,\n durationMs: Date.now() - startTime,\n tokensUsed: totalTokensUsed,\n };\n\n this.appendTrace(result);\n\n // Run post-pass hooks after successful digest\n for (const hook of this.postPassHooks) {\n try {\n await hook.fn(result);\n } catch (err) {\n this.log('warn', `Post-pass hook \"${hook.name}\" failed`, { error: (err as Error).message });\n }\n }\n\n return result;\n }\n}\n\n// --- Metabolism (Adaptive Timer) ---\n\nexport type MetabolismState = 'active' | 'cooling' | 'dormant';\n\n/** Milliseconds per second for config conversion. */\nconst MS_PER_SECOND = 1000;\n\nexport class Metabolism {\n state: MetabolismState = 'active';\n currentIntervalMs: number;\n\n private cooldownStep = 0;\n private lastSubstrateTime: number;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private activeIntervalMs: number;\n private cooldownIntervalsMs: number[];\n private dormancyThresholdMs: number;\n\n constructor(config: MycoConfig['digest']['metabolism']) {\n this.activeIntervalMs = config.active_interval * MS_PER_SECOND;\n this.cooldownIntervalsMs = config.cooldown_intervals.map((s) => s * MS_PER_SECOND);\n this.dormancyThresholdMs = config.dormancy_threshold * MS_PER_SECOND;\n this.currentIntervalMs = this.activeIntervalMs;\n this.lastSubstrateTime = Date.now();\n }\n\n /** Reset to active state when new substrate is found. */\n onSubstrateFound(): void {\n this.state = 'active';\n this.cooldownStep = 0;\n this.currentIntervalMs = this.activeIntervalMs;\n this.lastSubstrateTime = Date.now();\n }\n\n /** Advance cooldown when a cycle finds no new substrate. */\n onEmptyCycle(): void {\n if (this.state === 'dormant') return;\n\n this.state = 'cooling';\n if (this.cooldownStep < this.cooldownIntervalsMs.length) {\n this.currentIntervalMs = this.cooldownIntervalsMs[this.cooldownStep];\n this.cooldownStep++;\n }\n\n this.checkDormancy();\n }\n\n /** Enter dormant state if enough time has elapsed since last substrate. */\n checkDormancy(): void {\n const elapsed = Date.now() - this.lastSubstrateTime;\n if (elapsed >= this.dormancyThresholdMs) {\n this.state = 'dormant';\n // Keep the last cooldown interval as the dormant polling rate\n }\n }\n\n /** Return to active from any state, resetting timers and rescheduling immediately. */\n activate(): void {\n this.onSubstrateFound();\n // Reschedule with the new active interval — without this, the old\n // (possibly dormant) timer continues ticking at the wrong rate\n if (this.callback) {\n this.reschedule();\n }\n }\n\n /** Set lastSubstrateTime explicitly (for testing). */\n markLastSubstrate(time: number): void {\n this.lastSubstrateTime = time;\n }\n\n /** Begin scheduling digest cycles with adaptive intervals. */\n start(callback: () => Promise<void>): void {\n this.callback = callback;\n this.reschedule();\n }\n\n /** Stop the timer. */\n stop(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private callback: (() => Promise<void>) | null = null;\n\n private reschedule(): void {\n this.stop();\n if (!this.callback) return;\n const cb = this.callback;\n const schedule = (): void => {\n this.timer = setTimeout(async () => {\n await cb();\n schedule();\n }, this.currentIntervalMs);\n this.timer.unref();\n };\n schedule();\n }\n}\n","/**\n * Shared JSONL trace file utilities.\n *\n * Used by DigestEngine and ConsolidationEngine to read/write append-only\n * trace records with timestamp caching.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** Read the last JSON record's `timestamp` field from a JSONL file. Returns null if file is missing or empty. */\nexport function readLastTimestamp(filePath: string): string | null {\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf-8').trim();\n } catch {\n return null;\n }\n\n if (!content) return null;\n\n const lines = content.split('\\n');\n const lastLine = lines[lines.length - 1];\n try {\n const record = JSON.parse(lastLine) as { timestamp: string };\n return record.timestamp ?? null;\n } catch {\n return null;\n }\n}\n\n/** Append a JSON record to a JSONL file, creating parent directories if needed. */\nexport function appendTraceRecord(filePath: string, record: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.appendFileSync(filePath, JSON.stringify(record) + '\\n', 'utf-8');\n}\n","import type { MycoIndex } from '../index/sqlite.js';\nimport type { VectorIndex } from '../index/vectors.js';\nimport type { MycoConfig } from '../config/schema.js';\nimport type { LlmProvider, EmbeddingProvider } from '../intelligence/llm.js';\nimport type { DigestCycleResult, DigestLogFn } from '../daemon/digest.js';\nimport { rebuildIndex } from '../index/rebuild.js';\nimport { initFts } from '../index/fts.js';\nimport { generateEmbedding } from '../intelligence/embeddings.js';\nimport { batchExecute, EMBEDDING_BATCH_CONCURRENCY } from '../intelligence/batch.js';\nimport { DigestEngine } from '../daemon/digest.js';\nimport type { DigestCycleOptions } from '../daemon/digest.js';\nimport {\n EMBEDDING_INPUT_LIMIT,\n CURATION_CLUSTER_SIMILARITY,\n SUPERSESSION_MAX_TOKENS,\n LLM_REASONING_MODE,\n} from '../constants.js';\nimport { stripReasoningTokens } from '../intelligence/response.js';\nimport { loadPrompt, formatNoteForPrompt, formatNotesForPrompt } from '../prompts/index.js';\nimport { supersedeSpore, supersededIdsSchema, isActiveSpore } from '../vault/curation.js';\n\nexport interface OperationContext {\n vaultDir: string;\n config: MycoConfig;\n index: MycoIndex;\n vectorIndex?: VectorIndex;\n log?: (level: string, message: string, data?: Record<string, unknown>) => void;\n}\n\n// --- Rebuild ---\n\nexport interface RebuildResult {\n ftsCount: number;\n embeddedCount: number;\n failedCount: number;\n skippedCount: number;\n}\n\n/**\n * Rebuild FTS index and re-embed all active notes.\n * Core logic shared between CLI (`myco rebuild`) and daemon API (`POST /api/rebuild`).\n */\nexport async function runRebuild(\n ctx: OperationContext,\n embeddingProvider: EmbeddingProvider,\n onProgress?: (done: number, total: number) => void,\n): Promise<RebuildResult> {\n const { index, vaultDir } = ctx;\n\n // Phase 1: FTS rebuild\n initFts(index);\n const ftsCount = rebuildIndex(index, vaultDir);\n\n // Phase 2: Vector embeddings\n if (!ctx.vectorIndex) {\n return { ftsCount, embeddedCount: 0, failedCount: 0, skippedCount: 0 };\n }\n\n const allNotes = index.query({});\n // Skip superseded/archived spores\n const activeNotes = allNotes.filter((n) => {\n const status = (n.frontmatter as Record<string, unknown>)?.status as string | undefined;\n return status !== 'superseded' && status !== 'archived';\n });\n const skippedCount = allNotes.length - activeNotes.length;\n\n const vec = ctx.vectorIndex;\n const result = await batchExecute(\n activeNotes,\n async (note) => {\n const text = `${note.title}\\n${note.content}`.slice(0, EMBEDDING_INPUT_LIMIT);\n const emb = await generateEmbedding(embeddingProvider, text);\n vec.upsert(note.id, emb.embedding, {\n type: note.type,\n session_id: (note.frontmatter as Record<string, unknown>)?.session as string ?? '',\n });\n },\n {\n concurrency: EMBEDDING_BATCH_CONCURRENCY,\n onProgress,\n },\n );\n\n return {\n ftsCount,\n embeddedCount: result.succeeded,\n failedCount: result.failed,\n skippedCount,\n };\n}\n\n// --- Digest ---\n\nexport interface DigestOptions {\n tier?: number;\n full?: boolean;\n}\n\n/**\n * Run a single digest cycle.\n * Core logic shared between CLI (`myco digest`) and daemon API (`POST /api/digest`).\n */\nexport async function runDigest(\n ctx: OperationContext,\n llmProvider: LlmProvider,\n options?: DigestOptions,\n): Promise<DigestCycleResult | null> {\n const { config, vaultDir, index } = ctx;\n\n const log: DigestLogFn = ctx.log\n ? (level, message, data) => ctx.log!(level, message, data)\n : () => {};\n\n const engine = new DigestEngine({\n vaultDir,\n index,\n llmProvider,\n config,\n log,\n });\n\n const opts: DigestCycleOptions = {};\n const isReprocess = options?.full || options?.tier !== undefined;\n\n if (isReprocess) {\n opts.fullReprocess = true;\n opts.cleanSlate = true;\n }\n if (options?.tier !== undefined) {\n const eligible = engine.getEligibleTiers();\n if (!eligible.includes(options.tier)) {\n throw new Error(`Tier ${options.tier} is not eligible. Eligible tiers: [${eligible.join(', ')}]`);\n }\n opts.tiers = [options.tier];\n }\n\n return engine.runCycle(opts);\n}\n\n// --- Curation ---\n\nexport interface CurationDeps {\n vaultDir: string;\n config: MycoConfig;\n index: MycoIndex;\n vectorIndex: VectorIndex;\n llmProvider: LlmProvider;\n embeddingProvider: EmbeddingProvider;\n log?: (level: string, message: string, data?: Record<string, unknown>) => void;\n}\n\nexport interface CurationResult {\n scanned: number;\n clustersEvaluated: number;\n superseded: number;\n}\n\n// --- Curation internals ---\n\n/** Max concurrent embedding requests to avoid overwhelming the provider. */\nconst CURATION_EMBEDDING_BATCH_SIZE = 10;\n\ninterface SporeWithEmbedding {\n id: string;\n path: string;\n title: string;\n content: string;\n created: string;\n frontmatter: Record<string, unknown>;\n embedding: number[];\n}\n\ninterface Cluster {\n spores: SporeWithEmbedding[];\n centroid: number[];\n}\n\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0, normA = 0, normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n\nfunction updateCentroid(spores: SporeWithEmbedding[]): number[] {\n if (spores.length === 0) return [];\n const dim = spores[0].embedding.length;\n const centroid = new Array<number>(dim).fill(0);\n for (const s of spores) {\n for (let i = 0; i < dim; i++) {\n centroid[i] += s.embedding[i];\n }\n }\n for (let i = 0; i < dim; i++) {\n centroid[i] /= spores.length;\n }\n return centroid;\n}\n\nfunction clusterSpores(spores: SporeWithEmbedding[]): Cluster[] {\n const clusters: Cluster[] = [];\n for (const spore of spores) {\n let bestCluster: Cluster | null = null;\n let bestSimilarity = -1;\n for (const cluster of clusters) {\n const sim = cosineSimilarity(spore.embedding, cluster.centroid);\n if (sim > bestSimilarity) {\n bestSimilarity = sim;\n bestCluster = cluster;\n }\n }\n if (bestCluster !== null && bestSimilarity >= CURATION_CLUSTER_SIMILARITY) {\n bestCluster.spores.push(spore);\n bestCluster.centroid = updateCentroid(bestCluster.spores);\n } else {\n clusters.push({ spores: [spore], centroid: [...spore.embedding] });\n }\n }\n return clusters;\n}\n\n/**\n * Run vault curation: embed spores, cluster, ask LLM which are outdated, supersede.\n * Core logic shared between CLI (`myco curate`) and daemon API (`POST /api/curate`).\n */\nexport async function runCuration(\n deps: CurationDeps,\n dryRun: boolean,\n): Promise<CurationResult> {\n const { index, vectorIndex, llmProvider, embeddingProvider, vaultDir } = deps;\n const log = deps.log ?? (() => {});\n\n // 1. Query all spores and filter for active ones\n const allSpores = index.query({ type: 'spore' });\n const activeSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));\n\n if (activeSpores.length === 0) {\n return { scanned: 0, clustersEvaluated: 0, superseded: 0 };\n }\n\n // 2. Embed all active spores (batched for concurrency)\n const sporesWithEmbeddings: SporeWithEmbedding[] = [];\n let embedFailures = 0;\n\n for (let i = 0; i < activeSpores.length; i += CURATION_EMBEDDING_BATCH_SIZE) {\n const batch = activeSpores.slice(i, i + CURATION_EMBEDDING_BATCH_SIZE);\n const results = await Promise.allSettled(\n batch.map(async (spore) => {\n const text = spore.content.slice(0, EMBEDDING_INPUT_LIMIT);\n const result = await generateEmbedding(embeddingProvider, text);\n return { spore, embedding: result.embedding };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n const { spore, embedding } = result.value;\n sporesWithEmbeddings.push({\n id: spore.id,\n path: spore.path,\n title: spore.title,\n content: spore.content,\n created: spore.created,\n frontmatter: spore.frontmatter,\n embedding,\n });\n } else {\n embedFailures++;\n }\n }\n }\n\n if (embedFailures > 0) {\n log('warn', `${embedFailures} spore(s) could not be embedded and were skipped`);\n }\n\n // 3. Group by observation_type\n const byType = new Map<string, SporeWithEmbedding[]>();\n for (const spore of sporesWithEmbeddings) {\n const obsType = (spore.frontmatter['observation_type'] as string | undefined) ?? 'unknown';\n if (!byType.has(obsType)) byType.set(obsType, []);\n byType.get(obsType)!.push(spore);\n }\n\n // 4. Cluster within each type group\n const template = loadPrompt('supersession');\n let totalClusters = 0;\n let totalSuperseded = 0;\n\n for (const [obsType, typeSpores] of byType) {\n const clusters = clusterSpores(typeSpores);\n const multiSpore = clusters.filter((c) => c.spores.length >= 2);\n if (multiSpore.length === 0) continue;\n\n log('info', `Type: ${obsType} — ${typeSpores.length} spores, ${multiSpore.length} cluster(s) to evaluate`);\n totalClusters += multiSpore.length;\n\n for (const cluster of multiSpore) {\n const sorted = [...cluster.spores].sort((a, b) => a.created.localeCompare(b.created));\n const newest = sorted[sorted.length - 1];\n const candidates = sorted.slice(0, sorted.length - 1);\n\n // Build supersession prompt\n const newSporeText = formatNoteForPrompt(newest);\n const candidatesText = formatNotesForPrompt(candidates);\n\n const prompt = template\n .replace('{{new_spore}}', newSporeText)\n .replace('{{candidates}}', candidatesText);\n\n // Ask LLM which candidates are outdated\n let responseText: string;\n try {\n const response = await llmProvider.summarize(prompt, {\n maxTokens: SUPERSESSION_MAX_TOKENS,\n reasoning: LLM_REASONING_MODE,\n });\n responseText = stripReasoningTokens(response.text);\n } catch (err) {\n log('warn', `LLM call failed for cluster in ${obsType}: ${String(err)}`);\n continue;\n }\n\n // Parse response\n let rawIds: unknown;\n try {\n rawIds = JSON.parse(responseText);\n } catch {\n log('warn', `Could not parse LLM response for cluster in ${obsType}`);\n continue;\n }\n\n const parsed = supersededIdsSchema.safeParse(rawIds);\n if (!parsed.success) {\n log('warn', `LLM response schema invalid for cluster in ${obsType}`);\n continue;\n }\n\n // Validate IDs against actual candidates\n const candidateMap = new Map(candidates.map((c) => [c.id, c]));\n const validIds = parsed.data.filter((id) => candidateMap.has(id));\n if (validIds.length === 0) continue;\n\n for (const id of validIds) {\n const candidate = candidateMap.get(id)!;\n\n if (dryRun) {\n log('info', `[dry-run] Would supersede: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);\n totalSuperseded++;\n continue;\n }\n\n const wrote = supersedeSpore(id, newest.id, candidate.path, { index, vectorIndex, vaultDir });\n if (!wrote) {\n log('warn', `File not found for ${id}, skipping write`);\n continue;\n }\n\n log('info', `Superseded: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);\n totalSuperseded++;\n }\n }\n }\n\n return {\n scanned: activeSpores.length,\n clustersEvaluated: totalClusters,\n superseded: totalSuperseded,\n };\n}\n"],"mappings":"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","names":["fs","path","path","fs","YAML","opts"]}
|