@tekmidian/pai 0.37.0 → 0.39.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/dist/{auto-route-DM7GhJ8y.mjs → auto-route-BlWOWT4z.mjs} +2 -2
- package/dist/{auto-route-DM7GhJ8y.mjs.map → auto-route-BlWOWT4z.mjs.map} +1 -1
- package/dist/{providers-sXcK5bDZ.mjs → chain-CTtHligI.mjs} +1040 -782
- package/dist/chain-CTtHligI.mjs.map +1 -0
- package/dist/cli/index.mjs +4 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.mjs +4 -3
- package/dist/{clusters-Do4tEGyc.mjs → clusters-wlK0w41E.mjs} +1 -1
- package/dist/{clusters-Do4tEGyc.mjs.map → clusters-wlK0w41E.mjs.map} +1 -1
- package/dist/daemon/index.mjs +7 -7
- package/dist/{daemon-CGg1VCbA.mjs → daemon-CTkN_Vn8.mjs} +25 -25
- package/dist/{daemon-CGg1VCbA.mjs.map → daemon-CTkN_Vn8.mjs.map} +1 -1
- package/dist/{daemon-DsGGiIJM.mjs → daemon-CmHHmKpg.mjs} +7 -7
- package/dist/daemon-mcp/index.mjs +207 -34
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{detector-CMap-9vw.mjs → detector-Bwk_4Pk4.mjs} +1 -1
- package/dist/{detector-CMap-9vw.mjs.map → detector-Bwk_4Pk4.mjs.map} +1 -1
- package/dist/detector-DO730Zq0.mjs +5 -0
- package/dist/{factory-DD2T33C9.mjs → factory-A9x-T2Kg.mjs} +5 -5
- package/dist/{factory-DD2T33C9.mjs.map → factory-A9x-T2Kg.mjs.map} +1 -1
- package/dist/factory-BD-np0Vd.mjs +3 -0
- package/dist/hooks/route-agents-to-worker.mjs +112 -20
- package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
- package/dist/hooks/worker-proxy.mjs +110 -18
- package/dist/hooks/worker-proxy.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +176 -23
- package/dist/hooks/worker-status-line.mjs.map +4 -4
- package/dist/{indexer-backend-Cox9BCo-.mjs → indexer-backend-TG64CCQC.mjs} +1 -1
- package/dist/{indexer-backend-Cox9BCo-.mjs.map → indexer-backend-TG64CCQC.mjs.map} +1 -1
- package/dist/{latent-ideas-BC1oINZ-.mjs → latent-ideas-B7wq75Pt.mjs} +2 -2
- package/dist/{latent-ideas-BC1oINZ-.mjs.map → latent-ideas-B7wq75Pt.mjs.map} +1 -1
- package/dist/{link-boost-QFLrJwD6.mjs → link-boost-HkG7JWZR.mjs} +1 -1
- package/dist/{link-boost-QFLrJwD6.mjs.map → link-boost-HkG7JWZR.mjs.map} +1 -1
- package/dist/{neighborhood-D9MJ1c8f.mjs → neighborhood-lThN-MaQ.mjs} +1 -1
- package/dist/{neighborhood-D9MJ1c8f.mjs.map → neighborhood-lThN-MaQ.mjs.map} +1 -1
- package/dist/{note-context-d1wT_-GA.mjs → note-context-b6k0mAKi.mjs} +1 -1
- package/dist/{note-context-d1wT_-GA.mjs.map → note-context-b6k0mAKi.mjs.map} +1 -1
- package/dist/planner-BDI7bE9B.mjs +243 -0
- package/dist/planner-BDI7bE9B.mjs.map +1 -0
- package/dist/{postgres--BjPtLa0.mjs → postgres-mW1n7Vi1.mjs} +1 -1
- package/dist/{postgres--BjPtLa0.mjs.map → postgres-mW1n7Vi1.mjs.map} +1 -1
- package/dist/{program-CEIHn_Ma.mjs → program-DXBwJV7h.mjs} +274 -65
- package/dist/program-DXBwJV7h.mjs.map +1 -0
- package/dist/providers-FYkZjn_C.mjs +1405 -0
- package/dist/providers-FYkZjn_C.mjs.map +1 -0
- package/dist/{query-feedback-BUJxgw5B.mjs → query-feedback-BV4CcxqS.mjs} +1 -1
- package/dist/{query-feedback-BUJxgw5B.mjs.map → query-feedback-BV4CcxqS.mjs.map} +1 -1
- package/dist/query-feedback-DSVyHtrG.mjs +3 -0
- package/dist/router-Bk77E7hj.mjs +3 -0
- package/dist/{router-DK_sLsUL.mjs → router-DcHKnEPa.mjs} +1 -1
- package/dist/{router-DK_sLsUL.mjs.map → router-DcHKnEPa.mjs.map} +1 -1
- package/dist/skills/Worker/SKILL.md +34 -12
- package/dist/{sources-Bi7--33T.mjs → sources-kLnQsNrW.mjs} +1 -1
- package/dist/{sources-Bi7--33T.mjs.map → sources-kLnQsNrW.mjs.map} +1 -1
- package/dist/{sqlite-DtaL1glm.mjs → sqlite-BenGr3UP.mjs} +1 -1
- package/dist/{sqlite-DtaL1glm.mjs.map → sqlite-BenGr3UP.mjs.map} +1 -1
- package/dist/{state-BY2L6-vX.mjs → state-CAeyOdfq.mjs} +1 -1
- package/dist/{state-BY2L6-vX.mjs.map → state-CAeyOdfq.mjs.map} +1 -1
- package/dist/{state-8Hm9E4tW.mjs → state-Ca9F_sZc.mjs} +1 -1
- package/dist/{themes-BN0a2duq.mjs → themes-BI4GMRP9.mjs} +1 -1
- package/dist/{themes-BN0a2duq.mjs.map → themes-BI4GMRP9.mjs.map} +1 -1
- package/dist/{tools-CGPqpU3A.mjs → tools-Bp7hj6OW.mjs} +1 -1
- package/dist/{tools-y2bJpKom.mjs → tools-DGcB3o_S.mjs} +13 -13
- package/dist/{tools-y2bJpKom.mjs.map → tools-DGcB3o_S.mjs.map} +1 -1
- package/dist/{trace-h23JCcFD.mjs → trace-bobARFEX.mjs} +1 -1
- package/dist/{trace-h23JCcFD.mjs.map → trace-bobARFEX.mjs.map} +1 -1
- package/dist/{vault-indexer-DgsPjMgs.mjs → vault-indexer-C3OfXTNF.mjs} +1 -1
- package/dist/{vault-indexer-DgsPjMgs.mjs.map → vault-indexer-C3OfXTNF.mjs.map} +1 -1
- package/dist/{work-queue-worker-gsKd2LJa.mjs → work-queue-worker-DW8lz-Oo.mjs} +3 -3
- package/dist/{work-queue-worker-Dva_v_pI.mjs → work-queue-worker-R7UGIag_.mjs} +3 -3
- package/dist/{work-queue-worker-Dva_v_pI.mjs.map → work-queue-worker-R7UGIag_.mjs.map} +1 -1
- package/dist/{zettelkasten-vo7psPdT.mjs → zettelkasten-m5QPtb-L.mjs} +3 -3
- package/dist/{zettelkasten-vo7psPdT.mjs.map → zettelkasten-m5QPtb-L.mjs.map} +1 -1
- package/docs/commands/README.md +12 -7
- package/docs/commands/worker.md +110 -17
- package/docs/worker.md +244 -25
- package/package.json +1 -1
- package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +2 -2
- package/dist/detector-DtLExmHN.mjs +0 -5
- package/dist/factory-BXzqRYVZ.mjs +0 -3
- package/dist/program-CEIHn_Ma.mjs.map +0 -1
- package/dist/providers-sXcK5bDZ.mjs.map +0 -1
- package/dist/query-feedback-DhyLOe5S.mjs +0 -3
- package/dist/router-1zi8jiNF.mjs +0 -3
- /package/dist/{main-resolver-IhZo4pI0.mjs → main-resolver-D6IImXvF.mjs} +0 -0
package/dist/cli/index.mjs
CHANGED
|
@@ -7,14 +7,15 @@ import "../helpers-IjZkXBhj.mjs";
|
|
|
7
7
|
import "../sync-BWbe8JTg.mjs";
|
|
8
8
|
import "../embeddings-DOLZnT1X.mjs";
|
|
9
9
|
import "../search-Rpk1cSBC.mjs";
|
|
10
|
-
import { n as drainStdio, t as buildProgram } from "../program-
|
|
10
|
+
import { n as drainStdio, t as buildProgram } from "../program-DXBwJV7h.mjs";
|
|
11
11
|
import "../context-handover-cache-BpUojjsi.mjs";
|
|
12
12
|
import "../indexer-D7MvSQPY.mjs";
|
|
13
13
|
import "../ipc-client-BmypMNYk.mjs";
|
|
14
14
|
import "../config-B64vFg14.mjs";
|
|
15
|
-
import "../factory-
|
|
16
|
-
import "../
|
|
15
|
+
import "../factory-A9x-T2Kg.mjs";
|
|
16
|
+
import "../chain-CTtHligI.mjs";
|
|
17
17
|
import "../main-resolver-DlaLOFBA.mjs";
|
|
18
|
+
import "../providers-FYkZjn_C.mjs";
|
|
18
19
|
import { CommanderError } from "commander";
|
|
19
20
|
|
|
20
21
|
//#region src/cli/index.ts
|
package/dist/cli/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point.\n *\n * Thin entry: builds the Commander program (see ./program.ts) and parses argv.\n * All command construction lives in buildProgram() so the docs generator can\n * introspect the same tree that powers `--help`.\n *\n * Daily surface:\n * pai → deduped session listing (one row per name)\n * pai <name> → universal: switch live tab / resume / fresh\n * pai <uuid-prefix> → direct session resume via filesystem scan\n * pai pause [all] → save state (or mass-pause every live session)\n * pai end → finalize session\n * pai help [area] → rich man page for a command area\n */\n\nimport { CommanderError } from \"commander\";\nimport { buildProgram } from \"./program.js\";\nimport { err } from \"./utils.js\";\nimport { drainStdio } from \"./lib/exit.js\";\n\n// `parse()` does not await async command actions — Commander fires the\n// action and returns immediately, leaving its promise unawaited. When that\n// promise later rejects (e.g. a Todoist call inside `task done`/`task add`\n// throws), it becomes an unhandled rejection racing the process's natural\n// exit: depending on timing, Node either prints the trace and exits 1, or —\n// when nothing else is keeping the event loop alive — wins the race and\n// exits 0 first, silently swallowing both the error and any output the\n// action had queued. `parseAsync` makes `.parse` wait for the action, which\n// turns that race into a deterministic await: any rejection is caught here\n// and reported instead of disappearing.\nawait buildProgram()\n .parseAsync(process.argv)\n .catch((e) => {\n if (e instanceof CommanderError) {\n // program.ts's exitOverride() throws instead of calling process.exit()\n // so this catch is reached for --help, --version, and usage errors\n // too. Commander already wrote the help text or the formatted error\n // message itself (via outputHelp / configureOutput's writeErr) — just\n // adopt its exit code and let the process end naturally. A natural\n // exit flushes stdout/stderr even when either is a pipe; calling\n // process.exit() here would risk truncating output that's still\n // in-flight, same as the bug this replaces.\n process.exitCode = e.exitCode;\n return;\n }\n console.error(err(e instanceof Error ? e.message : String(e)));\n process.exitCode = 1;\n });\n\n// Belt-and-suspenders: explicitly wait for stdout/stderr to drain before this\n// module finishes and Node is free to decide the process is done. Avoiding\n// process.exit() everywhere (see lib/exit.ts) means every command path\n// relies on the event loop staying \"non-empty\" until a pending pipe write\n// completes — true by default, but not something to leave unverified when a\n// dependency's socket/timer/handle could in principle unref itself and let\n// Node consider the loop empty a tick early. This makes the wait explicit\n// and covers every command, not just the error/help paths above.\nawait drainStdio();\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * PAI Knowledge OS — CLI entry point.\n *\n * Thin entry: builds the Commander program (see ./program.ts) and parses argv.\n * All command construction lives in buildProgram() so the docs generator can\n * introspect the same tree that powers `--help`.\n *\n * Daily surface:\n * pai → deduped session listing (one row per name)\n * pai <name> → universal: switch live tab / resume / fresh\n * pai <uuid-prefix> → direct session resume via filesystem scan\n * pai pause [all] → save state (or mass-pause every live session)\n * pai end → finalize session\n * pai help [area] → rich man page for a command area\n */\n\nimport { CommanderError } from \"commander\";\nimport { buildProgram } from \"./program.js\";\nimport { err } from \"./utils.js\";\nimport { drainStdio } from \"./lib/exit.js\";\n\n// `parse()` does not await async command actions — Commander fires the\n// action and returns immediately, leaving its promise unawaited. When that\n// promise later rejects (e.g. a Todoist call inside `task done`/`task add`\n// throws), it becomes an unhandled rejection racing the process's natural\n// exit: depending on timing, Node either prints the trace and exits 1, or —\n// when nothing else is keeping the event loop alive — wins the race and\n// exits 0 first, silently swallowing both the error and any output the\n// action had queued. `parseAsync` makes `.parse` wait for the action, which\n// turns that race into a deterministic await: any rejection is caught here\n// and reported instead of disappearing.\nawait buildProgram()\n .parseAsync(process.argv)\n .catch((e) => {\n if (e instanceof CommanderError) {\n // program.ts's exitOverride() throws instead of calling process.exit()\n // so this catch is reached for --help, --version, and usage errors\n // too. Commander already wrote the help text or the formatted error\n // message itself (via outputHelp / configureOutput's writeErr) — just\n // adopt its exit code and let the process end naturally. A natural\n // exit flushes stdout/stderr even when either is a pipe; calling\n // process.exit() here would risk truncating output that's still\n // in-flight, same as the bug this replaces.\n process.exitCode = e.exitCode;\n return;\n }\n console.error(err(e instanceof Error ? e.message : String(e)));\n process.exitCode = 1;\n });\n\n// Belt-and-suspenders: explicitly wait for stdout/stderr to drain before this\n// module finishes and Node is free to decide the process is done. Avoiding\n// process.exit() everywhere (see lib/exit.ts) means every command path\n// relies on the event loop staying \"non-empty\" until a pending pipe write\n// completes — true by default, but not something to leave unverified when a\n// dependency's socket/timer/handle could in principle unref itself and let\n// Node consider the loop empty a tick early. This makes the wait explicit\n// and covers every command, not just the error/help paths above.\nawait drainStdio();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,cAAc,CACjB,WAAW,QAAQ,KAAK,CACxB,OAAO,MAAM;AACZ,KAAI,aAAa,gBAAgB;AAS/B,UAAQ,WAAW,EAAE;AACrB;;AAEF,SAAQ,MAAM,IAAI,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC,CAAC;AAC9D,SAAQ,WAAW;EACnB;AAUJ,MAAM,YAAY"}
|
package/dist/cli/program.mjs
CHANGED
|
@@ -6,13 +6,14 @@ import "../helpers-IjZkXBhj.mjs";
|
|
|
6
6
|
import "../sync-BWbe8JTg.mjs";
|
|
7
7
|
import "../embeddings-DOLZnT1X.mjs";
|
|
8
8
|
import "../search-Rpk1cSBC.mjs";
|
|
9
|
-
import { t as buildProgram } from "../program-
|
|
9
|
+
import { t as buildProgram } from "../program-DXBwJV7h.mjs";
|
|
10
10
|
import "../context-handover-cache-BpUojjsi.mjs";
|
|
11
11
|
import "../indexer-D7MvSQPY.mjs";
|
|
12
12
|
import "../ipc-client-BmypMNYk.mjs";
|
|
13
13
|
import "../config-B64vFg14.mjs";
|
|
14
|
-
import "../factory-
|
|
15
|
-
import "../
|
|
14
|
+
import "../factory-A9x-T2Kg.mjs";
|
|
15
|
+
import "../chain-CTtHligI.mjs";
|
|
16
16
|
import "../main-resolver-DlaLOFBA.mjs";
|
|
17
|
+
import "../providers-FYkZjn_C.mjs";
|
|
17
18
|
|
|
18
19
|
export { buildProgram };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clusters-Do4tEGyc.mjs","names":[],"sources":["../src/graph/clusters.ts"],"sourcesContent":["/**\n * clusters.ts — graph_clusters endpoint handler\n *\n * Reuses the zettelThemes() agglomerative clustering algorithm and enriches\n * each cluster with observation-type statistics, avg_recency from member\n * timestamps, and helper flags for the Obsidian knowledge plugin.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport type { Pool } from \"pg\";\nimport { STOP_WORDS } from \"../utils/stop-words.js\";\n\n// ---------------------------------------------------------------------------\n// Public param / result types\n// ---------------------------------------------------------------------------\n\nexport interface GraphClustersParams {\n project_id?: number;\n min_size?: number;\n max_clusters?: number;\n lookback_days?: number;\n similarity_threshold?: number;\n}\n\nexport interface ClusterNode {\n id: number;\n label: string;\n size: number;\n folder_diversity: number;\n avg_recency: number;\n linked_ratio: number;\n dominant_observation_type: string;\n observation_type_counts: Record<string, number>;\n suggest_index_note: boolean;\n has_idea_note: boolean;\n notes: Array<{ vault_path: string; title: string; indexed_at: number }>;\n}\n\nexport interface GraphClustersResult {\n clusters: ClusterNode[];\n total_notes_analyzed: number;\n time_window: { from: number; to: number };\n}\n\n// ---------------------------------------------------------------------------\n// Observation type enrichment\n// ---------------------------------------------------------------------------\n\n/**\n * Query pai_observations (Postgres) for observation types associated with\n * the given file paths. Returns a map from vault_path → type counts.\n *\n * Falls back to an empty map when the pool is not available or the query fails.\n */\nasync function fetchObservationTypes(\n pool: Pool,\n filePaths: string[],\n projectId?: number\n): Promise<Map<string, Record<string, number>>> {\n if (filePaths.length === 0) return new Map();\n\n try {\n const params: (string | number)[] = [...filePaths];\n let projectFilter = \"\";\n if (projectId !== undefined) {\n params.push(projectId);\n projectFilter = `AND project_id = $${params.length}`;\n }\n\n const result = await pool.query<{ path: string; type: string; cnt: string }>(\n `SELECT unnested_path AS path, type, COUNT(*) AS cnt\n FROM pai_observations,\n LATERAL unnest(files_modified || files_read) AS unnested_path\n WHERE unnested_path = ANY($1::text[])\n ${projectFilter}\n GROUP BY unnested_path, type`,\n [filePaths, ...params.slice(filePaths.length)]\n );\n\n const byPath = new Map<string, Record<string, number>>();\n for (const row of result.rows) {\n const existing = byPath.get(row.path) ?? {};\n existing[row.type] = (existing[row.type] ?? 0) + parseInt(row.cnt, 10);\n byPath.set(row.path, existing);\n }\n return byPath;\n } catch {\n return new Map();\n }\n}\n\n/**\n * Aggregate per-path observation type counts into cluster-level counts,\n * then pick the dominant type.\n */\nfunction aggregateObservationTypes(\n paths: string[],\n byPath: Map<string, Record<string, number>>\n): { dominant: string; counts: Record<string, number> } {\n const counts: Record<string, number> = {};\n for (const path of paths) {\n const pathCounts = byPath.get(path);\n if (!pathCounts) continue;\n for (const [type, n] of Object.entries(pathCounts)) {\n counts[type] = (counts[type] ?? 0) + n;\n }\n }\n\n let dominant = \"unknown\";\n let maxCount = 0;\n for (const [type, n] of Object.entries(counts)) {\n if (n > maxCount) {\n maxCount = n;\n dominant = type;\n }\n }\n\n return { dominant, counts };\n}\n\n// ---------------------------------------------------------------------------\n// Link-based fallback clustering (wikilink connected components)\n// ---------------------------------------------------------------------------\n\nconst SKIP_PREFIXES = [\n \"Attachments/\", \"🗓️ Daily Notes/\", \"Copilot/copilot-conversations/\",\n \"Z - Zettelkasten/Tweets/\",\n];\n\n/**\n * Cluster vault notes by wikilink connectivity when embeddings aren't available.\n * Uses BFS to find connected components in the link graph, then picks the\n * largest components as clusters. Labels are derived from the most common\n * title words in each component.\n */\nasync function clusterByLinks(\n backend: StorageBackend,\n lookbackDays: number,\n minSize: number,\n maxClusters: number,\n): Promise<{ themes: Array<{ id: number; label: string; notes: Array<{ path: string; title: string | null }>; size: number; folderDiversity: number; avgRecency: number; linkedRatio: number; suggestIndexNote: boolean }>; totalNotesAnalyzed: number; timeWindow: { from: number; to: number } }> {\n const now = Date.now();\n const from = now - lookbackDays * 86400000;\n\n // Get recent notes\n const recentFiles = await backend.getRecentVaultFiles(from);\n const recentNotes = recentFiles.filter(f => f.vaultPath.endsWith(\".md\"));\n\n const noteMap = new Map<string, { title: string | null; indexed_at: number }>();\n for (const n of recentNotes) {\n noteMap.set(n.vaultPath, { title: n.title, indexed_at: n.indexedAt });\n }\n\n // Build adjacency list from vault_links (only for recent notes)\n const adj = new Map<string, Set<string>>();\n for (const path of noteMap.keys()) {\n if (!adj.has(path)) adj.set(path, new Set());\n }\n\n const linkGraph = await backend.getVaultLinkGraph();\n\n for (const { source_path, target_path } of linkGraph) {\n if (noteMap.has(source_path) && noteMap.has(target_path)) {\n adj.get(source_path)!.add(target_path);\n adj.get(target_path)!.add(source_path);\n }\n }\n\n // Remove hub nodes before BFS\n const degrees = [...adj.entries()].map(([p, s]) => ({ path: p, degree: s.size }));\n degrees.sort((a, b) => b.degree - a.degree);\n const hubThreshold = Math.max(10, degrees[Math.floor(degrees.length * 0.05)]?.degree ?? 10);\n const hubNodes = new Set<string>();\n for (const { path, degree } of degrees) {\n if (degree >= hubThreshold) hubNodes.add(path);\n else break;\n }\n\n for (const hub of hubNodes) {\n adj.delete(hub);\n }\n for (const [, neighbors] of adj) {\n for (const hub of hubNodes) {\n neighbors.delete(hub);\n }\n }\n\n // BFS connected components\n const visited = new Set<string>();\n const components: string[][] = [];\n\n for (const path of noteMap.keys()) {\n if (visited.has(path) || hubNodes.has(path)) continue;\n if (SKIP_PREFIXES.some(p => path.startsWith(p))) { visited.add(path); continue; }\n const component: string[] = [];\n const queue = [path];\n visited.add(path);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n component.push(current);\n const neighbors = adj.get(current);\n if (!neighbors) continue;\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor) && !SKIP_PREFIXES.some(p => neighbor.startsWith(p))) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n\n if (component.length >= minSize) {\n components.push(component);\n }\n }\n\n components.sort((a, b) => b.length - a.length);\n const topComponents = components.slice(0, maxClusters);\n\n // STOP_WORDS imported from utils/stop-words.ts (module-level import)\n\n function generateLinkLabel(paths: string[]): string {\n const wordCounts = new Map<string, number>();\n for (const p of paths) {\n const title = noteMap.get(p)?.title;\n if (!title) continue;\n const words = title.toLowerCase().replace(/[^a-z0-9äöüàéèêëçñß\\s]/g, \" \").split(/\\s+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w));\n for (const word of words) {\n wordCounts.set(word, (wordCounts.get(word) ?? 0) + 1);\n }\n }\n const sorted = [...wordCounts.entries()].sort((a, b) => b[1] - a[1]);\n return sorted.slice(0, 3).map(([w]) => w).join(\" / \") || \"Linked Notes\";\n }\n\n const themes = topComponents.map((component, idx) => {\n const notes = component.map(p => ({\n path: p,\n title: noteMap.get(p)?.title ?? null,\n }));\n const avgRecency = component.reduce((sum, p) => sum + (noteMap.get(p)?.indexed_at ?? 0), 0) / component.length;\n const uniqueFolders = new Set(component.map(p => p.split(\"/\")[0]));\n\n return {\n id: idx,\n label: generateLinkLabel(component),\n notes,\n size: component.length,\n folderDiversity: uniqueFolders.size / component.length,\n avgRecency,\n linkedRatio: 1.0,\n suggestIndexNote: component.length >= 10,\n };\n });\n\n return {\n themes,\n totalNotesAnalyzed: recentNotes.length,\n timeWindow: { from, to: now },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Main handler\n// ---------------------------------------------------------------------------\n\nexport async function handleGraphClusters(\n pool: Pool | null,\n backend: StorageBackend,\n params: GraphClustersParams\n): Promise<GraphClustersResult> {\n const minSize = params.min_size ?? 3;\n const maxClusters = params.max_clusters ?? 20;\n const lookbackDays = params.lookback_days ?? 90;\n\n const vaultProjectId = params.project_id ?? 0;\n\n if (!vaultProjectId) {\n throw new Error(\n \"graph_clusters: project_id is required (pass the vault project's numeric ID)\"\n );\n }\n\n const themeResult = await clusterByLinks(backend, lookbackDays, minSize, maxClusters);\n\n const allPaths = themeResult.themes.flatMap((t) => t.notes.map((n) => n.path));\n\n const observationsByPath =\n pool !== null\n ? await fetchObservationTypes(pool, allPaths, params.project_id)\n : new Map<string, Record<string, number>>();\n\n // Fetch indexed_at timestamps for all notes in bulk\n const fileRows = await backend.getVaultFilesByPaths(allPaths);\n const indexedAtMap = new Map<string, number>(fileRows.map(f => [f.vaultPath, f.indexedAt]));\n\n const clusters: ClusterNode[] = themeResult.themes.map((theme) => {\n const notePaths = theme.notes.map((n) => n.path);\n\n const notesWithTimestamps = theme.notes.map((n) => ({\n vault_path: n.path,\n title: n.title ?? n.path.split(\"/\").pop() ?? n.path,\n indexed_at: indexedAtMap.get(n.path) ?? 0,\n }));\n\n const avgRecency = theme.avgRecency;\n\n const { dominant, counts } = aggregateObservationTypes(\n notePaths,\n observationsByPath\n );\n\n return {\n id: theme.id,\n label: theme.label,\n size: theme.size,\n folder_diversity: theme.folderDiversity,\n avg_recency: avgRecency,\n linked_ratio: theme.linkedRatio,\n dominant_observation_type: dominant,\n observation_type_counts: counts,\n suggest_index_note: theme.suggestIndexNote,\n has_idea_note: false,\n notes: notesWithTimestamps,\n };\n });\n\n clusters.sort((a, b) => b.size - a.size);\n\n return {\n clusters: clusters.slice(0, maxClusters),\n total_notes_analyzed: themeResult.totalNotesAnalyzed,\n time_window: themeResult.timeWindow,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clusters-wlK0w41E.mjs","names":[],"sources":["../src/graph/clusters.ts"],"sourcesContent":["/**\n * clusters.ts — graph_clusters endpoint handler\n *\n * Reuses the zettelThemes() agglomerative clustering algorithm and enriches\n * each cluster with observation-type statistics, avg_recency from member\n * timestamps, and helper flags for the Obsidian knowledge plugin.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport type { Pool } from \"pg\";\nimport { STOP_WORDS } from \"../utils/stop-words.js\";\n\n// ---------------------------------------------------------------------------\n// Public param / result types\n// ---------------------------------------------------------------------------\n\nexport interface GraphClustersParams {\n project_id?: number;\n min_size?: number;\n max_clusters?: number;\n lookback_days?: number;\n similarity_threshold?: number;\n}\n\nexport interface ClusterNode {\n id: number;\n label: string;\n size: number;\n folder_diversity: number;\n avg_recency: number;\n linked_ratio: number;\n dominant_observation_type: string;\n observation_type_counts: Record<string, number>;\n suggest_index_note: boolean;\n has_idea_note: boolean;\n notes: Array<{ vault_path: string; title: string; indexed_at: number }>;\n}\n\nexport interface GraphClustersResult {\n clusters: ClusterNode[];\n total_notes_analyzed: number;\n time_window: { from: number; to: number };\n}\n\n// ---------------------------------------------------------------------------\n// Observation type enrichment\n// ---------------------------------------------------------------------------\n\n/**\n * Query pai_observations (Postgres) for observation types associated with\n * the given file paths. Returns a map from vault_path → type counts.\n *\n * Falls back to an empty map when the pool is not available or the query fails.\n */\nasync function fetchObservationTypes(\n pool: Pool,\n filePaths: string[],\n projectId?: number\n): Promise<Map<string, Record<string, number>>> {\n if (filePaths.length === 0) return new Map();\n\n try {\n const params: (string | number)[] = [...filePaths];\n let projectFilter = \"\";\n if (projectId !== undefined) {\n params.push(projectId);\n projectFilter = `AND project_id = $${params.length}`;\n }\n\n const result = await pool.query<{ path: string; type: string; cnt: string }>(\n `SELECT unnested_path AS path, type, COUNT(*) AS cnt\n FROM pai_observations,\n LATERAL unnest(files_modified || files_read) AS unnested_path\n WHERE unnested_path = ANY($1::text[])\n ${projectFilter}\n GROUP BY unnested_path, type`,\n [filePaths, ...params.slice(filePaths.length)]\n );\n\n const byPath = new Map<string, Record<string, number>>();\n for (const row of result.rows) {\n const existing = byPath.get(row.path) ?? {};\n existing[row.type] = (existing[row.type] ?? 0) + parseInt(row.cnt, 10);\n byPath.set(row.path, existing);\n }\n return byPath;\n } catch {\n return new Map();\n }\n}\n\n/**\n * Aggregate per-path observation type counts into cluster-level counts,\n * then pick the dominant type.\n */\nfunction aggregateObservationTypes(\n paths: string[],\n byPath: Map<string, Record<string, number>>\n): { dominant: string; counts: Record<string, number> } {\n const counts: Record<string, number> = {};\n for (const path of paths) {\n const pathCounts = byPath.get(path);\n if (!pathCounts) continue;\n for (const [type, n] of Object.entries(pathCounts)) {\n counts[type] = (counts[type] ?? 0) + n;\n }\n }\n\n let dominant = \"unknown\";\n let maxCount = 0;\n for (const [type, n] of Object.entries(counts)) {\n if (n > maxCount) {\n maxCount = n;\n dominant = type;\n }\n }\n\n return { dominant, counts };\n}\n\n// ---------------------------------------------------------------------------\n// Link-based fallback clustering (wikilink connected components)\n// ---------------------------------------------------------------------------\n\nconst SKIP_PREFIXES = [\n \"Attachments/\", \"🗓️ Daily Notes/\", \"Copilot/copilot-conversations/\",\n \"Z - Zettelkasten/Tweets/\",\n];\n\n/**\n * Cluster vault notes by wikilink connectivity when embeddings aren't available.\n * Uses BFS to find connected components in the link graph, then picks the\n * largest components as clusters. Labels are derived from the most common\n * title words in each component.\n */\nasync function clusterByLinks(\n backend: StorageBackend,\n lookbackDays: number,\n minSize: number,\n maxClusters: number,\n): Promise<{ themes: Array<{ id: number; label: string; notes: Array<{ path: string; title: string | null }>; size: number; folderDiversity: number; avgRecency: number; linkedRatio: number; suggestIndexNote: boolean }>; totalNotesAnalyzed: number; timeWindow: { from: number; to: number } }> {\n const now = Date.now();\n const from = now - lookbackDays * 86400000;\n\n // Get recent notes\n const recentFiles = await backend.getRecentVaultFiles(from);\n const recentNotes = recentFiles.filter(f => f.vaultPath.endsWith(\".md\"));\n\n const noteMap = new Map<string, { title: string | null; indexed_at: number }>();\n for (const n of recentNotes) {\n noteMap.set(n.vaultPath, { title: n.title, indexed_at: n.indexedAt });\n }\n\n // Build adjacency list from vault_links (only for recent notes)\n const adj = new Map<string, Set<string>>();\n for (const path of noteMap.keys()) {\n if (!adj.has(path)) adj.set(path, new Set());\n }\n\n const linkGraph = await backend.getVaultLinkGraph();\n\n for (const { source_path, target_path } of linkGraph) {\n if (noteMap.has(source_path) && noteMap.has(target_path)) {\n adj.get(source_path)!.add(target_path);\n adj.get(target_path)!.add(source_path);\n }\n }\n\n // Remove hub nodes before BFS\n const degrees = [...adj.entries()].map(([p, s]) => ({ path: p, degree: s.size }));\n degrees.sort((a, b) => b.degree - a.degree);\n const hubThreshold = Math.max(10, degrees[Math.floor(degrees.length * 0.05)]?.degree ?? 10);\n const hubNodes = new Set<string>();\n for (const { path, degree } of degrees) {\n if (degree >= hubThreshold) hubNodes.add(path);\n else break;\n }\n\n for (const hub of hubNodes) {\n adj.delete(hub);\n }\n for (const [, neighbors] of adj) {\n for (const hub of hubNodes) {\n neighbors.delete(hub);\n }\n }\n\n // BFS connected components\n const visited = new Set<string>();\n const components: string[][] = [];\n\n for (const path of noteMap.keys()) {\n if (visited.has(path) || hubNodes.has(path)) continue;\n if (SKIP_PREFIXES.some(p => path.startsWith(p))) { visited.add(path); continue; }\n const component: string[] = [];\n const queue = [path];\n visited.add(path);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n component.push(current);\n const neighbors = adj.get(current);\n if (!neighbors) continue;\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor) && !SKIP_PREFIXES.some(p => neighbor.startsWith(p))) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n\n if (component.length >= minSize) {\n components.push(component);\n }\n }\n\n components.sort((a, b) => b.length - a.length);\n const topComponents = components.slice(0, maxClusters);\n\n // STOP_WORDS imported from utils/stop-words.ts (module-level import)\n\n function generateLinkLabel(paths: string[]): string {\n const wordCounts = new Map<string, number>();\n for (const p of paths) {\n const title = noteMap.get(p)?.title;\n if (!title) continue;\n const words = title.toLowerCase().replace(/[^a-z0-9äöüàéèêëçñß\\s]/g, \" \").split(/\\s+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w));\n for (const word of words) {\n wordCounts.set(word, (wordCounts.get(word) ?? 0) + 1);\n }\n }\n const sorted = [...wordCounts.entries()].sort((a, b) => b[1] - a[1]);\n return sorted.slice(0, 3).map(([w]) => w).join(\" / \") || \"Linked Notes\";\n }\n\n const themes = topComponents.map((component, idx) => {\n const notes = component.map(p => ({\n path: p,\n title: noteMap.get(p)?.title ?? null,\n }));\n const avgRecency = component.reduce((sum, p) => sum + (noteMap.get(p)?.indexed_at ?? 0), 0) / component.length;\n const uniqueFolders = new Set(component.map(p => p.split(\"/\")[0]));\n\n return {\n id: idx,\n label: generateLinkLabel(component),\n notes,\n size: component.length,\n folderDiversity: uniqueFolders.size / component.length,\n avgRecency,\n linkedRatio: 1.0,\n suggestIndexNote: component.length >= 10,\n };\n });\n\n return {\n themes,\n totalNotesAnalyzed: recentNotes.length,\n timeWindow: { from, to: now },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Main handler\n// ---------------------------------------------------------------------------\n\nexport async function handleGraphClusters(\n pool: Pool | null,\n backend: StorageBackend,\n params: GraphClustersParams\n): Promise<GraphClustersResult> {\n const minSize = params.min_size ?? 3;\n const maxClusters = params.max_clusters ?? 20;\n const lookbackDays = params.lookback_days ?? 90;\n\n const vaultProjectId = params.project_id ?? 0;\n\n if (!vaultProjectId) {\n throw new Error(\n \"graph_clusters: project_id is required (pass the vault project's numeric ID)\"\n );\n }\n\n const themeResult = await clusterByLinks(backend, lookbackDays, minSize, maxClusters);\n\n const allPaths = themeResult.themes.flatMap((t) => t.notes.map((n) => n.path));\n\n const observationsByPath =\n pool !== null\n ? await fetchObservationTypes(pool, allPaths, params.project_id)\n : new Map<string, Record<string, number>>();\n\n // Fetch indexed_at timestamps for all notes in bulk\n const fileRows = await backend.getVaultFilesByPaths(allPaths);\n const indexedAtMap = new Map<string, number>(fileRows.map(f => [f.vaultPath, f.indexedAt]));\n\n const clusters: ClusterNode[] = themeResult.themes.map((theme) => {\n const notePaths = theme.notes.map((n) => n.path);\n\n const notesWithTimestamps = theme.notes.map((n) => ({\n vault_path: n.path,\n title: n.title ?? n.path.split(\"/\").pop() ?? n.path,\n indexed_at: indexedAtMap.get(n.path) ?? 0,\n }));\n\n const avgRecency = theme.avgRecency;\n\n const { dominant, counts } = aggregateObservationTypes(\n notePaths,\n observationsByPath\n );\n\n return {\n id: theme.id,\n label: theme.label,\n size: theme.size,\n folder_diversity: theme.folderDiversity,\n avg_recency: avgRecency,\n linked_ratio: theme.linkedRatio,\n dominant_observation_type: dominant,\n observation_type_counts: counts,\n suggest_index_note: theme.suggestIndexNote,\n has_idea_note: false,\n notes: notesWithTimestamps,\n };\n });\n\n clusters.sort((a, b) => b.size - a.size);\n\n return {\n clusters: clusters.slice(0, maxClusters),\n total_notes_analyzed: themeResult.totalNotesAnalyzed,\n time_window: themeResult.timeWindow,\n };\n}\n"],"mappings":"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"}
|
package/dist/daemon/index.mjs
CHANGED
|
@@ -11,13 +11,13 @@ import "../context-handover-cache-BpUojjsi.mjs";
|
|
|
11
11
|
import "../indexer-D7MvSQPY.mjs";
|
|
12
12
|
import { t as PaiClient } from "../ipc-client-BmypMNYk.mjs";
|
|
13
13
|
import { i as ensureConfigDir, o as loadConfig } from "../config-B64vFg14.mjs";
|
|
14
|
-
import "../factory-
|
|
15
|
-
import { t as serve } from "../daemon-
|
|
16
|
-
import "../state-
|
|
17
|
-
import "../router-
|
|
18
|
-
import "../tools-
|
|
19
|
-
import "../detector-
|
|
20
|
-
import "../work-queue-worker-
|
|
14
|
+
import "../factory-A9x-T2Kg.mjs";
|
|
15
|
+
import { t as serve } from "../daemon-CTkN_Vn8.mjs";
|
|
16
|
+
import "../state-CAeyOdfq.mjs";
|
|
17
|
+
import "../router-DcHKnEPa.mjs";
|
|
18
|
+
import "../tools-DGcB3o_S.mjs";
|
|
19
|
+
import "../detector-Bwk_4Pk4.mjs";
|
|
20
|
+
import "../work-queue-worker-R7UGIag_.mjs";
|
|
21
21
|
import { Command } from "commander";
|
|
22
22
|
|
|
23
23
|
//#region src/daemon/index.ts
|
|
@@ -4,12 +4,12 @@ import { d as sha256 } from "./helpers-IjZkXBhj.mjs";
|
|
|
4
4
|
import { n as indexAll } from "./sync-BWbe8JTg.mjs";
|
|
5
5
|
import { t as configureEmbeddingModel } from "./embeddings-DOLZnT1X.mjs";
|
|
6
6
|
import { l as DEFAULT_NOTIFICATION_CONFIG, n as CONFIG_FILE, t as CONFIG_DIR } from "./config-B64vFg14.mjs";
|
|
7
|
-
import { n as getBackendOutage, t as createStorageBackend } from "./factory-
|
|
8
|
-
import { C as setStorageBackend, D as storageBackend, E as startTime, O as vaultIndexInProgress, S as setStartTime, T as shutdownRequested, _ as setLastIndexTime, a as indexSchedulerTimer, b as setRegistryDb, c as lastVaultIndexTime, d as setDaemonConfig, f as setEmbedInProgress, g as setLastEmbedTime, h as setIndexSchedulerTimer, i as indexInProgress, l as notificationConfig, m as setIndexInProgress, n as embedInProgress, o as lastEmbedTime, p as setEmbedSchedulerTimer, r as embedSchedulerTimer, s as lastIndexTime, t as daemonConfig, u as registryDb, v as setLastVaultIndexTime, w as setVaultIndexInProgress, x as setShutdownRequested, y as setNotificationConfig } from "./state-
|
|
9
|
-
import { t as routeNotification } from "./router-
|
|
10
|
-
import { C as toolProjectInfo, D as toolMemoryGet, O as toolMemorySearch, S as toolProjectHealth, T as toolProjectTodo, a as toolMemoryWakeup, b as toolSessionRoute, i as toolMemoryTaxonomy, n as toolMemoryFeedback, t as toolMemoryKgSearch, v as toolRegistrySearch, w as toolProjectList, x as toolProjectDetect, y as toolSessionList } from "./tools-
|
|
11
|
-
import { t as detectTopicShift } from "./detector-
|
|
12
|
-
import { a as enqueue, i as stopWorker, n as notifyNewWork, o as getStats, r as startWorker, s as loadQueue } from "./work-queue-worker-
|
|
7
|
+
import { n as getBackendOutage, t as createStorageBackend } from "./factory-A9x-T2Kg.mjs";
|
|
8
|
+
import { C as setStorageBackend, D as storageBackend, E as startTime, O as vaultIndexInProgress, S as setStartTime, T as shutdownRequested, _ as setLastIndexTime, a as indexSchedulerTimer, b as setRegistryDb, c as lastVaultIndexTime, d as setDaemonConfig, f as setEmbedInProgress, g as setLastEmbedTime, h as setIndexSchedulerTimer, i as indexInProgress, l as notificationConfig, m as setIndexInProgress, n as embedInProgress, o as lastEmbedTime, p as setEmbedSchedulerTimer, r as embedSchedulerTimer, s as lastIndexTime, t as daemonConfig, u as registryDb, v as setLastVaultIndexTime, w as setVaultIndexInProgress, x as setShutdownRequested, y as setNotificationConfig } from "./state-CAeyOdfq.mjs";
|
|
9
|
+
import { t as routeNotification } from "./router-DcHKnEPa.mjs";
|
|
10
|
+
import { C as toolProjectInfo, D as toolMemoryGet, O as toolMemorySearch, S as toolProjectHealth, T as toolProjectTodo, a as toolMemoryWakeup, b as toolSessionRoute, i as toolMemoryTaxonomy, n as toolMemoryFeedback, t as toolMemoryKgSearch, v as toolRegistrySearch, w as toolProjectList, x as toolProjectDetect, y as toolSessionList } from "./tools-DGcB3o_S.mjs";
|
|
11
|
+
import { t as detectTopicShift } from "./detector-Bwk_4Pk4.mjs";
|
|
12
|
+
import { a as enqueue, i as stopWorker, n as notifyNewWork, o as getStats, r as startWorker, s as loadQueue } from "./work-queue-worker-R7UGIag_.mjs";
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { setPriority } from "node:os";
|
|
15
15
|
import { connect, createServer } from "node:net";
|
|
@@ -110,7 +110,7 @@ async function runIndex() {
|
|
|
110
110
|
try {
|
|
111
111
|
process.stderr.write("[pai-daemon] Starting scheduled index run...\n");
|
|
112
112
|
if (storageBackend.backendType === "sqlite") {
|
|
113
|
-
const { SQLiteBackend } = await import("./sqlite-
|
|
113
|
+
const { SQLiteBackend } = await import("./sqlite-BenGr3UP.mjs");
|
|
114
114
|
if (storageBackend instanceof SQLiteBackend) {
|
|
115
115
|
const { projects, result } = await indexAll(storageBackend.getRawDb(), registryDb);
|
|
116
116
|
const elapsed = Date.now() - t0;
|
|
@@ -118,7 +118,7 @@ async function runIndex() {
|
|
|
118
118
|
process.stderr.write(`[pai-daemon] Index complete: ${projects} projects, ${result.filesProcessed} files, ${result.chunksCreated} chunks (${elapsed}ms)\n`);
|
|
119
119
|
}
|
|
120
120
|
} else {
|
|
121
|
-
const { indexAllWithBackend } = await import("./indexer-backend-
|
|
121
|
+
const { indexAllWithBackend } = await import("./indexer-backend-TG64CCQC.mjs");
|
|
122
122
|
const { projects, result } = await indexAllWithBackend(storageBackend, registryDb);
|
|
123
123
|
const elapsed = Date.now() - t0;
|
|
124
124
|
setLastIndexTime(Date.now());
|
|
@@ -145,7 +145,7 @@ async function runVaultIndex() {
|
|
|
145
145
|
process.stderr.write("[pai-daemon] Index/embed in progress, deferring vault index.\n");
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
148
|
-
const { lastVaultIndexTime } = await import("./state-
|
|
148
|
+
const { lastVaultIndexTime } = await import("./state-Ca9F_sZc.mjs");
|
|
149
149
|
if (lastVaultIndexTime > 0 && Date.now() - lastVaultIndexTime < VAULT_INDEX_MIN_INTERVAL_MS) return;
|
|
150
150
|
let vaultProjectId = daemonConfig.vaultProjectId;
|
|
151
151
|
if (!vaultProjectId) {
|
|
@@ -157,7 +157,7 @@ async function runVaultIndex() {
|
|
|
157
157
|
const t0 = Date.now();
|
|
158
158
|
process.stderr.write("[pai-daemon] Starting vault index run...\n");
|
|
159
159
|
try {
|
|
160
|
-
const { indexVault } = await import("./vault-indexer-
|
|
160
|
+
const { indexVault } = await import("./vault-indexer-C3OfXTNF.mjs");
|
|
161
161
|
const r = await indexVault(storageBackend, vaultProjectId, daemonConfig.vaultPath);
|
|
162
162
|
const elapsed = Date.now() - t0;
|
|
163
163
|
setLastVaultIndexTime(Date.now());
|
|
@@ -228,11 +228,11 @@ async function runEmbed() {
|
|
|
228
228
|
const rows = registryDb.prepare("SELECT id, slug FROM projects WHERE status = 'active'").all();
|
|
229
229
|
for (const r of rows) projectNames.set(r.id, r.slug);
|
|
230
230
|
} catch {}
|
|
231
|
-
const { embedChunksWithBackend } = await import("./indexer-backend-
|
|
231
|
+
const { embedChunksWithBackend } = await import("./indexer-backend-TG64CCQC.mjs");
|
|
232
232
|
const count = await embedChunksWithBackend(storageBackend, () => shutdownRequested, projectNames, { maxMillis: 24e4 });
|
|
233
233
|
let vaultEmbedCount = 0;
|
|
234
234
|
if (daemonConfig.vaultPath) try {
|
|
235
|
-
const { SQLiteBackend } = await import("./sqlite-
|
|
235
|
+
const { SQLiteBackend } = await import("./sqlite-BenGr3UP.mjs");
|
|
236
236
|
const { openFederation } = await import("./db-O-cyAPfS.mjs");
|
|
237
237
|
const federationDb = openFederation();
|
|
238
238
|
const vaultSqliteBackend = new SQLiteBackend(federationDb);
|
|
@@ -292,12 +292,12 @@ const REGISTRY_SCAN_STARTUP_DELAY_MS = 1e4;
|
|
|
292
292
|
function startRegistryScanScheduler() {
|
|
293
293
|
process.stderr.write("[pai-daemon] Registry scan scheduler: every 30min (first in 10s)\n");
|
|
294
294
|
setTimeout(() => {
|
|
295
|
-
import("./work-queue-worker-
|
|
295
|
+
import("./work-queue-worker-DW8lz-Oo.mjs").then(({ enqueueRegistryScan }) => enqueueRegistryScan()).catch((e) => {
|
|
296
296
|
process.stderr.write(`[pai-daemon] Startup registry scan error: ${e}\n`);
|
|
297
297
|
});
|
|
298
298
|
}, REGISTRY_SCAN_STARTUP_DELAY_MS);
|
|
299
299
|
const timer = setInterval(() => {
|
|
300
|
-
import("./work-queue-worker-
|
|
300
|
+
import("./work-queue-worker-DW8lz-Oo.mjs").then(({ enqueueRegistryScan }) => enqueueRegistryScan()).catch((e) => {
|
|
301
301
|
process.stderr.write(`[pai-daemon] Scheduled registry scan error: ${e}\n`);
|
|
302
302
|
});
|
|
303
303
|
}, REGISTRY_SCAN_INTERVAL_MS);
|
|
@@ -607,7 +607,7 @@ async function dispatchTool(method, params) {
|
|
|
607
607
|
case "zettel_themes":
|
|
608
608
|
case "zettel_god_notes":
|
|
609
609
|
case "zettel_communities": {
|
|
610
|
-
const { toolZettelExplore, toolZettelHealth, toolZettelSurprise, toolZettelSuggest, toolZettelConverse, toolZettelThemes, toolZettelGodNotes, toolZettelCommunities } = await import("./tools-
|
|
610
|
+
const { toolZettelExplore, toolZettelHealth, toolZettelSurprise, toolZettelSuggest, toolZettelConverse, toolZettelThemes, toolZettelGodNotes, toolZettelCommunities } = await import("./tools-Bp7hj6OW.mjs");
|
|
611
611
|
switch (method) {
|
|
612
612
|
case "zettel_explore": return toolZettelExplore(storageBackend, p);
|
|
613
613
|
case "zettel_health": return toolZettelHealth(storageBackend, p);
|
|
@@ -621,27 +621,27 @@ async function dispatchTool(method, params) {
|
|
|
621
621
|
break;
|
|
622
622
|
}
|
|
623
623
|
case "graph_clusters": {
|
|
624
|
-
const { handleGraphClusters } = await import("./clusters-
|
|
624
|
+
const { handleGraphClusters } = await import("./clusters-wlK0w41E.mjs");
|
|
625
625
|
return handleGraphClusters(storageBackend.getPool?.() ?? null, storageBackend, p);
|
|
626
626
|
}
|
|
627
627
|
case "graph_neighborhood": {
|
|
628
|
-
const { handleGraphNeighborhood } = await import("./neighborhood-
|
|
628
|
+
const { handleGraphNeighborhood } = await import("./neighborhood-lThN-MaQ.mjs");
|
|
629
629
|
return handleGraphNeighborhood(storageBackend.getPool?.() ?? null, storageBackend, p);
|
|
630
630
|
}
|
|
631
631
|
case "graph_note_context": {
|
|
632
|
-
const { handleGraphNoteContext } = await import("./note-context-
|
|
632
|
+
const { handleGraphNoteContext } = await import("./note-context-b6k0mAKi.mjs");
|
|
633
633
|
return handleGraphNoteContext(storageBackend.getPool?.() ?? null, storageBackend, p);
|
|
634
634
|
}
|
|
635
635
|
case "graph_trace": {
|
|
636
|
-
const { handleGraphTrace } = await import("./trace-
|
|
636
|
+
const { handleGraphTrace } = await import("./trace-bobARFEX.mjs");
|
|
637
637
|
return handleGraphTrace(storageBackend, p);
|
|
638
638
|
}
|
|
639
639
|
case "graph_latent_ideas": {
|
|
640
|
-
const { handleGraphLatentIdeas } = await import("./latent-ideas-
|
|
640
|
+
const { handleGraphLatentIdeas } = await import("./latent-ideas-B7wq75Pt.mjs");
|
|
641
641
|
return handleGraphLatentIdeas(storageBackend, p);
|
|
642
642
|
}
|
|
643
643
|
case "idea_materialize": {
|
|
644
|
-
const { handleIdeaMaterialize } = await import("./latent-ideas-
|
|
644
|
+
const { handleIdeaMaterialize } = await import("./latent-ideas-B7wq75Pt.mjs");
|
|
645
645
|
if (!daemonConfig.vaultPath) throw new Error("idea_materialize requires vaultPath to be configured in the daemon config");
|
|
646
646
|
return handleIdeaMaterialize(p, daemonConfig.vaultPath);
|
|
647
647
|
}
|
|
@@ -649,7 +649,7 @@ async function dispatchTool(method, params) {
|
|
|
649
649
|
case "kg_query":
|
|
650
650
|
case "kg_invalidate":
|
|
651
651
|
case "kg_contradictions": {
|
|
652
|
-
const { toolKgAdd, toolKgQuery, toolKgInvalidate, toolKgContradictions } = await import("./tools-
|
|
652
|
+
const { toolKgAdd, toolKgQuery, toolKgInvalidate, toolKgContradictions } = await import("./tools-Bp7hj6OW.mjs");
|
|
653
653
|
const pgPool = storageBackend.getPool?.() ?? null;
|
|
654
654
|
if (!pgPool) throw new Error(`${method} requires a Postgres storage backend`);
|
|
655
655
|
switch (method) {
|
|
@@ -661,7 +661,7 @@ async function dispatchTool(method, params) {
|
|
|
661
661
|
break;
|
|
662
662
|
}
|
|
663
663
|
case "memory_tunnels": {
|
|
664
|
-
const { toolMemoryTunnels } = await import("./tools-
|
|
664
|
+
const { toolMemoryTunnels } = await import("./tools-Bp7hj6OW.mjs");
|
|
665
665
|
return toolMemoryTunnels(registryDb, storageBackend, p);
|
|
666
666
|
}
|
|
667
667
|
case "memory_feedback": {
|
|
@@ -1312,7 +1312,7 @@ async function serve(config) {
|
|
|
1312
1312
|
process.stderr.write("[pai-daemon] Starting daemon...\n");
|
|
1313
1313
|
process.stderr.write(`[pai-daemon] Socket: ${config.socketPath}\n`);
|
|
1314
1314
|
process.stderr.write(`[pai-daemon] Storage backend: ${config.storageBackend}\n`);
|
|
1315
|
-
const { notificationConfig } = await import("./state-
|
|
1315
|
+
const { notificationConfig } = await import("./state-Ca9F_sZc.mjs");
|
|
1316
1316
|
process.stderr.write(`[pai-daemon] Notification mode: ${notificationConfig.mode}\n`);
|
|
1317
1317
|
try {
|
|
1318
1318
|
setPriority(process.pid, 10);
|
|
@@ -1376,4 +1376,4 @@ async function serve(config) {
|
|
|
1376
1376
|
|
|
1377
1377
|
//#endregion
|
|
1378
1378
|
export { serve as t };
|
|
1379
|
-
//# sourceMappingURL=daemon-
|
|
1379
|
+
//# sourceMappingURL=daemon-CTkN_Vn8.mjs.map
|