@goondocks/myco 0.12.0 → 0.12.2
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/{agent-run-5KYQJQTY.js → agent-run-CXK2SJEA.js} +4 -4
- package/dist/{agent-tasks-N7BDYKGB.js → agent-tasks-O72CTOGN.js} +4 -4
- package/dist/{chunk-RGRPAKEY.js → chunk-4CKDWUCX.js} +3 -3
- package/dist/{chunk-RGRPAKEY.js.map → chunk-4CKDWUCX.js.map} +1 -1
- package/dist/{chunk-TY7A5OZ5.js → chunk-ABZJLB6K.js} +2 -2
- package/dist/{chunk-HAG2YDH6.js → chunk-B7CPVELQ.js} +2 -2
- package/dist/{chunk-YRHSTVCZ.js → chunk-OIQJF7UV.js} +21 -2
- package/dist/chunk-OIQJF7UV.js.map +1 -0
- package/dist/{chunk-DPJVKNNP.js → chunk-UK2WZET6.js} +2 -2
- package/dist/{cli-Y5QZJAHX.js → cli-NTP2SSAU.js} +31 -31
- package/dist/{client-UGM6MG55.js → client-DFHTPO6U.js} +3 -3
- package/dist/{doctor-NFWPX75B.js → doctor-RUMMTET5.js} +4 -4
- package/dist/{executor-ESRLUCGU.js → executor-G3L2UHXF.js} +2 -2
- package/dist/{init-NMSG24BY.js → init-X3AKEAPM.js} +28 -12
- package/dist/{init-NMSG24BY.js.map → init-X3AKEAPM.js.map} +1 -1
- package/dist/{init-wizard-WIUCR4JE.js → init-wizard-W7XGO4FM.js} +2 -2
- package/dist/{main-3NTAT7ZD.js → main-6YKOV53K.js} +11 -10
- package/dist/main-6YKOV53K.js.map +1 -0
- package/dist/{post-compact-3DDK3OVZ.js → post-compact-OBSL3Q4N.js} +4 -4
- package/dist/{post-tool-use-LSG6N3W5.js → post-tool-use-WRKKE5U3.js} +3 -3
- package/dist/{post-tool-use-failure-5V3OCLI6.js → post-tool-use-failure-HQ5XXBYL.js} +4 -4
- package/dist/{pre-compact-F7Y7SDSZ.js → pre-compact-JWJ6NSJ7.js} +4 -4
- package/dist/{remove-VCWRNG54.js → remove-7JJAVZFY.js} +4 -4
- package/dist/{restart-YYJ7SH4K.js → restart-OKRZZMEQ.js} +5 -5
- package/dist/{search-L7KTBURJ.js → search-XNURLMCH.js} +4 -4
- package/dist/{server-GMRVF2PB.js → server-7BVCA5BY.js} +3 -3
- package/dist/{session-V3SNFG7J.js → session-UU6PID3W.js} +4 -4
- package/dist/{session-end-V3V3GMP2.js → session-end-XMTXUGS4.js} +3 -3
- package/dist/{session-start-OH7SBUIA.js → session-start-FDDGWWMC.js} +3 -3
- package/dist/{setup-llm-5AMWEAJ5.js → setup-llm-UGZDYJKD.js} +4 -4
- package/dist/src/cli.js +1 -1
- package/dist/src/daemon/main.js +1 -1
- package/dist/src/hooks/post-tool-use.js +1 -1
- package/dist/src/hooks/session-end.js +1 -1
- package/dist/src/hooks/session-start.js +1 -1
- package/dist/src/hooks/stop.js +1 -1
- package/dist/src/hooks/user-prompt-submit.js +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/symbionts/templates/agents-starter.md +5 -0
- package/dist/{stats-B2V7P45Y.js → stats-DPXRT7BQ.js} +5 -5
- package/dist/{stop-D6L2KRHZ.js → stop-RZNL4BGV.js} +3 -3
- package/dist/{stop-failure-7OM2AYRX.js → stop-failure-C7IUKTIW.js} +4 -4
- package/dist/{subagent-start-MKL5I54S.js → subagent-start-2MVHKQ5K.js} +4 -4
- package/dist/{subagent-stop-2E7VKZW2.js → subagent-stop-YVWHRVDZ.js} +4 -4
- package/dist/{task-completed-5QHIT773.js → task-completed-QFPSXH23.js} +4 -4
- package/dist/ui/assets/{index-aMc07Ym5.js → index-BE87MlCd.js} +112 -112
- package/dist/ui/index.html +1 -1
- package/dist/{update-4NVFET56.js → update-Z25DYA32.js} +4 -4
- package/dist/{user-prompt-submit-X4BCPMZ4.js → user-prompt-submit-KNB27MOC.js} +3 -3
- package/dist/{version-GTFCEIJ2.js → version-G3HCB5X5.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-YRHSTVCZ.js.map +0 -1
- package/dist/main-3NTAT7ZD.js.map +0 -1
- /package/dist/{agent-run-5KYQJQTY.js.map → agent-run-CXK2SJEA.js.map} +0 -0
- /package/dist/{agent-tasks-N7BDYKGB.js.map → agent-tasks-O72CTOGN.js.map} +0 -0
- /package/dist/{chunk-TY7A5OZ5.js.map → chunk-ABZJLB6K.js.map} +0 -0
- /package/dist/{chunk-HAG2YDH6.js.map → chunk-B7CPVELQ.js.map} +0 -0
- /package/dist/{chunk-DPJVKNNP.js.map → chunk-UK2WZET6.js.map} +0 -0
- /package/dist/{cli-Y5QZJAHX.js.map → cli-NTP2SSAU.js.map} +0 -0
- /package/dist/{client-UGM6MG55.js.map → client-DFHTPO6U.js.map} +0 -0
- /package/dist/{doctor-NFWPX75B.js.map → doctor-RUMMTET5.js.map} +0 -0
- /package/dist/{executor-ESRLUCGU.js.map → executor-G3L2UHXF.js.map} +0 -0
- /package/dist/{init-wizard-WIUCR4JE.js.map → init-wizard-W7XGO4FM.js.map} +0 -0
- /package/dist/{post-compact-3DDK3OVZ.js.map → post-compact-OBSL3Q4N.js.map} +0 -0
- /package/dist/{post-tool-use-LSG6N3W5.js.map → post-tool-use-WRKKE5U3.js.map} +0 -0
- /package/dist/{post-tool-use-failure-5V3OCLI6.js.map → post-tool-use-failure-HQ5XXBYL.js.map} +0 -0
- /package/dist/{pre-compact-F7Y7SDSZ.js.map → pre-compact-JWJ6NSJ7.js.map} +0 -0
- /package/dist/{remove-VCWRNG54.js.map → remove-7JJAVZFY.js.map} +0 -0
- /package/dist/{restart-YYJ7SH4K.js.map → restart-OKRZZMEQ.js.map} +0 -0
- /package/dist/{search-L7KTBURJ.js.map → search-XNURLMCH.js.map} +0 -0
- /package/dist/{server-GMRVF2PB.js.map → server-7BVCA5BY.js.map} +0 -0
- /package/dist/{session-V3SNFG7J.js.map → session-UU6PID3W.js.map} +0 -0
- /package/dist/{session-end-V3V3GMP2.js.map → session-end-XMTXUGS4.js.map} +0 -0
- /package/dist/{session-start-OH7SBUIA.js.map → session-start-FDDGWWMC.js.map} +0 -0
- /package/dist/{setup-llm-5AMWEAJ5.js.map → setup-llm-UGZDYJKD.js.map} +0 -0
- /package/dist/{stats-B2V7P45Y.js.map → stats-DPXRT7BQ.js.map} +0 -0
- /package/dist/{stop-D6L2KRHZ.js.map → stop-RZNL4BGV.js.map} +0 -0
- /package/dist/{stop-failure-7OM2AYRX.js.map → stop-failure-C7IUKTIW.js.map} +0 -0
- /package/dist/{subagent-start-MKL5I54S.js.map → subagent-start-2MVHKQ5K.js.map} +0 -0
- /package/dist/{subagent-stop-2E7VKZW2.js.map → subagent-stop-YVWHRVDZ.js.map} +0 -0
- /package/dist/{task-completed-5QHIT773.js.map → task-completed-QFPSXH23.js.map} +0 -0
- /package/dist/{update-4NVFET56.js.map → update-Z25DYA32.js.map} +0 -0
- /package/dist/{user-prompt-submit-X4BCPMZ4.js.map → user-prompt-submit-KNB27MOC.js.map} +0 -0
- /package/dist/{version-GTFCEIJ2.js.map → version-G3HCB5X5.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/server.ts","../src/daemon/router.ts","../src/daemon/static.ts","../src/daemon/lifecycle.ts","../src/daemon/port.ts","../src/symbionts/adapter.ts","../src/symbionts/claude-code.ts","../src/symbionts/cursor.ts","../src/symbionts/codex.ts","../src/symbionts/gemini.ts","../src/symbionts/windsurf.ts","../src/symbionts/vscode-copilot.ts","../src/symbionts/registry.ts","../src/capture/transcript-miner.ts","../src/daemon/plan-capture.ts","../src/db/queries/plans.ts","../src/daemon/api/config.ts","../src/db/queries/logs.ts","../src/daemon/api/log-explorer.ts","../src/daemon/api/restart.ts","../src/daemon/update-checker.ts","../src/daemon/update-installer.ts","../src/daemon/api/update.ts","../src/daemon/backup.ts","../src/daemon/api/backup.ts","../src/daemon/team-sync.ts","../src/daemon/api/team-connect.ts","../src/daemon/api/progress.ts","../src/daemon/api/models.ts","../src/daemon/api/stats.ts","../src/db/queries/activities.ts","../src/db/queries/attachments.ts","../src/daemon/api/sessions.ts","../src/daemon/api/mycelium.ts","../src/daemon/api/search.ts","../src/daemon/api/context.ts","../src/db/queries/feed.ts","../src/daemon/api/feed.ts","../src/daemon/api/symbionts.ts","../src/daemon/api/embedding.ts","../src/daemon/embedding/manager.ts","../src/daemon/embedding/sqlite-vec-store.ts","../src/intelligence/embeddings.ts","../src/daemon/embedding/provider-adapter.ts","../src/daemon/embedding/record-source.ts","../src/daemon/api/agent-tasks.ts","../src/daemon/api/providers.ts","../src/daemon/log-reconcile.ts","../src/daemon/power.ts","../src/daemon/jobs/session-cleanup.ts","../src/daemon/jobs/session-maintenance.ts","../src/daemon/main.ts"],"sourcesContent":["import http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { DaemonLogger } from './logger.js';\nimport { getPluginVersion } from '../version.js';\nimport { Router, type RouteHandler } from './router.js';\nimport { resolveStaticFile } from './static.js';\nimport { DAEMON_EVICT_TIMEOUT_MS, DAEMON_EVICT_POLL_MS } from '../constants.js';\nimport { LOG_KINDS } from '../constants/log-kinds.js';\n\nconst DEFAULT_STATUS = 200;\n\nexport interface DaemonServerConfig {\n vaultDir: string;\n logger: DaemonLogger;\n uiDir?: string;\n onRequest?: () => void;\n}\n\nexport class DaemonServer {\n port = 0;\n readonly version: string;\n uiDir: string | null;\n private server: http.Server | null = null;\n private vaultDir: string;\n private logger: DaemonLogger;\n private router = new Router();\n private onRequest: (() => void) | null;\n\n constructor(config: DaemonServerConfig) {\n this.vaultDir = config.vaultDir;\n this.logger = config.logger;\n this.uiDir = config.uiDir ?? null;\n this.onRequest = config.onRequest ?? null;\n this.version = getPluginVersion();\n this.registerDefaultRoutes();\n }\n\n registerRoute(method: string, routePath: string, handler: RouteHandler): void {\n this.router.add(method, routePath, handler);\n }\n\n async start(port: number = 0): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer((req, res) => this.handleRequest(req, res));\n this.server.on('error', reject);\n\n this.server.listen(port, '127.0.0.1', () => {\n const addr = this.server!.address() as { port: number };\n this.port = addr.port;\n this.writeDaemonJson();\n this.logger.info(LOG_KINDS.DAEMON_PORT, 'Server started', { port: this.port, dashboard: `http://localhost:${this.port}/` });\n resolve();\n });\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve) => {\n this.removeDaemonJson();\n if (this.server) {\n this.server.close(() => {\n this.logger.info(LOG_KINDS.DAEMON_START, 'Server stopped');\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n private registerDefaultRoutes(): void {\n this.registerRoute('GET', '/health', async () => ({\n body: {\n myco: true,\n version: this.version,\n pid: process.pid,\n uptime: process.uptime(),\n },\n }));\n }\n\n private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n // API/daemon routes take priority over static files\n const match = this.router.match(req.method!, req.url!);\n\n if (match) {\n this.onRequest?.();\n try {\n const body = (req.method === 'POST' || req.method === 'PUT') ? await readBody(req) : undefined;\n const result = await match.handler({\n body,\n query: match.query,\n params: match.params,\n pathname: match.pathname,\n });\n const status = result.status ?? DEFAULT_STATUS;\n if (Buffer.isBuffer(result.body)) {\n res.writeHead(status, result.headers ?? {});\n res.end(result.body);\n return;\n }\n const headers = { 'Content-Type': 'application/json', ...result.headers };\n res.writeHead(status, headers);\n res.end(JSON.stringify(result.body));\n } catch (error) {\n this.logger.error(LOG_KINDS.SERVER_ERROR, 'Request handler error', {\n path: req.url,\n error: (error as Error).message,\n });\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n return;\n }\n\n // No API route matched — serve static files (dashboard SPA)\n if (this.uiDir && req.method === 'GET') {\n const pathname = new URL(req.url!, 'http://localhost').pathname;\n const result = resolveStaticFile(this.uiDir, pathname);\n if (result) {\n try {\n const content = await fs.promises.readFile(result.filePath);\n res.writeHead(200, {\n 'Content-Type': result.contentType,\n 'Cache-Control': result.cacheControl,\n });\n res.end(content);\n } catch {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n }\n return;\n }\n }\n\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n }\n\n updateDaemonJsonSessions(sessions: string[]): void {\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n try {\n const info = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));\n info.sessions = sessions;\n fs.writeFileSync(jsonPath, JSON.stringify(info, null, 2));\n } catch { /* daemon.json may not exist during shutdown */ }\n }\n\n /**\n * Kill any existing daemon for this vault before taking over.\n * Prevents orphaned daemons when spawned from worktrees or plugin upgrades.\n * Must be called BEFORE resolvePort() so the old daemon releases the port.\n */\n async evictExistingDaemon(): Promise<void> {\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n let existingPid: number | undefined;\n try {\n const content = fs.readFileSync(jsonPath, 'utf-8');\n const info = JSON.parse(content);\n if (typeof info.pid === 'number' && info.pid !== process.pid) {\n existingPid = info.pid;\n }\n } catch { /* no daemon.json or invalid — nothing to evict */ }\n\n if (!existingPid) return;\n\n // Check if the process is alive\n try { process.kill(existingPid, 0); } catch { return; /* already dead */ }\n\n this.logger.info(LOG_KINDS.DAEMON_START, 'Evicting existing daemon', { pid: existingPid });\n try { process.kill(existingPid, 'SIGTERM'); } catch { return; }\n\n // Give SIGTERM a grace period, then escalate to SIGKILL to guarantee port release\n const deadline = Date.now() + DAEMON_EVICT_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, DAEMON_EVICT_POLL_MS));\n try { process.kill(existingPid, 0); } catch { return; /* dead */ }\n }\n\n this.logger.warn(LOG_KINDS.DAEMON_START, 'Evicted daemon did not exit in time, sending SIGKILL', { pid: existingPid });\n try { process.kill(existingPid, 'SIGKILL'); } catch { return; }\n\n // Verify SIGKILL took effect\n await new Promise((r) => setTimeout(r, DAEMON_EVICT_POLL_MS));\n try { process.kill(existingPid, 0); } catch { return; /* dead */ }\n this.logger.warn(LOG_KINDS.DAEMON_START, 'Evicted daemon still alive after SIGKILL', { pid: existingPid });\n }\n\n private writeDaemonJson(): void {\n const info = {\n pid: process.pid,\n port: this.port,\n started: new Date().toISOString(),\n sessions: [] as string[],\n };\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n fs.writeFileSync(jsonPath, JSON.stringify(info, null, 2));\n }\n\n private removeDaemonJson(): void {\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n try {\n const content = fs.readFileSync(jsonPath, 'utf-8');\n const info = JSON.parse(content);\n // Only delete if we still own the file — a successor daemon may have taken over.\n if (info.pid !== process.pid) return;\n fs.unlinkSync(jsonPath);\n } catch { /* already gone or unreadable */ }\n }\n}\n\nfunction readBody(req: http.IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n req.on('data', (chunk: string) => { data += chunk; });\n req.on('end', () => {\n try { resolve(data ? JSON.parse(data) : {}); }\n catch (e) { reject(e); }\n });\n req.on('error', reject);\n });\n}\n","export interface RouteRequest {\n body: unknown;\n query: Record<string, string>;\n params: Record<string, string>;\n pathname: string;\n}\n\nexport interface RouteResponse {\n status?: number;\n headers?: Record<string, string>;\n body: unknown;\n}\n\nexport type RouteHandler = (req: RouteRequest) => Promise<RouteResponse>;\n\ninterface RouteEntry {\n method: string;\n pattern: string;\n handler: RouteHandler;\n type: 'exact' | 'param' | 'prefix';\n segments?: string[];\n}\n\nexport interface RouteMatch {\n handler: RouteHandler;\n params: Record<string, string>;\n query: Record<string, string>;\n pathname: string;\n}\n\nexport class Router {\n private routes: RouteEntry[] = [];\n\n add(method: string, pattern: string, handler: RouteHandler): void {\n const type = pattern.includes(':') ? 'param'\n : pattern.endsWith('/*') ? 'prefix'\n : 'exact';\n const segments = type === 'param' ? pattern.split('/') : undefined;\n this.routes.push({ method, pattern, handler, type, segments });\n }\n\n /**\n * Match a request against registered routes.\n * Priority: exact > parameterized > prefix. Within parameterized routes,\n * first-registered wins if multiple patterns match at the same depth.\n */\n match(method: string, rawUrl: string): RouteMatch | undefined {\n const url = new URL(rawUrl, 'http://localhost');\n const pathname = url.pathname;\n const query: Record<string, string> = {};\n url.searchParams.forEach((v, k) => { query[k] = v; });\n\n // Priority: exact > param > prefix\n let paramMatch: RouteMatch | undefined;\n let prefixMatch: RouteMatch | undefined;\n\n for (const route of this.routes) {\n if (route.method !== method) continue;\n\n if (route.type === 'exact' && route.pattern === pathname) {\n return { handler: route.handler, params: {}, query, pathname };\n }\n\n if (route.type === 'param' && !paramMatch && route.segments) {\n const parts = pathname.split('/');\n if (parts.length === route.segments.length) {\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < route.segments.length; i++) {\n if (route.segments[i].startsWith(':')) {\n params[route.segments[i].slice(1)] = parts[i];\n } else if (route.segments[i] !== parts[i]) {\n matched = false;\n break;\n }\n }\n if (matched) {\n paramMatch = { handler: route.handler, params, query, pathname };\n }\n }\n }\n\n if (route.type === 'prefix' && !prefixMatch) {\n const prefix = route.pattern.slice(0, -1); // Remove trailing *\n if (pathname.startsWith(prefix)) {\n prefixMatch = { handler: route.handler, params: {}, query, pathname };\n }\n }\n }\n\n return paramMatch ?? prefixMatch;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst HASHED_ASSET_PREFIX = '/assets/';\nconst IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';\nconst NO_CACHE = 'no-cache';\n\nexport const MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n};\n\nexport interface StaticFileResult {\n filePath: string;\n contentType: string;\n cacheControl: string;\n}\n\n/** Resolve a request to a file in the UI directory. Returns undefined if blocked (path traversal). */\nexport function resolveStaticFile(uiDir: string, pathname: string): StaticFileResult | undefined {\n // Strip leading slash to get relative path\n const relative = pathname.startsWith('/') ? pathname.slice(1) : pathname;\n\n // Resolve \"/\" to index.html\n const resolved = path.resolve(uiDir, relative || 'index.html');\n if (!resolved.startsWith(path.resolve(uiDir))) {\n return undefined;\n }\n\n // Serve the file if it exists\n if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {\n const ext = path.extname(resolved);\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';\n const cacheControl = pathname.startsWith(HASHED_ASSET_PREFIX) ? IMMUTABLE_CACHE : NO_CACHE;\n return { filePath: resolved, contentType, cacheControl };\n }\n\n // SPA fallback: serve index.html for any non-file path\n const indexPath = path.join(uiDir, 'index.html');\n if (fs.existsSync(indexPath)) {\n return { filePath: indexPath, contentType: 'text/html', cacheControl: NO_CACHE };\n }\n\n return undefined;\n}\n","export interface SessionMetadata {\n started_at: string;\n branch?: string;\n}\n\nexport interface RegisteredSession extends SessionMetadata {\n id: string;\n}\n\ninterface RegistryOptions {\n gracePeriod: number;\n onEmpty: () => void;\n}\n\nexport class SessionRegistry {\n private _sessions: Map<string, SessionMetadata> = new Map();\n private graceTimer: ReturnType<typeof setTimeout> | null = null;\n private gracePeriod: number;\n private onEmpty: () => void;\n\n constructor(options: RegistryOptions) {\n this.gracePeriod = options.gracePeriod;\n this.onEmpty = options.onEmpty;\n }\n\n get sessions(): string[] {\n return [...this._sessions.keys()];\n }\n\n register(sessionId: string, metadata?: SessionMetadata): void {\n if (!this._sessions.has(sessionId)) {\n this._sessions.set(sessionId, metadata ?? { started_at: new Date().toISOString() });\n }\n this.cancelGrace();\n }\n\n getSession(sessionId: string): RegisteredSession | undefined {\n const meta = this._sessions.get(sessionId);\n if (!meta) return undefined;\n return { id: sessionId, ...meta };\n }\n\n unregister(sessionId: string): void {\n this._sessions.delete(sessionId);\n if (this._sessions.size === 0) {\n this.startGrace();\n }\n }\n\n destroy(): void {\n this.cancelGrace();\n this._sessions.clear();\n }\n\n private startGrace(): void {\n this.cancelGrace();\n this.graceTimer = setTimeout(() => {\n if (this._sessions.size === 0) {\n this.onEmpty();\n }\n }, this.gracePeriod * 1000);\n }\n\n private cancelGrace(): void {\n if (this.graceTimer) {\n clearTimeout(this.graceTimer);\n this.graceTimer = null;\n }\n }\n}\n","import { createHash } from 'node:crypto';\nimport net from 'node:net';\n\nexport const PORT_RANGE_START = 19200;\nexport const PORT_RANGE_SIZE = 10000;\nconst PORT_RETRY_COUNT = 10;\n\n/** Derive a deterministic port from a vault path. */\nexport function derivePort(vaultPath: string): number {\n const hash = createHash('md5').update(vaultPath).digest();\n const num = hash.readUInt16LE(0);\n return PORT_RANGE_START + (num % PORT_RANGE_SIZE);\n}\n\n/** Resolve the port to bind: try config port, derive from path, or fall back to ephemeral. */\nexport async function resolvePort(\n configPort: number | null,\n vaultPath: string,\n): Promise<number> {\n const basePort = configPort ?? derivePort(vaultPath);\n\n for (let offset = 0; offset < PORT_RETRY_COUNT; offset++) {\n const candidate = basePort + offset;\n if (candidate > 65535) break;\n if (await isPortAvailable(candidate)) return candidate;\n }\n\n // All candidates taken — fall back to ephemeral\n return 0;\n}\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once('error', () => resolve(false));\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n","/**\n * Symbiont adapter interface — declares what each coding agent provides to Myco.\n *\n * Each supported symbiont (Claude Code, Cursor, Cline, etc.) has an adapter that\n * tells Myco where to find transcripts, how to parse them, and what capabilities\n * the agent supports. The daemon uses these adapters at runtime to read the\n * authoritative conversation record.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** An image attached to a conversation turn */\nexport interface TranscriptImage {\n /** Base64-encoded image data */\n data: string;\n /** MIME type (e.g., image/png) */\n mediaType: string;\n}\n\n/** A single conversation turn extracted from an agent's transcript */\nexport interface TranscriptTurn {\n prompt: string;\n toolCount: number;\n /** Per-tool call counts (e.g., { Read: 5, Edit: 3 }). Populated from buffer events. */\n toolBreakdown?: Record<string, number>;\n /** Deduplicated file paths touched in this turn. Populated from buffer events. */\n files?: string[];\n aiResponse?: string;\n timestamp: string;\n /** Images attached to this turn's user prompt */\n images?: TranscriptImage[];\n}\n\n/**\n * Maps agent-specific hook field names to normalized names.\n * Each agent's hook system uses different field names for the same data.\n */\nexport interface HookFieldNames {\n /** Field name for the session ID (e.g., 'session_id', 'sessionId', 'trajectory_id') */\n sessionId: string;\n /** Field name for the transcript file path (e.g., 'transcript_path') */\n transcriptPath: string;\n /** Field name for the last AI response text (e.g., 'last_assistant_message') */\n lastResponse: string;\n /** Field name for the user prompt (e.g., 'prompt') */\n prompt: string;\n /** Field name for the tool name (e.g., 'tool_name') */\n toolName: string;\n /** Field name for the tool input (e.g., 'tool_input'). Supports dot notation for nested objects. */\n toolInput: string;\n /** Field name for the tool output (e.g., 'tool_output'). Supports dot notation for nested objects. */\n toolOutput: string;\n /** Env var fallback for session ID (e.g., 'GEMINI_SESSION_ID'). */\n sessionIdEnv?: string;\n}\n\nexport interface SymbiontAdapter {\n /** Agent identifier (matches plugin directory names) */\n readonly name: string;\n /** Human-readable display name */\n readonly displayName: string;\n /** Environment variable for the plugin root directory */\n readonly pluginRootEnvVar: string;\n /** Maps agent-specific hook body field names to normalized names */\n readonly hookFields: HookFieldNames;\n\n /**\n * Find the transcript file for a given session ID.\n * Returns the absolute path if found, null otherwise.\n */\n findTranscript(sessionId: string): string | null;\n\n /**\n * Parse a transcript file's content into normalized turns.\n * Each adapter handles its agent's specific format.\n */\n parseTurns(content: string): TranscriptTurn[];\n\n}\n\n/**\n * Scan subdirectories of baseDir for a JSONL transcript file matching sessionId.\n * Shared by claude-code, cursor, custom adapters, and tests.\n */\nexport function findJsonlInSubdirs(baseDir: string, sessionId: string): string | null {\n try {\n for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const candidate = path.join(baseDir, entry.name, `${sessionId}.jsonl`);\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n } catch { /* baseDir doesn't exist or unreadable */ }\n return null;\n}\n\n/**\n * Factory for creating simple per-project adapters from a base directory.\n * Used for user-configured transcript_paths and testing.\n */\nexport function createPerProjectAdapter(\n baseDir: string,\n parseTurns: SymbiontAdapter['parseTurns'],\n name?: string,\n): SymbiontAdapter {\n return {\n name: name ?? `custom:${path.basename(baseDir)}`,\n displayName: `Custom (${baseDir})`,\n pluginRootEnvVar: '',\n hookFields: { sessionId: 'session_id', transcriptPath: 'transcript_path', lastResponse: 'last_assistant_message', prompt: 'prompt', toolName: 'tool_name', toolInput: 'tool_input', toolOutput: 'tool_output' },\n findTranscript: (sessionId) => findJsonlInSubdirs(baseDir, sessionId),\n parseTurns,\n };\n}\n\n/** Map MIME type to file extension */\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/png': 'png',\n};\n\nexport function extensionForMimeType(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? 'png';\n}\n\n/** Map file extension to MIME type */\nconst EXT_TO_MIME: Record<string, string> = {\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.png': 'image/png',\n};\n\nexport function mimeTypeForExtension(ext: string): string {\n return EXT_TO_MIME[ext.toLowerCase()] ?? 'image/png';\n}\n\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\n\n/** Claude Code injects [Image: source: /path] text alongside base64 image blocks. Strip these since the actual images are captured as attachments. */\nconst IMAGE_TEXT_REF_PATTERN = /\\[Image: source: [^\\]]+\\]\\n*/g;\n\nexport interface ParseJsonlOptions {\n /** Field name containing the message role ('type' for Claude Code, 'role' for Cursor) */\n roleField: 'type' | 'role';\n /** Whether entries have a timestamp field to extract */\n extractTimestamp: boolean;\n /** Whether to check for text-only user messages (Claude Code has tool_result user messages to skip) */\n skipToolResultUsers: boolean;\n /** Whether to strip [Image: source: ...] text references from prompts (Claude Code-specific) */\n stripImageTextRefs: boolean;\n}\n\n/**\n * Shared JSONL transcript parser — used by both Claude Code and Cursor adapters.\n * Handles user/assistant role detection, text/image extraction, and tool counting.\n */\nexport function parseJsonlTurns(content: string, opts: ParseJsonlOptions): TranscriptTurn[] {\n const lines = content.split('\\n').filter(Boolean);\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const line of lines) {\n let entry: Record<string, unknown>;\n try { entry = JSON.parse(line); } catch { continue; }\n\n const role = entry[opts.roleField] as string;\n const timestamp = opts.extractTimestamp ? (entry.timestamp as string ?? '') : '';\n\n if (role === 'user') {\n // Skip meta messages (skill injections, deprecation notices, etc.) — they are\n // not real user prompts and should not appear as turns or influence the title.\n if (entry.isMeta === true) continue;\n\n const msg = entry.message as { content?: Array<{ type: string; text?: string; source?: { type?: string; data?: string; media_type?: string } }> } | undefined;\n const blocks = Array.isArray(msg?.content) ? msg!.content : [];\n const hasText = blocks.some((b) => b.type === 'text' && b.text?.trim());\n\n if (!hasText && opts.skipToolResultUsers) continue;\n if (!hasText) continue;\n\n if (current) turns.push(current);\n\n const rawPrompt = blocks\n .filter((b) => b.type === 'text' && b.text)\n .map((b) => b.text!)\n .join('\\n');\n\n const promptText = (opts.stripImageTextRefs ? rawPrompt.replace(IMAGE_TEXT_REF_PATTERN, '') : rawPrompt)\n .trim()\n .slice(0, PROMPT_PREVIEW_CHARS);\n\n const images: TranscriptImage[] = blocks\n .filter((b) => b.type === 'image' && b.source?.type === 'base64' && b.source.data)\n .map((b) => ({ data: b.source!.data!, mediaType: b.source!.media_type ?? 'image/png' }));\n\n current = { prompt: promptText, toolCount: 0, timestamp, ...(images.length > 0 ? { images } : {}) };\n } else if (role === 'assistant' && current) {\n const msg = entry.message as { content?: Array<{ type: string; text?: string }> } | undefined;\n if (Array.isArray(msg?.content)) {\n const textParts = msg!.content.filter((b) => b.type === 'text' && b.text).map((b) => b.text!);\n const text = textParts.join('\\n').trim();\n if (text) current.aiResponse = text;\n current.toolCount += msg!.content.filter((b) => b.type === 'tool_use').length;\n }\n }\n }\n\n if (current) turns.push(current);\n return turns;\n}\n","import type { SymbiontAdapter } from './adapter.js';\nimport { findJsonlInSubdirs, parseJsonlTurns } from './adapter.js';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst TRANSCRIPT_BASE = path.join(os.homedir(), '.claude', 'projects');\n\nexport const claudeCodeAdapter: SymbiontAdapter = {\n name: 'claude-code',\n displayName: 'Claude Code',\n pluginRootEnvVar: 'CLAUDE_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'session_id',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n },\n\n findTranscript: (sessionId) => findJsonlInSubdirs(TRANSCRIPT_BASE, sessionId),\n\n parseTurns: (content) => parseJsonlTurns(content, {\n roleField: 'type',\n extractTimestamp: true,\n skipToolResultUsers: true,\n stripImageTextRefs: true,\n }),\n};\n","import type { SymbiontAdapter } from './adapter.js';\nimport type { TranscriptTurn, TranscriptImage } from './adapter.js';\nimport { mimeTypeForExtension, parseJsonlTurns } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * Cursor stores conversation transcripts in:\n * ~/.cursor/projects/<project-path>/agent-transcripts/<session-id>.txt\n *\n * Images are saved as files in:\n * ~/.cursor/projects/<project-path>/assets/<filename>.png\n *\n * Transcript format is plain text with role markers on their own line:\n * user: — human prompt (may contain <image_files> and <user_query> tags)\n * assistant: — assistant response (may contain [Tool call] and [Thinking] blocks)\n */\n\nconst USER_MARKER = '\\nuser:\\n';\nconst ASSISTANT_MARKER = '\\nassistant:\\n';\nconst TOOL_CALL_MARKER = '[Tool call]';\nconst TOOL_RESULT_MARKER = '[Tool result]';\nconst THINKING_MARKER = '[Thinking]';\n\nfunction getCursorProjectsBase(): string {\n return path.join(os.homedir(), '.cursor', 'projects');\n}\n\nconst CURSOR_PROJECTS = getCursorProjectsBase();\n\nexport const cursorAdapter: SymbiontAdapter = {\n name: 'cursor',\n displayName: 'Cursor',\n pluginRootEnvVar: 'CURSOR_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'conversation_id',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n },\n\n findTranscript(sessionId: string): string | null {\n try {\n for (const project of fs.readdirSync(CURSOR_PROJECTS, { withFileTypes: true })) {\n if (!project.isDirectory()) continue;\n const transcriptsDir = path.join(CURSOR_PROJECTS, project.name, 'agent-transcripts');\n // Try .txt (older Cursor) then .jsonl inside session directory (newer Cursor)\n for (const candidate of [\n path.join(transcriptsDir, `${sessionId}.txt`),\n path.join(transcriptsDir, sessionId, `${sessionId}.jsonl`),\n ]) {\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n }\n } catch { /* projects dir doesn't exist */ }\n return null;\n },\n\n parseTurns(content: string): TranscriptTurn[] {\n // Detect format: JSONL (starts with '{') or plain text (starts with 'user:')\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n return parseCursorJsonl(content);\n }\n return parseCursorText(content);\n },\n};\n\n/** Parse Cursor's newer JSONL format — same structure as Claude's but uses 'role' field */\nfunction parseCursorJsonl(content: string): TranscriptTurn[] {\n return parseJsonlTurns(content, {\n roleField: 'role',\n extractTimestamp: false,\n skipToolResultUsers: false,\n stripImageTextRefs: false,\n });\n}\n\n/** Parse Cursor's older plain-text transcript format. */\nfunction parseCursorText(content: string): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n // Split on user marker — each block is a new human turn.\n const sections = ('\\n' + content).split(USER_MARKER).slice(1);\n\n for (const section of sections) {\n // Extract user query from <user_query> tags or raw text before first assistant response\n let promptText = '';\n const queryMatch = section.match(/<user_query>\\s*([\\s\\S]*?)\\s*<\\/user_query>/);\n if (queryMatch) {\n promptText = queryMatch[1].trim().slice(0, PROMPT_PREVIEW_CHARS);\n } else {\n // No tags — take text before the first assistant response.\n const beforeAssistant = section.split(ASSISTANT_MARKER)[0];\n promptText = beforeAssistant.replace(/<[^>]+>[\\s\\S]*?<\\/[^>]+>/g, '').trim().slice(0, PROMPT_PREVIEW_CHARS);\n }\n\n // Extract image references from <image_files> tags\n const images: TranscriptImage[] = [];\n const imageFilesMatch = section.match(/<image_files>([\\s\\S]*?)<\\/image_files>/);\n if (imageFilesMatch) {\n const imageBlock = imageFilesMatch[1];\n const pathMatches = imageBlock.matchAll(/^\\d+\\.\\s+(.+\\.(?:png|jpg|jpeg|gif|webp))\\s*$/gmi);\n for (const match of pathMatches) {\n const imagePath = match[1].trim();\n try {\n const data = fs.readFileSync(imagePath).toString('base64');\n const mediaType = mimeTypeForExtension(path.extname(imagePath));\n images.push({ data, mediaType });\n } catch {\n // Image file not accessible — skip\n }\n }\n }\n\n // Count tool calls in assistant sections\n const toolCallCount = section.split(TOOL_CALL_MARKER).length - 1;\n\n // Extract the last meaningful assistant text response.\n // Scan assistant blocks (split on \\nA:\\n) from the end.\n // A block is \"meaningful\" if it contains lines that aren't tool calls/results/thinking.\n let aiResponse: string | undefined;\n const assistantBlocks = section.split(ASSISTANT_MARKER).slice(1);\n for (let j = assistantBlocks.length - 1; j >= 0; j--) {\n const lines = assistantBlocks[j].split('\\n');\n const textLines: string[] = [];\n let skip = false;\n for (const line of lines) {\n // Skip tool calls, tool results, and thinking blocks\n if (line.startsWith(TOOL_CALL_MARKER) || line.startsWith(TOOL_RESULT_MARKER) || line.startsWith(THINKING_MARKER)) {\n skip = true;\n continue;\n }\n // Resume after a blank line following a skipped block\n if (skip && line.trim() === '') continue;\n if (skip && !line.startsWith(' ')) skip = false; // End of indented tool args\n if (skip) continue;\n textLines.push(line);\n }\n const text = textLines.join('\\n').trim();\n if (text) {\n aiResponse = text;\n break;\n }\n }\n\n if (promptText || images.length > 0) {\n turns.push({\n prompt: promptText,\n toolCount: toolCallCount,\n timestamp: '',\n ...(aiResponse ? { aiResponse } : {}),\n ...(images.length > 0 ? { images } : {}),\n });\n }\n }\n\n return turns;\n}\n","import type { SymbiontAdapter } from './adapter.js';\nimport { findJsonlInSubdirs, parseJsonlTurns } from './adapter.js';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst TRANSCRIPT_BASE = path.join(os.homedir(), '.codex');\n\nexport const codexAdapter: SymbiontAdapter = {\n name: 'codex',\n displayName: 'Codex',\n pluginRootEnvVar: 'CODEX_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'session_id',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n },\n\n findTranscript: (sessionId) => findJsonlInSubdirs(TRANSCRIPT_BASE, sessionId),\n\n // Codex uses 'role' field (like Cursor), not 'type' (like Claude Code)\n parseTurns: (content) => parseJsonlTurns(content, {\n roleField: 'role',\n extractTimestamp: false,\n skipToolResultUsers: false,\n stripImageTextRefs: false,\n }),\n};\n","import type { SymbiontAdapter, TranscriptTurn } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * Gemini CLI stores transcripts as single JSON files (not JSONL) in:\n * ~/.gemini/tmp/<project-name>/chats/session-<date>-<sessionId>.json\n *\n * Each file has a messages array with type: 'user' | 'gemini'.\n * User messages have content as array of { text } blocks.\n * Gemini messages have content as a string, with optional toolCalls array.\n */\n\nconst GEMINI_TMP = path.join(os.homedir(), '.gemini', 'tmp');\n\nexport const geminiAdapter: SymbiontAdapter = {\n name: 'gemini',\n displayName: 'Gemini CLI',\n pluginRootEnvVar: 'GEMINI_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'session_id',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n sessionIdEnv: 'GEMINI_SESSION_ID',\n },\n\n findTranscript(sessionId: string): string | null {\n // Gemini session files are named session-<date>-<sessionId-prefix>.json\n // and stored under ~/.gemini/tmp/<project>/chats/\n // The sessionId in the filename is a prefix of the full UUID.\n try {\n for (const project of fs.readdirSync(GEMINI_TMP, { withFileTypes: true })) {\n if (!project.isDirectory()) continue;\n const chatsDir = path.join(GEMINI_TMP, project.name, 'chats');\n try {\n for (const file of fs.readdirSync(chatsDir)) {\n if (!file.endsWith('.json')) continue;\n // Match by sessionId prefix in filename (session-<date>-<prefix>.json)\n if (file.includes(sessionId.slice(0, 8))) {\n // Verify the sessionId inside the file matches\n try {\n const data = JSON.parse(fs.readFileSync(path.join(chatsDir, file), 'utf-8'));\n if (data.sessionId === sessionId) {\n return path.join(chatsDir, file);\n }\n } catch { /* malformed file */ }\n }\n }\n } catch { /* chats dir doesn't exist */ }\n }\n } catch { /* tmp dir doesn't exist */ }\n return null;\n },\n\n parseTurns(content: string): TranscriptTurn[] {\n return parseGeminiJson(content);\n },\n};\n\n/** Gemini message types. */\nconst USER_TYPE = 'user';\nconst GEMINI_TYPE = 'gemini';\n\n/**\n * Parse Gemini's single-JSON transcript format.\n * The file contains { messages: [...] } where each message has type, content, and optional toolCalls.\n */\nfunction parseGeminiJson(content: string): TranscriptTurn[] {\n let data: { messages?: GeminiMessage[] };\n try { data = JSON.parse(content); } catch { return []; }\n\n const messages = data.messages;\n if (!Array.isArray(messages)) return [];\n\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const msg of messages) {\n if (msg.type === USER_TYPE) {\n if (current) turns.push(current);\n\n // User content is an array of { text } blocks\n const promptText = Array.isArray(msg.content)\n ? msg.content.map((b) => b.text ?? '').join('\\n').trim()\n : (typeof msg.content === 'string' ? msg.content : '');\n\n current = {\n prompt: promptText.slice(0, PROMPT_PREVIEW_CHARS),\n toolCount: 0,\n timestamp: msg.timestamp ?? '',\n };\n } else if (msg.type === GEMINI_TYPE && current) {\n // Gemini content is a plain string\n const text = typeof msg.content === 'string' ? msg.content.trim() : '';\n if (text) current.aiResponse = text;\n\n // Count tool calls\n if (Array.isArray(msg.toolCalls)) {\n current.toolCount += msg.toolCalls.length;\n }\n }\n }\n\n if (current) turns.push(current);\n return turns;\n}\n\n/** Shape of a message in Gemini's transcript JSON. */\ninterface GeminiMessage {\n type: string;\n content: string | Array<{ text?: string }>;\n timestamp?: string;\n toolCalls?: Array<{ name: string; args?: unknown }>;\n}\n","import type { SymbiontAdapter, TranscriptTurn } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst TRANSCRIPT_DIR = path.join(os.homedir(), '.windsurf', 'transcripts');\n\n/** Windsurf JSONL entry type field values. */\nconst USER_INPUT_TYPE = 'user_input';\nconst PLANNER_RESPONSE_TYPE = 'planner_response';\nconst CODE_ACTION_TYPE = 'code_action';\n\nexport const windsurfAdapter: SymbiontAdapter = {\n name: 'windsurf',\n displayName: 'Windsurf',\n pluginRootEnvVar: 'WINDSURF_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'trajectory_id',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n },\n\n findTranscript(sessionId: string): string | null {\n // Windsurf stores transcripts directly by trajectory ID\n const candidate = path.join(TRANSCRIPT_DIR, `${sessionId}.jsonl`);\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { return null; }\n },\n\n parseTurns(content: string): TranscriptTurn[] {\n return parseWindsurfJsonl(content);\n },\n};\n\n/**\n * Parse Windsurf's JSONL transcript format.\n *\n * Windsurf entries use a `type` field with values like 'user_input',\n * 'planner_response', 'code_action' — NOT the standard user/assistant roles.\n */\nfunction parseWindsurfJsonl(content: string): TranscriptTurn[] {\n const lines = content.split('\\n').filter(Boolean);\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const line of lines) {\n let entry: Record<string, unknown>;\n try { entry = JSON.parse(line); } catch { continue; }\n\n const type = entry.type as string;\n\n if (type === USER_INPUT_TYPE) {\n if (current) turns.push(current);\n\n // Extract prompt text — Windsurf may store it in different fields\n const promptText = (\n (entry.user_response as string) ??\n (entry.text as string) ??\n (entry.content as string) ??\n ''\n ).trim().slice(0, PROMPT_PREVIEW_CHARS);\n\n current = { prompt: promptText, toolCount: 0, timestamp: '' };\n } else if (type === PLANNER_RESPONSE_TYPE && current) {\n const text = (\n (entry.response as string) ??\n (entry.text as string) ??\n (entry.content as string) ??\n ''\n ).trim();\n if (text) current.aiResponse = text;\n } else if (type === CODE_ACTION_TYPE && current) {\n current.toolCount++;\n }\n }\n\n if (current) turns.push(current);\n return turns;\n}\n","import type { SymbiontAdapter, TranscriptTurn } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\n\n/**\n * VS Code Copilot stores chat transcripts as JSONL delta files at:\n * ~/Library/Application Support/Code/User/workspaceStorage/<hash>/chatSessions/<sessionId>.jsonl\n *\n * Format:\n * Line 0: kind=0 — full initial state { v: { sessionId, requests: [...] } }\n * Lines 1+: kind=1 (key update) or kind=2 (append to array at key path)\n *\n * Each request has:\n * - message.text — the user prompt\n * - response — array of parts accumulated via kind:2 deltas, each part is an\n * array of objects with `kind` field: 'thinking', 'toolInvocationSerialized',\n * 'markdownContent', 'progressTaskSerialized', or plain text { value: \"...\" }\n */\n\n/** Response part kinds that represent tool invocations. */\nconst TOOL_PART_KINDS = new Set(['toolInvocationSerialized', 'toolConfirmation', 'toolMessage']);\n\nexport const vscodeCopilotAdapter: SymbiontAdapter = {\n name: 'vscode-copilot',\n displayName: 'VS Code Copilot',\n pluginRootEnvVar: 'VSCODE_PLUGIN_ROOT',\n hookFields: {\n sessionId: 'sessionId',\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n prompt: 'prompt',\n toolName: 'tool_name',\n toolInput: 'tool_input',\n toolOutput: 'tool_output',\n },\n\n // VS Code doesn't have a predictable transcript directory — hooks provide the path\n findTranscript: () => null,\n\n parseTurns: (content) => parseVsCodeDeltaJsonl(content),\n};\n\n/**\n * Parse VS Code Copilot's delta JSONL transcript format.\n * Replays kind:1 (set) and kind:2 (append) deltas onto the initial state,\n * then extracts turns from the reconstructed requests array.\n */\nfunction parseVsCodeDeltaJsonl(content: string): TranscriptTurn[] {\n const lines = content.split('\\n').filter(Boolean);\n if (lines.length === 0) return [];\n\n // Parse initial state (kind: 0)\n let initial: VsCodeState;\n try {\n const first = JSON.parse(lines[0]);\n if (first.kind !== 0 || !first.v) return [];\n initial = first.v;\n } catch { return []; }\n\n // Replay deltas to reconstruct final state\n const state = JSON.parse(JSON.stringify(initial)) as VsCodeState;\n\n for (let i = 1; i < lines.length; i++) {\n try {\n const delta = JSON.parse(lines[i]);\n if (!delta.k || !Array.isArray(delta.k)) continue;\n applyDelta(state, delta.k, delta.v, delta.kind);\n } catch { /* malformed delta line */ }\n }\n\n // Extract turns from requests\n return extractTurns(state);\n}\n\n/** Apply a single delta to the state. */\nfunction applyDelta(state: Record<string, unknown>, keyPath: string[], value: unknown, kind: number): void {\n let obj: Record<string, unknown> = state;\n for (let j = 0; j < keyPath.length - 1; j++) {\n if (obj[keyPath[j]] === undefined || obj[keyPath[j]] === null) {\n obj[keyPath[j]] = {};\n }\n obj = obj[keyPath[j]] as Record<string, unknown>;\n }\n const lastKey = keyPath[keyPath.length - 1];\n\n if (kind === 1) {\n // Key update — set value\n obj[lastKey] = value;\n } else if (kind === 2) {\n // Append — push to array\n if (!Array.isArray(obj[lastKey])) obj[lastKey] = [];\n (obj[lastKey] as unknown[]).push(value);\n }\n}\n\n/** Extract transcript turns from the reconstructed state. */\nfunction extractTurns(state: VsCodeState): TranscriptTurn[] {\n if (!Array.isArray(state.requests)) return [];\n\n const turns: TranscriptTurn[] = [];\n\n for (const req of state.requests) {\n const promptText = req.message?.text?.trim() ?? '';\n if (!promptText) continue;\n\n const timestamp = req.timestamp ? new Date(req.timestamp).toISOString() : '';\n\n // Count tool invocations and extract AI response text from response parts\n let toolCount = 0;\n let aiResponse = '';\n const responseParts = normalizeResponseParts(req.response);\n\n for (const part of responseParts) {\n if (TOOL_PART_KINDS.has(part.kind ?? '')) {\n toolCount++;\n } else if (part.kind === 'markdownContent' || part.kind === 'markdownVuln') {\n // Markdown content is the AI's text response\n const text = part.content?.value ?? part.value ?? '';\n if (text) aiResponse = text;\n } else if (!part.kind && typeof part.value === 'string' && part.value.trim()) {\n // Plain text parts (no kind field) — accumulate as AI response\n aiResponse += (aiResponse ? '\\n' : '') + part.value.trim();\n }\n }\n\n turns.push({\n prompt: promptText.slice(0, PROMPT_PREVIEW_CHARS),\n toolCount,\n timestamp,\n ...(aiResponse ? { aiResponse: aiResponse.trim() } : {}),\n });\n }\n\n return turns;\n}\n\n/**\n * VS Code response parts can be:\n * - An array of arrays (each delta append pushes an array of part objects)\n * - An indexed object { 0: [...], 1: [...] } from the initial state\n * Flatten to a single array of part objects.\n */\nfunction normalizeResponseParts(response: unknown): VsCodeResponsePart[] {\n if (!response) return [];\n\n // Array of arrays → flatten\n if (Array.isArray(response)) {\n return response.flat().filter((p): p is VsCodeResponsePart => p && typeof p === 'object');\n }\n\n // Indexed object → extract values and flatten\n if (typeof response === 'object') {\n return Object.values(response)\n .flat()\n .filter((p): p is VsCodeResponsePart => p && typeof p === 'object' && !Array.isArray(p));\n }\n\n return [];\n}\n\n// --- Types ---\n\ninterface VsCodeState {\n sessionId?: string;\n requests?: VsCodeRequest[];\n [key: string]: unknown;\n}\n\ninterface VsCodeRequest {\n requestId?: string;\n timestamp?: number;\n message?: { text?: string };\n response?: unknown;\n [key: string]: unknown;\n}\n\ninterface VsCodeResponsePart {\n kind?: string;\n value?: string;\n content?: { value?: string };\n [key: string]: unknown;\n}\n","import type { SymbiontAdapter, TranscriptTurn } from './adapter.js';\nimport { claudeCodeAdapter } from './claude-code.js';\nimport { cursorAdapter } from './cursor.js';\nimport { codexAdapter } from './codex.js';\nimport { geminiAdapter } from './gemini.js';\nimport { windsurfAdapter } from './windsurf.js';\nimport { vscodeCopilotAdapter } from './vscode-copilot.js';\nimport fs from 'node:fs';\n\n/**\n * All known symbiont adapters, ordered by priority.\n * When searching for a transcript, adapters are tried in order.\n * Add new adapters here as symbiont support grows.\n */\nconst ALL_ADAPTERS: SymbiontAdapter[] = [\n claudeCodeAdapter,\n cursorAdapter,\n codexAdapter,\n geminiAdapter,\n windsurfAdapter,\n vscodeCopilotAdapter,\n];\n\nexport class SymbiontRegistry {\n private adapters: SymbiontAdapter[];\n\n constructor(additionalAdapters: SymbiontAdapter[] = []) {\n this.adapters = [...ALL_ADAPTERS, ...additionalAdapters];\n }\n\n /**\n * Find and parse transcript turns for a session.\n * Tries each adapter in priority order. Returns the first match.\n */\n getTranscriptTurns(sessionId: string): { turns: TranscriptTurn[]; source: string } | null {\n for (const adapter of this.adapters) {\n const filePath = adapter.findTranscript(sessionId);\n if (!filePath) continue;\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: adapter.name };\n }\n } catch {\n // Adapter found a path but read/parse failed — try next\n }\n }\n return null;\n }\n\n /** List all registered adapter names */\n get adapterNames(): string[] {\n return this.adapters.map((a) => a.name);\n }\n\n /** Get a specific adapter by name */\n getAdapter(name: string): SymbiontAdapter | undefined {\n return this.adapters.find((a) => a.name === name);\n }\n\n /** Detect which symbiont is currently active based on environment variables */\n detectActiveAgent(): SymbiontAdapter | undefined {\n for (const adapter of this.adapters) {\n if (process.env[adapter.pluginRootEnvVar]) {\n return adapter;\n }\n }\n return undefined;\n }\n\n /**\n * Parse turns from a known transcript file path (provided by hook).\n * Tries each adapter's parseTurns until one produces results.\n * Skips directory scanning entirely — the path is already known.\n */\n parseTurnsFromPath(filePath: string): { turns: TranscriptTurn[]; source: string } | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n // Try the active agent's parser first, then fall back to others\n const active = this.detectActiveAgent();\n const orderedAdapters = active\n ? [active, ...this.adapters.filter((a) => a !== active)]\n : this.adapters;\n\n for (const adapter of orderedAdapters) {\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: `${adapter.name}:direct` };\n }\n }\n } catch {\n // File unreadable — caller will fall back to directory scanning\n }\n return null;\n }\n\n /**\n * Resolve the plugin root directory from the active agent's environment variable.\n * Returns undefined if no agent env var is set (e.g., running from CLI directly).\n */\n resolvePluginRoot(): string | undefined {\n for (const adapter of this.adapters) {\n const value = process.env[adapter.pluginRootEnvVar];\n if (value) return value;\n }\n return undefined;\n }\n}\n","import { SymbiontRegistry } from '../symbionts/registry.js';\nimport type { SymbiontAdapter } from '../symbionts/adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\n\n// Re-export TranscriptTurn from its canonical home in symbionts/adapter.ts\nexport type { TranscriptTurn } from '../symbionts/adapter.js';\nimport type { TranscriptTurn } from '../symbionts/adapter.js';\n\ninterface TranscriptConfig {\n /** Additional symbiont adapters to register (useful for testing or custom symbionts) */\n additionalAdapters?: SymbiontAdapter[];\n}\n\nexport class TranscriptMiner {\n private registry: SymbiontRegistry;\n\n constructor(config?: TranscriptConfig) {\n this.registry = new SymbiontRegistry(config?.additionalAdapters);\n }\n\n /**\n * Extract all conversation turns for a session.\n * Convenience wrapper — delegates to getAllTurnsWithSource.\n */\n getAllTurns(sessionId: string): TranscriptTurn[] {\n return this.getAllTurnsWithSource(sessionId).turns;\n }\n\n /**\n * Extract turns using the hook-provided transcript path first (fast, no scanning),\n * then fall back to adapter registry scanning if the path isn't provided.\n */\n getAllTurnsWithSource(sessionId: string, transcriptPath?: string): { turns: TranscriptTurn[]; source: string } {\n // Primary: use the path provided by the hook (no directory scanning needed)\n if (transcriptPath) {\n const result = this.registry.parseTurnsFromPath(transcriptPath);\n if (result) return result;\n }\n\n // Fallback: scan known agent directories\n const result = this.registry.getTranscriptTurns(sessionId);\n if (result) return result;\n return { turns: [], source: 'none' };\n }\n}\n\n/**\n * Build turns from buffer events — the fallback when no agent transcript is available.\n * Buffer events come from hooks (user_prompt, tool_use) and lack AI responses.\n * Turns will have prompts and tool counts but no aiResponse.\n */\nexport function extractTurnsFromBuffer(events: Array<Record<string, unknown>>): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const event of events) {\n const type = event.type as string;\n if (type === 'user_prompt') {\n if (current) turns.push(current);\n current = {\n prompt: String(event.prompt ?? '').slice(0, PROMPT_PREVIEW_CHARS),\n toolCount: 0,\n timestamp: String(event.timestamp ?? new Date().toISOString()),\n };\n } else if (type === 'tool_use') {\n if (current) current.toolCount++;\n }\n }\n if (current) turns.push(current);\n return turns;\n}\n","/**\n * Event-driven plan capture module.\n *\n * Provides pure detection and storage functions for capturing plan files\n * written to watched directories. Called by the daemon's event handler\n * (Task 6) when a tool event targets a plan directory.\n *\n * All functions are stateless — no file I/O, no event handling.\n */\n\nimport { createHash } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { CONTENT_HASH_ALGORITHM } from '@myco/constants.js';\nimport { upsertPlan } from '@myco/db/queries/plans.js';\nimport type { PlanRow } from '@myco/db/queries/plans.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Tool names that constitute a file write operation. */\nconst FILE_WRITE_TOOLS = new Set(['Write', 'Edit', 'Create']);\n\n/** Regex matching a top-level markdown heading (# Title). */\nconst HEADING_REGEX = /^#\\s+(.+)$/m;\n\n/** Number of hex chars to use from the MD5 hash for plan IDs. */\nconst PLAN_ID_HASH_LENGTH = 16;\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a file path falls inside any watched plan directory.\n *\n * Both the file path and watch directories are resolved against projectRoot\n * before comparison, so relative and absolute paths both work correctly.\n */\nexport function isInPlanDirectory(\n filePath: string,\n watchDirs: string[],\n projectRoot: string,\n): boolean {\n const abs = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath);\n return watchDirs.some((dir) => {\n // Expand ~ to home directory (manifests use ~/... for global plan dirs)\n const expanded = dir.startsWith('~/') ? path.join(os.homedir(), dir.slice(2)) : dir;\n const absDir = path.isAbsolute(expanded) ? expanded : path.resolve(projectRoot, expanded);\n // Ensure we match a directory boundary, not a prefix of a sibling dir name.\n // e.g. absDir = /foo/plans must NOT match /foo/plans-extra\n const prefix = absDir.endsWith(path.sep) ? absDir : absDir + path.sep;\n return abs === absDir || abs.startsWith(prefix);\n });\n}\n\n/** Configuration for plan directory matching. */\nexport interface PlanWatchConfig {\n watchDirs: string[];\n projectRoot: string;\n extensions?: string[];\n}\n\n/**\n * Check if a tool event is a file write to a plan directory.\n *\n * Returns the file path if it matches, null otherwise. Only Write, Edit,\n * and Create tools are considered. Extension filtering enforces the\n * `artifact_extensions` config setting (e.g. ['.md']).\n */\nexport function isPlanWriteEvent(\n toolName: string,\n toolInput: Record<string, unknown> | undefined,\n config: PlanWatchConfig,\n): string | null {\n if (!FILE_WRITE_TOOLS.has(toolName)) return null;\n const filePath = toolInput?.file_path ?? toolInput?.path;\n if (typeof filePath !== 'string') return null;\n if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;\n if (config.extensions?.length) {\n const ext = path.extname(filePath).toLowerCase();\n if (!config.extensions.includes(ext)) return null;\n }\n return filePath;\n}\n\n/**\n * Extract a plan title from markdown content.\n *\n * Looks for the first top-level heading (# Title). If none is found,\n * falls back to the provided filename. Returns null if neither is available.\n */\nexport function parsePlanTitle(content: string, filename?: string): string | null {\n const match = HEADING_REGEX.exec(content);\n if (match) return match[1].trim();\n return filename ?? null;\n}\n\n/** Input to capturePlan. */\nexport interface CapturePlanInput {\n /** Absolute or relative path to the source plan file. */\n sourcePath: string;\n /** Full markdown content of the plan file. */\n content: string;\n /** Session ID that triggered the write event. */\n sessionId: string;\n /** Optional prompt batch ID at the time of capture. */\n promptBatchId?: number | null;\n}\n\n/**\n * Store a plan in the database.\n *\n * The plan ID is derived deterministically from sourcePath (MD5 hash,\n * first 16 chars), so repeated writes to the same file upsert rather than\n * insert duplicate rows.\n *\n * The content hash (SHA256) is used by upsertPlan to decide whether to\n * reset the embedded flag — if the content is unchanged the flag is\n * preserved.\n */\nexport function capturePlan(input: CapturePlanInput): PlanRow {\n const now = Math.floor(Date.now() / 1000);\n const contentHash = createHash(CONTENT_HASH_ALGORITHM).update(input.content).digest('hex');\n const id = createHash('md5').update(input.sourcePath).digest('hex').slice(0, PLAN_ID_HASH_LENGTH);\n const title = parsePlanTitle(input.content, path.basename(input.sourcePath));\n\n return upsertPlan({\n id,\n title,\n content: input.content,\n source_path: input.sourcePath,\n session_id: input.sessionId,\n prompt_batch_id: input.promptBatchId ?? null,\n content_hash: contentHash,\n status: 'active',\n created_at: now,\n updated_at: now,\n });\n}\n","/**\n * Plan CRUD query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport { DEFAULT_MACHINE_ID } from '@myco/constants.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of plans returned by listPlans when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Default plan status for new plans. */\nconst DEFAULT_STATUS = 'active';\n\n/** Default processed flag for new plans. */\nconst DEFAULT_PROCESSED = 0;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting/upserting a plan. */\nexport interface PlanInsert {\n id: string;\n created_at: number;\n status?: string;\n author?: string | null;\n title?: string | null;\n content?: string | null;\n source_path?: string | null;\n tags?: string | null;\n session_id?: string | null;\n prompt_batch_id?: number | null;\n content_hash?: string | null;\n processed?: number;\n updated_at?: number | null;\n machine_id?: string;\n}\n\n/** Row shape returned from plan queries. */\nexport interface PlanRow {\n id: string;\n status: string;\n author: string | null;\n title: string | null;\n content: string | null;\n source_path: string | null;\n tags: string | null;\n session_id: string | null;\n prompt_batch_id: number | null;\n content_hash: string | null;\n processed: number;\n embedded: number;\n created_at: number;\n updated_at: number | null;\n machine_id: string;\n synced_at: number | null;\n}\n\n/** Filter options for `listPlans`. */\nexport interface ListPlansOptions {\n status?: string;\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst PLAN_COLUMNS = [\n 'id',\n 'status',\n 'author',\n 'title',\n 'content',\n 'source_path',\n 'tags',\n 'session_id',\n 'prompt_batch_id',\n 'content_hash',\n 'processed',\n 'embedded',\n 'created_at',\n 'updated_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = PLAN_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed PlanRow. */\nfunction toPlanRow(row: Record<string, unknown>): PlanRow {\n return {\n id: row.id as string,\n status: row.status as string,\n author: (row.author as string) ?? null,\n title: (row.title as string) ?? null,\n content: (row.content as string) ?? null,\n source_path: (row.source_path as string) ?? null,\n tags: (row.tags as string) ?? null,\n session_id: (row.session_id as string) ?? null,\n prompt_batch_id: (row.prompt_batch_id as number) ?? null,\n content_hash: (row.content_hash as string) ?? null,\n processed: row.processed as number,\n embedded: (row.embedded as number) ?? 0,\n created_at: row.created_at as number,\n updated_at: (row.updated_at as number) ?? null,\n machine_id: (row.machine_id as string) ?? DEFAULT_MACHINE_ID,\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a plan or update it if the id already exists.\n *\n * On conflict the row is updated with the values from `data`.\n */\nexport function upsertPlan(data: PlanInsert): PlanRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO plans (\n id, status, author, title, content,\n source_path, tags, session_id, prompt_batch_id, content_hash,\n processed, created_at, updated_at, machine_id\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?\n )\n ON CONFLICT (id) DO UPDATE SET\n status = EXCLUDED.status,\n author = EXCLUDED.author,\n title = EXCLUDED.title,\n content = EXCLUDED.content,\n source_path = EXCLUDED.source_path,\n tags = EXCLUDED.tags,\n session_id = EXCLUDED.session_id,\n prompt_batch_id = EXCLUDED.prompt_batch_id,\n content_hash = EXCLUDED.content_hash,\n processed = EXCLUDED.processed,\n updated_at = EXCLUDED.updated_at,\n embedded = CASE\n WHEN EXCLUDED.content_hash != plans.content_hash THEN 0\n ELSE plans.embedded\n END`,\n ).run(\n data.id,\n data.status ?? DEFAULT_STATUS,\n data.author ?? null,\n data.title ?? null,\n data.content ?? null,\n data.source_path ?? null,\n data.tags ?? null,\n data.session_id ?? null,\n data.prompt_batch_id ?? null,\n data.content_hash ?? null,\n data.processed ?? DEFAULT_PROCESSED,\n data.created_at,\n data.updated_at ?? null,\n data.machine_id ?? DEFAULT_MACHINE_ID,\n );\n\n const row = toPlanRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM plans WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n\n syncRow('plans', row);\n\n return row;\n}\n\n/**\n * Retrieve a single plan by id.\n *\n * @returns the plan row, or null if not found.\n */\nexport function getPlan(id: string): PlanRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM plans WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toPlanRow(row);\n}\n\n/**\n * List plans with optional filters, ordered by created_at DESC.\n */\nexport function listPlans(\n options: ListPlansOptions = {},\n): PlanRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n\n params.push(limit);\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM plans\n ${where}\n ORDER BY created_at DESC\n LIMIT ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toPlanRow);\n}\n\n/**\n * List all plans associated with a specific session, ordered by created_at DESC.\n */\nexport function listPlansBySession(sessionId: string): PlanRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM plans\n WHERE session_id = ?\n ORDER BY created_at DESC`,\n ).all(sessionId) as Record<string, unknown>[];\n\n return rows.map(toPlanRow);\n}\n","import { loadConfig, updateConfig } from '../../config/loader.js';\nimport { MycoConfigSchema, type MycoConfig } from '../../config/schema.js';\nimport type { RouteResponse } from '../router.js';\n\n/**\n * Section-level deep merge: for each top-level section in `incoming`, merge it\n * into `current` — incoming fields overwrite, but fields in `current` that are\n * absent from `incoming` survive. This prevents a save that only touches\n * `context.digest_tier` from wiping `agent.tasks`.\n */\nfunction mergeConfigSections(current: MycoConfig, incoming: MycoConfig): MycoConfig {\n return {\n ...current,\n daemon: { ...current.daemon, ...incoming.daemon },\n embedding: { ...current.embedding, ...incoming.embedding },\n capture: { ...current.capture, ...incoming.capture },\n agent: { ...current.agent, ...incoming.agent },\n context: { ...current.context, ...incoming.context },\n backup: { ...current.backup, ...incoming.backup },\n team: { ...current.team, ...incoming.team },\n };\n}\n\nexport async function handleGetConfig(vaultDir: string): Promise<RouteResponse> {\n const config = loadConfig(vaultDir);\n return { body: config };\n}\n\nexport async function handlePutConfig(vaultDir: string, body: unknown): Promise<RouteResponse> {\n const result = MycoConfigSchema.safeParse(body);\n if (!result.success) {\n return {\n status: 400,\n body: { error: 'validation_failed', issues: result.error.issues },\n };\n }\n const updated = updateConfig(vaultDir, (current) => mergeConfigSections(current, result.data));\n return { body: updated };\n}\n","/**\n * Log entry CRUD query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport { LEVEL_ORDER, type LogLevel } from '@myco/daemon/logger.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of log entries per page for search results. */\nconst DEFAULT_PAGE_SIZE = 100;\n\n/** Default number of entries returned by getLogsSince. */\nconst DEFAULT_STREAM_LIMIT = 200;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required when inserting a log entry. */\nexport interface LogEntryInsert {\n timestamp: string;\n level: string;\n kind: string;\n component: string;\n message: string;\n data: string | null;\n session_id: string | null;\n}\n\n/** Row shape returned from log_entries queries (all columns). */\nexport interface LogEntryRow {\n id: number;\n timestamp: string;\n level: string;\n kind: string;\n component: string;\n message: string;\n data: string | null;\n session_id: string | null;\n}\n\n/** Filter options for `searchLogs`. */\nexport interface LogSearchParams {\n q?: string;\n level?: string;\n component?: string;\n kind?: string;\n session_id?: string;\n from?: string;\n to?: string;\n page?: number;\n page_size?: number;\n}\n\n/** Paginated result from `searchLogs`. */\nexport interface LogSearchResult {\n entries: LogEntryRow[];\n total: number;\n page: number;\n page_size: number;\n}\n\n/** Result from `getLogsSince` for streaming/tailing. */\nexport interface LogStreamResult {\n entries: LogEntryRow[];\n cursor: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a raw SQLite result row into a typed LogEntryRow. */\nfunction toLogEntryRow(row: Record<string, unknown>): LogEntryRow {\n return {\n id: row.id as number,\n timestamp: row.timestamp as string,\n level: row.level as string,\n kind: row.kind as string,\n component: row.component as string,\n message: row.message as string,\n data: (row.data as string) ?? null,\n session_id: (row.session_id as string) ?? null,\n };\n}\n\n/**\n * Return all level names whose numeric order is >= the given minimum level.\n *\n * Example: levelsAtOrAbove('warn') → ['warn', 'error']\n */\nfunction levelsAtOrAbove(minLevel: string): string[] {\n const minOrder = LEVEL_ORDER[minLevel as LogLevel] ?? 0;\n return (Object.keys(LEVEL_ORDER) as LogLevel[]).filter(\n (l) => LEVEL_ORDER[l] >= minOrder,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a log entry into `log_entries`.\n *\n * FTS sync is handled automatically by the `log_entries_ai` trigger.\n * Returns the new row's integer id.\n */\nexport function insertLogEntry(entry: LogEntryInsert): number {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO log_entries (timestamp, level, kind, component, message, data, session_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n entry.timestamp,\n entry.level,\n entry.kind,\n entry.component,\n entry.message,\n entry.data,\n entry.session_id,\n );\n\n return info.lastInsertRowid as number;\n}\n\n/**\n * Search log entries with optional filters and pagination.\n *\n * Supports:\n * - Full-text search via FTS5 (q param)\n * - Level filter (returns entries at or above the specified level)\n * - Component filter (comma-separated list)\n * - Kind filter\n * - Session ID filter\n * - Time range (from / to ISO timestamps)\n * - Pagination (page / page_size, 1-based page index)\n *\n * Results are ordered by timestamp DESC, id DESC.\n */\nexport function searchLogs(params: LogSearchParams): LogSearchResult {\n const db = getDatabase();\n\n const page = params.page ?? 1;\n const pageSize = params.page_size ?? DEFAULT_PAGE_SIZE;\n const offset = (page - 1) * pageSize;\n\n const conditions: string[] = [];\n const queryParams: unknown[] = [];\n\n // Full-text search via FTS5 sub-select\n if (params.q !== undefined && params.q.length > 0) {\n conditions.push(`le.id IN (SELECT rowid FROM log_entries_fts WHERE log_entries_fts MATCH ?)`);\n queryParams.push(params.q);\n }\n\n // Level filter — include all levels at or above the minimum\n if (params.level !== undefined && params.level.length > 0) {\n const levels = levelsAtOrAbove(params.level);\n if (levels.length > 0) {\n conditions.push(`le.level IN (SELECT value FROM json_each(?))`);\n queryParams.push(JSON.stringify(levels));\n }\n }\n\n // Component filter — comma-separated list\n if (params.component !== undefined && params.component.length > 0) {\n const components = params.component.split(',').map((c) => c.trim()).filter(Boolean);\n if (components.length > 0) {\n conditions.push(`le.component IN (SELECT value FROM json_each(?))`);\n queryParams.push(JSON.stringify(components));\n }\n }\n\n // Kind filter\n if (params.kind !== undefined && params.kind.length > 0) {\n conditions.push(`le.kind = ?`);\n queryParams.push(params.kind);\n }\n\n // Session ID filter\n if (params.session_id !== undefined && params.session_id.length > 0) {\n conditions.push(`le.session_id = ?`);\n queryParams.push(params.session_id);\n }\n\n // Time range\n if (params.from !== undefined && params.from.length > 0) {\n conditions.push(`le.timestamp >= ?`);\n queryParams.push(params.from);\n }\n\n if (params.to !== undefined && params.to.length > 0) {\n conditions.push(`le.timestamp <= ?`);\n queryParams.push(params.to);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n const countRow = db.prepare(\n `SELECT COUNT(*) as count FROM log_entries le ${where}`,\n ).get(...queryParams) as { count: number };\n\n const rows = db.prepare(\n `SELECT le.id, le.timestamp, le.level, le.kind, le.component, le.message, le.data, le.session_id\n FROM log_entries le\n ${where}\n ORDER BY le.timestamp DESC, le.id DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...queryParams, pageSize, offset) as Record<string, unknown>[];\n\n return {\n entries: rows.map(toLogEntryRow),\n total: countRow.count,\n page,\n page_size: pageSize,\n };\n}\n\n/**\n * Return log entries with id > sinceId in ascending order, for streaming/tailing.\n *\n * Returns entries and a cursor (the id of the last entry returned,\n * or sinceId if no entries were found).\n */\nexport function getLogsSince(sinceId: number, limit?: number): LogStreamResult {\n const db = getDatabase();\n const effectiveLimit = limit ?? DEFAULT_STREAM_LIMIT;\n\n const rows = db.prepare(\n `SELECT id, timestamp, level, kind, component, message, data, session_id\n FROM log_entries\n WHERE id > ?\n ORDER BY id ASC\n LIMIT ?`,\n ).all(sinceId, effectiveLimit) as Record<string, unknown>[];\n\n const entries = rows.map(toLogEntryRow);\n const cursor = entries.length > 0 ? entries[entries.length - 1].id : sinceId;\n\n return { entries, cursor };\n}\n\n/**\n * Retrieve a single log entry by id.\n *\n * @returns the entry row, or null if not found.\n */\nexport function getLogEntry(id: number): LogEntryRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT id, timestamp, level, kind, component, message, data, session_id\n FROM log_entries\n WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toLogEntryRow(row);\n}\n\n/**\n * Delete log entries older than `beforeTimestamp`.\n *\n * FTS cleanup is handled automatically by the `log_entries_ad` trigger.\n *\n * @returns the number of rows deleted from log_entries.\n */\nexport function deleteOldLogs(beforeTimestamp: string): number {\n const db = getDatabase();\n\n const info = db.prepare(\n `DELETE FROM log_entries WHERE timestamp < ?`,\n ).run(beforeTimestamp);\n\n return info.changes;\n}\n\n/**\n * Return the maximum timestamp in the log_entries table.\n *\n * Used for reconciliation to detect gaps between file logs and DB logs.\n * Returns null if the table is empty.\n */\nexport function getMaxTimestamp(): string | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT MAX(timestamp) as max_ts FROM log_entries`,\n ).get() as { max_ts: string | null };\n\n return row.max_ts;\n}\n","/**\n * Log explorer API handlers — search, stream (polling), and detail.\n */\n\nimport { searchLogs, getLogsSince, getLogEntry } from '@myco/db/queries/logs.js';\nimport type { LogEntryRow } from '@myco/db/queries/logs.js';\nimport { getSession } from '@myco/db/queries/sessions.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Search (historical mode)\n// ---------------------------------------------------------------------------\n\nexport async function handleLogSearch(req: RouteRequest): Promise<RouteResponse> {\n const { q, level, component, kind, session_id, from, to, page, page_size } = req.query;\n\n const result = searchLogs({\n q: q || undefined,\n level: level || undefined,\n component: component || undefined,\n kind: kind || undefined,\n session_id: session_id || undefined,\n from: from || undefined,\n to: to || undefined,\n page: page ? parseInt(page, 10) : undefined,\n page_size: page_size ? parseInt(page_size, 10) : undefined,\n });\n\n return {\n body: {\n entries: result.entries.map(formatEntry),\n total: result.total,\n page: result.page,\n page_size: result.page_size,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Stream (real-time polling mode)\n// ---------------------------------------------------------------------------\n\nexport async function handleLogStream(req: RouteRequest): Promise<RouteResponse> {\n const sinceStr = req.query.since;\n const limitStr = req.query.limit;\n const sinceId = sinceStr ? parseInt(sinceStr, 10) : 0;\n const limit = limitStr ? parseInt(limitStr, 10) : undefined;\n\n const result = getLogsSince(sinceId, limit);\n\n return {\n body: {\n entries: result.entries.map(formatEntry),\n cursor: result.cursor,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Detail (single entry with resolved references)\n// ---------------------------------------------------------------------------\n\nexport async function handleLogDetail(req: RouteRequest): Promise<RouteResponse> {\n const id = parseInt(req.params.id, 10);\n if (isNaN(id)) return { status: 400, body: { error: 'Invalid log entry ID' } };\n\n const entry = getLogEntry(id);\n if (!entry) return { status: 404, body: { error: 'Log entry not found' } };\n\n const parsed = entry.data ? JSON.parse(entry.data) : {};\n const resolved: Record<string, unknown> = {};\n\n // Resolve session_id to session title\n if (entry.session_id) {\n try {\n const session = getSession(entry.session_id);\n if (session) {\n resolved.session_title = (session as { title?: string }).title ?? null;\n }\n } catch { /* session may not exist */ }\n }\n\n return {\n body: {\n ...entry,\n data: parsed,\n resolved,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formatEntry(entry: LogEntryRow) {\n return {\n ...entry,\n data: entry.data ? JSON.parse(entry.data) : null,\n };\n}\n","import { spawn } from 'node:child_process';\nimport { z } from 'zod';\nimport { resolveCliEntryPath } from '../../hooks/client.js';\nimport type { RouteResponse } from '../router.js';\nimport type { ProgressTracker } from './progress.js';\nimport { RESTART_RESPONSE_FLUSH_MS } from '../../constants.js';\n\nconst RestartBodySchema = z.object({\n force: z.boolean().optional(),\n}).optional();\n/** Delay before the child process starts — allows the parent to fully release the port. */\nconst RESTART_CHILD_DELAY_SECONDS = 3;\n\nexport interface RestartHandlerDeps {\n vaultDir: string;\n progressTracker: ProgressTracker;\n}\n\nexport async function handleRestart(\n deps: RestartHandlerDeps,\n body: unknown,\n): Promise<RouteResponse> {\n const parsed = RestartBodySchema.safeParse(body);\n const force = parsed.success ? parsed.data?.force : false;\n\n // Check for active operations unless force is set\n if (!force && deps.progressTracker.hasActiveOperations()) {\n return {\n status: 409,\n body: { status: 'busy', message: 'Active operations in progress. Use force=true to override.' },\n };\n }\n\n // Schedule: respond → wait for flush → SIGTERM self → child starts after parent exits.\n // The child waits RESTART_CHILD_DELAY_SECONDS before starting to ensure the parent\n // has fully released the port and cleaned up daemon.json.\n const { execPath, cliEntry } = resolveCliEntryPath();\n const shellCmd = `sleep ${RESTART_CHILD_DELAY_SECONDS} && ${execPath} ${cliEntry} daemon --vault ${deps.vaultDir}`;\n\n const child = spawn('/bin/sh', ['-c', shellCmd], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n\n // Schedule self-termination after response flushes\n setTimeout(() => {\n process.kill(process.pid, 'SIGTERM');\n }, RESTART_RESPONSE_FLUSH_MS);\n\n return { body: { status: 'restarting' } };\n}\n","/**\n * Update checker — fetches the npm registry for @goondocks/myco, compares\n * versions against the current installation, caches results, and supports\n * stable/beta release channels.\n *\n * - Stable channel: compare against dist-tags.latest only.\n * - Beta channel: compare against max(dist-tags.latest, dist-tags.beta).\n * Beta users can always reach stable (no-downgrade rule).\n * - Dev mode exemption: if MYCO_CMD is set the binary is a dev symlink;\n * update checks are skipped entirely.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport semver from 'semver';\n\nimport {\n NPM_REGISTRY_URL,\n MYCO_GLOBAL_DIR,\n UPDATE_CHECK_CACHE_PATH,\n UPDATE_CONFIG_PATH,\n UPDATE_ERROR_PATH,\n UPDATE_CHECK_INTERVAL_HOURS,\n MS_PER_HOUR,\n DEFAULT_RELEASE_CHANNEL,\n RELEASE_CHANNELS,\n type ReleaseChannel,\n} from '../constants/update.js';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Persisted update configuration stored in ~/.myco/update.yaml */\nexport interface UpdateConfig {\n channel: ReleaseChannel;\n check_interval_hours: number;\n}\n\n/** Cached result of a registry check stored in ~/.myco/last-update-check.json */\nexport interface CachedCheck {\n checked_at: string;\n current_version: string;\n latest_stable: string;\n latest_beta: string | null;\n channel: ReleaseChannel;\n}\n\n/** Result returned to callers of checkForUpdate / statusFromCache */\nexport interface CheckResult {\n update_available: boolean;\n running_version: string;\n latest_version: string;\n latest_stable: string;\n latest_beta: string | null;\n channel: ReleaseChannel;\n check_interval_hours: number;\n last_check: string;\n error: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Fetch timeout for registry requests. */\nconst REGISTRY_FETCH_TIMEOUT_MS = 10_000;\n\n// ---------------------------------------------------------------------------\n// Dev-mode exemption\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true when MYCO_CMD is set — indicates a dev symlink is in use and\n * update checks should be skipped.\n */\nexport function isUpdateExempt(): boolean {\n return Boolean(process.env.MYCO_CMD);\n}\n\n// ---------------------------------------------------------------------------\n// Config helpers\n// ---------------------------------------------------------------------------\n\n/** Default config returned when no update.yaml exists. */\nfunction defaultUpdateConfig(): UpdateConfig {\n return {\n channel: DEFAULT_RELEASE_CHANNEL,\n check_interval_hours: UPDATE_CHECK_INTERVAL_HOURS,\n };\n}\n\n/**\n * Reads ~/.myco/update.yaml. Returns defaults when the file is missing or\n * unparseable.\n */\nexport function readUpdateConfig(): UpdateConfig {\n try {\n const raw = fs.readFileSync(UPDATE_CONFIG_PATH, 'utf-8');\n const parsed = YAML.parse(raw) as Partial<UpdateConfig>;\n\n const channel = RELEASE_CHANNELS.includes(parsed?.channel as ReleaseChannel)\n ? (parsed.channel as ReleaseChannel)\n : DEFAULT_RELEASE_CHANNEL;\n\n const check_interval_hours =\n typeof parsed?.check_interval_hours === 'number' && parsed.check_interval_hours > 0\n ? parsed.check_interval_hours\n : UPDATE_CHECK_INTERVAL_HOURS;\n\n return { channel, check_interval_hours };\n } catch {\n return defaultUpdateConfig();\n }\n}\n\n/**\n * Writes UpdateConfig to ~/.myco/update.yaml. Creates ~/.myco/ if needed.\n */\nexport function writeUpdateConfig(config: UpdateConfig): void {\n fs.mkdirSync(MYCO_GLOBAL_DIR, { recursive: true });\n fs.writeFileSync(UPDATE_CONFIG_PATH, YAML.stringify(config), 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Cache helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Reads ~/.myco/last-update-check.json. Returns null when the file is missing\n * or unparseable.\n */\nexport function readCachedCheck(): CachedCheck | null {\n try {\n const raw = fs.readFileSync(UPDATE_CHECK_CACHE_PATH, 'utf-8');\n return JSON.parse(raw) as CachedCheck;\n } catch {\n return null;\n }\n}\n\n/**\n * Deletes the cache file. Used when switching channels so the stale cached\n * result is not returned.\n */\nexport function clearCachedCheck(): void {\n try {\n fs.unlinkSync(UPDATE_CHECK_CACHE_PATH);\n } catch {\n // File not present — that's fine.\n }\n}\n\n/**\n * Returns true when the cache is null (never checked) or older than\n * intervalHours.\n */\nexport function isCacheStale(cache: CachedCheck | null, intervalHours: number): boolean {\n if (cache === null) return true;\n\n const checkedAt = new Date(cache.checked_at).getTime();\n if (isNaN(checkedAt)) return true;\n\n const ageMs = Date.now() - checkedAt;\n return ageMs > intervalHours * MS_PER_HOUR;\n}\n\n// ---------------------------------------------------------------------------\n// Error file\n// ---------------------------------------------------------------------------\n\n/**\n * Reads ~/.myco/update-error.json. Returns the error string when present, null\n * otherwise.\n */\nexport function readUpdateError(): string | null {\n try {\n const raw = fs.readFileSync(UPDATE_ERROR_PATH, 'utf-8');\n const parsed = JSON.parse(raw) as { error?: string };\n return parsed?.error ?? null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry types\n// ---------------------------------------------------------------------------\n\ninterface NpmDistTags {\n latest: string;\n beta?: string;\n [tag: string]: string | undefined;\n}\n\ninterface NpmRegistryResponse {\n 'dist-tags': NpmDistTags;\n}\n\n// ---------------------------------------------------------------------------\n// Channel comparison logic\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the target version to compare against based on channel.\n * - Stable: dist-tags.latest\n * - Beta: max(dist-tags.latest, dist-tags.beta) — no-downgrade rule\n */\nfunction resolveTargetVersion(distTags: NpmDistTags, channel: ReleaseChannel): string {\n const stable = distTags.latest;\n const beta = distTags.beta ?? null;\n\n if (channel === 'stable' || beta === null) {\n return stable;\n }\n\n // Beta channel: pick whichever is higher (stable can exceed beta tag)\n const higher = semver.gt(beta, stable) ? beta : stable;\n return higher;\n}\n\n// ---------------------------------------------------------------------------\n// CheckResult builder\n// ---------------------------------------------------------------------------\n\nfunction buildCheckResult(\n currentVersion: string,\n cache: CachedCheck,\n config: UpdateConfig,\n error: string | null,\n): CheckResult {\n const targetVersion = resolveTargetVersion(\n { latest: cache.latest_stable, beta: cache.latest_beta ?? undefined },\n cache.channel,\n );\n\n const update_available =\n semver.valid(currentVersion) !== null &&\n semver.valid(targetVersion) !== null &&\n semver.gt(targetVersion, currentVersion);\n\n return {\n update_available,\n running_version: currentVersion,\n latest_version: targetVersion,\n latest_stable: cache.latest_stable,\n latest_beta: cache.latest_beta,\n channel: cache.channel,\n check_interval_hours: config.check_interval_hours,\n last_check: cache.checked_at,\n error,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Primary exports\n// ---------------------------------------------------------------------------\n\n/**\n * Fetches the npm registry, compares versions, and writes the result to cache.\n *\n * On network failure, returns the last cached result (with an error field) if\n * one exists. If no cache exists and the fetch fails, the error field is set\n * and update_available is false.\n */\nexport async function checkForUpdate(currentVersion: string): Promise<CheckResult> {\n const config = readUpdateConfig();\n const existingCache = readCachedCheck();\n\n let distTags: NpmDistTags;\n let fetchError: string | null = null;\n\n try {\n const response = await fetch(NPM_REGISTRY_URL, {\n signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`Registry responded with ${response.status}`);\n }\n\n const data = (await response.json()) as NpmRegistryResponse;\n distTags = data['dist-tags'];\n } catch (err) {\n fetchError = err instanceof Error ? err.message : String(err);\n\n // Fall back to stale cache on network error\n if (existingCache !== null) {\n return buildCheckResult(currentVersion, existingCache, config, fetchError);\n }\n\n // No cache at all — return a no-update result with the error\n return {\n update_available: false,\n running_version: currentVersion,\n latest_version: currentVersion,\n latest_stable: currentVersion,\n latest_beta: null,\n channel: config.channel,\n check_interval_hours: config.check_interval_hours,\n last_check: new Date().toISOString(),\n error: fetchError,\n };\n }\n\n const latestStable = distTags.latest;\n const latestBeta = distTags.beta ?? null;\n const targetVersion = resolveTargetVersion(distTags, config.channel);\n\n // Write fresh cache\n const freshCache: CachedCheck = {\n checked_at: new Date().toISOString(),\n current_version: currentVersion,\n latest_stable: latestStable,\n latest_beta: latestBeta,\n channel: config.channel,\n };\n\n try {\n fs.mkdirSync(path.dirname(UPDATE_CHECK_CACHE_PATH), { recursive: true });\n fs.writeFileSync(UPDATE_CHECK_CACHE_PATH, JSON.stringify(freshCache, null, 2), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n\n return buildCheckResult(currentVersion, freshCache, config, null);\n}\n\n/**\n * Builds a CheckResult from cached data without hitting the registry.\n * Returns null when no cache exists.\n *\n * Accepts optional pre-read `cache` and `config` to avoid redundant file\n * reads when the caller has already loaded them (e.g. for a staleness check).\n */\nexport function statusFromCache(\n currentVersion: string,\n cache?: CachedCheck | null,\n config?: UpdateConfig,\n): CheckResult | null {\n const resolvedCache = cache !== undefined ? cache : readCachedCheck();\n if (resolvedCache === null) return null;\n\n const resolvedConfig = config !== undefined ? config : readUpdateConfig();\n return buildCheckResult(currentVersion, resolvedCache, resolvedConfig, null);\n}\n","/**\n * Update installer — generates and spawns a detached shell script that installs\n * the npm update and restarts the daemon after the current process exits.\n *\n * The script is written to a temp file with mode 0o755, spawned detached with\n * stdio ignored, and unreffed so the parent process can exit immediately.\n */\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { spawn } from 'node:child_process';\n\nimport {\n NPM_PACKAGE_NAME,\n MYCO_GLOBAL_DIR,\n UPDATE_ERROR_PATH,\n UPDATE_SCRIPT_DELAY_SECONDS,\n} from '../constants/update.js';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Parameters required to generate and spawn an update script. */\nexport interface InstallParams {\n /** The target version to install (e.g. \"0.11.0\"). */\n targetVersion: string;\n /** Absolute path to the project root for `myco update --project`. */\n projectRoot: string;\n /** Absolute path to the vault directory for `myco daemon --vault`. */\n vaultDir: string;\n}\n\n// ---------------------------------------------------------------------------\n// Script generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generates a POSIX shell script string that:\n * 1. Waits UPDATE_SCRIPT_DELAY_SECONDS for the daemon to exit.\n * 2. Runs `npm install -g <package>@<version>`.\n * 3. On success: runs `myco update --project <projectRoot>` (non-fatal).\n * 4. On success: clears ~/.myco/update-error.json.\n * 5. On failure: writes error JSON to ~/.myco/update-error.json.\n * 6. Always: starts `myco daemon --vault <vaultDir>` in background.\n * 7. Cleans up the script file itself.\n */\nexport function generateUpdateScript(params: InstallParams): string {\n const { targetVersion, projectRoot, vaultDir } = params;\n\n // Use JSON.stringify for safe path quoting (handles spaces, special chars).\n const packageSpec = `${NPM_PACKAGE_NAME}@${targetVersion}`;\n const quotedProjectRoot = JSON.stringify(projectRoot);\n const quotedVaultDir = JSON.stringify(vaultDir);\n const quotedErrorPath = JSON.stringify(UPDATE_ERROR_PATH);\n const errorJson = JSON.stringify(\n JSON.stringify({ error: `npm install failed for ${packageSpec}` }),\n );\n\n // Use ${MYCO_CMD:-myco} so dev environments (myco-dev) survive the restart.\n // Matches the myco-run pattern: exec \"${MYCO_CMD:-myco}\" \"$@\"\n return `#!/bin/sh\nset -e\nMYCO=\"\\${MYCO_CMD:-myco}\"\n\n# Wait for daemon to exit cleanly\nsleep ${UPDATE_SCRIPT_DELAY_SECONDS}\n\n# Attempt the update\nif npm install -g ${packageSpec} 2>&1; then\n # Sync project files (gitignore, symbiont registration)\n \"$MYCO\" update --project ${quotedProjectRoot} || true\n # Clear any previous error\n rm -f ${quotedErrorPath}\nelse\n # Write error and attempt restart with old version\n echo ${errorJson} > ${quotedErrorPath}\nfi\n\n# Restart daemon (works whether install succeeded or failed)\n\"$MYCO\" daemon --vault ${quotedVaultDir} &\n\n# Clean up this script\nrm -f \"$0\"\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Script spawning\n// ---------------------------------------------------------------------------\n\n/**\n * Writes the generated update script to a temp file, spawns it detached, and\n * unrefs the child so the parent process can exit without waiting.\n *\n * Returns the path to the temporary script file.\n */\nexport function spawnUpdateScript(params: InstallParams): string {\n // Ensure ~/.myco/ exists before writing the error path or checking state.\n fs.mkdirSync(MYCO_GLOBAL_DIR, { recursive: true });\n\n const scriptName = `myco-update-${Date.now()}.sh`;\n const scriptPath = path.join(os.tmpdir(), scriptName);\n\n const script = generateUpdateScript(params);\n fs.writeFileSync(scriptPath, script, { encoding: 'utf-8', mode: 0o755 });\n\n const child = spawn('/bin/sh', [scriptPath], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n\n return scriptPath;\n}\n","/**\n * Update API handlers — status, manual check, apply, and channel switch.\n *\n * Factory function injects vaultDir, projectRoot, currentVersion, and a\n * scheduleShutdown callback; returns handlers for:\n * GET /api/update/status\n * POST /api/update/check\n * POST /api/update/apply\n * PUT /api/update/channel\n */\n\nimport { z } from 'zod';\n\nimport {\n isUpdateExempt,\n checkForUpdate,\n statusFromCache,\n readCachedCheck,\n readUpdateConfig,\n writeUpdateConfig,\n clearCachedCheck,\n isCacheStale,\n} from '../update-checker.js';\nimport { spawnUpdateScript } from '../update-installer.js';\nimport { RELEASE_CHANNELS } from '../../constants/update.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Dependencies injected by the daemon when registering update routes. */\nexport interface UpdateDeps {\n /** Absolute path to the active vault directory. */\n vaultDir: string;\n /** Absolute path to the project root (used by `myco update --project`). */\n projectRoot: string;\n /** The currently running version (from package.json at startup). */\n currentVersion: string;\n /** Callback that schedules a graceful daemon shutdown after the update script spawns. */\n scheduleShutdown: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Zod schema\n// ---------------------------------------------------------------------------\n\nconst ChannelBodySchema = z.object({\n channel: z.enum(RELEASE_CHANNELS),\n});\n\n// ---------------------------------------------------------------------------\n// Handler factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create update API handlers with injected dependencies.\n *\n * Returns an object with named handlers for each update endpoint.\n */\nexport function createUpdateHandlers(deps: UpdateDeps) {\n const { vaultDir, projectRoot, currentVersion, scheduleShutdown } = deps;\n\n /**\n * GET /api/update/status — returns cached update state.\n *\n * When the cache is stale, kicks off a background registry check\n * (fire-and-forget) and immediately returns the current cached value.\n */\n async function handleUpdateStatus(_req: RouteRequest): Promise<RouteResponse> {\n if (isUpdateExempt()) {\n return { body: { exempt: true, running_version: currentVersion } };\n }\n\n const config = readUpdateConfig();\n const cache = readCachedCheck();\n\n if (isCacheStale(cache, config.check_interval_hours)) {\n // Fire-and-forget — don't block the response on the registry fetch.\n checkForUpdate(currentVersion).catch(() => {});\n }\n\n // Pass pre-read config and cache to avoid reading the files a second time.\n const status = statusFromCache(currentVersion, cache, config);\n if (!status) {\n // No cache yet — return minimal response; background check will populate it.\n return {\n body: {\n exempt: false,\n update_available: false,\n running_version: currentVersion,\n latest_version: currentVersion,\n latest_stable: currentVersion,\n latest_beta: null,\n channel: config.channel,\n check_interval_hours: config.check_interval_hours,\n last_check: '',\n error: null,\n },\n };\n }\n return { body: { exempt: false, ...status } };\n }\n\n /**\n * POST /api/update/check — forces an immediate registry check (blocking).\n *\n * Intended for user-initiated \"Check Now\" actions where the caller wants\n * fresh data before rendering.\n */\n async function handleUpdateCheck(_req: RouteRequest): Promise<RouteResponse> {\n if (isUpdateExempt()) {\n return {\n status: 400,\n body: { error: 'update_exempt', message: 'Updates disabled in dev mode' },\n };\n }\n\n const result = await checkForUpdate(currentVersion);\n return { body: { exempt: false, ...result } };\n }\n\n /**\n * POST /api/update/apply — spawns the update script and schedules shutdown.\n *\n * Returns 400 when no update is available or when in dev mode.\n */\n async function handleUpdateApply(_req: RouteRequest): Promise<RouteResponse> {\n if (isUpdateExempt()) {\n return { status: 400, body: { error: 'update_exempt' } };\n }\n\n const status = statusFromCache(currentVersion);\n if (!status || !status.update_available) {\n return { status: 400, body: { error: 'no_update_available' } };\n }\n\n spawnUpdateScript({ targetVersion: status.latest_version, projectRoot, vaultDir });\n scheduleShutdown();\n\n return { body: { status: 'applying', version: status.latest_version } };\n }\n\n /**\n * PUT /api/update/channel — switches the release channel and clears the cache.\n *\n * Returns 400 when the channel value is not in RELEASE_CHANNELS.\n */\n async function handleUpdateChannel(req: RouteRequest): Promise<RouteResponse> {\n const parsed = ChannelBodySchema.safeParse(req.body);\n if (!parsed.success) {\n return { status: 400, body: { error: 'invalid_channel' } };\n }\n\n const { channel } = parsed.data;\n const config = readUpdateConfig();\n\n writeUpdateConfig({ ...config, channel });\n clearCachedCheck();\n\n const channelStatus = statusFromCache(currentVersion);\n if (!channelStatus) {\n return {\n body: {\n exempt: false,\n update_available: false,\n running_version: currentVersion,\n latest_version: currentVersion,\n latest_stable: currentVersion,\n latest_beta: null,\n channel,\n check_interval_hours: config.check_interval_hours,\n last_check: '',\n error: null,\n },\n };\n }\n return { body: { exempt: false, ...channelStatus } };\n }\n\n return {\n handleUpdateStatus,\n handleUpdateCheck,\n handleUpdateApply,\n handleUpdateChannel,\n };\n}\n","/**\n * Backup engine — SQL-dump backup and restore for synced vault tables.\n *\n * Produces portable `INSERT OR IGNORE` SQL dumps scoped to a single machine.\n * Restore merges foreign machine data without overwriting local records.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Database } from 'better-sqlite3';\nimport { SYNC_PROTOCOL_VERSION, epochSeconds } from '@myco/constants.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Tables included in backup dumps (all synced tables). */\nexport const BACKUP_TABLES = [\n 'sessions',\n 'prompt_batches',\n 'spores',\n 'entities',\n 'graph_edges',\n 'entity_mentions',\n 'resolution_events',\n 'plans',\n 'artifacts',\n 'digest_extracts',\n 'team_members',\n] as const;\n\n/** File extension for backup dumps. */\nconst BACKUP_EXTENSION = '.sql';\n\n/** Header comment template for backup files. */\nconst BACKUP_HEADER_TEMPLATE = '-- Myco backup';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Metadata for a backup file on disk. */\nexport interface BackupMeta {\n machine_id: string;\n file_name: string;\n size_bytes: number;\n modified_at: string;\n}\n\n/** Per-table counts returned by restore preview/execute. */\nexport interface TableCounts {\n table: string;\n new: number;\n existing: number;\n}\n\n/** Result returned by restoreBackup. */\nexport interface RestoreResult {\n tables: TableCounts[];\n total_restored: number;\n total_skipped: number;\n}\n\n// ---------------------------------------------------------------------------\n// SQL value serialization\n// ---------------------------------------------------------------------------\n\n/**\n * Escape a string value for inclusion in a SQL literal.\n * Doubles single quotes per SQL standard.\n */\nfunction escapeSql(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\n/**\n * Serialize a JavaScript value into a SQL literal.\n *\n * - null / undefined → NULL\n * - number → numeric literal\n * - Buffer → X'hex'\n * - string → 'escaped string'\n */\nfunction toSqlLiteral(value: unknown): string {\n if (value === null || value === undefined) return 'NULL';\n if (typeof value === 'number') return String(value);\n if (Buffer.isBuffer(value)) return `X'${value.toString('hex')}'`;\n return `'${escapeSql(String(value))}'`;\n}\n\n// ---------------------------------------------------------------------------\n// Backup\n// ---------------------------------------------------------------------------\n\n/**\n * Create a SQL dump backup of all synced tables.\n *\n * Writes `INSERT OR IGNORE` statements for every row in BACKUP_TABLES\n * to `{backupDir}/{machineId}.sql`. Idempotent — overwrites any existing\n * backup for the same machine.\n *\n * @returns the absolute path of the created backup file.\n */\nexport function createBackup(\n db: Database,\n backupDir: string,\n machineId: string,\n): string {\n fs.mkdirSync(backupDir, { recursive: true });\n\n const lines: string[] = [];\n const timestamp = epochSeconds();\n\n // Header\n lines.push(`${BACKUP_HEADER_TEMPLATE}: machine_id=${machineId}, created_at=${timestamp}`);\n lines.push(`-- Protocol version: ${SYNC_PROTOCOL_VERSION}`);\n lines.push('');\n\n for (const table of BACKUP_TABLES) {\n const rows = db.prepare(`SELECT * FROM ${table}`).all() as Record<string, unknown>[];\n if (rows.length === 0) continue;\n\n lines.push(`-- Table: ${table} (${rows.length} rows)`);\n\n // Get column names from the first row\n const columns = Object.keys(rows[0]);\n const columnList = columns.map((c) => `\"${c}\"`).join(', ');\n\n for (const row of rows) {\n const values = columns.map((c) => toSqlLiteral(row[c])).join(', ');\n lines.push(`INSERT OR IGNORE INTO ${table} (${columnList}) VALUES (${values});`);\n }\n\n lines.push('');\n }\n\n const filePath = path.join(backupDir, `${machineId}${BACKUP_EXTENSION}`);\n fs.writeFileSync(filePath, lines.join('\\n'), 'utf-8');\n\n return filePath;\n}\n\n// ---------------------------------------------------------------------------\n// List\n// ---------------------------------------------------------------------------\n\n/**\n * Scan the backup directory for `.sql` files and return metadata.\n *\n * Machine ID is derived from the filename (stripping the extension).\n */\nexport function listBackups(backupDir: string): BackupMeta[] {\n let entries: string[];\n try {\n entries = fs.readdirSync(backupDir);\n } catch {\n return [];\n }\n\n const backups: BackupMeta[] = [];\n\n for (const entry of entries) {\n if (!entry.endsWith(BACKUP_EXTENSION)) continue;\n\n const filePath = path.join(backupDir, entry);\n const stat = fs.statSync(filePath);\n\n backups.push({\n machine_id: entry.slice(0, -BACKUP_EXTENSION.length),\n file_name: entry,\n size_bytes: stat.size,\n modified_at: stat.mtime.toISOString(),\n });\n }\n\n return backups.sort((a, b) => b.modified_at.localeCompare(a.modified_at));\n}\n\n// ---------------------------------------------------------------------------\n// Restore helpers\n// ---------------------------------------------------------------------------\n\n/** Regex matching INSERT OR IGNORE statements generated by createBackup. */\nconst INSERT_REGEX = /^INSERT OR IGNORE INTO (\\w+)\\s+\\(([^)]+)\\)\\s+VALUES\\s+\\((.+)\\);$/;\n\n/** Parsed INSERT statement. */\ninterface ParsedInsert {\n table: string;\n columns: string[];\n valueSql: string;\n}\n\n/**\n * Parse all INSERT statements from a backup file.\n */\nfunction parseBackupFile(backupPath: string): ParsedInsert[] {\n const content = fs.readFileSync(backupPath, 'utf-8');\n const inserts: ParsedInsert[] = [];\n\n for (const line of content.split('\\n')) {\n const match = INSERT_REGEX.exec(line);\n if (!match) continue;\n\n inserts.push({\n table: match[1],\n columns: match[2].split(',').map((c) => c.trim().replace(/\"/g, '')),\n valueSql: match[3],\n });\n }\n\n return inserts;\n}\n\n// ---------------------------------------------------------------------------\n// Restore preview\n// ---------------------------------------------------------------------------\n\n/**\n * Preview what a restore would do without making changes.\n *\n * For each INSERT in the backup, checks if a conflicting row already exists\n * (via INSERT OR IGNORE in a savepoint that gets rolled back).\n *\n * Returns per-table counts of new vs existing records.\n */\nexport function restorePreview(\n db: Database,\n backupPath: string,\n): TableCounts[] {\n const inserts = parseBackupFile(backupPath);\n const counts = new Map<string, { new: number; existing: number }>();\n\n // Defer FK checks — backup may reference rows in non-synced tables\n db.pragma('foreign_keys = OFF');\n // Use a savepoint so we can test INSERTs without persisting\n db.exec('SAVEPOINT restore_preview');\n try {\n for (const insert of inserts) {\n if (!counts.has(insert.table)) {\n counts.set(insert.table, { new: 0, existing: 0 });\n }\n const tableCounts = counts.get(insert.table)!;\n\n try {\n const columnList = insert.columns.map((c) => `\"${c}\"`).join(', ');\n const stmt = `INSERT OR IGNORE INTO ${insert.table} (${columnList}) VALUES (${insert.valueSql})`;\n const result = db.prepare(stmt).run();\n\n if (result.changes > 0) {\n tableCounts.new++;\n } else {\n tableCounts.existing++;\n }\n } catch {\n tableCounts.existing++;\n }\n }\n } finally {\n db.exec('ROLLBACK TO restore_preview');\n db.exec('RELEASE restore_preview');\n db.pragma('foreign_keys = ON');\n }\n\n return Array.from(counts.entries()).map(([table, c]) => ({\n table,\n new: c.new,\n existing: c.existing,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Restore\n// ---------------------------------------------------------------------------\n\n/**\n * Restore a backup by running all INSERTs in a transaction.\n *\n * Uses `INSERT OR IGNORE` — existing records are skipped, new records\n * are inserted. Returns per-table counts.\n */\nexport function restoreBackup(\n db: Database,\n backupPath: string,\n): RestoreResult {\n const inserts = parseBackupFile(backupPath);\n const counts = new Map<string, { new: number; existing: number }>();\n\n // Defer FK checks — backup may reference rows in non-synced tables (e.g. agents)\n // that don't exist yet. Re-enable after the transaction.\n db.pragma('foreign_keys = OFF');\n try {\n const runRestore = db.transaction(() => {\n for (const insert of inserts) {\n if (!counts.has(insert.table)) {\n counts.set(insert.table, { new: 0, existing: 0 });\n }\n const tableCounts = counts.get(insert.table)!;\n\n const columnList = insert.columns.map((c) => `\"${c}\"`).join(', ');\n const stmt = `INSERT OR IGNORE INTO ${insert.table} (${columnList}) VALUES (${insert.valueSql})`;\n const result = db.prepare(stmt).run();\n\n if (result.changes > 0) {\n tableCounts.new++;\n } else {\n tableCounts.existing++;\n }\n }\n });\n\n runRestore();\n } finally {\n db.pragma('foreign_keys = ON');\n }\n\n const tables = Array.from(counts.entries()).map(([table, c]) => ({\n table,\n new: c.new,\n existing: c.existing,\n }));\n\n const total_restored = tables.reduce((sum, t) => sum + t.new, 0);\n const total_skipped = tables.reduce((sum, t) => sum + t.existing, 0);\n\n return { tables, total_restored, total_skipped };\n}\n","/**\n * Backup API handlers — create, list, preview, and restore backups.\n *\n * Factory function injects backupDir and machineId; returns handlers\n * for POST /api/backup, GET /api/backups, POST /api/restore/preview,\n * and POST /api/restore.\n */\n\nimport type { Database } from 'better-sqlite3';\nimport type { RouteRequest, RouteResponse } from '../router.js';\nimport {\n createBackup,\n listBackups,\n restorePreview,\n restoreBackup,\n} from '../backup.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Dependencies injected by the daemon when registering backup routes. */\nexport interface BackupDeps {\n db: Database;\n backupDir: string;\n machineId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Handler factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create backup API handlers with injected dependencies.\n *\n * Returns an object with named handlers for each backup endpoint.\n */\nexport function createBackupHandlers(deps: BackupDeps) {\n /** POST /api/backup — create a new backup of all synced tables. */\n async function handleCreateBackup(_req: RouteRequest): Promise<RouteResponse> {\n const filePath = createBackup(deps.db, deps.backupDir, deps.machineId);\n const backups = listBackups(deps.backupDir);\n const created = backups.find((b) => b.machine_id === deps.machineId);\n\n return {\n body: {\n file_path: filePath,\n machine_id: deps.machineId,\n size_bytes: created?.size_bytes ?? 0,\n },\n };\n }\n\n /** GET /api/backups — list all backup files with metadata. */\n async function handleListBackups(_req: RouteRequest): Promise<RouteResponse> {\n const backups = listBackups(deps.backupDir);\n return { body: { backups } };\n }\n\n /** POST /api/restore/preview — dry-run restore to show new/existing counts. */\n async function handleRestorePreview(req: RouteRequest): Promise<RouteResponse> {\n const { machine_id } = req.body as { machine_id?: string };\n if (!machine_id) {\n return { status: 400, body: { error: 'missing_machine_id' } };\n }\n\n const backups = listBackups(deps.backupDir);\n const backup = backups.find((b) => b.machine_id === machine_id);\n if (!backup) {\n return { status: 404, body: { error: 'backup_not_found' } };\n }\n\n const backupPath = `${deps.backupDir}/${backup.file_name}`;\n const tables = restorePreview(deps.db, backupPath);\n const total_new = tables.reduce((sum, t) => sum + t.new, 0);\n const total_existing = tables.reduce((sum, t) => sum + t.existing, 0);\n\n return { body: { machine_id, tables, total_new, total_existing } };\n }\n\n /** POST /api/restore — execute restore from a backup file. */\n async function handleRestore(req: RouteRequest): Promise<RouteResponse> {\n const { machine_id } = req.body as { machine_id?: string };\n if (!machine_id) {\n return { status: 400, body: { error: 'missing_machine_id' } };\n }\n\n const backups = listBackups(deps.backupDir);\n const backup = backups.find((b) => b.machine_id === machine_id);\n if (!backup) {\n return { status: 404, body: { error: 'backup_not_found' } };\n }\n\n const backupPath = `${deps.backupDir}/${backup.file_name}`;\n const result = restoreBackup(deps.db, backupPath);\n\n return { body: { machine_id, ...result } };\n }\n\n return {\n handleCreateBackup,\n handleListBackups,\n handleRestorePreview,\n handleRestore,\n };\n}\n","/**\n * Team sync HTTP client.\n *\n * Communicates with the Cloudflare Worker to push outbox records,\n * search team knowledge, and check connection health.\n */\n\nimport type { OutboxRow } from '@myco/db/queries/team-outbox.js';\nimport { TEAM_SEARCH_TIMEOUT_MS, TEAM_HEALTH_TIMEOUT_MS } from '@myco/constants.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TeamSyncClientOptions {\n workerUrl: string;\n apiKey: string;\n machineId: string;\n syncProtocolVersion: number;\n /** Inject custom fetch for testing. */\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface TeamSearchOptions {\n limit?: number;\n tables?: string[];\n timeoutMs?: number;\n}\n\nexport interface TeamSearchResult {\n id: string;\n table_name: string;\n content: string;\n score: number;\n machine_id: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface TeamSearchResponse {\n results: TeamSearchResult[];\n machine_ids: string[];\n}\n\nexport interface TeamHealthResponse {\n status: string;\n node_count: number;\n sync_protocol_version: number;\n}\n\nexport interface TeamConnectInfo {\n machine_id: string;\n vault_name?: string;\n agent?: string;\n version?: string;\n}\n\nexport interface TeamConfigResponse {\n config: Record<string, unknown>;\n sync_protocol_version: number;\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\nexport class TeamSyncClient {\n private readonly workerUrl: string;\n private readonly apiKey: string;\n private readonly machineId: string;\n private readonly syncProtocolVersion: number;\n private readonly fetchFn: typeof globalThis.fetch;\n\n constructor(options: TeamSyncClientOptions) {\n this.workerUrl = options.workerUrl.replace(/\\/+$/, '');\n this.apiKey = options.apiKey;\n this.machineId = options.machineId;\n this.syncProtocolVersion = options.syncProtocolVersion;\n this.fetchFn = options.fetch ?? globalThis.fetch;\n }\n\n /**\n * Register this machine with the team worker.\n */\n async connect(info: TeamConnectInfo): Promise<TeamConfigResponse> {\n const res = await this.request('POST', '/connect', {\n ...info,\n machine_id: this.machineId,\n sync_protocol_version: this.syncProtocolVersion,\n });\n return res as TeamConfigResponse;\n }\n\n /**\n * Push a batch of outbox records to the team worker.\n *\n * @returns the number of records accepted by the worker.\n */\n async pushBatch(records: OutboxRow[]): Promise<{ synced: number; skipped: number; errors: Array<{ id: string; table: string; error: string }> }> {\n const res = await this.request('POST', '/sync', {\n machine_id: this.machineId,\n sync_protocol_version: this.syncProtocolVersion,\n records: records.map((r) => {\n const data = typeof r.payload === 'string' ? JSON.parse(r.payload) : r.payload;\n return {\n table: r.table_name,\n id: String(r.row_id),\n machine_id: r.machine_id,\n operation: r.operation,\n data,\n content_hash: data.content_hash ?? null,\n };\n }),\n });\n return res as { synced: number; skipped: number; errors: Array<{ id: string; table: string; error: string }> };\n }\n\n /**\n * Search team knowledge across all connected machines.\n *\n * Uses AbortController for timeout enforcement.\n */\n async search(query: string, options: TeamSearchOptions = {}): Promise<TeamSearchResponse> {\n const timeoutMs = options.timeoutMs ?? TEAM_SEARCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const params = new URLSearchParams({ q: query });\n if (options.limit) params.set('limit', String(options.limit));\n if (options.tables) params.set('tables', options.tables.join(','));\n\n const res = await this.fetchFn(`${this.workerUrl}/search?${params}`, {\n method: 'GET',\n headers: this.headers(),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw new Error(`Team search failed: ${res.status} ${res.statusText}`);\n }\n\n return (await res.json()) as TeamSearchResponse;\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Check worker health.\n */\n async health(): Promise<TeamHealthResponse> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), TEAM_HEALTH_TIMEOUT_MS);\n\n try {\n const res = await this.fetchFn(`${this.workerUrl}/health`, {\n method: 'GET',\n headers: this.headers(),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw new Error(`Health check failed: ${res.status} ${res.statusText}`);\n }\n\n return (await res.json()) as TeamHealthResponse;\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Get team configuration from the worker.\n */\n async getConfig(): Promise<TeamConfigResponse> {\n const res = await this.request('GET', '/config');\n return res as TeamConfigResponse;\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private headers(): Record<string, string> {\n return {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n };\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<unknown> {\n const res = await this.fetchFn(`${this.workerUrl}${path}`, {\n method,\n headers: this.headers(),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Team sync request ${method} ${path} failed: ${res.status} ${text}`);\n }\n\n return res.json();\n }\n}\n","/**\n * Team connect/disconnect/status API handlers.\n *\n * Factory pattern: `createTeamHandlers(deps)` returns route handlers that\n * close over the daemon's shared state (vault dir, machine ID, team client).\n */\n\nimport { updateTeamConfig, loadConfig } from '@myco/config/loader.js';\nimport { writeSecret, readSecrets } from '@myco/config/secrets.js';\nimport { countPending } from '@myco/db/queries/team-outbox.js';\nimport { TeamSyncClient } from '../team-sync.js';\nimport { SYNC_PROTOCOL_VERSION, TEAM_API_KEY_SECRET } from '@myco/constants.js';\nimport { getPluginVersion } from '@myco/version.js';\nimport { SCHEMA_VERSION } from '@myco/db/schema.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TeamHandlerDeps {\n vaultDir: string;\n machineId: string;\n getTeamClient: () => TeamSyncClient | null;\n setTeamClient: (client: TeamSyncClient | null) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createTeamHandlers(deps: TeamHandlerDeps) {\n const { vaultDir, machineId } = deps;\n\n /**\n * POST /api/team/connect\n * Body: { url: string, api_key: string }\n *\n * Creates a TeamSyncClient, tests the connection, saves config + secrets.\n */\n async function handleConnect(req: RouteRequest): Promise<RouteResponse> {\n const { url, api_key } = req.body as { url?: string; api_key?: string };\n\n if (!url || !api_key) {\n return {\n status: 400,\n body: { error: 'missing_fields', message: 'Both url and api_key are required' },\n };\n }\n\n // Validate URL format\n try {\n new URL(url);\n } catch {\n return {\n status: 400,\n body: { error: 'invalid_url', message: 'Invalid worker URL' },\n };\n }\n\n // Create client and test connection\n const client = new TeamSyncClient({\n workerUrl: url,\n apiKey: api_key,\n machineId,\n syncProtocolVersion: SYNC_PROTOCOL_VERSION,\n });\n\n try {\n await client.health();\n } catch (err) {\n return {\n status: 502,\n body: {\n error: 'connection_failed',\n message: `Could not connect to team worker: ${(err as Error).message}`,\n },\n };\n }\n\n // Save config and secret\n updateTeamConfig(vaultDir, {\n enabled: true,\n worker_url: url,\n });\n writeSecret(vaultDir, TEAM_API_KEY_SECRET, api_key);\n\n // Set the live client\n deps.setTeamClient(client);\n\n const config = loadConfig(vaultDir);\n return { body: { connected: true, team: config.team } };\n }\n\n /**\n * POST /api/team/disconnect\n *\n * Disables team sync and clears the live client reference.\n */\n async function handleDisconnect(_req: RouteRequest): Promise<RouteResponse> {\n updateTeamConfig(vaultDir, { enabled: false });\n deps.setTeamClient(null);\n\n return { body: { connected: false } };\n }\n\n /**\n * GET /api/team/status\n *\n * Returns connection status, health check result, pending sync count, and machine_id.\n */\n async function handleStatus(_req: RouteRequest): Promise<RouteResponse> {\n const config = loadConfig(vaultDir);\n const client = deps.getTeamClient();\n const secrets = readSecrets(vaultDir);\n const hasApiKey = Boolean(secrets[TEAM_API_KEY_SECRET]);\n\n let healthy = false;\n let healthError: string | undefined;\n\n if (client && config.team.enabled) {\n try {\n await client.health();\n healthy = true;\n } catch (err) {\n healthError = (err as Error).message;\n }\n }\n\n let pendingCount = 0;\n try {\n pendingCount = countPending();\n } catch {\n // DB may not have the table yet\n }\n\n return {\n body: {\n enabled: config.team.enabled,\n worker_url: config.team.worker_url ?? null,\n has_api_key: hasApiKey,\n api_key: secrets[TEAM_API_KEY_SECRET] ?? null,\n healthy,\n health_error: healthError,\n pending_sync_count: pendingCount,\n machine_id: machineId,\n package_version: getPluginVersion(),\n schema_version: SCHEMA_VERSION,\n sync_protocol_version: SYNC_PROTOCOL_VERSION,\n },\n };\n }\n\n return { handleConnect, handleDisconnect, handleStatus };\n}\n","import { randomUUID } from 'node:crypto';\nimport type { RouteResponse } from '../router.js';\n\n/** Maximum number of concurrently tracked operations. */\nconst MAX_CONCURRENT_OPERATIONS = 10;\n\n/** Time-to-live for completed/failed entries before cleanup (ms). */\nconst PROGRESS_TTL_MS = 5 * 60 * 1000;\n\nexport type ProgressStatus = 'running' | 'completed' | 'failed';\n\nexport interface ProgressEntry {\n token: string;\n type: string;\n status: ProgressStatus;\n percent?: number;\n message?: string;\n created: number;\n updated: number;\n}\n\nexport class ProgressTracker {\n private entries = new Map<string, ProgressEntry>();\n\n /**\n * Create a new tracked operation. Returns the existing token if an\n * operation of the same type is already running (duplicate prevention).\n * Throws if the maximum concurrent operations limit is reached.\n */\n /**\n * Create a new tracked operation or return existing one.\n * Returns `{ token, isNew }` — if `isNew` is false, the operation\n * was already running and the caller should NOT launch it again.\n * Throws if the maximum concurrent operations limit is reached.\n */\n create(type: string): { token: string; isNew: boolean } {\n // Lazy cleanup of stale completed/failed entries before checking limits\n this.cleanup();\n\n // Duplicate prevention: if an operation of the same type is already running, return its token\n for (const entry of this.entries.values()) {\n if (entry.type === type && entry.status === 'running') {\n return { token: entry.token, isNew: false };\n }\n }\n\n // Enforce concurrency limit (count only running entries)\n const runningCount = [...this.entries.values()].filter((e) => e.status === 'running').length;\n if (runningCount >= MAX_CONCURRENT_OPERATIONS) {\n throw new Error(`Maximum concurrent operations reached (${MAX_CONCURRENT_OPERATIONS})`);\n }\n\n const token = randomUUID();\n const now = Date.now();\n this.entries.set(token, {\n token,\n type,\n status: 'running',\n created: now,\n updated: now,\n });\n return { token, isNew: true };\n }\n\n /**\n * Update progress for a tracked operation.\n */\n update(token: string, data: { percent?: number; message?: string; status?: ProgressStatus }): void {\n const entry = this.entries.get(token);\n if (!entry) return;\n\n if (data.percent !== undefined) entry.percent = data.percent;\n if (data.message !== undefined) entry.message = data.message;\n if (data.status !== undefined) entry.status = data.status;\n entry.updated = Date.now();\n }\n\n /**\n * Get the current state of a tracked operation.\n */\n get(token: string): ProgressEntry | undefined {\n return this.entries.get(token);\n }\n\n /**\n * Check whether any operations are currently running.\n */\n hasActiveOperations(): boolean {\n for (const entry of this.entries.values()) {\n if (entry.status === 'running') return true;\n }\n return false;\n }\n\n /**\n * Remove completed/failed entries older than PROGRESS_TTL_MS.\n */\n cleanup(): void {\n const cutoff = Date.now() - PROGRESS_TTL_MS;\n for (const [token, entry] of this.entries) {\n if (entry.status !== 'running' && entry.updated < cutoff) {\n this.entries.delete(token);\n }\n }\n }\n}\n\nexport async function handleGetProgress(\n tracker: ProgressTracker,\n token: string,\n): Promise<RouteResponse> {\n const entry = tracker.get(token);\n if (!entry) {\n return { status: 404, body: { error: 'not_found', message: 'Progress token not found' } };\n }\n return { body: entry };\n}\n","import { OllamaBackend } from '../../intelligence/ollama.js';\nimport { LmStudioBackend } from '../../intelligence/lm-studio.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\nconst MODEL_LIST_TIMEOUT_MS = 5000;\n\n/** Well-known Anthropic models — no list API available locally. */\nexport const ANTHROPIC_MODELS = [\n 'claude-opus-4-6',\n 'claude-sonnet-4-6',\n 'claude-haiku-4-5-20251001',\n];\n\n/** Patterns that indicate an embedding model (case-insensitive). */\nconst EMBEDDING_PATTERNS = [\n 'embed', 'bge-', 'nomic-embed', 'e5-', 'gte-', 'granite-embedding',\n];\n\n/** Filter models to only include embedding models. */\nfunction filterEmbeddingModels(models: string[]): string[] {\n return models.filter((m) => {\n const name = m.toLowerCase();\n return EMBEDDING_PATTERNS.some((p) => name.includes(p));\n });\n}\n\n/** Filter models to exclude embedding models (LLM-only). */\nfunction filterLlmModels(models: string[]): string[] {\n return models.filter((m) => {\n const name = m.toLowerCase();\n return !EMBEDDING_PATTERNS.some((p) => name.includes(p));\n });\n}\n\nexport async function handleGetModels(req: RouteRequest): Promise<RouteResponse> {\n const provider = req.query.provider;\n const type = req.query.type; // 'llm' | 'embedding' | undefined (all)\n\n if (!provider) {\n return { status: 400, body: { error: 'provider query parameter required' } };\n }\n\n let models: string[] = [];\n\n try {\n if (provider === 'ollama') {\n const backend = new OllamaBackend({ base_url: req.query.base_url });\n models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);\n } else if (provider === 'lm-studio' || provider === 'openai-compatible') {\n const backend = new LmStudioBackend({ base_url: req.query.base_url });\n models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);\n } else if (provider === 'anthropic') {\n models = ANTHROPIC_MODELS;\n }\n } catch {\n // Provider unreachable — return empty list\n }\n\n // Filter by type if requested\n if (type === 'embedding') {\n models = filterEmbeddingModels(models);\n } else if (type === 'llm') {\n models = filterLlmModels(models);\n }\n\n return { body: { provider, models } };\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_FILENAME } from '../../config/loader.js';\n\n/** Compute config hash from the YAML file on disk. Cache this at startup and after saves. */\nexport function computeConfigHash(vaultDir: string): string {\n try {\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n const raw = fs.readFileSync(configPath, 'utf-8');\n return createHash('md5').update(raw).digest('hex');\n } catch {\n return '';\n }\n}\n","/**\n * Activity insert/query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of activities returned by listActivities when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Default success flag for new activities. */\nconst DEFAULT_SUCCESS = 1;\n\n/** Default processed flag for new activities. */\nconst DEFAULT_PROCESSED = 0;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting an activity. */\nexport interface ActivityInsert {\n session_id: string;\n tool_name: string;\n timestamp: number;\n created_at: number;\n prompt_batch_id?: number | null;\n tool_input?: string | null;\n tool_output_summary?: string | null;\n file_path?: string | null;\n files_affected?: string | null;\n duration_ms?: number | null;\n success?: number;\n error_message?: string | null;\n processed?: number;\n content_hash?: string | null;\n}\n\n/** Row shape returned from activity queries. */\nexport interface ActivityRow {\n id: number;\n session_id: string;\n prompt_batch_id: number | null;\n tool_name: string;\n tool_input: string | null;\n tool_output_summary: string | null;\n file_path: string | null;\n files_affected: string | null;\n duration_ms: number | null;\n success: number;\n error_message: string | null;\n timestamp: number;\n processed: number;\n content_hash: string | null;\n created_at: number;\n}\n\n/** Filter options for `listActivities`. */\nexport interface ListActivitiesOptions {\n session_id?: string;\n prompt_batch_id?: number;\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst ACTIVITY_COLUMNS = [\n 'id',\n 'session_id',\n 'prompt_batch_id',\n 'tool_name',\n 'tool_input',\n 'tool_output_summary',\n 'file_path',\n 'files_affected',\n 'duration_ms',\n 'success',\n 'error_message',\n 'timestamp',\n 'processed',\n 'content_hash',\n 'created_at',\n] as const;\n\nconst SELECT_COLUMNS = ACTIVITY_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed ActivityRow. */\nfunction toActivityRow(row: Record<string, unknown>): ActivityRow {\n return {\n id: row.id as number,\n session_id: row.session_id as string,\n prompt_batch_id: (row.prompt_batch_id as number) ?? null,\n tool_name: row.tool_name as string,\n tool_input: (row.tool_input as string) ?? null,\n tool_output_summary: (row.tool_output_summary as string) ?? null,\n file_path: (row.file_path as string) ?? null,\n files_affected: (row.files_affected as string) ?? null,\n duration_ms: (row.duration_ms as number) ?? null,\n success: row.success as number,\n error_message: (row.error_message as string) ?? null,\n timestamp: row.timestamp as number,\n processed: row.processed as number,\n content_hash: (row.content_hash as string) ?? null,\n created_at: row.created_at as number,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new activity row.\n *\n * The `id` is auto-generated by the INTEGER PRIMARY KEY (AUTOINCREMENT).\n * FTS5 index is kept in sync via a follow-up INSERT into activities_fts.\n */\nexport function insertActivity(data: ActivityInsert): ActivityRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO activities (\n session_id, prompt_batch_id, tool_name, tool_input,\n tool_output_summary, file_path, files_affected, duration_ms,\n success, error_message, timestamp, processed,\n content_hash, created_at\n ) VALUES (\n ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n data.session_id,\n data.prompt_batch_id ?? null,\n data.tool_name,\n data.tool_input ?? null,\n data.tool_output_summary ?? null,\n data.file_path ?? null,\n data.files_affected ?? null,\n data.duration_ms ?? null,\n data.success ?? DEFAULT_SUCCESS,\n data.error_message ?? null,\n data.timestamp,\n data.processed ?? DEFAULT_PROCESSED,\n data.content_hash ?? null,\n data.created_at,\n );\n\n const activityId = Number(info.lastInsertRowid);\n\n // FTS5 sync\n const toolName = data.tool_name;\n const toolInput = data.tool_input ?? null;\n const filePath = data.file_path ?? null;\n if (toolName || toolInput || filePath) {\n db.prepare(\n 'INSERT INTO activities_fts(rowid, tool_name, tool_input, file_path) VALUES (?, ?, ?, ?)',\n ).run(activityId, toolName ?? '', toolInput ?? '', filePath ?? '');\n }\n\n return toActivityRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM activities WHERE id = ?`).get(activityId) as Record<string, unknown>,\n );\n}\n\n/** Fields required when inserting an activity with inline batch linkage. */\nexport interface StatelessActivityInsert {\n session_id: string;\n tool_name: string;\n timestamp: number;\n created_at: number;\n tool_input?: string | null;\n tool_output_summary?: string | null;\n file_path?: string | null;\n files_affected?: string | null;\n duration_ms?: number | null;\n success?: number;\n error_message?: string | null;\n content_hash?: string | null;\n}\n\n/**\n * Insert an activity with batch linkage resolved via inline subquery.\n *\n * The `prompt_batch_id` is set to the latest open batch for the session\n * (i.e., `ended_at IS NULL`, ordered by `id DESC`). If no open batch exists,\n * `prompt_batch_id` will be NULL. The caller never needs a separate SELECT.\n *\n * FTS5 index is kept in sync via a follow-up INSERT into activities_fts.\n */\nexport function insertActivityWithBatch(\n data: StatelessActivityInsert,\n): ActivityRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO activities (\n session_id, prompt_batch_id, tool_name, tool_input,\n tool_output_summary, file_path, files_affected, duration_ms,\n success, error_message, timestamp, processed,\n content_hash, created_at\n ) VALUES (\n ?,\n (SELECT id FROM prompt_batches WHERE session_id = ? AND ended_at IS NULL ORDER BY id DESC LIMIT 1),\n ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n data.session_id,\n data.session_id,\n data.tool_name,\n data.tool_input ?? null,\n data.tool_output_summary ?? null,\n data.file_path ?? null,\n data.files_affected ?? null,\n data.duration_ms ?? null,\n data.success ?? DEFAULT_SUCCESS,\n data.error_message ?? null,\n data.timestamp,\n DEFAULT_PROCESSED,\n data.content_hash ?? null,\n data.created_at,\n );\n\n const activityId = Number(info.lastInsertRowid);\n\n // FTS5 sync\n const toolName = data.tool_name;\n const toolInput = data.tool_input ?? null;\n const filePath = data.file_path ?? null;\n if (toolName || toolInput || filePath) {\n db.prepare(\n 'INSERT INTO activities_fts(rowid, tool_name, tool_input, file_path) VALUES (?, ?, ?, ?)',\n ).run(activityId, toolName ?? '', toolInput ?? '', filePath ?? '');\n }\n\n return toActivityRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM activities WHERE id = ?`).get(activityId) as Record<string, unknown>,\n );\n}\n\n/**\n * List activities with optional filters, ordered by timestamp ASC.\n *\n * At least one of `session_id` or `prompt_batch_id` should be provided\n * to avoid unbounded queries.\n */\nexport function listActivities(\n options: ListActivitiesOptions = {},\n): ActivityRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.session_id !== undefined) {\n conditions.push(`session_id = ?`);\n params.push(options.session_id);\n }\n\n if (options.prompt_batch_id !== undefined) {\n conditions.push(`prompt_batch_id = ?`);\n params.push(options.prompt_batch_id);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n\n params.push(limit);\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM activities\n ${where}\n ORDER BY timestamp ASC\n LIMIT ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toActivityRow);\n}\n\n/**\n * List all activities for a specific batch, ordered by timestamp ASC.\n */\nexport function listActivitiesByBatch(\n batchId: number,\n): ActivityRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM activities\n WHERE prompt_batch_id = ?\n ORDER BY timestamp ASC`,\n ).all(batchId) as Record<string, unknown>[];\n\n return rows.map(toActivityRow);\n}\n\n/**\n * Count total activities for a given session.\n */\nexport function countActivities(sessionId: string): number {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT COUNT(*) AS count FROM activities WHERE session_id = ?`,\n ).get(sessionId) as Record<string, unknown>;\n\n return row.count as number;\n}\n","/**\n * Attachment CRUD query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting an attachment. */\nexport interface AttachmentInsert {\n id: string;\n session_id: string;\n prompt_batch_id?: number;\n file_path: string;\n media_type?: string;\n description?: string;\n data?: Buffer;\n content_hash?: string;\n created_at: number;\n}\n\n/** Row shape returned from attachment queries (all columns, including BLOB). */\nexport interface AttachmentRow {\n id: string;\n session_id: string;\n prompt_batch_id: number | null;\n file_path: string;\n media_type: string | null;\n description: string | null;\n data: Buffer | null;\n content_hash: string | null;\n created_at: number;\n}\n\n/**\n * Row shape returned by list queries — excludes the `data` BLOB column.\n * Use this type when you only need metadata (e.g. listing attachments for a session).\n * The full row (including binary data) is only fetched by getAttachmentByFilePath.\n */\nexport type AttachmentListRow = Omit<AttachmentRow, 'data'>;\n\n// ---------------------------------------------------------------------------\n// Column lists\n// ---------------------------------------------------------------------------\n\nconst ATTACHMENT_COLUMNS = [\n 'id',\n 'session_id',\n 'prompt_batch_id',\n 'file_path',\n 'media_type',\n 'description',\n 'data',\n 'content_hash',\n 'created_at',\n] as const;\n\n/** Column list that omits the `data` BLOB — used by list queries to avoid loading megabytes of binary data. */\nconst ATTACHMENT_LIST_COLUMNS = [\n 'id',\n 'session_id',\n 'prompt_batch_id',\n 'file_path',\n 'media_type',\n 'description',\n 'content_hash',\n 'created_at',\n] as const;\n\nconst SELECT_COLUMNS = ATTACHMENT_COLUMNS.join(', ');\nconst SELECT_LIST_COLUMNS = ATTACHMENT_LIST_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize shared metadata fields from a SQLite result row. */\nfunction toAttachmentBase(row: Record<string, unknown>): AttachmentListRow {\n return {\n id: row.id as string,\n session_id: row.session_id as string,\n prompt_batch_id: (row.prompt_batch_id as number) ?? null,\n file_path: row.file_path as string,\n media_type: (row.media_type as string) ?? null,\n description: (row.description as string) ?? null,\n content_hash: (row.content_hash as string) ?? null,\n created_at: row.created_at as number,\n };\n}\n\n/** Normalize a SQLite result row into a typed AttachmentRow (includes BLOB). */\nfunction toAttachmentRow(row: Record<string, unknown>): AttachmentRow {\n return { ...toAttachmentBase(row), data: (row.data as Buffer) ?? null };\n}\n\n/** Normalize a SQLite result row into a typed AttachmentListRow (no BLOB). */\nfunction toAttachmentListRow(row: Record<string, unknown>): AttachmentListRow {\n return toAttachmentBase(row);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert an attachment record.\n *\n * Idempotent — ON CONFLICT (id) DO NOTHING means a second insert with the\n * same id silently succeeds without duplicating the row.\n *\n * @returns the inserted row, or undefined if the id already existed.\n */\nexport function insertAttachment(data: AttachmentInsert): AttachmentRow | undefined {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO attachments (${SELECT_COLUMNS})\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO NOTHING`,\n ).run(\n data.id,\n data.session_id,\n data.prompt_batch_id ?? null,\n data.file_path,\n data.media_type ?? null,\n data.description ?? null,\n data.data ?? null,\n data.content_hash ?? null,\n data.created_at,\n );\n\n if (info.changes === 0) return undefined;\n\n return toAttachmentRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM attachments WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n}\n\n/**\n * List all attachments for a given session, ordered by created_at ASC.\n *\n * The `data` BLOB column is intentionally excluded — use getAttachmentByFilePath\n * when you need the binary content (e.g. for the serving route).\n *\n * @returns array of attachment metadata rows (empty array if none exist).\n */\nexport function listAttachmentsBySession(sessionId: string): AttachmentListRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_LIST_COLUMNS} FROM attachments WHERE session_id = ? ORDER BY created_at ASC`,\n ).all(sessionId) as Record<string, unknown>[];\n\n return rows.map(toAttachmentListRow);\n}\n\n/**\n * Find an attachment by its file_path.\n *\n * @returns the first matching attachment row, or null if none exists.\n */\nexport function getAttachmentByFilePath(filePath: string): AttachmentRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM attachments WHERE file_path = ? LIMIT 1`,\n ).get(filePath) as Record<string, unknown> | undefined;\n\n return row ? toAttachmentRow(row) : null;\n}\n","import { getSession, listSessions, countSessions } from '@myco/db/queries/sessions.js';\nimport { listBatchesBySession } from '@myco/db/queries/batches.js';\nimport { listActivitiesByBatch } from '@myco/db/queries/activities.js';\nimport { listAttachmentsBySession } from '@myco/db/queries/attachments.js';\nimport { listPlansBySession } from '@myco/db/queries/plans.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\nconst DEFAULT_LIST_LIMIT = 50;\nconst DEFAULT_LIST_OFFSET = 0;\n\nexport async function handleListSessions(req: RouteRequest): Promise<RouteResponse> {\n const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT;\n const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET;\n const status = req.query.status || undefined;\n const agent = req.query.agent || undefined;\n const search = req.query.search || undefined;\n\n const filterOpts = { status, agent, search };\n\n const sessions = listSessions({ ...filterOpts, limit, offset }).map((s) => ({\n id: s.id,\n date: new Date(s.started_at * 1000).toISOString().slice(0, 10),\n title: s.title || s.id.slice(0, 8),\n status: s.status,\n agent: s.agent,\n prompt_count: s.prompt_count,\n tool_count: s.tool_count,\n started_at: s.started_at,\n ended_at: s.ended_at,\n }));\n const total = countSessions(filterOpts);\n\n return { body: { sessions, total, offset, limit } };\n}\n\nexport async function handleGetSession(req: RouteRequest): Promise<RouteResponse> {\n const session = getSession(req.params.id);\n if (!session) return { status: 404, body: { error: 'not_found' } };\n return { body: session };\n}\n\nexport async function handleGetSessionBatches(req: RouteRequest): Promise<RouteResponse> {\n const batches = listBatchesBySession(req.params.id);\n return { body: batches };\n}\n\nexport async function handleGetBatchActivities(req: RouteRequest): Promise<RouteResponse> {\n const batchId = Number(req.params.id);\n if (isNaN(batchId)) return { status: 400, body: { error: 'invalid_batch_id' } };\n const activities = listActivitiesByBatch(batchId);\n return { body: activities };\n}\n\nexport async function handleGetSessionAttachments(req: RouteRequest): Promise<RouteResponse> {\n const attachments = listAttachmentsBySession(req.params.id);\n return { body: attachments };\n}\n\nexport async function handleGetSessionPlans(req: RouteRequest): Promise<RouteResponse> {\n const plans = listPlansBySession(req.params.id);\n return { body: plans };\n}\n","import { listSpores, countSpores, getSpore } from '@myco/db/queries/spores.js';\nimport { listEntities, getEntity } from '@myco/db/queries/entities.js';\nimport { listDigestExtracts } from '@myco/db/queries/digest-extracts.js';\nimport { getGraphForNode } from '@myco/db/queries/graph-edges.js';\nimport { getDatabase } from '@myco/db/client.js';\nimport { DEFAULT_AGENT_ID } from '@myco/constants.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of items returned by list endpoints. */\nconst DEFAULT_LIST_LIMIT = 50;\n\n/** Default pagination offset for list endpoints. */\nconst DEFAULT_LIST_OFFSET = 0;\n\n/** Default graph traversal depth. */\nconst DEFAULT_GRAPH_DEPTH = 1;\n\n/** Maximum graph traversal depth (capped for performance). */\nconst MAX_GRAPH_DEPTH = 3;\n\n/** Spore node name preview length (first N chars of content). */\nconst SPORE_NAME_PREVIEW_CHARS = 60;\n\n/** Edge types to exclude from graph visualization (too granular). */\nconst EXCLUDED_GRAPH_EDGE_TYPES = new Set(['HAS_BATCH', 'EXTRACTED_FROM']);\n\n// ---------------------------------------------------------------------------\n// Spore handlers\n// ---------------------------------------------------------------------------\n\nexport async function handleListSpores(req: RouteRequest): Promise<RouteResponse> {\n const agentId = req.query.agent_id; // undefined = all agents\n const type = req.query.type;\n const status = req.query.status;\n const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT;\n const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET;\n const search = req.query.search || undefined;\n\n const filterOpts = {\n ...(agentId ? { agent_id: agentId } : {}),\n observation_type: type,\n status,\n search,\n };\n\n const spores = listSpores({ ...filterOpts, limit, offset });\n const total = countSpores(filterOpts);\n\n return { body: { spores, total, offset, limit } };\n}\n\nexport async function handleGetSpore(req: RouteRequest): Promise<RouteResponse> {\n const spore = getSpore(req.params.id);\n if (!spore) return { status: 404, body: { error: 'not_found' } };\n return { body: spore };\n}\n\n// ---------------------------------------------------------------------------\n// Entity handlers\n// ---------------------------------------------------------------------------\n\nexport async function handleListEntities(req: RouteRequest): Promise<RouteResponse> {\n const agentId = req.query.agent_id ?? DEFAULT_AGENT_ID;\n const type = req.query.type;\n const mentioned_in = req.query.mentioned_in;\n const note_type = req.query.note_type;\n const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT;\n const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET;\n\n const entities = listEntities({\n agent_id: agentId,\n type,\n mentioned_in,\n note_type,\n limit,\n offset,\n });\n\n return { body: { entities } };\n}\n\n// ---------------------------------------------------------------------------\n// Graph handler\n// ---------------------------------------------------------------------------\n\nexport async function handleGetGraph(req: RouteRequest): Promise<RouteResponse> {\n const depth = Math.min(Number(req.query.depth) || DEFAULT_GRAPH_DEPTH, MAX_GRAPH_DEPTH);\n\n // Verify center entity exists\n const center = getEntity(req.params.id);\n if (!center) return { status: 404, body: { error: 'not_found' } };\n\n // Use graph_edges for BFS traversal\n const graph = getGraphForNode(req.params.id, 'entity', { depth });\n\n // Filter out batch-related edges (too granular for visualization)\n const filteredEdges = graph.edges.filter(\n (e) => !EXCLUDED_GRAPH_EDGE_TYPES.has(e.type),\n );\n\n const graphDb = getDatabase();\n\n // Collect ALL unique node IDs from filtered edges, grouped by type\n const entityIds = new Set<string>();\n const sporeIds = new Set<string>();\n const sessionIds = new Set<string>();\n\n for (const edge of filteredEdges) {\n for (const [id, type] of [\n [edge.source_id, edge.source_type],\n [edge.target_id, edge.target_type],\n ] as [string, string][]) {\n switch (type) {\n case 'entity': entityIds.add(id); break;\n case 'spore': sporeIds.add(id); break;\n case 'session': sessionIds.add(id); break;\n // batch nodes are intentionally excluded\n }\n }\n }\n // Center entity is always included\n entityIds.add(center.id);\n\n // --- Batch-fetch entity nodes ---\n const entityIdArray = Array.from(entityIds);\n let entityNodes: Array<Record<string, unknown>> = [];\n if (entityIdArray.length > 0) {\n const placeholders = entityIdArray.map(() => '?').join(', ');\n entityNodes = graphDb.prepare(\n `SELECT id, type, name, properties, status, first_seen as created_at\n FROM entities WHERE id IN (${placeholders})`,\n ).all(...entityIdArray) as Array<Record<string, unknown>>;\n }\n\n // --- Batch-fetch spore nodes ---\n const sporeIdArray = Array.from(sporeIds);\n let sporeNodes: Array<Record<string, unknown>> = [];\n if (sporeIdArray.length > 0) {\n const placeholders = sporeIdArray.map(() => '?').join(', ');\n sporeNodes = graphDb.prepare(\n `SELECT id, observation_type, status, content, properties, created_at\n FROM spores WHERE id IN (${placeholders})`,\n ).all(...sporeIdArray) as Array<Record<string, unknown>>;\n }\n\n // --- Batch-fetch session nodes ---\n const sessionIdArray = Array.from(sessionIds);\n let sessionNodes: Array<Record<string, unknown>> = [];\n if (sessionIdArray.length > 0) {\n const placeholders = sessionIdArray.map(() => '?').join(', ');\n sessionNodes = graphDb.prepare(\n `SELECT id, title, summary, status, started_at as created_at\n FROM sessions WHERE id IN (${placeholders})`,\n ).all(...sessionIdArray) as Array<Record<string, unknown>>;\n }\n\n // --- Batch-fetch mention counts for entity nodes ---\n const mentionCounts = new Map<string, number>();\n if (entityIdArray.length > 0) {\n const placeholders = entityIdArray.map(() => '?').join(', ');\n const mentionRows = graphDb.prepare(\n `SELECT entity_id, COUNT(*) as count FROM entity_mentions\n WHERE entity_id IN (${placeholders}) GROUP BY entity_id`,\n ).all(...entityIdArray) as Array<Record<string, unknown>>;\n for (const row of mentionRows) {\n mentionCounts.set(row.entity_id as string, Number(row.count));\n }\n }\n\n // --- Build unified nodes array ---\n const allNodes = [\n ...entityNodes.map((n) => ({\n id: n.id as string,\n name: n.name as string,\n type: n.type as string,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n properties: (n.properties as string) ?? undefined,\n mention_count: mentionCounts.get(n.id as string) ?? 0,\n })),\n ...sporeNodes.map((n) => ({\n id: n.id as string,\n name: ((n.content as string) ?? '').slice(0, SPORE_NAME_PREVIEW_CHARS),\n type: 'spore' as const,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n content: n.content as string | undefined,\n properties: (n.properties as string) ?? undefined,\n observation_type: n.observation_type as string | undefined,\n })),\n ...sessionNodes.map((n) => ({\n id: n.id as string,\n name: (n.title as string) ?? `Session ${(n.id as string).slice(-6)}`,\n type: 'session' as const,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n content: (n.summary as string) ?? undefined,\n })),\n ];\n\n // Identify the center node from the unified array\n const centerNode = allNodes.find((n) => n.id === center.id);\n\n // Map edges to UI-friendly shape (label + weight instead of type + confidence)\n const uiEdges = filteredEdges.map((e) => ({\n source_id: e.source_id,\n target_id: e.target_id,\n label: e.type,\n weight: e.confidence,\n }));\n\n return {\n body: {\n center: centerNode ?? { ...center, mention_count: mentionCounts.get(center.id) ?? 0 },\n nodes: allNodes.filter((n) => n.id !== center.id),\n edges: uiEdges,\n depth,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Full graph handler\n// ---------------------------------------------------------------------------\n\n/** Maximum nodes returned in full graph view to prevent overload. */\nconst FULL_GRAPH_NODE_LIMIT = 500;\n\nexport async function handleGetFullGraph(_req: RouteRequest): Promise<RouteResponse> {\n const db = getDatabase();\n\n // Fetch all entities\n const entityRows = db.prepare(\n `SELECT id, type, name, properties, status, first_seen as created_at\n FROM entities WHERE agent_id = ? LIMIT ?`,\n ).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT) as Array<Record<string, unknown>>;\n\n // Fetch active spores (skip superseded)\n const sporeRows = db.prepare(\n `SELECT id, observation_type, status, content, properties, created_at\n FROM spores WHERE agent_id = ? AND status = 'active' LIMIT ?`,\n ).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT) as Array<Record<string, unknown>>;\n\n // Fetch recent sessions\n const sessionRows = db.prepare(\n `SELECT id, title, summary, status, started_at as created_at\n FROM sessions ORDER BY created_at DESC LIMIT ?`,\n ).all(FULL_GRAPH_NODE_LIMIT) as Array<Record<string, unknown>>;\n\n // Collect all node IDs for edge filtering\n const allIds = new Set<string>();\n for (const r of [...entityRows, ...sporeRows, ...sessionRows]) {\n allIds.add(r.id as string);\n }\n\n // Fetch edges between known nodes, excluding batch-level edges\n const excludedTypes = Array.from(EXCLUDED_GRAPH_EDGE_TYPES).map(() => '?').join(', ');\n const allIdsList = Array.from(allIds);\n const idPlaceholders = allIdsList.map(() => '?').join(', ');\n const edgeRows = db.prepare(\n `SELECT source_id, source_type, target_id, target_type, type, confidence\n FROM graph_edges\n WHERE agent_id = ?\n AND type NOT IN (${excludedTypes})\n AND source_id IN (${idPlaceholders})\n AND target_id IN (${idPlaceholders})`,\n ).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList) as Array<Record<string, unknown>>;\n\n const filteredEdges = edgeRows;\n\n // Mention counts for entity sizing\n const mentionCounts = new Map<string, number>();\n const entityIdArray = entityRows.map((r) => r.id as string);\n if (entityIdArray.length > 0) {\n const placeholders = entityIdArray.map(() => '?').join(', ');\n const mentionRows = db.prepare(\n `SELECT entity_id, COUNT(*) as count FROM entity_mentions\n WHERE entity_id IN (${placeholders}) GROUP BY entity_id`,\n ).all(...entityIdArray) as Array<Record<string, unknown>>;\n for (const row of mentionRows) {\n mentionCounts.set(row.entity_id as string, Number(row.count));\n }\n }\n\n // Build nodes\n const nodes = [\n ...entityRows.map((n) => ({\n id: n.id as string,\n name: n.name as string,\n type: n.type as string,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n properties: (n.properties as string) ?? undefined,\n mention_count: mentionCounts.get(n.id as string) ?? 0,\n })),\n ...sporeRows.map((n) => ({\n id: n.id as string,\n name: ((n.content as string) ?? '').slice(0, SPORE_NAME_PREVIEW_CHARS),\n type: 'spore' as const,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n content: n.content as string | undefined,\n properties: (n.properties as string) ?? undefined,\n observation_type: n.observation_type as string | undefined,\n })),\n ...sessionRows.map((n) => ({\n id: n.id as string,\n name: (n.title as string) ?? `Session ${(n.id as string).slice(-6)}`,\n type: 'session' as const,\n status: (n.status as string) ?? undefined,\n created_at: n.created_at as number | undefined,\n content: (n.summary as string) ?? undefined,\n })),\n ];\n\n const edges = filteredEdges.map((e) => ({\n source_id: e.source_id as string,\n target_id: e.target_id as string,\n label: e.type as string,\n weight: e.confidence as number | undefined,\n }));\n\n return { body: { nodes, edges } };\n}\n\n// ---------------------------------------------------------------------------\n// Digest handler\n// ---------------------------------------------------------------------------\n\nexport async function handleGetDigest(req: RouteRequest): Promise<RouteResponse> {\n const agentId = req.query.agent_id ?? DEFAULT_AGENT_ID;\n const extracts = listDigestExtracts(agentId);\n return { body: { tiers: extracts } };\n}\n","/**\n * Search API handler — supports FTS, semantic, and auto modes.\n *\n * - mode=fts: FTS5 full-text search (prompt_batches + activities)\n * - mode=semantic: Vector similarity search via VectorStore (sessions, spores, plans, artifacts)\n * - mode=auto (default): Tries semantic first, falls back to FTS if provider unavailable\n */\n\nimport { fullTextSearch, hydrateSearchResults } from '@myco/db/queries/search.js';\nimport {\n SEARCH_RESULTS_DEFAULT_LIMIT,\n SEARCH_SIMILARITY_THRESHOLD,\n TEAM_SOURCE_PREFIX,\n} from '@myco/constants.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\nimport type { EmbeddingManager } from '../embedding/manager.js';\nimport type { TeamSyncClient, TeamSearchResult } from '../team-sync.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Valid search modes. */\ntype SearchMode = 'auto' | 'semantic' | 'fts';\n\n/** Dependencies injected by the daemon when registering the route. */\nexport interface SearchDeps {\n embeddingManager: EmbeddingManager;\n getTeamClient?: () => TeamSyncClient | null;\n machineId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Handler factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a search handler with injected dependencies.\n *\n * Returns an async handler that supports `mode` query parameter:\n * - `auto` (default): tries semantic search, falls back to FTS\n * - `semantic`: vector similarity search only\n * - `fts`: FTS5 text search only\n */\nexport function createSearchHandler(deps: SearchDeps) {\n return async function handleSearch(req: RouteRequest): Promise<RouteResponse> {\n const query = req.query.q;\n if (!query) return { status: 400, body: { error: 'missing_query' } };\n\n const mode = (req.query.mode ?? 'auto') as SearchMode;\n const type = req.query.type;\n const limit = Number(req.query.limit) || SEARCH_RESULTS_DEFAULT_LIMIT;\n const namespace = req.query.namespace;\n\n // --- FTS-only mode ---\n if (mode === 'fts') {\n const results = fullTextSearch(query, { type, limit });\n return { body: { mode: 'fts', results } };\n }\n\n // --- Semantic or auto mode: attempt vector search ---\n const queryVector = await deps.embeddingManager.embedQuery(query);\n\n // If provider unavailable, auto falls back to FTS; semantic returns empty\n if (queryVector === null) {\n if (mode === 'auto') {\n const results = fullTextSearch(query, { type, limit });\n return { body: { mode: 'fts', results, fallback: true } };\n }\n // mode === 'semantic' but no provider\n return { body: { mode: 'semantic', results: [], provider_unavailable: true } };\n }\n\n // Vector search with optional namespace/type filtering\n const searchNamespace = namespace ?? type;\n const vectorResults = deps.embeddingManager.searchVectors(queryVector, {\n namespace: searchNamespace,\n limit,\n threshold: SEARCH_SIMILARITY_THRESHOLD,\n });\n\n // Hydrate local vector results into full SearchResults\n const localResults = hydrateSearchResults(vectorResults).map((r) => ({\n ...r,\n source: 'local',\n }));\n\n // Fan out to team search in parallel (if connected)\n const teamClient = deps.getTeamClient?.();\n let teamResults: Array<TeamSearchResult & { source: string }> = [];\n if (teamClient) {\n try {\n const teamResponse = await teamClient.search(query, { limit });\n teamResults = teamResponse.results.map((r) => ({\n ...r,\n source: `${TEAM_SOURCE_PREFIX}${r.machine_id}`,\n }));\n } catch {\n // Team search failure is non-blocking — local results still returned\n }\n }\n\n // Deduplicate: skip team results from this machine (we already have them locally)\n const dedupedTeam = deps.machineId\n ? teamResults.filter((r) => r.machine_id !== deps.machineId)\n : teamResults;\n\n // Merge by score (highest first), slice to limit\n const merged = [...localResults, ...dedupedTeam]\n .sort((a, b) => (b.score ?? 0) - (a.score ?? 0))\n .slice(0, limit);\n\n return { body: { mode: 'semantic', results: merged } };\n };\n}\n","/**\n * Context injection API handlers — digest at session start, semantic spore search per prompt.\n *\n * - POST /context: Injects digest extract + branch/session metadata at session start\n * - POST /context/prompt: Searches spore embeddings for relevant observations per prompt\n */\n\nimport { z } from 'zod';\nimport { getDigestExtract } from '@myco/db/queries/digest-extracts.js';\nimport { hydrateSearchResults } from '@myco/db/queries/search.js';\nimport {\n DEFAULT_AGENT_ID,\n EXCLUDED_SPORE_STATUSES,\n LOG_CONTEXT_PREVIEW_CHARS,\n PROMPT_CONTEXT_MIN_LENGTH,\n PROMPT_CONTEXT_MIN_SIMILARITY,\n PROMPT_CONTEXT_MAX_TOKENS,\n PROMPT_VECTOR_OVER_FETCH,\n estimateTokens,\n} from '@myco/constants.js';\nimport { LOG_KINDS } from '@myco/constants/log-kinds.js';\nimport type { MycoConfig } from '@myco/config/schema.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\nimport type { EmbeddingManager } from '../embedding/manager.js';\nimport type { DaemonLogger } from '../logger.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Dependencies injected by the daemon when registering context routes. */\nexport interface ContextDeps {\n embeddingManager: EmbeddingManager;\n logger: DaemonLogger;\n config: MycoConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Validation schemas\n// ---------------------------------------------------------------------------\n\nconst SessionContextBody = z.object({\n session_id: z.string().optional(),\n branch: z.string().optional(),\n});\n\nconst PromptContextBody = z.object({\n prompt: z.string(),\n session_id: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Session-start context handler\n// ---------------------------------------------------------------------------\n\n/**\n * Create a handler that injects digest extract + metadata at session start.\n *\n * Reads the configured digest tier from digest_extracts. If an extract exists,\n * it becomes the primary context payload. Branch and session ID are always included.\n */\nexport function createSessionContextHandler(deps: ContextDeps) {\n return async function handleSessionContext(req: RouteRequest): Promise<RouteResponse> {\n const { session_id, branch } = SessionContextBody.parse(req.body);\n const { logger, config } = deps;\n\n logger.debug(LOG_KINDS.CONTEXT_QUERY, 'Session context query', { session_id });\n\n try {\n const parts: string[] = [];\n\n // Digest extract — the primary session context payload\n const tier = config.context.digest_tier;\n const extract = getDigestExtract(DEFAULT_AGENT_ID, tier);\n\n if (extract) {\n parts.push(extract.content);\n logger.info(LOG_KINDS.CONTEXT_DIGEST, 'Digest extract found', {\n session_id,\n tier,\n content_length: extract.content.length,\n generated_at: extract.generated_at,\n });\n } else {\n logger.debug(LOG_KINDS.CONTEXT_DIGEST, 'No digest extract available', { session_id, tier });\n }\n\n // Branch info\n if (branch) {\n parts.push(`Branch:: \\`${branch}\\``);\n }\n\n // Session ID — always included\n parts.push(`Session:: \\`${session_id}\\``);\n\n const source = extract ? 'digest' : 'basic';\n const contextText = parts.join('\\n\\n');\n\n const estimatedTokens = estimateTokens(contextText);\n const preview = contextText.slice(0, LOG_CONTEXT_PREVIEW_CHARS);\n logger.info(LOG_KINDS.CONTEXT_SESSION, 'Session context injected', {\n session_id,\n source,\n tier: extract ? tier : undefined,\n text_length: contextText.length,\n estimated_tokens: estimatedTokens,\n generated_at: extract?.generated_at,\n preview,\n });\n logger.debug(LOG_KINDS.CONTEXT_SESSION, `Session context: \"${preview}…\" (${estimatedTokens} est. tokens, source=${source}${extract ? `, tier=${tier}, generated=${extract.generated_at}` : ''})`, {\n session_id,\n });\n\n return {\n body: {\n text: contextText,\n source,\n ...(extract ? { tier } : {}),\n },\n };\n } catch (error) {\n logger.error(LOG_KINDS.CONTEXT_SESSION, 'Session context failed', { error: (error as Error).message });\n return { body: { text: '' } };\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Per-prompt context handler\n// ---------------------------------------------------------------------------\n\n/**\n * Create a handler that searches spore embeddings for observations relevant to the prompt.\n *\n * Embeds the prompt, searches the 'spores' namespace via vector similarity,\n * post-filters by status, and returns formatted spore context.\n */\nexport function createPromptContextHandler(deps: ContextDeps) {\n return async function handlePromptContext(req: RouteRequest): Promise<RouteResponse> {\n const { prompt, session_id } = PromptContextBody.parse(req.body);\n const { logger, config, embeddingManager } = deps;\n\n // Guard: prompt search disabled\n if (!config.context.prompt_search) {\n logger.debug(LOG_KINDS.CONTEXT_PROMPT, 'Prompt search disabled by config', { session_id });\n return { body: { text: '' } };\n }\n\n // Guard: prompt too short\n if (prompt.length < PROMPT_CONTEXT_MIN_LENGTH) {\n logger.debug(LOG_KINDS.CONTEXT_PROMPT, 'Prompt too short for search', {\n session_id,\n length: prompt.length,\n min: PROMPT_CONTEXT_MIN_LENGTH,\n });\n return { body: { text: '' } };\n }\n\n // Guard: max spores is 0 (disabled)\n const maxSpores = config.context.prompt_max_spores;\n if (maxSpores === 0) {\n logger.debug(LOG_KINDS.CONTEXT_PROMPT, 'Prompt spore injection disabled (max_spores=0)', { session_id });\n return { body: { text: '' } };\n }\n\n // Embed the prompt\n const queryVector = await embeddingManager.embedQuery(prompt);\n if (!queryVector) {\n logger.debug(LOG_KINDS.CONTEXT_EMBED, 'Embedding provider unavailable for prompt search', { session_id });\n return { body: { text: '' } };\n }\n\n // Search spores namespace — over-fetch to compensate for post-filtering\n const vectorResults = embeddingManager.searchVectors(queryVector, {\n namespace: 'spores',\n limit: maxSpores * PROMPT_VECTOR_OVER_FETCH,\n threshold: PROMPT_CONTEXT_MIN_SIMILARITY,\n });\n\n logger.debug(LOG_KINDS.CONTEXT_SEARCH, 'Prompt vector search completed', {\n session_id,\n raw_results: vectorResults.length,\n top_similarity: vectorResults[0]?.similarity,\n });\n\n if (vectorResults.length === 0) {\n return { body: { text: '' } };\n }\n\n // Post-filter: exclude superseded/archived spores via domain_metadata\n const eligible = vectorResults.filter(\n (r) => !EXCLUDED_SPORE_STATUSES.has(r.metadata.status as string),\n );\n\n if (eligible.length === 0) {\n logger.debug(LOG_KINDS.CONTEXT_FILTER, 'All spore results excluded by status filter', { session_id });\n return { body: { text: '' } };\n }\n\n // Take top N and hydrate with full record data\n const topResults = eligible.slice(0, maxSpores);\n const hydrated = hydrateSearchResults(topResults);\n const spores = hydrated.filter((r) => r.type === 'spore');\n\n if (spores.length === 0) {\n return { body: { text: '' } };\n }\n\n // Format spore context with token budget enforcement\n const text = formatSporeContext(spores);\n\n const promptTokens = estimateTokens(text);\n const titles = spores.map((s) => s.title);\n logger.info(LOG_KINDS.CONTEXT_PROMPT, 'Prompt context injected', {\n session_id,\n spore_count: spores.length,\n scores: spores.map((s) => s.score.toFixed(3)),\n spore_titles: titles,\n estimated_tokens: promptTokens,\n preview: text.slice(0, LOG_CONTEXT_PREVIEW_CHARS),\n });\n logger.debug(LOG_KINDS.CONTEXT_PROMPT, `Prompt context: ${spores.length} spores [${titles.join(', ')}] (~${promptTokens} tokens)`, {\n session_id,\n scores: spores.map((s) => s.score.toFixed(3)),\n });\n\n return { body: { text } };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Format hydrated spore search results as markdown context for injection.\n * Respects PROMPT_CONTEXT_MAX_TOKENS budget.\n */\nfunction formatSporeContext(\n spores: Array<{ title: string; preview: string; score: number }>,\n): string {\n const header = 'Relevant vault observations:';\n let text = header;\n let tokens = estimateTokens(text);\n\n for (const spore of spores) {\n const line = `\\n- (${spore.title}) ${spore.preview}`;\n const lineTokens = estimateTokens(line);\n\n if (tokens + lineTokens > PROMPT_CONTEXT_MAX_TOKENS) break;\n\n text += line;\n tokens += lineTokens;\n }\n\n // Don't return just the header with no items\n return text === header ? '' : text;\n}\n","/**\n * Activity feed query — unified timeline across sessions, agent_runs, and spores.\n *\n * Uses UNION ALL to merge per-table subqueries, then a final ORDER BY + LIMIT\n * to produce a cross-table timeline ordered by timestamp descending.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport { FEED_DEFAULT_LIMIT } from '@myco/constants.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single entry in the unified activity feed. */\nexport interface FeedEntry {\n type: 'session' | 'agent_run' | 'spore';\n id: string;\n summary: string;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Return the most recent activity across sessions, agent runs, and spores,\n * merged into a single timeline sorted by timestamp descending.\n *\n * Each branch contributes up to `limit` candidates; the final result is\n * also capped at `limit`.\n *\n * SQLite does not support per-branch ORDER BY + LIMIT inside UNION ALL\n * parenthesized subqueries the way PostgreSQL does. Instead, each branch\n * is wrapped as a subquery (SELECT ... ORDER BY ... LIMIT ?) to achieve\n * the same effect.\n *\n * @param limit - max entries to return (defaults to FEED_DEFAULT_LIMIT)\n */\nexport function getActivityFeed(limit: number = FEED_DEFAULT_LIMIT): FeedEntry[] {\n const db = getDatabase();\n\n const rows = db.prepare(`\n SELECT * FROM (\n SELECT 'session' as type, id, COALESCE(title, 'Session ' || substr(id, 1, 8)) as summary,\n COALESCE(ended_at, started_at) as timestamp\n FROM sessions ORDER BY started_at DESC LIMIT ?\n )\n\n UNION ALL\n\n SELECT * FROM (\n SELECT 'agent_run' as type, id, task || ' — ' || status as summary,\n COALESCE(completed_at, started_at) as timestamp\n FROM agent_runs ORDER BY started_at DESC LIMIT ?\n )\n\n UNION ALL\n\n SELECT * FROM (\n SELECT 'spore' as type, id, observation_type || ': ' || substr(content, 1, 80) as summary,\n created_at as timestamp\n FROM spores WHERE status = 'active' ORDER BY created_at DESC LIMIT ?\n )\n\n ORDER BY timestamp DESC LIMIT ?\n `).all(limit, limit, limit, limit) as FeedEntry[];\n\n return rows;\n}\n","import { getActivityFeed } from '@myco/db/queries/feed.js';\nimport { FEED_DEFAULT_LIMIT } from '@myco/constants.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Handler\n// ---------------------------------------------------------------------------\n\nexport async function handleGetFeed(req: RouteRequest): Promise<RouteResponse> {\n const limit = Number(req.query.limit) || FEED_DEFAULT_LIMIT;\n const feed = getActivityFeed(limit);\n return { body: feed };\n}\n","import { loadManifests } from '@myco/symbionts/detect.js';\nimport type { RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Public manifest fields exposed via the API (no internal hook config). */\ninterface SymbiontInfo {\n name: string;\n displayName: string;\n binary: string;\n resumeCommand?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Handler\n// ---------------------------------------------------------------------------\n\n/**\n * List all registered symbiont manifests.\n *\n * Returns the public-facing subset of each manifest — enough for the UI\n * to build resume commands, display agent names, etc.\n */\nexport async function handleListSymbionts(): Promise<RouteResponse> {\n const manifests = loadManifests();\n\n const symbionts: SymbiontInfo[] = manifests.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n binary: m.binary,\n ...(m.resumeCommand ? { resumeCommand: m.resumeCommand } : {}),\n }));\n\n return { body: { symbionts } };\n}\n","import { getEmbeddingQueueDepth } from '@myco/db/queries/embeddings.js';\nimport { loadConfig } from '../../config/loader.js';\nimport { EMBEDDING_BATCH_SIZE } from '../../constants.js';\nimport type { EmbeddingManager } from '../embedding/index.js';\nimport type { RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Status when no items are pending embedding. */\nconst EMBEDDING_STATUS_IDLE = 'idle';\n\n/** Status when items are waiting to be embedded. */\nconst EMBEDDING_STATUS_PENDING = 'pending';\n\n// ---------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------\n\nexport async function handleGetEmbeddingStatus(vaultDir: string): Promise<RouteResponse> {\n const config = loadConfig(vaultDir);\n\n const { queue_depth, embedded_count } = getEmbeddingQueueDepth();\n\n return {\n body: {\n provider: config.embedding.provider,\n model: config.embedding.model,\n base_url: config.embedding.base_url ?? null,\n queue_depth,\n embedded_count,\n status: queue_depth === 0 ? EMBEDDING_STATUS_IDLE : EMBEDDING_STATUS_PENDING,\n },\n };\n}\n\nexport function handleEmbeddingDetails(manager: EmbeddingManager): RouteResponse {\n const details = manager.getDetails();\n return { body: details };\n}\n\nexport function handleEmbeddingRebuild(manager: EmbeddingManager): RouteResponse {\n const result = manager.rebuildAll();\n return { body: result };\n}\n\nexport async function handleEmbeddingReconcile(manager: EmbeddingManager): Promise<RouteResponse> {\n const result = await manager.reconcile(EMBEDDING_BATCH_SIZE);\n return { body: result };\n}\n\nexport function handleEmbeddingCleanOrphans(manager: EmbeddingManager): RouteResponse {\n const result = manager.cleanOrphans();\n return { body: result };\n}\n\nexport async function handleEmbeddingReembedStale(manager: EmbeddingManager): Promise<RouteResponse> {\n const result = await manager.reembedStale(EMBEDDING_BATCH_SIZE);\n return { body: result };\n}\n","/**\n * EmbeddingManager — orchestrates the embedding lifecycle.\n *\n * Coordinates three injected dependencies:\n * - VectorStore: stores/retrieves vectors (sync, sqlite-vec)\n * - ManagerEmbeddingProvider: generates vectors from text (async)\n * - EmbeddableRecordSource: queries record store for embeddable rows (sync)\n *\n * All write-path methods (onContentWritten, onStatusChanged, onRemoved) are\n * fire-and-forget safe — they catch and log errors, never throw.\n *\n * The reconcile() method is called by the reconcile worker on a timer.\n * Operations UI calls rebuildAll(), cleanOrphans(), getDetails().\n */\n\nimport { createHash } from 'node:crypto';\nimport { CONTENT_HASH_ALGORITHM, epochSeconds } from '@myco/constants.js';\nimport { LOG_KINDS } from '@myco/constants/log-kinds.js';\nimport {\n EMBEDDABLE_NAMESPACES,\n type EmbeddableNamespace,\n type DomainMetadata,\n type EmbeddingDetails,\n type ReconcileResult,\n type VectorStore,\n type VectorSearchResult,\n type ManagerEmbeddingProvider,\n type EmbeddableRecordSource,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Spore status that qualifies for embedding. */\nconst ACTIVE_STATUS = 'active';\n\n// ---------------------------------------------------------------------------\n// Logger interface (matches DaemonLogger method signatures)\n// ---------------------------------------------------------------------------\n\ninterface Logger {\n debug(cat: string, msg: string, data?: Record<string, unknown>): void;\n info(cat: string, msg: string, data?: Record<string, unknown>): void;\n warn(cat: string, msg: string, data?: Record<string, unknown>): void;\n error(cat: string, msg: string, data?: Record<string, unknown>): void;\n}\n\n// ---------------------------------------------------------------------------\n// EmbeddingManager\n// ---------------------------------------------------------------------------\n\nexport class EmbeddingManager {\n constructor(\n private vectorStore: VectorStore,\n private embeddingProvider: ManagerEmbeddingProvider,\n private recordSource: EmbeddableRecordSource,\n private logger: Logger,\n ) {}\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private contentHash(text: string): string {\n return createHash(CONTENT_HASH_ALGORITHM).update(text).digest('hex');\n }\n\n // -------------------------------------------------------------------------\n // Write-path event handlers\n // -------------------------------------------------------------------------\n\n /**\n * Called when content is written (session note, spore, plan, artifact).\n * Embeds the text and stores the vector. Fire-and-forget safe.\n */\n async onContentWritten(\n namespace: EmbeddableNamespace,\n id: string,\n text: string,\n metadata: DomainMetadata,\n ): Promise<void> {\n try {\n const embedding = await this.embeddingProvider.embed(text);\n if (embedding === null) {\n this.logger.warn(LOG_KINDS.EMBEDDING_PROVIDER, 'Provider unavailable, skipping embed', {\n namespace,\n id,\n });\n return;\n }\n\n const hash = this.contentHash(text);\n\n this.vectorStore.upsert(namespace, id, embedding, {\n model: this.embeddingProvider.model,\n provider: this.embeddingProvider.providerName,\n dimensions: this.embeddingProvider.dimensions,\n content_hash: hash,\n embedded_at: epochSeconds(),\n domain_metadata: metadata,\n });\n\n this.recordSource.markEmbedded(namespace, id);\n\n this.logger.debug(LOG_KINDS.EMBEDDING_EMBED, 'Vector stored', { namespace, id });\n } catch (err) {\n this.logger.warn(LOG_KINDS.EMBEDDING_EMBED, 'Failed to embed content', {\n namespace,\n id,\n error: String(err),\n });\n }\n }\n\n /**\n * Called when a spore's status changes (e.g., superseded, archived).\n * Removes the vector for non-active statuses.\n */\n onStatusChanged(namespace: 'spores', id: string, status: string): void {\n try {\n if (status === ACTIVE_STATUS) return;\n\n this.vectorStore.remove(namespace, id);\n this.recordSource.clearEmbedded(namespace, id);\n\n this.logger.debug(LOG_KINDS.EMBEDDING_CLEANUP, 'Vector removed', {\n namespace,\n id,\n reason: `status=${status}`,\n });\n } catch (err) {\n this.logger.warn(LOG_KINDS.EMBEDDING_CLEANUP, 'Failed to remove vector on status change', {\n namespace,\n id,\n status,\n error: String(err),\n });\n }\n }\n\n /**\n * Called when a record is deleted. Removes the vector.\n * No clearEmbedded needed — the record itself is being deleted.\n */\n onRemoved(namespace: EmbeddableNamespace, id: string): void {\n try {\n this.vectorStore.remove(namespace, id);\n\n this.logger.debug(LOG_KINDS.EMBEDDING_CLEANUP, 'Vector removed', {\n namespace,\n id,\n reason: 'record deleted',\n });\n } catch (err) {\n this.logger.warn(LOG_KINDS.EMBEDDING_CLEANUP, 'Failed to remove vector on delete', {\n namespace,\n id,\n error: String(err),\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Reconciliation\n // -------------------------------------------------------------------------\n\n /**\n * Embed missing rows, re-embed stale vectors, and clean orphans across all namespaces.\n * Called by the reconcile worker on a timer.\n */\n async reconcile(batchSize: number): Promise<ReconcileResult> {\n const start = Date.now();\n let embedded = 0;\n let stale_reembedded = 0;\n let orphans_cleaned = 0;\n const currentModel = this.embeddingProvider.model;\n\n for (const namespace of EMBEDDABLE_NAMESPACES) {\n // Phase 1: Embed missing rows\n const rows = this.recordSource.getEmbeddableRows(namespace, batchSize);\n\n for (const row of rows) {\n const embedding = await this.embeddingProvider.embed(row.text);\n if (embedding === null) {\n this.logger.warn(LOG_KINDS.EMBEDDING_PROVIDER, 'Provider unavailable during reconcile, returning partial progress', {\n namespace,\n embedded,\n });\n return {\n embedded,\n stale_reembedded,\n orphans_cleaned,\n duration_ms: Date.now() - start,\n };\n }\n\n const hash = this.contentHash(row.text);\n\n this.vectorStore.upsert(namespace, row.id, embedding, {\n model: currentModel,\n provider: this.embeddingProvider.providerName,\n dimensions: this.embeddingProvider.dimensions,\n content_hash: hash,\n embedded_at: epochSeconds(),\n domain_metadata: row.metadata,\n });\n\n this.recordSource.markEmbedded(namespace, row.id);\n embedded++;\n }\n\n // Phase 2: Re-embed stale vectors (model mismatch)\n const staleIds = this.vectorStore.getStaleIds(namespace, currentModel, batchSize);\n if (staleIds.length > 0) {\n const records = this.recordSource.getRecordContent(namespace, staleIds);\n const foundIds = new Set(records.map((r) => r.id));\n\n for (const record of records) {\n const embedding = await this.embeddingProvider.embed(record.text);\n if (embedding === null) {\n this.logger.warn(LOG_KINDS.EMBEDDING_PROVIDER, 'Provider unavailable during stale re-embed, returning partial progress', {\n namespace,\n stale_reembedded,\n });\n return {\n embedded,\n stale_reembedded,\n orphans_cleaned,\n duration_ms: Date.now() - start,\n };\n }\n\n this.vectorStore.upsert(namespace, record.id, embedding, {\n model: currentModel,\n provider: this.embeddingProvider.providerName,\n dimensions: this.embeddingProvider.dimensions,\n content_hash: this.contentHash(record.text),\n embedded_at: epochSeconds(),\n domain_metadata: record.metadata,\n });\n\n stale_reembedded++;\n }\n\n // Clean stale vectors whose source records no longer exist\n for (const staleId of staleIds) {\n if (!foundIds.has(staleId)) {\n this.vectorStore.remove(namespace, staleId);\n this.logger.warn(LOG_KINDS.EMBEDDING_CLEANUP, 'Stale orphan vector cleaned', {\n namespace,\n id: staleId,\n });\n orphans_cleaned++;\n }\n }\n }\n\n // Phase 3: Orphan sweep\n orphans_cleaned += this.sweepOrphans(namespace);\n }\n\n const duration_ms = Date.now() - start;\n\n if (embedded > 0 || stale_reembedded > 0 || orphans_cleaned > 0) {\n this.logger.info(LOG_KINDS.EMBEDDING_RECONCILE, 'Reconcile cycle completed', {\n embedded,\n stale_reembedded,\n orphans_cleaned,\n duration_ms,\n });\n }\n\n return { embedded, stale_reembedded, orphans_cleaned, duration_ms };\n }\n\n /**\n * Remove orphan vectors (vectors without corresponding active records).\n */\n cleanOrphans(): { orphans_cleaned: number } {\n let orphans_cleaned = 0;\n for (const namespace of EMBEDDABLE_NAMESPACES) {\n orphans_cleaned += this.sweepOrphans(namespace);\n }\n return { orphans_cleaned };\n }\n\n // -------------------------------------------------------------------------\n // Operations\n // -------------------------------------------------------------------------\n\n /**\n * Clear all vectors and reset embedded flags.\n * The reconcile worker picks up all rows on subsequent cycles.\n */\n rebuildAll(): { queued: number } {\n const { cleared } = this.vectorStore.clear();\n this.recordSource.clearAllEmbedded();\n\n this.logger.info(LOG_KINDS.EMBEDDING_REBUILD, 'Rebuild started', { cleared });\n\n return { queued: cleared };\n }\n\n /**\n * Re-embed vectors that were created with a different model.\n */\n async reembedStale(batchSize: number): Promise<{ reembedded: number }> {\n let reembedded = 0;\n const currentModel = this.embeddingProvider.model;\n\n for (const namespace of EMBEDDABLE_NAMESPACES) {\n const staleIds = this.vectorStore.getStaleIds(namespace, currentModel, batchSize);\n if (staleIds.length === 0) continue;\n\n const records = this.recordSource.getRecordContent(namespace, staleIds);\n\n for (const record of records) {\n const embedding = await this.embeddingProvider.embed(record.text);\n if (embedding === null) {\n this.logger.warn(LOG_KINDS.EMBEDDING_PROVIDER, 'Provider unavailable during re-embed', {\n namespace,\n reembedded,\n });\n return { reembedded };\n }\n\n const hash = this.contentHash(record.text);\n\n this.vectorStore.upsert(namespace, record.id, embedding, {\n model: currentModel,\n provider: this.embeddingProvider.providerName,\n dimensions: this.embeddingProvider.dimensions,\n content_hash: hash,\n embedded_at: epochSeconds(),\n domain_metadata: record.metadata,\n });\n\n reembedded++;\n }\n }\n\n return { reembedded };\n }\n\n /**\n * Get details for the operations UI: vector stats, pending counts, provider info.\n */\n getDetails(): EmbeddingDetails {\n const stats = this.vectorStore.stats();\n\n const pending: Record<string, number> = {};\n for (const namespace of EMBEDDABLE_NAMESPACES) {\n pending[namespace] = this.recordSource.getPendingCount(namespace);\n }\n\n return {\n ...stats,\n pending,\n provider: {\n name: this.embeddingProvider.providerName,\n model: this.embeddingProvider.model,\n available: true, // If we got here, the manager was constructed with a provider\n },\n };\n }\n\n /**\n * Pass-through for search handler — embed a query string.\n */\n async embedQuery(text: string): Promise<number[] | null> {\n return this.embeddingProvider.embed(text);\n }\n\n /**\n * Pass-through for search handler — similarity search via the vector store.\n * Keeps the VectorStore private to the manager.\n */\n searchVectors(query: number[], options?: {\n namespace?: string;\n limit?: number;\n threshold?: number;\n filters?: Record<string, unknown>;\n }): VectorSearchResult[] {\n return this.vectorStore.search(query, options);\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n /**\n * Sweep orphan vectors for a single namespace. Returns count removed.\n *\n * Compares vector IDs against active record IDs — vectors without a matching\n * active record are removed. Does NOT short-circuit on count equality because\n * equal counts can mask orphans (e.g., 3 orphan vectors + 3 active records\n * missing vectors = same count, zero cleanup).\n */\n private sweepOrphans(namespace: EmbeddableNamespace): number {\n const embeddedIds = this.vectorStore.getEmbeddedIds(namespace);\n if (embeddedIds.length === 0) return 0;\n\n const activeIds = this.recordSource.getActiveRecordIds(namespace);\n const activeSet = new Set(activeIds);\n let cleaned = 0;\n\n for (const vecId of embeddedIds) {\n if (!activeSet.has(vecId)) {\n this.vectorStore.remove(namespace, vecId);\n this.logger.warn(LOG_KINDS.EMBEDDING_CLEANUP, 'Orphan vector cleaned', {\n namespace,\n id: vecId,\n });\n cleaned++;\n }\n }\n\n return cleaned;\n }\n}\n","/**\n * SqliteVecVectorStore — vector storage backed by sqlite-vec in a separate vectors.db.\n *\n * Fully decoupled from the record store (myco.db). Owns:\n * - One vec0 virtual table per embeddable namespace (cosine distance metric)\n * - A regular `embedding_metadata` table for provider/model/hash tracking\n *\n * All methods are synchronous (better-sqlite3 is sync).\n */\n\nimport Database from 'better-sqlite3';\nimport type { Database as DatabaseType, Statement } from 'better-sqlite3';\nimport * as sqliteVec from 'sqlite-vec';\nimport { EMBEDDING_DIMENSIONS } from '@myco/db/schema.js';\nimport {\n EMBEDDABLE_NAMESPACES,\n type EmbeddableNamespace,\n type VectorStore,\n type VectorSearchResult,\n type VectorStoreStats,\n} from '@myco/daemon/embedding/types.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default search result limit when none is specified. */\nconst DEFAULT_SEARCH_LIMIT = 10;\n\n/** Default similarity threshold — results below this are excluded. */\nconst DEFAULT_SIMILARITY_THRESHOLD = 0;\n\n/** Fallback model name when metadata omits it. */\nconst DEFAULT_META_MODEL = 'unknown';\n\n/** Fallback provider name when metadata omits it. */\nconst DEFAULT_META_PROVIDER = 'unknown';\n\n/** Fallback content hash when metadata omits it. */\nconst DEFAULT_META_CONTENT_HASH = '';\n\n/** Metadata columns safe to filter on in search queries (prevents SQL injection via key names). */\nconst FILTERABLE_COLUMNS = new Set(['model', 'provider', 'namespace']);\n\n/**\n * Convert cosine *distance* (0 = identical, 2 = opposite) to a similarity\n * score in [−1, 1]. Cosine distance = 1 − cosine_similarity.\n */\nfunction cosineDistanceToSimilarity(distance: number): number {\n return 1 - distance;\n}\n\n// ---------------------------------------------------------------------------\n// Schema DDL\n// ---------------------------------------------------------------------------\n\nconst METADATA_TABLE = `\n CREATE TABLE IF NOT EXISTS embedding_metadata (\n namespace TEXT NOT NULL,\n record_id TEXT NOT NULL,\n model TEXT NOT NULL,\n provider TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n content_hash TEXT NOT NULL,\n embedded_at INTEGER NOT NULL,\n domain_metadata TEXT,\n PRIMARY KEY (namespace, record_id)\n )`;\n\nconst METADATA_MODEL_INDEX = `\n CREATE INDEX IF NOT EXISTS idx_emb_meta_model\n ON embedding_metadata (namespace, model)`;\n\n/** Build the DDL for a single vec0 virtual table. */\nfunction vecTableDDL(namespace: EmbeddableNamespace): string {\n return `CREATE VIRTUAL TABLE IF NOT EXISTS vec_${namespace} USING vec0(\n record_id TEXT PRIMARY KEY,\n embedding float[${EMBEDDING_DIMENSIONS}] distance_metric=cosine\n )`;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport class SqliteVecVectorStore implements VectorStore {\n private db: DatabaseType;\n\n // Cached prepared statements (lazy-initialized per namespace)\n private deleteVecStmts = new Map<string, Statement>();\n private insertVecStmts = new Map<string, Statement>();\n private upsertMetaStmt!: Statement;\n private deleteMetaStmt!: Statement;\n private searchStmts = new Map<string, Statement>();\n private statsCountStmt!: Statement;\n private statsModelsStmt!: Statement;\n private staleIdsStmt!: Statement;\n private embeddedIdsStmt!: Statement;\n\n constructor(dbPath?: string) {\n this.db = new Database(dbPath ?? ':memory:');\n sqliteVec.load(this.db);\n this.db.pragma('journal_mode = WAL');\n this.createSchema();\n this.prepareStatements();\n }\n\n // -------------------------------------------------------------------------\n // Schema\n // -------------------------------------------------------------------------\n\n private createSchema(): void {\n this.db.exec(METADATA_TABLE);\n this.db.exec(METADATA_MODEL_INDEX);\n for (const ns of EMBEDDABLE_NAMESPACES) {\n this.db.exec(vecTableDDL(ns));\n }\n }\n\n private prepareStatements(): void {\n this.upsertMetaStmt = this.db.prepare(`\n INSERT INTO embedding_metadata (namespace, record_id, model, provider, dimensions, content_hash, embedded_at, domain_metadata)\n VALUES (@namespace, @record_id, @model, @provider, @dimensions, @content_hash, @embedded_at, @domain_metadata)\n ON CONFLICT (namespace, record_id) DO UPDATE SET\n model = excluded.model,\n provider = excluded.provider,\n dimensions = excluded.dimensions,\n content_hash = excluded.content_hash,\n embedded_at = excluded.embedded_at,\n domain_metadata = excluded.domain_metadata\n `);\n\n this.deleteMetaStmt = this.db.prepare(\n `DELETE FROM embedding_metadata WHERE namespace = ? AND record_id = ?`\n );\n this.statsCountStmt = this.db.prepare(\n `SELECT COUNT(*) AS cnt FROM embedding_metadata WHERE namespace = ?`\n );\n this.statsModelsStmt = this.db.prepare(\n `SELECT model, COUNT(*) AS cnt FROM embedding_metadata WHERE namespace = ? GROUP BY model`\n );\n this.staleIdsStmt = this.db.prepare(\n `SELECT record_id FROM embedding_metadata WHERE namespace = ? AND model != ? LIMIT ?`\n );\n this.embeddedIdsStmt = this.db.prepare(\n `SELECT record_id FROM embedding_metadata WHERE namespace = ?`\n );\n\n // Per-namespace statements\n for (const ns of EMBEDDABLE_NAMESPACES) {\n this.deleteVecStmts.set(\n ns,\n this.db.prepare(`DELETE FROM vec_${ns} WHERE record_id = ?`)\n );\n this.insertVecStmts.set(\n ns,\n this.db.prepare(`INSERT INTO vec_${ns}(record_id, embedding) VALUES (?, ?)`)\n );\n this.searchStmts.set(\n ns,\n this.db.prepare(`\n SELECT v.record_id, v.distance,\n em.model, em.provider, em.content_hash, em.embedded_at, em.domain_metadata\n FROM vec_${ns} v\n LEFT JOIN embedding_metadata em\n ON em.namespace = '${ns}' AND em.record_id = v.record_id\n WHERE v.embedding MATCH ?\n AND k = ?\n ORDER BY v.distance\n `)\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // VectorStore interface\n // -------------------------------------------------------------------------\n\n upsert(\n namespace: string,\n id: string,\n embedding: number[],\n metadata?: Record<string, unknown>,\n ): void {\n this.validateNamespace(namespace);\n const ns = namespace as EmbeddableNamespace;\n\n const vec = new Float32Array(embedding);\n\n const txn = this.db.transaction(() => {\n // Delete-then-insert for vec0 (INSERT OR REPLACE not fully supported)\n this.deleteVecStmts.get(ns)!.run(id);\n this.insertVecStmts.get(ns)!.run(id, vec);\n\n // Upsert metadata\n this.upsertMetaStmt.run({\n namespace: ns,\n record_id: id,\n model: (metadata?.['model'] as string) ?? DEFAULT_META_MODEL,\n provider: (metadata?.['provider'] as string) ?? DEFAULT_META_PROVIDER,\n dimensions: embedding.length,\n content_hash: (metadata?.['content_hash'] as string) ?? DEFAULT_META_CONTENT_HASH,\n embedded_at: (metadata?.['embedded_at'] as number) ?? Date.now(),\n domain_metadata: metadata?.['domain_metadata']\n ? JSON.stringify(metadata['domain_metadata'])\n : null,\n });\n });\n\n txn();\n }\n\n remove(namespace: string, id: string): void {\n this.validateNamespace(namespace);\n const ns = namespace as EmbeddableNamespace;\n\n const txn = this.db.transaction(() => {\n this.deleteVecStmts.get(ns)!.run(id);\n this.deleteMetaStmt.run(ns, id);\n });\n\n txn();\n }\n\n clear(namespace?: string): { cleared: number } {\n let cleared = 0;\n\n const targets = namespace\n ? [this.validatedNamespace(namespace)]\n : [...EMBEDDABLE_NAMESPACES];\n\n const txn = this.db.transaction(() => {\n for (const ns of targets) {\n // Count rows before clearing\n const countRow = this.db\n .prepare(`SELECT COUNT(*) as cnt FROM embedding_metadata WHERE namespace = ?`)\n .get(ns) as { cnt: number };\n cleared += countRow.cnt;\n\n // Delete all vectors in this namespace's vec table\n this.db.exec(`DELETE FROM vec_${ns}`);\n\n // Delete metadata for this namespace\n this.db\n .prepare(`DELETE FROM embedding_metadata WHERE namespace = ?`)\n .run(ns);\n }\n });\n\n txn();\n return { cleared };\n }\n\n /**\n * KNN similarity search across one or all namespaces.\n *\n * Threshold filtering is applied **post-KNN**: sqlite-vec returns the top-k\n * nearest neighbors first, then results below `threshold` are discarded.\n * This means fewer than `limit` results may be returned when a threshold is set.\n * This is standard KNN behavior, not a bug.\n */\n search(\n query: number[],\n options?: {\n namespace?: string;\n limit?: number;\n threshold?: number;\n filters?: Record<string, unknown>;\n },\n ): VectorSearchResult[] {\n const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;\n const threshold = options?.threshold ?? DEFAULT_SIMILARITY_THRESHOLD;\n const queryVec = new Float32Array(query);\n\n const targets = options?.namespace\n ? [this.validatedNamespace(options.namespace)]\n : [...EMBEDDABLE_NAMESPACES];\n\n const hasFilters = options?.filters && Object.keys(options.filters).length > 0;\n const results: VectorSearchResult[] = [];\n\n for (const ns of targets) {\n let rows: Array<Record<string, unknown>>;\n\n if (hasFilters) {\n // Build a filtered query that JOINs with embedding_metadata\n const { sql, params } = this.buildFilteredSearchQuery(\n ns,\n options!.filters!,\n limit,\n );\n const stmt = this.db.prepare(sql);\n rows = stmt.all(queryVec, limit, ...params) as Array<Record<string, unknown>>;\n } else {\n rows = this.searchStmts.get(ns)!.all(queryVec, limit) as Array<Record<string, unknown>>;\n }\n\n for (const row of rows) {\n const similarity = cosineDistanceToSimilarity(row.distance as number);\n if (similarity >= threshold) {\n results.push({\n id: row.record_id as string,\n namespace: ns,\n similarity,\n metadata: {\n model: row.model,\n provider: row.provider,\n content_hash: row.content_hash,\n embedded_at: row.embedded_at,\n ...(row.domain_metadata ? JSON.parse(row.domain_metadata as string) : {}),\n },\n });\n }\n }\n }\n\n // Sort by similarity DESC across all namespaces, then truncate to limit\n results.sort((a, b) => b.similarity - a.similarity);\n return results.slice(0, limit);\n }\n\n stats(namespace?: string): VectorStoreStats {\n const targets = namespace\n ? [this.validatedNamespace(namespace)]\n : [...EMBEDDABLE_NAMESPACES];\n\n let total = 0;\n const by_namespace: Record<string, { embedded: number; stale: number }> = {};\n const models: Record<string, number> = {};\n\n for (const ns of targets) {\n const countRow = this.statsCountStmt.get(ns) as { cnt: number };\n const modelRows = this.statsModelsStmt.all(ns) as Array<{ model: string; cnt: number }>;\n\n // \"stale\" = count of rows whose model is NOT the most common model.\n // Without knowing the \"current model\" (which stats() doesn't receive),\n // we approximate by treating the majority model as current.\n let stale = 0;\n let maxModelCount = 0;\n for (const mr of modelRows) {\n models[mr.model] = (models[mr.model] ?? 0) + mr.cnt;\n if (mr.cnt > maxModelCount) maxModelCount = mr.cnt;\n }\n stale = countRow.cnt - maxModelCount;\n if (stale < 0) stale = 0;\n\n by_namespace[ns] = { embedded: countRow.cnt, stale };\n total += countRow.cnt;\n }\n\n return { total, by_namespace, models };\n }\n\n getStaleIds(namespace: string, currentModel: string, limit: number): string[] {\n this.validateNamespace(namespace);\n const rows = this.staleIdsStmt.all(namespace, currentModel, limit) as Array<{ record_id: string }>;\n return rows.map((r) => r.record_id);\n }\n\n getEmbeddedIds(namespace: string): string[] {\n this.validateNamespace(namespace);\n const rows = this.embeddedIdsStmt.all(namespace) as Array<{ record_id: string }>;\n return rows.map((r) => r.record_id);\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n close(): void {\n this.db.close();\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private validateNamespace(namespace: string): void {\n if (!(EMBEDDABLE_NAMESPACES as readonly string[]).includes(namespace)) {\n throw new Error(\n `Invalid namespace \"${namespace}\". Must be one of: ${EMBEDDABLE_NAMESPACES.join(', ')}`,\n );\n }\n }\n\n private validatedNamespace(namespace: string): EmbeddableNamespace {\n this.validateNamespace(namespace);\n return namespace as EmbeddableNamespace;\n }\n\n /**\n * Build a filtered KNN query that JOINs vec results with embedding_metadata.\n * Filters are applied as WHERE conditions on the metadata table.\n */\n private buildFilteredSearchQuery(\n namespace: EmbeddableNamespace,\n filters: Record<string, unknown>,\n _limit: number,\n ): { sql: string; params: unknown[] } {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n for (const [key, value] of Object.entries(filters)) {\n if (FILTERABLE_COLUMNS.has(key)) {\n conditions.push(`em.${key} = ?`);\n params.push(value);\n }\n }\n\n const whereClause =\n conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n const sql = `\n WITH knn AS (\n SELECT record_id, distance\n FROM vec_${namespace}\n WHERE embedding MATCH ?\n AND k = ?\n ORDER BY distance\n )\n SELECT knn.record_id, knn.distance,\n em.model, em.provider, em.content_hash, em.embedded_at, em.domain_metadata\n FROM knn\n INNER JOIN embedding_metadata em\n ON em.namespace = '${namespace}' AND em.record_id = knn.record_id\n ${whereClause}\n `;\n\n return { sql, params };\n }\n}\n","import type { EmbeddingProvider, EmbeddingResponse } from './llm.js';\n\nexport async function generateEmbedding(\n backend: EmbeddingProvider,\n text: string,\n): Promise<EmbeddingResponse> {\n const raw = await backend.embed(text);\n return {\n embedding: normalize(raw.embedding),\n model: raw.model,\n dimensions: raw.dimensions,\n };\n}\n\nfunction normalize(vec: number[]): number[] {\n const magnitude = Math.sqrt(vec.reduce((sum, v) => sum + v * v, 0));\n if (magnitude === 0) return vec;\n return vec.map((v) => v / magnitude);\n}\n","/**\n * Adapter wrapping the existing EmbeddingProvider interface into the\n * ManagerEmbeddingProvider contract used by EmbeddingManager.\n *\n * Uses `generateEmbedding` (L2-normalised) rather than raw `provider.embed()`\n * so vectors are always unit-length before storage.\n */\n\nimport type { EmbeddingProvider } from '@myco/intelligence/llm.js';\nimport type { EmbeddingProviderConfig } from '@myco/config/schema.js';\nimport { generateEmbedding } from '@myco/intelligence/embeddings.js';\nimport { EMBEDDING_DIMENSIONS } from '@myco/db/schema.js';\nimport type { ManagerEmbeddingProvider } from './types.js';\n\n/** TTL for cached availability check (ms). Avoids HTTP probe on every embed(). */\nconst AVAILABILITY_CACHE_TTL_MS = 5_000;\n\n/** Ollama default tag — untagged model names implicitly resolve to :latest. */\nconst OLLAMA_DEFAULT_TAG = ':latest';\n\n/** Providers that use Docker-style model tags where untagged means :latest. */\nconst TAGGED_PROVIDERS = new Set(['ollama']);\n\n/**\n * Normalize model string for consistent storage. Ollama treats 'bge-m3' and\n * 'bge-m3:latest' as the same model — normalize to include the tag so vector\n * metadata comparisons don't produce false stale counts.\n */\nfunction normalizeModelName(model: string, provider: string): string {\n if (TAGGED_PROVIDERS.has(provider) && !model.includes(':')) {\n return model + OLLAMA_DEFAULT_TAG;\n }\n return model;\n}\n\nexport class EmbeddingProviderAdapter implements ManagerEmbeddingProvider {\n readonly model: string;\n readonly providerName: string;\n readonly dimensions: number;\n\n /** Cached availability state to avoid per-embed HTTP probes. */\n private cachedAvailable: boolean | null = null;\n private cachedAvailableAt = 0;\n\n constructor(\n private provider: EmbeddingProvider,\n config: EmbeddingProviderConfig,\n ) {\n this.model = normalizeModelName(config.model, config.provider);\n this.providerName = config.provider;\n this.dimensions = EMBEDDING_DIMENSIONS;\n }\n\n async embed(text: string): Promise<number[] | null> {\n try {\n const isUp = await this.checkAvailability();\n if (!isUp) return null;\n const result = await generateEmbedding(this.provider, text);\n return result.embedding;\n } catch {\n // Provider went down mid-embed — invalidate cache\n this.cachedAvailable = null;\n return null;\n }\n }\n\n /** Check availability with a short TTL cache to avoid HTTP probes on every call. */\n private async checkAvailability(): Promise<boolean> {\n const now = Date.now();\n if (this.cachedAvailable !== null && (now - this.cachedAvailableAt) < AVAILABILITY_CACHE_TTL_MS) {\n return this.cachedAvailable;\n }\n this.cachedAvailable = await this.provider.isAvailable();\n this.cachedAvailableAt = now;\n return this.cachedAvailable;\n }\n}\n","/**\n * SqliteRecordSource — queries the record store for rows that need embedding.\n *\n * Delegates to existing helpers from `@myco/db/queries/embeddings.js` where\n * possible (markEmbedded, clearEmbedded, getUnembedded). Custom queries are\n * used for spore status filtering, metadata enrichment, and content retrieval.\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport {\n markEmbedded as dbMarkEmbedded,\n clearEmbedded as dbClearEmbedded,\n getUnembedded,\n assertValidTable as assertValidNamespace,\n EMBEDDABLE_TABLES,\n EMBEDDABLE_TEXT_COLUMNS,\n type EmbeddableTable,\n} from '@myco/db/queries/embeddings.js';\nimport type { DomainMetadata, EmbeddableRecordSource } from '@myco/daemon/embedding/types.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Spore status that qualifies for embedding. */\nconst ACTIVE_STATUS = 'active';\n\n/** Build metadata for a session row. */\nfunction sessionMetadata(row: Record<string, unknown>): DomainMetadata {\n return {\n ...(row.project_root != null ? { project_root: row.project_root as string } : {}),\n };\n}\n\n/** Build metadata for a spore row. */\nfunction sporeMetadata(row: Record<string, unknown>): DomainMetadata {\n return {\n ...(row.status != null ? { status: row.status as string } : {}),\n ...(row.session_id != null ? { session_id: row.session_id as string } : {}),\n ...(row.observation_type != null ? { observation_type: row.observation_type as string } : {}),\n };\n}\n\n/** Build metadata for an artifact row — empty. */\nfunction emptyMetadata(): DomainMetadata {\n return {};\n}\n\n/** Build metadata for a plan row. */\nfunction planMetadata(row: Record<string, unknown>): DomainMetadata {\n return {\n ...(row.session_id != null ? { session_id: row.session_id as string } : {}),\n ...(row.source_path != null ? { source_path: row.source_path as string } : {}),\n };\n}\n\n/** Get the metadata builder for a given namespace. */\nfunction metadataFor(namespace: EmbeddableTable, row: Record<string, unknown>): DomainMetadata {\n switch (namespace) {\n case 'sessions':\n return sessionMetadata(row);\n case 'spores':\n return sporeMetadata(row);\n case 'plans':\n return planMetadata(row);\n case 'artifacts':\n return emptyMetadata();\n }\n}\n\n// ---------------------------------------------------------------------------\n// SqliteRecordSource\n// ---------------------------------------------------------------------------\n\nexport class SqliteRecordSource implements EmbeddableRecordSource {\n /**\n * Get rows that need embedding (embedded=0, content non-null).\n *\n * For spores: additionally filters WHERE status = 'active'.\n * For sessions: delegates to getUnembedded (which filters summary IS NOT NULL).\n */\n getEmbeddableRows(namespace: string, limit: number): Array<{\n id: string;\n text: string;\n metadata: DomainMetadata;\n }> {\n assertValidNamespace(namespace);\n\n if (namespace === 'spores') {\n return this.getUnembeddedActiveSpores(limit);\n }\n\n // For sessions/plans/artifacts: delegate to getUnembedded, then enrich with metadata\n const rows = getUnembedded(namespace, limit);\n const db = getDatabase();\n return rows.map((row) => {\n const fullRow = db.prepare(`SELECT * FROM ${namespace} WHERE id = ?`).get(row.id) as Record<string, unknown>;\n return {\n id: String(row.id),\n text: row.text,\n metadata: metadataFor(namespace as EmbeddableTable, fullRow),\n };\n });\n }\n\n /**\n * Get IDs of all records that should have embeddings.\n *\n * - sessions: those with a non-null summary\n * - spores: those with status = 'active'\n * - plans/artifacts: those with non-null content\n */\n getActiveRecordIds(namespace: string): string[] {\n assertValidNamespace(namespace);\n const db = getDatabase();\n\n switch (namespace) {\n case 'sessions': {\n const rows = db.prepare(\n `SELECT id FROM sessions WHERE summary IS NOT NULL`,\n ).all() as Array<{ id: string }>;\n return rows.map((r) => r.id);\n }\n case 'spores': {\n const rows = db.prepare(\n `SELECT id FROM spores WHERE status = ?`,\n ).all(ACTIVE_STATUS) as Array<{ id: string }>;\n return rows.map((r) => r.id);\n }\n case 'plans': {\n const rows = db.prepare(\n `SELECT id FROM plans WHERE content IS NOT NULL`,\n ).all() as Array<{ id: string }>;\n return rows.map((r) => r.id);\n }\n case 'artifacts': {\n const rows = db.prepare(\n `SELECT id FROM artifacts WHERE content IS NOT NULL`,\n ).all() as Array<{ id: string }>;\n return rows.map((r) => r.id);\n }\n }\n }\n\n /**\n * Fetch content + metadata for specific record IDs.\n *\n * Returns same shape as getEmbeddableRows but for specific records.\n * Empty ids array returns empty result.\n */\n getRecordContent(namespace: string, ids: string[]): Array<{\n id: string;\n text: string;\n metadata: DomainMetadata;\n }> {\n assertValidNamespace(namespace);\n\n if (ids.length === 0) return [];\n\n const db = getDatabase();\n const textCol = EMBEDDABLE_TEXT_COLUMNS[namespace as EmbeddableTable];\n const placeholders = ids.map(() => '?').join(', ');\n\n const rows = db.prepare(\n `SELECT *, ${textCol} AS text FROM ${namespace} WHERE id IN (${placeholders})`,\n ).all(...ids) as Array<Record<string, unknown>>;\n\n return rows.map((row) => ({\n id: String(row.id),\n text: row.text as string,\n metadata: metadataFor(namespace as EmbeddableTable, row),\n }));\n }\n\n /** Mark a record as embedded. Delegates to existing helper. */\n markEmbedded(namespace: string, id: string): void {\n dbMarkEmbedded(namespace, id);\n }\n\n /** Clear the embedded flag on a record. Delegates to existing helper. */\n clearEmbedded(namespace: string, id: string): void {\n dbClearEmbedded(namespace, id);\n }\n\n /**\n * Clear the embedded flag on all records, optionally scoped to a namespace.\n *\n * If namespace is omitted, clears all embeddable tables.\n */\n clearAllEmbedded(namespace?: string): void {\n const db = getDatabase();\n\n if (namespace !== undefined) {\n assertValidNamespace(namespace);\n db.prepare(`UPDATE ${namespace} SET embedded = 0`).run();\n return;\n }\n\n for (const table of EMBEDDABLE_TABLES) {\n db.prepare(`UPDATE ${table} SET embedded = 0`).run();\n }\n }\n\n /**\n * Count rows that need embedding — lightweight SELECT COUNT(*), no row materialization.\n */\n getPendingCount(namespace: string): number {\n assertValidNamespace(namespace);\n const db = getDatabase();\n\n const contentFilter = namespace === 'sessions' ? ' AND summary IS NOT NULL' : '';\n const statusFilter = namespace === 'spores' ? ` AND status = '${ACTIVE_STATUS}'` : '';\n\n const row = db.prepare(\n `SELECT COUNT(*) AS cnt FROM ${namespace} WHERE embedded = 0${contentFilter}${statusFilter}`,\n ).get() as { cnt: number };\n\n return Number(row.cnt);\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n /** Custom query for spores: embedded=0 AND status='active'. */\n private getUnembeddedActiveSpores(limit: number): Array<{\n id: string;\n text: string;\n metadata: DomainMetadata;\n }> {\n const db = getDatabase();\n const rows = db.prepare(\n `SELECT id, content AS text, status, session_id, observation_type\n FROM spores\n WHERE embedded = 0 AND status = ?\n ORDER BY created_at ASC\n LIMIT ?`,\n ).all(ACTIVE_STATUS, limit) as Array<Record<string, unknown>>;\n\n return rows.map((row) => ({\n id: String(row.id),\n text: row.text as string,\n metadata: sporeMetadata(row),\n }));\n }\n}\n","/**\n * API route handlers for agent task CRUD.\n *\n * Thin handlers that delegate to the registry and loader. Each handler\n * takes a RouteRequest and the vault directory, returning a RouteResponse.\n *\n * Route overview:\n * GET /api/agent/tasks — list all tasks (built-in + user)\n * GET /api/agent/tasks/:id — get a single task by name\n * POST /api/agent/tasks — create a new user task\n * POST /api/agent/tasks/:id/copy — copy an existing task to user dir\n * DELETE /api/agent/tasks/:id — delete a user task (built-ins blocked)\n */\n\nimport { stringify as stringifyYaml, parse as parseYaml } from 'yaml';\nimport { errorMessage as toErrorMessage } from '@myco/utils/error-message.js';\nimport { taskFromParsed } from '@myco/agent/loader.js';\nimport { AgentTaskSchema } from '@myco/agent/schemas.js';\nimport {\n loadAllTasks,\n validateTaskName,\n writeUserTask,\n deleteUserTask,\n copyTaskToUser,\n} from '@myco/agent/registry.js';\nimport { resolveDefinitionsDir } from '@myco/agent/loader.js';\nimport { USER_TASK_SOURCE } from '@myco/constants.js';\nimport { loadConfig, updateConfig } from '../../config/loader.js';\nimport { withTaskConfig } from '../../config/updates.js';\nimport type { TaskConfigUpdate } from '../../config/updates.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** HTTP status: 200 OK (returned as undefined — default) */\nconst HTTP_OK = 200;\n\n/** HTTP status: 201 Created */\nconst HTTP_CREATED = 201;\n\n/** HTTP status: 400 Bad Request */\nconst HTTP_BAD_REQUEST = 400;\n\n/** HTTP status: 403 Forbidden */\nconst HTTP_FORBIDDEN = 403;\n\n/** HTTP status: 404 Not Found */\nconst HTTP_NOT_FOUND = 404;\n\n/** HTTP status: 409 Conflict */\nconst HTTP_CONFLICT = 409;\n\n// ---------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------\n\n/**\n * List all tasks: built-in definitions merged with user-created overrides.\n *\n * Optionally filtered by `?source=user` or `?source=built-in`.\n */\nexport async function handleListTasks(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n let tasks = Array.from(allTasks.values());\n\n const sourceFilter = req.query?.source as string | undefined;\n if (sourceFilter) {\n tasks = tasks.filter((t) => t.source === sourceFilter);\n }\n\n return { status: HTTP_OK, body: { tasks } };\n}\n\n/**\n * Get a single task by its name (used as the URL `:id` parameter).\n *\n * Returns 404 if the task is not found.\n */\nexport async function handleGetTask(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const task = allTasks.get(req.params.id);\n\n if (!task) {\n return { status: HTTP_NOT_FOUND, body: { error: 'task_not_found' } };\n }\n\n return { status: HTTP_OK, body: { task } };\n}\n\n/**\n * Create a new user task from the request body.\n *\n * Validates:\n * - Body must parse against AgentTaskSchema.\n * - Task name must be valid (lowercase letters, digits, hyphens only).\n * - No existing user task with the same name may exist.\n *\n * Returns 201 on success.\n */\nexport async function handleCreateTask(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n // Parse and validate body against schema\n const result = AgentTaskSchema.safeParse(req.body);\n if (!result.success) {\n return {\n status: HTTP_BAD_REQUEST,\n body: { error: 'validation_failed', issues: result.error.issues },\n };\n }\n\n const parsed = result.data;\n\n // Validate task name format\n if (!validateTaskName(parsed.name)) {\n return {\n status: HTTP_BAD_REQUEST,\n body: { error: 'invalid_task_name', name: parsed.name },\n };\n }\n\n // Check for existing user task with the same name (built-ins can be shadowed)\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const existing = allTasks.get(parsed.name);\n if (existing && existing.source === USER_TASK_SOURCE) {\n return {\n status: HTTP_CONFLICT,\n body: { error: 'task_already_exists', name: parsed.name },\n };\n }\n\n const task = {\n ...parsed,\n isBuiltin: false,\n source: USER_TASK_SOURCE,\n };\n\n writeUserTask(vaultDir, task);\n\n return { status: HTTP_CREATED, body: { task } };\n}\n\n/**\n * Copy an existing task (built-in or user) to the user task directory.\n *\n * The source task is identified by `req.params.id`.\n * An optional new name may be provided via `req.body.name`.\n *\n * Returns 201 on success.\n */\nexport async function handleCopyTask(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const sourceName = req.params.id;\n const newName = (req.body as Record<string, unknown> | undefined)?.name as string | undefined;\n\n const definitionsDir = resolveDefinitionsDir();\n\n // Validate the new name if provided\n if (newName !== undefined && !validateTaskName(newName)) {\n return {\n status: HTTP_BAD_REQUEST,\n body: { error: 'invalid_task_name', name: newName },\n };\n }\n\n try {\n const copy = copyTaskToUser(definitionsDir, vaultDir, sourceName, newName);\n return { status: HTTP_CREATED, body: { task: copy } };\n } catch (err) {\n const message = toErrorMessage(err);\n if (message.includes('not found')) {\n return { status: HTTP_NOT_FOUND, body: { error: 'task_not_found', name: sourceName } };\n }\n return { status: HTTP_BAD_REQUEST, body: { error: 'copy_failed', message } };\n }\n}\n\n/**\n * Get the raw YAML content of a user task file.\n *\n * Built-in tasks return their serialized AgentTask as YAML.\n * Returns 404 if the task doesn't exist.\n */\nexport async function handleGetTaskYaml(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const taskName = req.params.id;\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const task = allTasks.get(taskName);\n\n if (!task) {\n return { status: HTTP_NOT_FOUND, body: { error: 'task_not_found', name: taskName } };\n }\n\n // Serialize task to YAML (strip internal fields)\n const { isBuiltin: _ib, source: _src, ...serializable } = task;\n const yaml = stringifyYaml(serializable);\n\n return { status: HTTP_OK, body: { yaml, source: task.source } };\n}\n\n/**\n * Update a user task from raw YAML content.\n *\n * Parses the YAML through AgentTaskSchema for validation.\n * Built-in tasks cannot be updated (returns 403).\n * Returns the updated task on success.\n */\nexport async function handleUpdateTask(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const taskName = req.params.id;\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const existing = allTasks.get(taskName);\n\n if (!existing) {\n return { status: HTTP_NOT_FOUND, body: { error: 'task_not_found', name: taskName } };\n }\n\n if (existing.isBuiltin || existing.source !== USER_TASK_SOURCE) {\n return { status: HTTP_FORBIDDEN, body: { error: 'cannot_update_builtin', name: taskName } };\n }\n\n const body = req.body as Record<string, unknown> | undefined;\n const yamlContent = body?.yaml;\n if (typeof yamlContent !== 'string') {\n return { status: HTTP_BAD_REQUEST, body: { error: 'missing_yaml_field' } };\n }\n\n try {\n const parsed = AgentTaskSchema.parse(parseYaml(yamlContent));\n const task = { ...taskFromParsed(parsed), isBuiltin: false, source: USER_TASK_SOURCE };\n\n // Ensure the name matches the URL param (prevent renaming via YAML)\n if (task.name !== taskName) {\n return { status: HTTP_BAD_REQUEST, body: { error: 'name_mismatch', expected: taskName, got: task.name } };\n }\n\n writeUserTask(vaultDir, task);\n return { status: HTTP_OK, body: { task } };\n } catch (err) {\n const message = toErrorMessage(err);\n return { status: HTTP_BAD_REQUEST, body: { error: 'validation_failed', message } };\n }\n}\n\n/**\n * Delete a user task by name.\n *\n * Built-in tasks may not be deleted (returns 403).\n * Returns 404 if the task does not exist.\n */\nexport async function handleDeleteTask(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const taskName = req.params.id;\n const definitionsDir = resolveDefinitionsDir();\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const task = allTasks.get(taskName);\n\n // Task must exist\n if (!task) {\n return { status: HTTP_NOT_FOUND, body: { error: 'task_not_found', name: taskName } };\n }\n\n // Built-in tasks cannot be deleted\n if (task.isBuiltin || task.source !== USER_TASK_SOURCE) {\n return {\n status: HTTP_FORBIDDEN,\n body: { error: 'cannot_delete_builtin', name: taskName },\n };\n }\n\n deleteUserTask(vaultDir, taskName);\n\n return { status: HTTP_OK, body: { deleted: taskName } };\n}\n\n/**\n * Get the full config override for a specific task from myco.yaml.\n *\n * Returns: provider, model, maxTurns, timeoutSeconds, and per-phase overrides.\n */\nexport async function handleGetTaskConfig(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const taskId = req.params.id;\n const config = loadConfig(vaultDir);\n const taskConfig = config.agent.tasks?.[taskId] ?? null;\n return { status: HTTP_OK, body: { taskId, config: taskConfig } };\n}\n\n/**\n * Update config overrides for a specific task in myco.yaml.\n *\n * Accepts partial updates — only provided fields are set. Fields set to\n * `null` are removed. Supports: provider, model, maxTurns, timeoutSeconds, phases.\n *\n * Phase overrides are keyed by phase name and support: provider, model, maxTurns.\n */\nexport async function handleUpdateTaskConfig(\n req: RouteRequest,\n vaultDir: string,\n): Promise<RouteResponse> {\n const taskId = req.params.id;\n const body = req.body as TaskConfigUpdate | undefined;\n\n if (!body) {\n return { status: HTTP_BAD_REQUEST, body: { error: 'missing_body' } };\n }\n\n const updated = updateConfig(vaultDir, (config) =>\n withTaskConfig(config, taskId, body),\n );\n\n return {\n status: HTTP_OK,\n body: { taskId, config: updated.agent.tasks?.[taskId] ?? null },\n };\n}\n","/**\n * API route handlers for provider detection and connectivity testing.\n *\n * Route overview:\n * GET /api/providers — detect available LLM providers and their models\n * POST /api/providers/test — test connectivity to a specific provider\n */\n\nimport { OllamaBackend } from '../../intelligence/ollama.js';\nimport { LmStudioBackend } from '../../intelligence/lm-studio.js';\nimport { checkLocalProvider } from '../../intelligence/provider-check.js';\nimport { ANTHROPIC_MODELS } from './models.js';\nimport type { RouteRequest, RouteResponse } from '../router.js';\n\n/** HTTP status codes. */\nconst HTTP_OK = 200;\nconst HTTP_BAD_REQUEST = 400;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ProviderInfo {\n type: string;\n available: boolean;\n baseUrl?: string;\n models: string[];\n}\n\ninterface TestResult {\n ok: boolean;\n latency_ms?: number;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------\n\n/**\n * Detect available providers (Ollama, LM Studio, Anthropic).\n *\n * Uses Promise.allSettled for parallel detection with timeouts so one\n * slow/unavailable provider doesn't block the others.\n */\nexport async function handleGetProviders(): Promise<RouteResponse> {\n const results = await Promise.allSettled([\n detectLocalProviderInfo('ollama', OllamaBackend.DEFAULT_BASE_URL),\n detectLocalProviderInfo('lmstudio', LmStudioBackend.DEFAULT_BASE_URL),\n detectCloud(),\n ]);\n\n const providers: ProviderInfo[] = results.map((r) =>\n r.status === 'fulfilled'\n ? r.value\n : { type: 'unknown', available: false, models: [] },\n );\n\n return { status: HTTP_OK, body: { providers } };\n}\n\n/**\n * Test connectivity to a specific provider.\n *\n * Accepts: { type: 'cloud' | 'ollama' | 'lmstudio', baseUrl?: string, model?: string }\n * Returns: { ok: boolean, latency_ms?: number, error?: string }\n */\nexport async function handleTestProvider(req: RouteRequest): Promise<RouteResponse> {\n const body = req.body as Record<string, unknown> | undefined;\n const type = body?.type as string | undefined;\n\n if (!type || !['cloud', 'ollama', 'lmstudio'].includes(type)) {\n return {\n status: HTTP_BAD_REQUEST,\n body: { error: 'type is required and must be one of: cloud, ollama, lmstudio' },\n };\n }\n\n const baseUrl = body?.baseUrl as string | undefined;\n const start = performance.now();\n let result: TestResult;\n\n try {\n if (type === 'ollama') {\n result = await testLocalProvider(new OllamaBackend({ base_url: baseUrl }), 'Ollama', OllamaBackend.DEFAULT_BASE_URL, baseUrl);\n } else if (type === 'lmstudio') {\n result = await testLocalProvider(new LmStudioBackend({ base_url: baseUrl }), 'LM Studio', LmStudioBackend.DEFAULT_BASE_URL, baseUrl);\n } else {\n result = testCloud();\n }\n } catch (err) {\n result = { ok: false, error: String(err) };\n }\n\n if (result.ok) {\n result.latency_ms = Math.round(performance.now() - start);\n }\n\n return { status: HTTP_OK, body: result };\n}\n\n// ---------------------------------------------------------------------------\n// Detection helpers\n// ---------------------------------------------------------------------------\n\n/** Detect a local provider (Ollama or LM Studio) and wrap as ProviderInfo. */\nasync function detectLocalProviderInfo(\n type: 'ollama' | 'lmstudio',\n defaultBaseUrl: string,\n): Promise<ProviderInfo> {\n const status = await checkLocalProvider(type);\n // Filter out Myco-created context variants (e.g., gpt-oss-ctx32768)\n const models = status.models.filter(m => !/-ctx\\d+/.test(m));\n return { type, available: status.available, baseUrl: defaultBaseUrl, models };\n}\n\nasync function detectCloud(): Promise<ProviderInfo> {\n // Cloud is always available — the SDK handles auth internally via OAuth,\n // API key, Bedrock, Vertex, or Foundry. The daemon can't reliably detect\n // which method is in use since env vars aren't always inherited.\n return { type: 'cloud', available: true, models: ANTHROPIC_MODELS };\n}\n\n// ---------------------------------------------------------------------------\n// Test helpers\n// ---------------------------------------------------------------------------\n\n/** Test a local provider's connectivity — shared pattern. */\nasync function testLocalProvider(\n backend: { isAvailable(): Promise<boolean> },\n label: string,\n defaultBaseUrl: string,\n baseUrl?: string,\n): Promise<TestResult> {\n const available = await backend.isAvailable();\n if (!available) {\n return { ok: false, error: `${label} not reachable at ${baseUrl ?? defaultBaseUrl}` };\n }\n return { ok: true };\n}\n\nfunction testCloud(): TestResult {\n // SDK handles auth — always report OK. Auth failures surface at runtime.\n return { ok: true };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { insertLogEntry } from '@myco/db/queries/logs.js';\nimport { kindToComponent } from '@myco/constants/log-kinds.js';\n\n/**\n * Replay JSONL log entries that are newer than the last entry in SQLite.\n * Returns the number of entries replayed.\n */\nexport function reconcileLogBuffer(logDir: string, sinceTimestamp: string): number {\n let replayed = 0;\n\n // Read log files in order: rotated files first (oldest), then current\n const files: string[] = [];\n for (let i = 3; i >= 1; i--) {\n const rotated = path.join(logDir, `daemon.${i}.log`);\n if (fs.existsSync(rotated)) files.push(rotated);\n }\n const current = path.join(logDir, 'daemon.log');\n if (fs.existsSync(current)) files.push(current);\n\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf-8');\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line);\n if (entry.timestamp > sinceTimestamp) {\n const { timestamp, level, kind, component, message, ...rest } = entry;\n insertLogEntry({\n timestamp,\n level,\n kind: kind ?? `${component ?? 'unknown'}.unknown`,\n component: component ?? kindToComponent(kind ?? 'unknown'),\n message,\n data: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null,\n session_id: rest.session_id ?? null,\n });\n replayed++;\n }\n } catch {\n // Skip malformed lines\n }\n }\n }\n\n return replayed;\n}\n","import type { DaemonLogger } from './logger.js';\nimport { LOG_KINDS } from '../constants/log-kinds.js';\n\nexport type PowerState = 'active' | 'idle' | 'sleep' | 'deep_sleep';\n\nexport interface PowerJob {\n name: string;\n runIn: PowerState[];\n fn: () => Promise<void>;\n}\n\nexport interface PowerManagerConfig {\n idleThresholdMs: number;\n sleepThresholdMs: number;\n deepSleepThresholdMs: number;\n activeIntervalMs: number;\n sleepIntervalMs: number;\n logger: DaemonLogger;\n}\n\nexport class PowerManager {\n private state: PowerState = 'active';\n private lastActivity: number = Date.now();\n private jobs: PowerJob[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private running = false;\n private config: PowerManagerConfig;\n private logger: DaemonLogger;\n\n constructor(config: PowerManagerConfig) {\n this.config = config;\n this.logger = config.logger;\n }\n\n register(job: PowerJob): void {\n this.jobs.push(job);\n }\n\n recordActivity(): void {\n this.lastActivity = Date.now();\n\n if (this.state === 'deep_sleep') {\n this.logger.info(LOG_KINDS.POWER_STATE, 'Waking from deep sleep');\n this.state = 'active';\n this.scheduleNextTick();\n }\n }\n\n start(): void {\n this.lastActivity = Date.now();\n this.state = 'active';\n this.running = true;\n this.scheduleNextTick();\n this.logger.info(LOG_KINDS.POWER_STATE, 'PowerManager started', {\n jobs: this.jobs.map((j) => j.name),\n });\n }\n\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.logger.info(LOG_KINDS.POWER_STATE, 'PowerManager stopped');\n }\n\n getState(): PowerState {\n this.evaluateState();\n return this.state;\n }\n\n private evaluateState(): void {\n const idleMs = Date.now() - this.lastActivity;\n let target: PowerState;\n\n if (idleMs >= this.config.deepSleepThresholdMs) {\n target = 'deep_sleep';\n } else if (idleMs >= this.config.sleepThresholdMs) {\n target = 'sleep';\n } else if (idleMs >= this.config.idleThresholdMs) {\n target = 'idle';\n } else {\n target = 'active';\n }\n\n if (target !== this.state) {\n this.logger.info(LOG_KINDS.POWER_STATE, 'Power state transition', {\n from: this.state,\n to: target,\n idle_ms: idleMs,\n });\n this.state = target;\n }\n }\n\n private scheduleNextTick(): void {\n if (!this.running) return;\n if (this.timer) clearTimeout(this.timer);\n\n const interval =\n this.state === 'sleep'\n ? this.config.sleepIntervalMs\n : this.config.activeIntervalMs;\n\n this.timer = setTimeout(() => this.tick(), interval);\n }\n\n private async tick(): Promise<void> {\n if (!this.running) return;\n\n this.evaluateState();\n\n if (this.state === 'deep_sleep') {\n this.logger.info(LOG_KINDS.POWER_STATE, 'Entering deep sleep — timer stopped');\n this.timer = null;\n return;\n }\n\n // Run eligible jobs\n const eligible = this.jobs.filter((j) => j.runIn.includes(this.state));\n this.logger.debug(LOG_KINDS.POWER_TICK, 'Tick', {\n state: this.state,\n jobs: eligible.map((j) => j.name),\n });\n\n for (const job of eligible) {\n try {\n await job.fn();\n } catch (err) {\n this.logger.error(LOG_KINDS.POWER_JOB_ERROR, `Job \"${job.name}\" failed`, {\n error: (err as Error).message,\n });\n }\n }\n\n this.scheduleNextTick();\n }\n}\n","/**\n * Post-transaction cleanup after a session cascade delete.\n *\n * Shared by the DELETE /api/sessions/:id route and the session-maintenance job\n * to ensure both code paths perform identical cleanup (embedding vectors,\n * vault markdown files, and attachment files on disk).\n */\n\nimport { unlink, glob } from 'node:fs/promises';\nimport type { DeleteCascadeResult } from '../../db/queries/sessions.js';\nimport type { EmbeddingManager } from '../embedding/manager.js';\n\n/**\n * Remove embedding vectors and vault files for a cascade-deleted session.\n *\n * All operations are best-effort — partial failures are swallowed so that\n * one missing file does not block cleanup of the rest.\n */\nexport async function cleanupAfterSessionCascade(\n sessionId: string,\n result: DeleteCascadeResult,\n embeddingManager: EmbeddingManager,\n vaultDir: string,\n): Promise<void> {\n // Embedding vectors\n try { embeddingManager.onRemoved('sessions', sessionId); } catch { /* best-effort */ }\n for (const sporeId of result.deletedSporeIds) {\n try { embeddingManager.onRemoved('spores', sporeId); } catch { /* best-effort */ }\n }\n\n // Session markdown\n try {\n for await (const f of glob(`sessions/**/session-${sessionId}.md`, { cwd: vaultDir })) {\n await unlink(`${vaultDir}/${f}`).catch(() => {});\n }\n } catch { /* best-effort */ }\n\n // Spore markdown files\n for (const sporeId of result.deletedSporeIds) {\n try {\n for await (const f of glob(`spores/**/${sporeId}*.md`, { cwd: vaultDir })) {\n await unlink(`${vaultDir}/${f}`).catch(() => {});\n }\n } catch { /* best-effort */ }\n }\n\n // Attachment files on disk\n for (const filePath of result.deletedAttachmentPaths) {\n try { await unlink(filePath); } catch { /* best-effort */ }\n }\n}\n","/**\n * Session maintenance job.\n *\n * Two tasks run in sequence:\n * 1. Complete stale active sessions — active sessions with no new prompts\n * in more than STALE_SESSION_THRESHOLD_MS are marked completed.\n * 2. Delete dead sessions — sessions with ≤ DEAD_SESSION_MAX_PROMPTS prompts\n * are deleted via cascade, including vault file and embedding vector cleanup.\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport { deleteSessionCascade } from '@myco/db/queries/sessions.js';\nimport {\n epochSeconds,\n MS_PER_SECOND,\n STALE_SESSION_THRESHOLD_MS,\n DEAD_SESSION_MAX_PROMPTS,\n} from '../../constants.js';\nimport type { DaemonLogger } from '../logger.js';\nimport type { EmbeddingManager } from '../embedding/manager.js';\nimport { cleanupAfterSessionCascade } from './session-cleanup.js';\nimport { LOG_KINDS } from '../../constants/log-kinds.js';\n\n/** Stale threshold in epoch seconds (derived from the ms constant). */\nconst STALE_SESSION_THRESHOLD_S = STALE_SESSION_THRESHOLD_MS / MS_PER_SECOND;\n\n/**\n * Complete active sessions whose last prompt is older than the stale threshold.\n *\n * Uses COALESCE to fall back to the session's started_at when no prompt\n * batches exist (session was registered but never received a prompt).\n *\n * @returns number of sessions completed\n */\nexport function completeStaleActiveSessions(registeredSessionIds: string[]): number {\n const db = getDatabase();\n const cutoff = epochSeconds() - STALE_SESSION_THRESHOLD_S;\n\n // Build exclusion clause for registered sessions\n const excludePlaceholders = registeredSessionIds.length > 0\n ? `AND id NOT IN (${registeredSessionIds.map(() => '?').join(', ')})`\n : '';\n\n const params: unknown[] = [cutoff, ...registeredSessionIds];\n\n const info = db.prepare(\n `UPDATE sessions\n SET status = 'completed'\n WHERE status = 'active'\n AND COALESCE(\n (SELECT MAX(pb.started_at) FROM prompt_batches pb WHERE pb.session_id = sessions.id),\n sessions.started_at\n ) < ?\n ${excludePlaceholders}`,\n ).run(...params);\n\n return info.changes;\n}\n\n/**\n * Find session IDs with prompt_count <= DEAD_SESSION_MAX_PROMPTS.\n *\n * Excludes currently registered sessions.\n */\nexport function findDeadSessionIds(registeredSessionIds: string[]): string[] {\n const db = getDatabase();\n\n const excludePlaceholders = registeredSessionIds.length > 0\n ? `AND id NOT IN (${registeredSessionIds.map(() => '?').join(', ')})`\n : '';\n\n const params: unknown[] = [DEAD_SESSION_MAX_PROMPTS, ...registeredSessionIds];\n\n const rows = db.prepare(\n `SELECT id FROM sessions\n WHERE prompt_count <= ?\n ${excludePlaceholders}`,\n ).all(...params) as { id: string }[];\n\n return rows.map((r) => r.id);\n}\n\nexport interface SessionMaintenanceDeps {\n logger: DaemonLogger;\n registeredSessionIds: () => string[];\n embeddingManager: EmbeddingManager;\n vaultDir: string;\n}\n\n/**\n * Run both maintenance tasks in sequence:\n * 1. Complete stale active sessions\n * 2. Delete dead sessions (cascade)\n */\nexport async function runSessionMaintenance(deps: SessionMaintenanceDeps): Promise<void> {\n const { logger, registeredSessionIds, embeddingManager, vaultDir } = deps;\n const registered = registeredSessionIds();\n\n // Task 1: Complete stale sessions\n const completed = completeStaleActiveSessions(registered);\n if (completed > 0) {\n logger.info(LOG_KINDS.MAINTENANCE_SESSION, 'Completed stale sessions', { count: completed });\n }\n\n // Task 2: Delete dead sessions\n const deadIds = findDeadSessionIds(registered);\n if (deadIds.length === 0) return;\n\n let deletedCount = 0;\n for (const sessionId of deadIds) {\n const result = deleteSessionCascade(sessionId);\n if (!result.deleted) continue;\n\n await cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir);\n\n deletedCount++;\n logger.info(LOG_KINDS.MAINTENANCE_SESSION, 'Deleted dead session', {\n session_id: sessionId,\n counts: result.counts,\n });\n }\n\n if (deletedCount > 0) {\n logger.info(LOG_KINDS.MAINTENANCE_SESSION, 'Dead session cleanup complete', { deleted: deletedCount });\n }\n}\n","/**\n * Myco daemon — SQLite capture engine.\n *\n * All data goes to a local SQLite database (better-sqlite3). The intelligence\n * pipeline (extraction, embedding, consolidation, digest) is removed — it\n * moves to Phase 2 Agent SDK. What remains is the capture layer: session\n * lifecycle, prompt batch tracking, activity recording, and transcript mining.\n */\n\nimport { DaemonServer } from './server.js';\nimport { SessionRegistry } from './lifecycle.js';\nimport { DaemonLogger } from './logger.js';\nimport { loadConfig, updateConfig, updateBackupConfig } from '../config/loader.js';\nimport { resolvePort } from './port.js';\nimport { TranscriptMiner, extractTurnsFromBuffer } from '../capture/transcript-miner.js';\nimport { createPerProjectAdapter, extensionForMimeType, type TranscriptTurn } from '../symbionts/adapter.js';\nimport { claudeCodeAdapter } from '../symbionts/claude-code.js';\nimport { findPackageRoot } from '../utils/find-package-root.js';\nimport { EventBuffer, listBufferSessionIds, cleanStaleBuffers } from '../capture/buffer.js';\nimport { loadManifests } from '../symbionts/detect.js';\nimport { isPlanWriteEvent, capturePlan, type PlanWatchConfig } from './plan-capture.js';\nimport type { RegisteredSession } from './lifecycle.js';\nimport { handleGetConfig, handlePutConfig } from './api/config.js';\nimport { handleLogSearch, handleLogStream, handleLogDetail } from './api/log-explorer.js';\nimport { handleRestart } from './api/restart.js';\nimport { createUpdateHandlers } from './api/update.js';\nimport { getMachineId } from './machine-id.js';\nimport { createBackupHandlers } from './api/backup.js';\nimport { createTeamHandlers } from './api/team-connect.js';\nimport { TeamSyncClient } from './team-sync.js';\nimport { initTeamContext } from './team-context.js';\nimport { createBackup } from './backup.js';\nimport { listPending, markSent, pruneOld, backfillUnsynced } from '../db/queries/team-outbox.js';\nimport { readSecrets } from '../config/secrets.js';\nimport { ProgressTracker, handleGetProgress } from './api/progress.js';\nimport { handleGetModels } from './api/models.js';\nimport { computeConfigHash } from './api/stats.js';\nimport {\n handleListSessions,\n handleGetSession,\n handleGetSessionBatches,\n handleGetBatchActivities,\n handleGetSessionAttachments,\n handleGetSessionPlans,\n} from './api/sessions.js';\nimport {\n handleListSpores,\n handleGetSpore,\n handleListEntities,\n handleGetGraph,\n handleGetFullGraph,\n handleGetDigest,\n} from './api/mycelium.js';\nimport { createSearchHandler } from './api/search.js';\nimport { createSessionContextHandler, createPromptContextHandler } from './api/context.js';\nimport { handleGetFeed } from './api/feed.js';\nimport { handleListSymbionts } from './api/symbionts.js';\nimport {\n handleGetEmbeddingStatus,\n handleEmbeddingDetails,\n handleEmbeddingRebuild,\n handleEmbeddingReconcile,\n handleEmbeddingCleanOrphans,\n handleEmbeddingReembedStale,\n} from './api/embedding.js';\nimport { EmbeddingManager, SqliteVecVectorStore, EmbeddingProviderAdapter, SqliteRecordSource } from './embedding/index.js';\nimport { createEmbeddingProvider } from '../intelligence/llm.js';\nimport {\n handleListTasks,\n handleGetTask,\n handleGetTaskYaml,\n handleUpdateTask,\n handleCreateTask,\n handleCopyTask,\n handleDeleteTask,\n handleGetTaskConfig,\n handleUpdateTaskConfig,\n} from './api/agent-tasks.js';\nimport { handleGetProviders, handleTestProvider } from './api/providers.js';\nimport { listTurnsByRun } from '../db/queries/turns.js';\nimport { gatherStats } from '../services/stats.js';\nimport { initDatabase, vaultDbPath, closeDatabase, getDatabase } from '../db/client.js';\nimport { createSchema } from '../db/schema.js';\nimport { upsertSession, closeSession, updateSession, listSessions, getSession, deleteSessionCascade, getSessionImpact } from '../db/queries/sessions.js';\nimport { incrementActivityCount, populateBatchResponses, getBatchIdByPromptNumber, findBatchByPromptPrefix, closeOpenBatches, insertBatchStateless, getLatestBatch, setResponseSummary, listBatchesBySession } from '../db/queries/batches.js';\nimport { insertActivityWithBatch } from '../db/queries/activities.js';\nimport { insertAttachment, getAttachmentByFilePath } from '../db/queries/attachments.js';\nimport { listRuns, countRuns, getRun, getRunningRun } from '../db/queries/runs.js';\nimport { listReports } from '../db/queries/reports.js';\nimport { insertSpore, updateSporeStatus } from '../db/queries/spores.js';\nimport { listPlans } from '../db/queries/plans.js';\nimport { registerAgent } from '../db/queries/agents.js';\nimport { insertLogEntry, deleteOldLogs, getMaxTimestamp } from '../db/queries/logs.js';\nimport { reconcileLogBuffer } from './log-reconcile.js';\nimport {\n DEFAULT_AGENT_ID,\n STALE_BUFFER_MAX_AGE_MS,\n LOG_PROMPT_PREVIEW_CHARS,\n LOG_MESSAGE_PREVIEW_CHARS,\n USER_AGENT_ID,\n USER_AGENT_NAME,\n EMBEDDING_BATCH_SIZE,\n POWER_IDLE_THRESHOLD_MS,\n POWER_SLEEP_THRESHOLD_MS,\n POWER_DEEP_SLEEP_THRESHOLD_MS,\n POWER_ACTIVE_INTERVAL_MS,\n POWER_SLEEP_INTERVAL_MS,\n SYNC_PROTOCOL_VERSION,\n RESTART_RESPONSE_FLUSH_MS,\n epochSeconds,\n MS_PER_SECOND,\n MS_PER_DAY,\n} from '../constants.js';\nimport { PowerManager } from './power.js';\nimport { runSessionMaintenance } from './jobs/session-maintenance.js';\nimport { cleanupAfterSessionCascade } from './jobs/session-cleanup.js';\nimport { createBatchLineage } from '../db/queries/lineage.js';\nimport { loadSecrets } from '../config/secrets.js';\nimport { LOG_KINDS } from '../constants/log-kinds.js';\nimport { z } from 'zod';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n\n/** Default limit for listing agent runs in the API. */\nconst AGENT_RUNS_DEFAULT_LIMIT = 50;\n\n/** Max chars of tool input stored in the activity row. */\nconst TOOL_INPUT_STORE_LIMIT = 4000;\n\n/** Max chars of tool output summary stored in the activity row. */\nconst TOOL_OUTPUT_STORE_LIMIT = 2000;\n\n/** Max chars for deriving a title from the first user prompt. */\nconst TITLE_PREVIEW_CHARS = 80;\n\n/** Prefixes that identify system-injected messages (not real user prompts). */\nconst SYSTEM_MESSAGE_PREFIXES = [\n '<task-notification>',\n '<system-reminder>',\n] as const;\n\n/** Returns true if the prompt is a system-injected message, not a real user prompt. */\nfunction isSystemMessage(prompt: string): boolean {\n const trimmed = prompt.trimStart();\n return SYSTEM_MESSAGE_PREFIXES.some((prefix) => trimmed.startsWith(prefix));\n}\n\n/** Event types replayed during buffer reconciliation. */\nconst REPLAYABLE_EVENT_TYPES: ReadonlySet<string> = new Set(['user_prompt', 'tool_use', 'tool_failure']);\n\n// ---------------------------------------------------------------------------\n// Event handling helpers (exported for testing)\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a UserPromptSubmit event: close previous batch, open new one.\n *\n * Fully stateless — prompt_number is derived from an inline DB subquery,\n * and open batches are closed with a blind UPDATE (no prior SELECT).\n *\n * @returns the new batch ID and prompt number\n */\nexport function handleUserPrompt(\n sessionId: string,\n prompt: string | undefined,\n): { batchId: number; promptNumber: number } {\n const now = epochSeconds();\n\n // Close any open batches for this session — blind UPDATE, no prior read\n closeOpenBatches(sessionId, now);\n\n // Insert new batch with prompt_number derived from DB\n const batch = insertBatchStateless({\n session_id: sessionId,\n user_prompt: prompt ?? null,\n started_at: now,\n created_at: now,\n });\n\n // insertBatchStateless guarantees non-null prompt_number via COALESCE subquery\n const promptNumber = batch.prompt_number!;\n\n // Create HAS_BATCH lineage edge (best-effort)\n try { createBatchLineage(DEFAULT_AGENT_ID, sessionId, batch.id, now); } catch { /* lineage best-effort */ }\n\n // Update session prompt count\n updateSession(sessionId, { prompt_count: promptNumber });\n\n return { batchId: batch.id, promptNumber };\n}\n\n/**\n * Handle a PostToolUse event: insert activity with inline batch linkage.\n *\n * Fully stateless — the batch ID is resolved via an inline subquery in\n * `insertActivityWithBatch`, so no in-memory state is needed.\n */\nexport function handleToolUse(\n sessionId: string,\n toolName: string,\n toolInput: unknown,\n toolOutput: string | undefined,\n): void {\n const now = epochSeconds();\n\n // Extract file_path from tool input if present\n const inputObj = toolInput as Record<string, unknown> | undefined;\n const filePath = typeof inputObj?.file_path === 'string' ? inputObj.file_path : null;\n\n const activity = insertActivityWithBatch({\n session_id: sessionId,\n tool_name: toolName,\n tool_input: toolInput ? JSON.stringify(toolInput).slice(0, TOOL_INPUT_STORE_LIMIT) : null,\n tool_output_summary: toolOutput?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n file_path: filePath,\n timestamp: now,\n created_at: now,\n });\n\n // Increment batch activity count if linked to a batch\n if (activity.prompt_batch_id !== null) {\n incrementActivityCount(activity.prompt_batch_id);\n }\n // Session-level tool_count is updated at stop time from transcript data.\n}\n\n/**\n * Handle stop event: close all open batches for this session.\n *\n * Does NOT close the session — the Stop hook fires after every assistant\n * turn, not just session end. Session closure happens in /sessions/unregister\n * (SessionEnd hook).\n *\n * Fully stateless — uses `closeOpenBatches` (blind UPDATE) instead of\n * reading from an in-memory map.\n */\nexport function handleStopBatches(\n sessionId: string,\n): void {\n closeOpenBatches(sessionId, epochSeconds());\n}\n\n/**\n * Handle a tool failure event: insert activity with success=0.\n */\nexport function handleToolFailure(\n sessionId: string,\n toolName: string,\n toolInput: unknown,\n error: string | undefined,\n isInterrupt: boolean | undefined,\n): void {\n const now = epochSeconds();\n const inputObj = toolInput as Record<string, unknown> | undefined;\n const filePath = typeof inputObj?.file_path === 'string' ? inputObj.file_path : null;\n\n const activity = insertActivityWithBatch({\n session_id: sessionId,\n tool_name: toolName,\n tool_input: toolInput ? JSON.stringify(toolInput).slice(0, TOOL_INPUT_STORE_LIMIT) : null,\n tool_output_summary: error?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n file_path: filePath,\n success: 0,\n error_message: error?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? (isInterrupt ? 'interrupted' : null),\n timestamp: now,\n created_at: now,\n });\n\n if (activity.prompt_batch_id !== null) {\n incrementActivityCount(activity.prompt_batch_id);\n }\n}\n\n/**\n * Handle a subagent start event: record that a subagent was spawned.\n */\nexport function handleSubagentStart(\n sessionId: string,\n agentId: string | undefined,\n agentType: string | undefined,\n): void {\n const now = epochSeconds();\n insertActivityWithBatch({\n session_id: sessionId,\n tool_name: 'subagent_start',\n tool_input: JSON.stringify({ agent_id: agentId, agent_type: agentType }).slice(0, TOOL_INPUT_STORE_LIMIT),\n timestamp: now,\n created_at: now,\n });\n}\n\n/**\n * Handle a subagent stop event: record that a subagent completed.\n */\nexport function handleSubagentStop(\n sessionId: string,\n agentId: string | undefined,\n agentType: string | undefined,\n lastAssistantMessage: string | undefined,\n): void {\n const now = epochSeconds();\n insertActivityWithBatch({\n session_id: sessionId,\n tool_name: 'subagent_stop',\n tool_input: JSON.stringify({ agent_id: agentId, agent_type: agentType }).slice(0, TOOL_INPUT_STORE_LIMIT),\n tool_output_summary: lastAssistantMessage?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n timestamp: now,\n created_at: now,\n });\n}\n\n/**\n * Handle a stop failure event: record that the stop hook encountered an error.\n */\nexport function handleStopFailure(\n sessionId: string,\n error: string | undefined,\n errorDetails: string | undefined,\n): void {\n const now = epochSeconds();\n insertActivityWithBatch({\n session_id: sessionId,\n tool_name: 'stop_failure',\n tool_output_summary: errorDetails?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n success: 0,\n error_message: error?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n timestamp: now,\n created_at: now,\n });\n}\n\n/**\n * Handle a task completed event: record task completion as an activity.\n */\nexport function handleTaskCompleted(\n sessionId: string,\n taskId: string | undefined,\n taskSubject: string | undefined,\n taskDescription: string | undefined,\n): void {\n const now = epochSeconds();\n insertActivityWithBatch({\n session_id: sessionId,\n tool_name: 'task_completed',\n tool_input: JSON.stringify({ task_id: taskId, task_subject: taskSubject, task_description: taskDescription }).slice(0, TOOL_INPUT_STORE_LIMIT),\n tool_output_summary: taskSubject?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n timestamp: now,\n created_at: now,\n });\n}\n\n/**\n * Handle a compact event (pre or post): record compaction in the activity stream.\n */\nexport function handleCompact(\n sessionId: string,\n phase: 'pre' | 'post',\n trigger: string | undefined,\n compactSummary: string | undefined,\n): void {\n const now = epochSeconds();\n insertActivityWithBatch({\n session_id: sessionId,\n tool_name: `${phase}_compact`,\n tool_input: trigger ? JSON.stringify({ trigger }).slice(0, TOOL_INPUT_STORE_LIMIT) : null,\n tool_output_summary: compactSummary?.slice(0, TOOL_OUTPUT_STORE_LIMIT) ?? null,\n timestamp: now,\n created_at: now,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Stale daemon cleanup\n// ---------------------------------------------------------------------------\n\n/**\n * Kill any stale daemon process for this vault before starting a new one.\n * Reads daemon.json — if a live process exists with that PID, kill it.\n * This prevents orphaned daemons from accumulating across restarts.\n */\nfunction killStaleDaemon(vaultDir: string, logger: DaemonLogger): void {\n const daemonJsonPath = path.join(vaultDir, 'daemon.json');\n try {\n if (!fs.existsSync(daemonJsonPath)) return;\n const info = JSON.parse(fs.readFileSync(daemonJsonPath, 'utf-8')) as { pid?: number };\n if (!info.pid) return;\n\n // Don't kill ourselves\n if (info.pid === process.pid) return;\n\n try {\n process.kill(info.pid, 0);\n process.kill(info.pid, 'SIGTERM');\n logger.info(LOG_KINDS.DAEMON_START, 'Killed stale daemon', { pid: info.pid });\n } catch { /* already dead */ }\n\n fs.unlinkSync(daemonJsonPath);\n } catch { /* daemon.json unreadable — ignore */ }\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nexport async function main(): Promise<void> {\n const vaultArg = process.argv.find((_, i) => process.argv[i - 1] === '--vault');\n if (!vaultArg) {\n process.stderr.write('Usage: mycod --vault <path>\\n');\n process.exit(1);\n }\n\n const vaultDir = path.resolve(vaultArg);\n\n // Load API keys from secrets.env into process.env before any provider init\n loadSecrets(vaultDir);\n\n const config = loadConfig(vaultDir);\n\n const manifests = loadManifests();\n const symbiontPlanDirs = manifests.flatMap((m) => m.capture?.planDirs ?? []);\n const projectRoot = process.cwd();\n let planWatchConfig: PlanWatchConfig = {\n watchDirs: [...new Set([...symbiontPlanDirs, ...(config.capture.plan_dirs ?? [])])],\n projectRoot,\n extensions: config.capture.artifact_extensions,\n };\n\n const logger = new DaemonLogger(path.join(vaultDir, 'logs'), {\n level: config.daemon.log_level,\n });\n\n // Kill any stale daemon for this vault before starting\n killStaleDaemon(vaultDir, logger);\n\n logger.info(LOG_KINDS.DAEMON_CONFIG, 'Config loaded', {\n vault: vaultDir,\n embedding_provider: config.embedding.provider,\n });\n logger.info(LOG_KINDS.CAPTURE_PLAN, 'Plan watch directories', { dirs: planWatchConfig.watchDirs });\n\n // --- Machine identity ---\n const machineId = getMachineId(vaultDir);\n logger.info(LOG_KINDS.DAEMON_START, 'Machine ID resolved', { machine_id: machineId });\n\n // --- SQLite initialization ---\n const db = initDatabase(vaultDbPath(vaultDir));\n createSchema(db, machineId);\n logger.info(LOG_KINDS.DAEMON_START, 'SQLite initialized', { vault: vaultDir });\n\n // --- Team context ---\n initTeamContext(config.team.enabled, machineId);\n\n // Wire logger to SQLite persistence\n logger.setPersistFn((entry) => {\n const { timestamp, level, kind, component, message, ...rest } = entry;\n insertLogEntry({\n timestamp,\n level,\n kind,\n component,\n message,\n data: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null,\n session_id: (rest.session_id as string) ?? null,\n });\n });\n\n // Reconcile log entries missed while daemon was down\n const lastLogTimestamp = getMaxTimestamp();\n if (lastLogTimestamp) {\n const logDir = path.join(vaultDir, 'logs');\n const replayedCount = reconcileLogBuffer(logDir, lastLogTimestamp);\n if (replayedCount > 0) {\n logger.info(LOG_KINDS.DAEMON_RECONCILE, `Replayed ${replayedCount} log entries from buffer`, { replayed: replayedCount });\n }\n }\n\n // --- Embedding lifecycle manager ---\n const vectorsDbPath = path.join(vaultDir, 'vectors.db');\n const vectorStore = new SqliteVecVectorStore(vectorsDbPath);\n const llmProvider = createEmbeddingProvider(config.embedding);\n const embeddingProvider = new EmbeddingProviderAdapter(llmProvider, config.embedding);\n const recordSource = new SqliteRecordSource();\n const embeddingManager = new EmbeddingManager(vectorStore, embeddingProvider, recordSource, logger);\n logger.info(LOG_KINDS.EMBEDDING_EMBED, 'EmbeddingManager initialized', { vectors_db: vectorsDbPath });\n\n // --- Register built-in agents and tasks ---\n try {\n const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir } = await import('../agent/loader.js');\n const definitionsDir = resolveDefinitionsDir();\n await registerBuiltInAgentsAndTasks(definitionsDir, vaultDir);\n logger.info(LOG_KINDS.AGENT_TASK, 'Built-in agents and tasks registered');\n } catch (err) {\n logger.warn(LOG_KINDS.AGENT_ERROR, 'Failed to register built-in agents/tasks', { error: (err as Error).message });\n }\n\n // Clean up stale \"running\" agent runs from previous daemon — they'll never complete\n try {\n const staleDb = getDatabase();\n // SQLite doesn't support RETURNING — query first, then update\n const staleRows = staleDb.prepare(\n `SELECT id FROM agent_runs WHERE status = 'running'`,\n ).all() as Array<{ id: string }>;\n\n if (staleRows.length > 0) {\n staleDb.prepare(\n `UPDATE agent_runs SET status = 'failed', completed_at = ?, error = 'Daemon restarted while run was in progress' WHERE status = 'running'`,\n ).run(epochSeconds());\n logger.info(LOG_KINDS.AGENT_RUN, 'Cleaned stale running agent runs', {\n count: staleRows.length,\n ids: staleRows.map((r) => r.id),\n });\n }\n } catch (err) {\n logger.warn(LOG_KINDS.AGENT_ERROR, 'Failed to clean stale runs', { error: (err as Error).message });\n }\n\n // Resolve dist/ui/ from the package root\n let uiDir: string | null = null;\n {\n const root = findPackageRoot(path.dirname(new URL(import.meta.url).pathname));\n if (root) {\n const candidate = path.join(root, 'dist', 'ui');\n if (fs.existsSync(candidate)) uiDir = candidate;\n }\n }\n if (uiDir) {\n logger.debug(LOG_KINDS.DAEMON_START, 'Static UI directory found', { path: uiDir });\n }\n\n const powerManager = new PowerManager({\n idleThresholdMs: POWER_IDLE_THRESHOLD_MS,\n sleepThresholdMs: POWER_SLEEP_THRESHOLD_MS,\n deepSleepThresholdMs: POWER_DEEP_SLEEP_THRESHOLD_MS,\n activeIntervalMs: POWER_ACTIVE_INTERVAL_MS,\n sleepIntervalMs: POWER_SLEEP_INTERVAL_MS,\n logger,\n });\n\n const server = new DaemonServer({\n vaultDir,\n logger,\n uiDir: uiDir ?? undefined,\n onRequest: () => powerManager.recordActivity(),\n });\n\n // The daemon serves the dashboard UI and must stay running regardless of\n // active sessions. No auto-shutdown — runs until explicitly killed.\n const registry = new SessionRegistry({\n gracePeriod: 0,\n onEmpty: () => {},\n });\n\n const transcriptMiner = new TranscriptMiner({\n additionalAdapters: config.capture.transcript_paths.map((p) =>\n createPerProjectAdapter(p, claudeCodeAdapter.parseTurns),\n ),\n });\n\n let activeStopProcessing: Promise<void> | null = null;\n\n // Cache derived titles per session — the title comes from the first prompt\n // and never changes, so we only need to query the DB once.\n const sessionTitleCache = new Map<string, string>();\n\n /**\n * Fire-and-forget trigger for the title-summary agent task.\n * Guards: summary_batch_interval must be > 0 (0 = disabled), no run\n * already in progress. Callers add their own preconditions (e.g. batch\n * threshold, missing title).\n */\n async function triggerTitleSummary(sessionId: string): Promise<void> {\n if (config.agent.summary_batch_interval <= 0) return;\n const running = getRunningRun(DEFAULT_AGENT_ID);\n if (running) return;\n try {\n const { runAgent } = await import('../agent/executor.js');\n runAgent(vaultDir, {\n task: 'title-summary',\n instruction: `Process session ${sessionId} only`,\n embeddingManager,\n }).catch(err => logger.warn(LOG_KINDS.AGENT_ERROR, 'Title-summary task failed', { error: String(err) }));\n } catch { /* agent unavailable */ }\n }\n\n const bufferDir = path.join(vaultDir, 'buffer');\n const sessionBuffers = new Map<string, EventBuffer>();\n\n // Clean up stale buffer files (>24h) on startup\n const startupCleanedCount = cleanStaleBuffers(bufferDir, STALE_BUFFER_MAX_AGE_MS);\n if (startupCleanedCount > 0) {\n logger.info(LOG_KINDS.CAPTURE_BUFFER, 'Buffer cleanup complete', { stale_removed: startupCleanedCount });\n }\n\n // Track sessions already reconciled this daemon lifetime to avoid\n // redundant file reads (startup scan + register + event can all fire).\n const reconciledSessions = new Set<string>();\n\n // Reconcile all remaining buffer files on startup — recover events from\n // sessions that had activity while the daemon was down.\n for (const sessionId of listBufferSessionIds(bufferDir)) {\n try {\n reconcileSession(sessionId);\n } catch (err) {\n logger.warn(LOG_KINDS.LIFECYCLE_RECONCILE, 'Startup reconciliation failed', { session_id: sessionId, error: String(err) });\n }\n }\n\n /**\n * Replay a single buffer event into the DB via the appropriate handler.\n *\n * Shared between reconcileSession (buffer replay) and the live /events\n * route to eliminate dispatch duplication.\n *\n * @returns 'prompt' | 'activity' | null indicating what was created.\n */\n function replayEvent(sessionId: string, event: Record<string, unknown>): 'prompt' | 'activity' | null {\n if (event.type === 'user_prompt') {\n if (isSystemMessage(String(event.prompt ?? ''))) return null;\n handleUserPrompt(sessionId, String(event.prompt ?? ''));\n return 'prompt';\n }\n if (event.type === 'tool_use') {\n handleToolUse(\n sessionId,\n String(event.tool_name ?? ''),\n event.tool_input,\n typeof event.output_preview === 'string' ? event.output_preview : undefined,\n );\n return 'activity';\n }\n if (event.type === 'tool_failure') {\n handleToolFailure(\n sessionId,\n String(event.tool_name ?? ''),\n event.tool_input,\n typeof event.error === 'string' ? event.error : undefined,\n !!event.is_interrupt,\n );\n return 'activity';\n }\n return null;\n }\n\n /**\n * Reconcile buffer events against DB state for a session.\n *\n * The buffer is the authoritative event log. The DB (prompt_batches +\n * activities) is a derived view. After a daemon restart, the DB may be\n * missing events the daemon didn't process while it was down.\n *\n * Activities belong to batches — they're linked via the latest open batch\n * at insertion time. So we can't reconcile them separately. Instead, we\n * find where the DB diverges from the buffer (by prompt count) and replay\n * the FULL event stream from that point: prompts open batches, tool events\n * attach to the open batch — exactly the normal flow.\n */\n function reconcileSession(sessionId: string): void {\n if (reconciledSessions.has(sessionId)) return;\n reconciledSessions.add(sessionId);\n\n // Read buffer file directly — avoid EventBuffer constructor which reads\n // the file to compute a count we don't need.\n const bufferPath = path.join(bufferDir, `${sessionId}.jsonl`);\n if (!fs.existsSync(bufferPath)) return;\n const content = fs.readFileSync(bufferPath, 'utf-8').trim();\n if (!content) return;\n\n // Buffer files outlive session rows — sessions may have been manually\n // deleted or cleaned up by the session cleanup job. Skip reconciliation\n // for sessions that no longer exist rather than resurrecting them.\n if (!getSession(sessionId)) {\n logger.debug(LOG_KINDS.LIFECYCLE_RECONCILE, 'Skipping reconciliation for deleted session', { session_id: sessionId });\n return;\n }\n\n const allEvents: Array<Record<string, unknown>> = content.split('\\n').map((line) => JSON.parse(line));\n\n // Find the divergence point: how many real prompts does the DB have?\n const existingBatchCount = listBatchesBySession(sessionId).length;\n\n let promptsSeen = 0;\n let replayStartIndex = -1;\n\n for (let i = 0; i < allEvents.length; i++) {\n const e = allEvents[i];\n if (e.type === 'user_prompt' && !isSystemMessage(String(e.prompt ?? ''))) {\n promptsSeen++;\n if (promptsSeen === existingBatchCount + 1) {\n replayStartIndex = i;\n break;\n }\n }\n }\n\n if (replayStartIndex === -1) return;\n\n // Replay full event stream from the divergence point\n const eventsToReplay = allEvents.slice(replayStartIndex).filter(\n (e) => REPLAYABLE_EVENT_TYPES.has(String(e.type)),\n );\n\n let promptsRecovered = 0;\n let activitiesRecovered = 0;\n\n for (const event of eventsToReplay) {\n try {\n const result = replayEvent(sessionId, event);\n if (result === 'prompt') promptsRecovered++;\n else if (result === 'activity') activitiesRecovered++;\n } catch (err) {\n logger.warn(LOG_KINDS.LIFECYCLE_RECONCILE, 'Reconciliation: failed to replay event', {\n type: String(event.type),\n error: String(err),\n });\n }\n }\n\n if (promptsRecovered > 0 || activitiesRecovered > 0) {\n logger.info(LOG_KINDS.LIFECYCLE_RECONCILE, 'Buffer reconciliation complete', {\n session_id: sessionId,\n prompts_recovered: promptsRecovered,\n activities_recovered: activitiesRecovered,\n });\n }\n }\n\n // Route body schemas\n const RegisterBody = z.object({\n session_id: z.string(),\n agent: z.string().optional(),\n branch: z.string().optional(),\n started_at: z.string().optional(),\n });\n const UnregisterBody = z.object({ session_id: z.string() });\n const EventBody = z.object({ type: z.string(), session_id: z.string() }).passthrough();\n const StopBody = z.object({\n session_id: z.string(),\n user: z.string().optional(),\n transcript_path: z.string().optional(),\n last_assistant_message: z.string().optional(),\n });\n // --- Session routes ---\n\n server.registerRoute('POST', '/sessions/register', async (req) => {\n const { session_id, agent, branch, started_at } = RegisterBody.parse(req.body);\n const resolvedStartedAt = started_at ?? new Date().toISOString();\n registry.register(session_id, { started_at: resolvedStartedAt, branch });\n server.updateDaemonJsonSessions(registry.sessions);\n\n // Upsert session in SQLite — always reset to active on register\n const now = epochSeconds();\n const startedEpoch = Math.floor(new Date(resolvedStartedAt).getTime() / 1000);\n upsertSession({\n id: session_id,\n agent: agent ?? 'claude-code',\n user: null,\n project_root: process.cwd(),\n branch: branch ?? null,\n started_at: startedEpoch,\n created_at: now,\n status: 'active',\n });\n // Clear ended_at if session was previously completed (reload scenario)\n updateSession(session_id, { ended_at: null, status: 'active' });\n\n // Reconcile buffer against DB — recover prompts lost if daemon was down mid-session.\n reconcileSession(session_id);\n\n logger.info(LOG_KINDS.LIFECYCLE_REGISTER, 'Session registered', { session_id, branch, started_at: started_at ?? null });\n return { body: { ok: true, sessions: registry.sessions } };\n });\n\n server.registerRoute('POST', '/sessions/unregister', async (req) => {\n const { session_id } = UnregisterBody.parse(req.body);\n registry.unregister(session_id);\n // Opportunistically clean stale buffers for OTHER sessions (>24h).\n // We do NOT delete THIS session's buffer — session reload reuses the same ID.\n cleanStaleBuffers(bufferDir, STALE_BUFFER_MAX_AGE_MS, session_id);\n // Close the session in SQLite — this is the authoritative end-of-session.\n // The Stop hook fires per-turn and does NOT close the session.\n closeSession(session_id, epochSeconds());\n\n // Prune in-memory state\n sessionBuffers.delete(session_id);\n sessionTitleCache.delete(session_id);\n reconciledSessions.delete(session_id);\n server.updateDaemonJsonSessions(registry.sessions);\n logger.info(LOG_KINDS.LIFECYCLE_UNREGISTER, 'Session unregistered', { session_id });\n return { body: { ok: true, sessions: registry.sessions } };\n });\n\n // --- Event routes ---\n\n server.registerRoute('POST', '/events', async (req) => {\n const validated = EventBody.parse(req.body);\n const event = { ...validated, timestamp: validated.timestamp ?? new Date().toISOString() } as Record<string, unknown> & { type: string; session_id: string; timestamp: string };\n logger.debug(LOG_KINDS.HOOKS_EVENT, 'Event received', { type: event.type, session_id: event.session_id });\n\n // Ensure session is registered (idempotent — handles daemon restarts mid-session)\n if (!registry.getSession(event.session_id)) {\n registry.register(event.session_id, { started_at: event.timestamp });\n logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, 'Auto-registered session from event', { session_id: event.session_id });\n\n // Ensure SQLite session exists — explicitly set status='active' so\n // resumed sessions (previously 'completed') get reopened.\n const now = epochSeconds();\n const startedEpoch = Math.floor(new Date(event.timestamp).getTime() / 1000);\n upsertSession({\n id: event.session_id,\n agent: (event as Record<string, unknown>).agent as string ?? 'claude-code',\n status: 'active',\n started_at: startedEpoch,\n created_at: now,\n });\n\n // Reconcile buffer against DB — recover any prompts lost during downtime.\n reconcileSession(event.session_id);\n }\n\n // Persist to disk so events survive daemon restarts\n if (!sessionBuffers.has(event.session_id)) {\n sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));\n }\n sessionBuffers.get(event.session_id)!.append(event);\n\n // --- Prompt batch tracking ---\n if (event.type === 'user_prompt') {\n const promptText = String(event.prompt ?? '');\n\n // Skip system-injected messages (task notifications, system reminders) —\n // they trigger UserPromptSubmit but are not real user prompts.\n if (isSystemMessage(promptText)) {\n logger.debug(LOG_KINDS.HOOKS_PROMPT, 'Skipped system-injected message', {\n session_id: event.session_id,\n prefix: promptText.trimStart().slice(0, LOG_PROMPT_PREVIEW_CHARS),\n });\n } else {\n logger.info(LOG_KINDS.HOOKS_PROMPT, 'User prompt received', {\n session_id: event.session_id,\n prompt_preview: promptText.slice(0, LOG_PROMPT_PREVIEW_CHARS),\n prompt_length: promptText.length,\n });\n try {\n const { batchId, promptNumber } = handleUserPrompt(event.session_id, promptText || undefined);\n logger.debug(LOG_KINDS.CAPTURE_BATCH, 'Batch opened', { session_id: event.session_id, batch_id: batchId, prompt_number: promptNumber });\n\n // Batch-threshold summary trigger\n const batchCount = promptNumber;\n const summaryInterval = config.agent.summary_batch_interval;\n if (summaryInterval > 0 && batchCount > 0 && batchCount % summaryInterval === 0) {\n triggerTitleSummary(event.session_id);\n }\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_BATCH, 'Failed to open batch', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n }\n\n if (event.type === 'tool_use') {\n const toolName = String(event.tool_name ?? '');\n logger.debug(LOG_KINDS.HOOKS_TOOL, 'Tool use event', {\n session_id: event.session_id,\n tool_name: toolName,\n });\n // Plan capture — detect writes to watched directories (async, non-blocking)\n const planFilePath = isPlanWriteEvent(\n toolName,\n event.tool_input as Record<string, unknown> | undefined,\n planWatchConfig,\n );\n if (planFilePath) {\n const captureSessionId = event.session_id;\n fs.promises.readFile(planFilePath, 'utf-8').then((planContent) => {\n const latestBatch = getLatestBatch(captureSessionId);\n capturePlan({\n sourcePath: path.relative(projectRoot, planFilePath),\n content: planContent,\n sessionId: captureSessionId,\n promptBatchId: latestBatch?.id ?? null,\n });\n logger.info(LOG_KINDS.CAPTURE_PLAN, 'Plan captured', {\n session_id: captureSessionId,\n source_path: planFilePath,\n });\n }).catch((err) => {\n logger.warn(LOG_KINDS.CAPTURE_PLAN, 'Failed to capture plan', {\n error: (err as Error).message,\n path: planFilePath,\n });\n });\n }\n try {\n handleToolUse(\n event.session_id,\n toolName,\n event.tool_input,\n typeof event.output_preview === 'string' ? event.output_preview : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record activity', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'tool_failure') {\n const toolName = String(event.tool_name ?? '');\n logger.info(LOG_KINDS.HOOKS_TOOL, 'Tool failure event', {\n session_id: event.session_id,\n tool_name: toolName,\n is_interrupt: !!event.is_interrupt,\n });\n try {\n handleToolFailure(\n event.session_id,\n toolName,\n event.tool_input,\n typeof event.error === 'string' ? event.error : undefined,\n !!event.is_interrupt,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record tool failure', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'subagent_start') {\n logger.info(LOG_KINDS.HOOKS_SUBAGENT, 'Subagent start event', {\n session_id: event.session_id,\n agent_id: event.agent_id,\n agent_type: event.agent_type,\n });\n try {\n handleSubagentStart(\n event.session_id,\n typeof event.agent_id === 'string' ? event.agent_id : undefined,\n typeof event.agent_type === 'string' ? event.agent_type : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record subagent start', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'subagent_stop') {\n logger.info(LOG_KINDS.HOOKS_SUBAGENT, 'Subagent stop event', {\n session_id: event.session_id,\n agent_id: event.agent_id,\n agent_type: event.agent_type,\n });\n try {\n handleSubagentStop(\n event.session_id,\n typeof event.agent_id === 'string' ? event.agent_id : undefined,\n typeof event.agent_type === 'string' ? event.agent_type : undefined,\n typeof event.last_assistant_message === 'string' ? event.last_assistant_message : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record subagent stop', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'stop_failure') {\n logger.warn(LOG_KINDS.HOOKS_STOP, 'Stop failure event', {\n session_id: event.session_id,\n error: event.error,\n });\n try {\n handleStopFailure(\n event.session_id,\n typeof event.error === 'string' ? event.error : undefined,\n typeof event.error_details === 'string' ? event.error_details : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record stop failure', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'task_completed') {\n logger.info(LOG_KINDS.HOOKS_EVENT, 'Task completed event', {\n session_id: event.session_id,\n task_id: event.task_id,\n task_subject: event.task_subject,\n });\n try {\n handleTaskCompleted(\n event.session_id,\n typeof event.task_id === 'string' ? event.task_id : undefined,\n typeof event.task_subject === 'string' ? event.task_subject : undefined,\n typeof event.task_description === 'string' ? event.task_description : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record task completion', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'pre_compact') {\n logger.info(LOG_KINDS.HOOKS_EVENT, 'Pre-compact event', { session_id: event.session_id });\n try {\n handleCompact(\n event.session_id,\n 'pre',\n typeof event.trigger === 'string' ? event.trigger : undefined,\n undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record pre-compact', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n if (event.type === 'post_compact') {\n logger.info(LOG_KINDS.HOOKS_EVENT, 'Post-compact event', { session_id: event.session_id });\n try {\n handleCompact(\n event.session_id,\n 'post',\n typeof event.trigger === 'string' ? event.trigger : undefined,\n typeof event.compact_summary === 'string' ? event.compact_summary : undefined,\n );\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ACTIVITY, 'Failed to record post-compact', { session_id: event.session_id, error: (err as Error).message });\n }\n }\n\n return { body: { ok: true } };\n });\n\n // --- Stop route ---\n\n server.registerRoute('POST', '/events/stop', async (req) => {\n const { session_id: sessionId, user, transcript_path: hookTranscriptPath, last_assistant_message: lastAssistantMessage } = StopBody.parse(req.body);\n // Ensure session is registered (handles daemon restarts mid-session)\n if (!registry.getSession(sessionId)) {\n registry.register(sessionId, { started_at: new Date().toISOString() });\n logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, 'Auto-registered session from stop event', { session_id: sessionId });\n }\n const sessionMeta = registry.getSession(sessionId);\n logger.info(LOG_KINDS.HOOKS_STOP, 'Stop received', {\n session_id: sessionId,\n has_transcript_path: !!hookTranscriptPath,\n has_response: !!lastAssistantMessage,\n });\n logger.debug(LOG_KINDS.HOOKS_STOP, 'Stop event detail', {\n session_id: sessionId,\n transcript_path: hookTranscriptPath ?? null,\n last_message_preview: lastAssistantMessage?.slice(0, LOG_MESSAGE_PREVIEW_CHARS) ?? null,\n });\n\n // Respond immediately — the hook should not block on processing.\n const run = () => processStopEvent(sessionId, user, sessionMeta, hookTranscriptPath, lastAssistantMessage).catch((err) => {\n logger.error(LOG_KINDS.PROCESSOR_SESSION, 'Stop processing failed', { session_id: sessionId, error: (err as Error).message });\n });\n\n const prev = activeStopProcessing ?? Promise.resolve();\n activeStopProcessing = prev.then(run).finally(() => { activeStopProcessing = null; });\n\n return { body: { ok: true } };\n });\n\n /** Correlate buffer tool_use events with transcript turns by timestamp to populate toolBreakdown and files. */\n function enrichTurnsWithToolMetadata(turns: TranscriptTurn[], events: Array<Record<string, unknown>>): void {\n if (events.length === 0 || turns.length === 0) return;\n\n const toolEvents = events.filter((e) => e.type === 'tool_use');\n if (toolEvents.length === 0) return;\n\n let cursor = 0;\n for (let i = 0; i < turns.length; i++) {\n const turnEnd = i + 1 < turns.length ? turns[i + 1].timestamp : null;\n const breakdown: Record<string, number> = {};\n const files = new Set<string>();\n\n while (cursor < toolEvents.length) {\n const ts = String(toolEvents[cursor].timestamp ?? '');\n if (turnEnd !== null && ts >= turnEnd) break;\n const evt = toolEvents[cursor];\n const toolName = String(evt.tool_name ?? evt.tool ?? 'unknown');\n breakdown[toolName] = (breakdown[toolName] ?? 0) + 1;\n const input = evt.tool_input as Record<string, unknown> | undefined;\n const filePath = input?.file_path ?? input?.path;\n if (typeof filePath === 'string') files.add(filePath);\n cursor++;\n }\n\n if (Object.keys(breakdown).length > 0) {\n turns[i].toolBreakdown = breakdown;\n if (files.size > 0) turns[i].files = [...files];\n }\n }\n }\n\n async function processStopEvent(\n sessionId: string,\n user: string | undefined,\n sessionMeta: RegisteredSession | undefined,\n hookTranscriptPath?: string,\n lastAssistantMessage?: string,\n ): Promise<void> {\n\n // --- Phase 1: Gather transcript data ---\n\n const transcriptResult = transcriptMiner.getAllTurnsWithSource(sessionId, hookTranscriptPath);\n let allTurns = transcriptResult.turns;\n let turnSource = transcriptResult.source;\n\n const bufferEvents = sessionBuffers.get(sessionId)?.readAll() ?? [];\n\n if (allTurns.length === 0) {\n allTurns = extractTurnsFromBuffer(bufferEvents);\n turnSource = 'buffer';\n } else if (bufferEvents.length > 0) {\n const lastTranscriptTs = allTurns[allTurns.length - 1].timestamp;\n if (lastTranscriptTs) {\n const newerEvents = bufferEvents.filter((e) =>\n String(e.timestamp ?? '') > lastTranscriptTs,\n );\n if (newerEvents.length > 0) {\n const bufferTurns = extractTurnsFromBuffer(newerEvents);\n allTurns = [...allTurns, ...bufferTurns];\n turnSource = `${transcriptResult.source}+buffer`;\n logger.info(LOG_KINDS.PROCESSOR_TRANSCRIPT, 'Appended buffer turns missing from transcript', {\n session_id: sessionId, transcriptTurns: transcriptResult.turns.length, bufferTurns: bufferTurns.length,\n });\n }\n }\n }\n\n // Attach the last assistant message from the hook to the most recent turn\n if (lastAssistantMessage && allTurns.length > 0) {\n const lastTurn = allTurns[allTurns.length - 1];\n if (!lastTurn.aiResponse) {\n lastTurn.aiResponse = lastAssistantMessage;\n }\n }\n\n enrichTurnsWithToolMetadata(allTurns, bufferEvents);\n\n const imageCount = allTurns.reduce((sum, t) => sum + (t.images?.length ?? 0), 0);\n logger.debug(LOG_KINDS.PROCESSOR_TRANSCRIPT, 'Transcript parsed', {\n session_id: sessionId,\n turn_count: allTurns.length,\n image_count: imageCount,\n });\n\n // --- Phase 2: Capture response + close session ---\n\n // Get the latest batch BEFORE closing — this is the batch for the current turn.\n const latestBatch = getLatestBatch(sessionId);\n\n // Primary capture: put last_assistant_message directly on the latest batch.\n // No positional mapping needed — the hook gives us the response directly.\n if (lastAssistantMessage && latestBatch && !latestBatch.response_summary) {\n try { setResponseSummary(latestBatch.id, lastAssistantMessage); }\n catch (err) { logger.warn(LOG_KINDS.PROCESSOR_BATCH, 'Failed to set response_summary on latest batch', { error: String(err) }); }\n }\n\n // Close open batches but do NOT close the session — the Stop hook fires\n // after every assistant turn, not just session end. The session is closed\n // when the SessionEnd hook fires (via /sessions/unregister).\n closeOpenBatches(sessionId, epochSeconds());\n\n // Derive a simple title from the first user prompt — but only if the\n // session has no title yet. Once the LLM (or anything else) sets a title,\n // stop overwriting it with the fallback.\n const existingSession = getSession(sessionId);\n const hasTitle = existingSession?.title !== null && existingSession?.title !== undefined;\n\n if (!hasTitle) {\n let title = sessionTitleCache.get(sessionId) ?? null;\n if (!title) {\n const firstBatch = listBatchesBySession(sessionId, { limit: 1 })[0];\n if (firstBatch?.user_prompt) {\n title = firstBatch.user_prompt.slice(0, TITLE_PREVIEW_CHARS);\n if (firstBatch.user_prompt.length > TITLE_PREVIEW_CHARS) {\n title += '...';\n }\n sessionTitleCache.set(sessionId, title);\n }\n }\n }\n\n // Update session with transcript metadata (no LLM calls)\n const updateFields: Record<string, unknown> = {\n transcript_path: hookTranscriptPath ?? null,\n prompt_count: allTurns.length,\n tool_count: allTurns.reduce((sum, t) => sum + t.toolCount, 0),\n };\n if (user) updateFields.user = user;\n if (!hasTitle && sessionTitleCache.has(sessionId)) {\n updateFields.title = sessionTitleCache.get(sessionId);\n }\n\n updateSession(sessionId, updateFields as Parameters<typeof updateSession>[1]);\n\n // Enhanced capture: populate response_summary on earlier batches from transcript.\n // Maps by batch insertion order (id ASC) to transcript turn position.\n // This is best-effort — the parser may skip empty-text turns, causing misalignment.\n // The primary capture (above) handles the current turn reliably.\n const responses: Array<{ turnIndex: number; response: string }> = [];\n for (let i = 0; i < allTurns.length; i++) {\n if (allTurns[i].aiResponse) {\n responses.push({ turnIndex: i + 1, response: allTurns[i].aiResponse! });\n }\n }\n if (responses.length > 0) {\n try { populateBatchResponses(sessionId, responses); }\n catch (err) { logger.warn(LOG_KINDS.PROCESSOR_BATCH, 'Failed to populate batch responses', { error: String(err) }); }\n }\n\n // Trigger title/summary if the session still needs one.\n if (!hasTitle) {\n triggerTitleSummary(sessionId);\n }\n\n // Write images to attachments — decoupled from transcript turn indices.\n // After context compaction, transcript turn indices no longer match batch prompt_numbers.\n // Instead, match each turn to its batch by prompt text (content-based, not position-based).\n // Binary data is stored in the DB BLOB column; DB uses ON CONFLICT DO NOTHING → idempotent.\n const sessionShort = sessionId.slice(-6);\n for (let i = 0; i < allTurns.length; i++) {\n const turn = allTurns[i];\n if (!turn.images?.length) continue;\n\n // Resolve which batch this turn belongs to:\n // 1. Last turn → use latestBatch (always correct, comes from the current stop event)\n // 2. Earlier turns → match by prompt text prefix against DB\n // 3. Fallback → null batch_id (still saved, UI matches by filename pattern)\n const isLastTurn = i === allTurns.length - 1;\n let resolvedBatchId: number | null = null;\n let resolvedPromptNumber: number = i + 1; // default to turn index (pre-compaction compatible)\n\n if (isLastTurn && latestBatch) {\n resolvedBatchId = latestBatch.id;\n resolvedPromptNumber = latestBatch.prompt_number ?? resolvedPromptNumber;\n } else if (turn.prompt) {\n try {\n const match = findBatchByPromptPrefix(sessionId, turn.prompt);\n if (match) {\n resolvedBatchId = match.id;\n resolvedPromptNumber = match.prompt_number;\n }\n } catch { /* fallback to index-based */ }\n }\n\n for (let j = 0; j < turn.images.length; j++) {\n const img = turn.images[j];\n const ext = extensionForMimeType(img.mediaType);\n const filename = `${sessionShort}-t${resolvedPromptNumber}-${j + 1}.${ext}`;\n const imageBuffer = Buffer.from(img.data, 'base64');\n try {\n insertAttachment({\n id: `${sessionShort}-b${resolvedPromptNumber}-${j + 1}`,\n session_id: sessionId,\n prompt_batch_id: resolvedBatchId ?? undefined,\n file_path: filename,\n media_type: img.mediaType,\n data: imageBuffer,\n created_at: epochSeconds(),\n });\n logger.debug(LOG_KINDS.CAPTURE_ATTACHMENT, 'Image stored in DB', { filename, batch: resolvedPromptNumber });\n } catch (err) {\n logger.warn(LOG_KINDS.CAPTURE_ATTACHMENT, 'Failed to record attachment', { error: String(err) });\n }\n }\n }\n\n logger.info(LOG_KINDS.PROCESSOR_SESSION, 'Session captured', {\n session_id: sessionId,\n turns: allTurns.length,\n source: turnSource,\n title: existingSession?.title ?? sessionTitleCache.get(sessionId) ?? '(untitled)',\n });\n }\n\n // --- Context injection (digest + semantic spore search) ---\n const contextDeps = { embeddingManager, config, logger };\n server.registerRoute('POST', '/context', createSessionContextHandler(contextDeps));\n server.registerRoute('POST', '/context/prompt', createPromptContextHandler(contextDeps));\n\n // --- Dashboard API routes ---\n const progressTracker = new ProgressTracker();\n let configHash = computeConfigHash(vaultDir);\n\n server.registerRoute('GET', '/api/config', async () => handleGetConfig(vaultDir));\n server.registerRoute('GET', '/api/symbionts', handleListSymbionts);\n server.registerRoute('PUT', '/api/config', async (req) => {\n const result = await handlePutConfig(vaultDir, req.body);\n if (!result.status || result.status < 400) {\n configHash = computeConfigHash(vaultDir);\n }\n return result;\n });\n\n // Pre-compute symbiont plan dirs for the config endpoint (manifests don't change at runtime)\n const symbiontPlanDirsByAgent: Record<string, string[]> = {};\n for (const m of manifests) {\n const dirs = m.capture?.planDirs ?? [];\n if (dirs.length > 0) symbiontPlanDirsByAgent[m.displayName] = dirs;\n }\n\n server.registerRoute('GET', '/api/config/plan-dirs', async () => {\n return { body: { symbiont: symbiontPlanDirsByAgent, custom: planWatchConfig.watchDirs.filter((d) => !symbiontPlanDirs.includes(d)) } };\n });\n\n server.registerRoute('POST', '/api/config/plan-dirs', async (req) => {\n const body = req.body as { plan_dirs: string[] };\n if (!Array.isArray(body.plan_dirs)) {\n return { status: 400, body: { error: 'plan_dirs must be an array' } };\n }\n const updated = updateConfig(vaultDir, (cfg) => ({\n ...cfg,\n capture: { ...cfg.capture, plan_dirs: body.plan_dirs },\n }));\n // Refresh in-memory config so plan capture picks up new dirs immediately\n planWatchConfig = { ...planWatchConfig, watchDirs: [...new Set([...symbiontPlanDirs, ...body.plan_dirs])] };\n return { body: { custom: updated.capture.plan_dirs } };\n });\n\n // V2 stats — vault counts, embedding coverage, agent status, digest freshness\n server.registerRoute('GET', '/api/stats', async () => {\n const stats = gatherStats(vaultDir, { active_sessions: registry.sessions });\n // Overlay live daemon fields from the running process (more accurate than daemon.json)\n stats.daemon.pid = process.pid;\n stats.daemon.port = server.port;\n stats.daemon.version = server.version;\n stats.daemon.uptime_seconds = Math.floor(process.uptime());\n return { body: { ...stats, config_hash: configHash } };\n });\n\n server.registerRoute('GET', '/api/logs/search', handleLogSearch);\n server.registerRoute('GET', '/api/logs/stream', handleLogStream);\n server.registerRoute('GET', '/api/logs/:id', handleLogDetail);\n\n // External log ingestion: allows MCP server (separate process) to write through the daemon logger\n const ExternalLogBody = z.object({\n level: z.enum(['debug', 'info', 'warn', 'error']),\n component: z.string(),\n message: z.string(),\n data: z.record(z.string(), z.unknown()).optional(),\n });\n\n server.registerRoute('POST', '/api/log', async (req) => {\n const { level, component, message, data } = ExternalLogBody.parse(req.body);\n logger.log(level, LOG_KINDS.MCP_EVENT, message, { ...data, mcp_component: component });\n return { body: { ok: true } };\n });\n\n server.registerRoute('GET', '/api/models', async (req) => handleGetModels(req));\n server.registerRoute('POST', '/api/restart', async (req) => handleRestart({ vaultDir, progressTracker }, req.body));\n\n // --- Update routes ---\n const updateProjectRoot = path.dirname(vaultDir);\n const updateHandlers = createUpdateHandlers({\n vaultDir,\n projectRoot: updateProjectRoot,\n currentVersion: server.version,\n scheduleShutdown: () => {\n setTimeout(() => {\n process.kill(process.pid, 'SIGTERM');\n }, RESTART_RESPONSE_FLUSH_MS);\n },\n });\n\n server.registerRoute('GET', '/api/update/status', async (req) => updateHandlers.handleUpdateStatus(req));\n server.registerRoute('POST', '/api/update/check', async (req) => updateHandlers.handleUpdateCheck(req));\n server.registerRoute('POST', '/api/update/apply', async (req) => updateHandlers.handleUpdateApply(req));\n server.registerRoute('PUT', '/api/update/channel', async (req) => updateHandlers.handleUpdateChannel(req));\n\n server.registerRoute('GET', '/api/progress/:token', async (req) => handleGetProgress(progressTracker, req.params.token));\n\n server.registerRoute('GET', '/api/sessions', handleListSessions);\n\n server.registerRoute('GET', '/api/sessions/:id', handleGetSession);\n server.registerRoute('GET', '/api/sessions/:id/impact', async (req) => {\n const sessionId = req.params.id;\n const session = getSession(sessionId);\n if (!session) return { status: 404, body: { error: 'Session not found' } };\n const impact = getSessionImpact(sessionId);\n return { body: impact };\n });\n\n server.registerRoute('DELETE', '/api/sessions/:id', async (req) => {\n const sessionId = req.params.id;\n const result = deleteSessionCascade(sessionId);\n if (!result.deleted) return { status: 404, body: { error: 'Session not found' } };\n\n // Post-transaction cleanup (fire-and-forget)\n cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir).catch(() => {});\n\n logger.info(LOG_KINDS.API_SESSION_DELETE, 'Session cascade deleted', {\n session_id: sessionId,\n counts: result.counts,\n });\n return { body: { ok: true, counts: result.counts } };\n });\n server.registerRoute('GET', '/api/sessions/:id/batches', handleGetSessionBatches);\n server.registerRoute('GET', '/api/batches/:id/activities', handleGetBatchActivities);\n server.registerRoute('GET', '/api/sessions/:id/attachments', handleGetSessionAttachments);\n server.registerRoute('GET', '/api/sessions/:id/plans', handleGetSessionPlans);\n\n // --- Mycelium API routes ---\n server.registerRoute('GET', '/api/spores', handleListSpores);\n server.registerRoute('GET', '/api/spores/:id', handleGetSpore);\n server.registerRoute('GET', '/api/entities', handleListEntities);\n server.registerRoute('GET', '/api/graph', handleGetFullGraph);\n server.registerRoute('GET', '/api/graph/:id', handleGetGraph);\n server.registerRoute('GET', '/api/digest', handleGetDigest);\n\n /** Media type lookup for attachment file serving. */\n const ATTACHMENT_MEDIA_TYPES: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n };\n\n server.registerRoute('GET', '/api/attachments/:filename', async (req) => {\n const filename = req.params.filename;\n // Prevent path traversal\n if (filename.includes('..') || filename.includes('/')) {\n return { status: 400, body: { error: 'invalid_filename' } };\n }\n\n // Try DB first (new path)\n const att = getAttachmentByFilePath(filename);\n if (att?.data) {\n const contentType = att.media_type ?? 'application/octet-stream';\n return { status: 200, headers: { 'Content-Type': contentType }, body: att.data };\n }\n\n // Fallback to disk for pre-migration attachments\n const filePath = path.join(vaultDir, 'attachments', filename);\n let diskData: Buffer;\n try {\n diskData = fs.readFileSync(filePath);\n } catch {\n return { status: 404, body: { error: 'not_found' } };\n }\n const ext = path.extname(filename).slice(1).toLowerCase();\n const contentType = ATTACHMENT_MEDIA_TYPES[ext] ?? 'application/octet-stream';\n return { status: 200, headers: { 'Content-Type': contentType }, body: diskData };\n });\n\n // --- Agent API routes ---\n\n const AgentRunBody = z.object({\n task: z.string().optional(),\n instruction: z.string().optional(),\n agentId: z.string().optional(),\n });\n\n server.registerRoute('POST', '/api/agent/run', async (req) => {\n const { task, instruction, agentId } = AgentRunBody.parse(req.body);\n\n const { runAgent } = await import('../agent/executor.js');\n const resultPromise = runAgent(vaultDir, { task, instruction, agentId, embeddingManager });\n\n // runAgent inserts the run synchronously before the first await,\n // so by the time it yields, the run is already in the DB.\n const effectiveAgentId = agentId ?? 'myco-agent';\n const latestRun = getRunningRun(effectiveAgentId);\n const runId = latestRun?.id;\n\n resultPromise\n .then((result) => {\n if (result.status === 'failed') {\n logger.error(LOG_KINDS.AGENT_ERROR, 'Agent run failed', {\n runId: result.runId,\n error: result.error ?? 'No error message',\n phases: result.phases?.map(p => `${p.name}:${p.status}`) ?? [],\n });\n } else {\n logger.info(LOG_KINDS.AGENT_RUN, 'Agent run completed', {\n runId: result.runId,\n status: result.status,\n phases: result.phases?.map(p => `${p.name}:${p.status}`) ?? [],\n });\n }\n })\n .catch((err) => {\n logger.error(LOG_KINDS.AGENT_ERROR, 'Agent run threw unhandled error', {\n error: (err as Error).message ?? String(err),\n stack: (err as Error).stack?.split('\\n').slice(0, 3).join(' | '),\n });\n });\n\n return { body: { ok: true, message: 'Agent started', runId } };\n });\n\n server.registerRoute('GET', '/api/agent/runs', async (req) => {\n const limit = req.query.limit ? Number(req.query.limit) : AGENT_RUNS_DEFAULT_LIMIT;\n const offset = req.query.offset ? Number(req.query.offset) : 0;\n const agentId = req.query.agentId || undefined;\n const status = req.query.status || undefined;\n const task = req.query.task || undefined;\n const search = req.query.search || undefined;\n\n const filterOpts = { agent_id: agentId, status, task, search };\n const runs = listRuns({ ...filterOpts, limit, offset });\n const total = countRuns(filterOpts);\n\n return { body: { runs, total, offset, limit } };\n });\n\n server.registerRoute('GET', '/api/agent/runs/:id', async (req) => {\n const run = getRun(req.params.id);\n if (!run) {\n return { status: 404, body: { error: 'Run not found' } };\n }\n return { body: { run } };\n });\n\n server.registerRoute('GET', '/api/agent/runs/:id/reports', async (req) => {\n const reports = listReports(req.params.id);\n return { body: { reports } };\n });\n\n server.registerRoute('GET', '/api/agent/runs/:id/turns', async (req) => {\n const turns = listTurnsByRun(req.params.id);\n return { body: turns };\n });\n\n server.registerRoute('GET', '/api/agent/tasks', async (req) => handleListTasks(req, vaultDir));\n server.registerRoute('GET', '/api/agent/tasks/:id', async (req) => handleGetTask(req, vaultDir));\n server.registerRoute('GET', '/api/agent/tasks/:id/yaml', async (req) => handleGetTaskYaml(req, vaultDir));\n server.registerRoute('PUT', '/api/agent/tasks/:id', async (req) => handleUpdateTask(req, vaultDir));\n server.registerRoute('POST', '/api/agent/tasks', async (req) => handleCreateTask(req, vaultDir));\n server.registerRoute('POST', '/api/agent/tasks/:id/copy', async (req) => handleCopyTask(req, vaultDir));\n server.registerRoute('DELETE', '/api/agent/tasks/:id', async (req) => handleDeleteTask(req, vaultDir));\n server.registerRoute('GET', '/api/agent/tasks/:id/config', async (req) => handleGetTaskConfig(req, vaultDir));\n server.registerRoute('PUT', '/api/agent/tasks/:id/config', async (req) => handleUpdateTaskConfig(req, vaultDir));\n\n // --- Provider detection & testing ---\n server.registerRoute('GET', '/api/providers', async () => handleGetProviders());\n server.registerRoute('POST', '/api/providers/test', async (req) => handleTestProvider(req));\n\n // --- MCP proxy routes ---\n // These routes exist so the MCP server can proxy tool calls through the\n // daemon instead of opening its own SQLite connection.\n\n const SPORE_ID_RANDOM_BYTES = 4;\n const RESOLUTION_ID_RANDOM_BYTES = 8;\n\n const RememberBody = z.object({\n content: z.string(),\n type: z.string().optional(),\n tags: z.array(z.string()).optional(),\n });\n\n server.registerRoute('POST', '/api/mcp/remember', async (req) => {\n const { content, type, tags } = RememberBody.parse(req.body);\n const { randomBytes } = await import('node:crypto');\n\n const observationType = type ?? 'discovery';\n const id = `${observationType}-${randomBytes(SPORE_ID_RANDOM_BYTES).toString('hex')}`;\n const now = epochSeconds();\n\n // Ensure the user agent exists (idempotent upsert)\n registerAgent({\n id: USER_AGENT_ID,\n name: USER_AGENT_NAME,\n created_at: now,\n });\n\n const spore = insertSpore({\n id,\n agent_id: USER_AGENT_ID,\n observation_type: observationType,\n content,\n tags: tags ? tags.join(', ') : null,\n created_at: now,\n });\n\n embeddingManager.onContentWritten('spores', spore.id, content, {\n status: 'active',\n observation_type: observationType,\n }).catch(() => {});\n\n return {\n body: {\n id: spore.id,\n observation_type: spore.observation_type,\n status: spore.status,\n created_at: spore.created_at,\n },\n };\n });\n\n server.registerRoute('GET', '/api/mcp/plans', async (req) => {\n const statusFilter = req.query.status === 'all' ? undefined : req.query.status;\n const limit = req.query.limit ? Number(req.query.limit) : undefined;\n\n const rows = listPlans({ status: statusFilter, limit });\n\n const plans = rows.map((row) => {\n const content = row.content ?? '';\n const checked = (content.match(/- \\[x\\]/gi) ?? []).length;\n const unchecked = (content.match(/- \\[ \\]/g) ?? []).length;\n const total = checked + unchecked;\n const progress = total === 0 ? 'N/A' : `${checked}/${total}`;\n\n return {\n id: row.id,\n title: row.title,\n status: row.status,\n progress,\n tags: row.tags ? row.tags.split(',').map((t) => t.trim()) : [],\n created_at: row.created_at,\n };\n });\n\n return { body: { plans } };\n });\n\n server.registerRoute('GET', '/api/mcp/sessions', async (req) => {\n const limit = req.query.limit ? Number(req.query.limit) : 20;\n const status = req.query.status;\n\n const rows = listSessions({ limit, status });\n const sessions = rows.map((row) => ({\n id: row.id,\n agent: row.agent,\n user: row.user,\n branch: row.branch,\n started_at: row.started_at,\n ended_at: row.ended_at,\n status: row.status,\n title: row.title,\n summary: (row.summary ?? '').slice(0, 300),\n prompt_count: row.prompt_count,\n tool_count: row.tool_count,\n parent_session_id: row.parent_session_id,\n }));\n\n return { body: { sessions } };\n });\n\n server.registerRoute('GET', '/api/mcp/team', async () => {\n const teamDb = getDatabase();\n const rows = teamDb.prepare(\n `SELECT id, \"user\", role, joined, tags\n FROM team_members\n ORDER BY id ASC`,\n ).all() as Array<Record<string, unknown>>;\n\n const members = rows.map((row) => ({\n id: row.id as string,\n user: row.user as string,\n role: (row.role as string) ?? null,\n joined: (row.joined as string) ?? null,\n tags: row.tags ? (row.tags as string).split(',').map((t) => t.trim()) : [],\n }));\n\n return { body: { members } };\n });\n\n const SupersedeBody = z.object({\n old_spore_id: z.string(),\n new_spore_id: z.string(),\n reason: z.string().optional(),\n });\n\n server.registerRoute('POST', '/api/mcp/supersede', async (req) => {\n const { old_spore_id, new_spore_id, reason } = SupersedeBody.parse(req.body);\n const { randomBytes } = await import('node:crypto');\n const now = epochSeconds();\n\n // Update status to superseded\n updateSporeStatus(old_spore_id, 'superseded', now);\n try { embeddingManager.onStatusChanged('spores', old_spore_id, 'superseded'); } catch { /* best-effort */ }\n\n // Ensure user agent exists (idempotent)\n registerAgent({\n id: USER_AGENT_ID,\n name: USER_AGENT_NAME,\n created_at: now,\n });\n\n // Record resolution event for audit trail\n const { insertResolutionEvent } = await import('../db/queries/resolution-events.js');\n const resolutionId = `res-${randomBytes(RESOLUTION_ID_RANDOM_BYTES).toString('hex')}`;\n\n insertResolutionEvent({\n id: resolutionId,\n agent_id: USER_AGENT_ID,\n spore_id: old_spore_id,\n action: 'supersede',\n new_spore_id,\n reason: reason ?? null,\n created_at: now,\n });\n\n return {\n body: {\n old_spore: old_spore_id,\n new_spore: new_spore_id,\n status: 'superseded' as const,\n },\n };\n });\n\n // --- Backup routes ---\n const backupDir = config.backup.dir\n ? path.resolve(config.backup.dir)\n : path.resolve(vaultDir, 'backups');\n const backupHandlers = createBackupHandlers({ db, backupDir, machineId });\n server.registerRoute('POST', '/api/backup', backupHandlers.handleCreateBackup);\n server.registerRoute('GET', '/api/backups', backupHandlers.handleListBackups);\n server.registerRoute('POST', '/api/restore/preview', backupHandlers.handleRestorePreview);\n server.registerRoute('POST', '/api/restore', backupHandlers.handleRestore);\n\n server.registerRoute('GET', '/api/backup/config', async () => {\n const cfg = loadConfig(vaultDir);\n return { body: { dir: cfg.backup.dir ?? null, default_dir: path.resolve(vaultDir, 'backups') } };\n });\n\n server.registerRoute('PUT', '/api/backup/config', async (req) => {\n const { dir } = req.body as { dir?: string | null };\n updateBackupConfig(vaultDir, { dir: dir || undefined });\n return { body: { dir: dir || null } };\n });\n\n // --- Team sync routes ---\n let teamClient: TeamSyncClient | null = null;\n\n // Initialize team client from saved config if team sync is enabled\n if (config.team.enabled && config.team.worker_url) {\n const secrets = readSecrets(vaultDir);\n const teamApiKey = secrets['MYCO_TEAM_API_KEY'];\n if (teamApiKey) {\n teamClient = new TeamSyncClient({\n workerUrl: config.team.worker_url,\n apiKey: teamApiKey,\n machineId,\n syncProtocolVersion: SYNC_PROTOCOL_VERSION,\n });\n logger.info(LOG_KINDS.TEAM_SYNC_START, 'Team sync client initialized', { worker_url: config.team.worker_url });\n\n // Register this node with the team worker (fire-and-forget)\n teamClient.connect({\n machine_id: machineId,\n version: server.version,\n }).then(() => {\n logger.info(LOG_KINDS.TEAM_SYNC_START, 'Node registered with team worker');\n }).catch((err) => {\n logger.warn(LOG_KINDS.TEAM_SYNC_ERROR, 'Node registration failed (will retry on next flush)', { error: (err as Error).message });\n });\n\n // Backfill unsynced records into outbox (fire-and-forget — can be large)\n setTimeout(() => {\n try {\n const backfilled = backfillUnsynced(machineId);\n if (backfilled > 0) {\n logger.info(LOG_KINDS.TEAM_SYNC_START, `Backfilled ${backfilled} unsynced records into outbox`);\n }\n } catch (err) {\n logger.error(LOG_KINDS.TEAM_SYNC_ERROR, 'Backfill failed', { error: (err as Error).message });\n }\n }, 0);\n }\n }\n\n const teamHandlers = createTeamHandlers({\n vaultDir,\n machineId,\n getTeamClient: () => teamClient,\n setTeamClient: (c) => { teamClient = c; },\n });\n server.registerRoute('POST', '/api/team/connect', teamHandlers.handleConnect);\n server.registerRoute('POST', '/api/team/disconnect', teamHandlers.handleDisconnect);\n server.registerRoute('GET', '/api/team/status', teamHandlers.handleStatus);\n\n server.registerRoute('POST', '/api/team/backfill', async () => {\n const count = backfillUnsynced(machineId);\n return { body: { enqueued: count } };\n });\n\n // --- Search, activity feed, and embedding status ---\n\n server.registerRoute('GET', '/api/search', createSearchHandler({ embeddingManager, getTeamClient: () => teamClient, machineId }));\n server.registerRoute('GET', '/api/activity', handleGetFeed);\n server.registerRoute('GET', '/api/embedding/status', async () => handleGetEmbeddingStatus(vaultDir));\n server.registerRoute('GET', '/api/embedding/details', async () => handleEmbeddingDetails(embeddingManager));\n server.registerRoute('POST', '/api/embedding/rebuild', async () => handleEmbeddingRebuild(embeddingManager));\n server.registerRoute('POST', '/api/embedding/reconcile', async () => handleEmbeddingReconcile(embeddingManager));\n server.registerRoute('POST', '/api/embedding/clean-orphans', async () => handleEmbeddingCleanOrphans(embeddingManager));\n server.registerRoute('POST', '/api/embedding/reembed-stale', async () => handleEmbeddingReembedStale(embeddingManager));\n\n // --- Start server ---\n\n await server.evictExistingDaemon();\n const resolvedPort = await resolvePort(config.daemon.port, vaultDir);\n if (resolvedPort === 0) {\n logger.warn(LOG_KINDS.DAEMON_PORT, 'All preferred ports occupied, using ephemeral port');\n }\n await server.start(resolvedPort);\n logger.info(LOG_KINDS.DAEMON_READY, 'Daemon ready', { vault: vaultDir, port: server.port });\n\n // Persist the resolved port to config if it was auto-derived\n if (config.daemon.port === null && resolvedPort !== 0) {\n try {\n updateConfig(vaultDir, (c) => ({\n ...c,\n daemon: { ...c.daemon, port: resolvedPort },\n }));\n logger.info(LOG_KINDS.DAEMON_CONFIG, 'Persisted auto-derived port to myco.yaml', { port: resolvedPort });\n } catch (err) {\n logger.warn(LOG_KINDS.DAEMON_CONFIG, 'Failed to persist auto-derived port', { error: (err as Error).message });\n }\n }\n\n // --- Register power-managed jobs ---\n\n let reconcileRunning = false;\n powerManager.register({\n name: 'embedding-reconcile',\n runIn: ['active', 'idle'],\n fn: async () => {\n if (reconcileRunning) return;\n reconcileRunning = true;\n try {\n await embeddingManager.reconcile(EMBEDDING_BATCH_SIZE);\n } finally {\n reconcileRunning = false;\n }\n },\n });\n\n powerManager.register({\n name: 'session-maintenance',\n runIn: ['active', 'idle', 'sleep'],\n fn: () => runSessionMaintenance({\n logger,\n registeredSessionIds: () => registry.sessions,\n embeddingManager,\n vaultDir,\n }),\n });\n\n // Agent auto-run: only when enabled, with its own interval guard.\n // Runs on the PowerManager tick but skips unless enough time has elapsed\n // since the last run (config.agent.interval_seconds).\n if (config.agent.auto_run) {\n let agentRunning = false;\n const agentIntervalMs = config.agent.interval_seconds * MS_PER_SECOND;\n\n // Seed lastAgentRun from the most recent completed/failed run so daemon\n // restarts don't immediately re-trigger the agent.\n const lastRunRow = getDatabase().prepare(\n `SELECT started_at FROM agent_runs WHERE agent_id = ? AND status IN ('completed', 'failed') ORDER BY started_at DESC LIMIT 1`,\n ).get(DEFAULT_AGENT_ID) as { started_at: number } | undefined;\n let lastAgentRun = lastRunRow ? lastRunRow.started_at * MS_PER_SECOND : 0;\n\n powerManager.register({\n name: 'agent-auto-run',\n runIn: ['active', 'idle'],\n fn: async () => {\n if (agentRunning) return;\n if (Date.now() - lastAgentRun < agentIntervalMs) return;\n\n // Pre-check: only spawn agent if there's unprocessed work\n const agentDb = getDatabase();\n const checkRow = agentDb.prepare('SELECT COUNT(*) as count FROM prompt_batches WHERE processed = 0').get() as { count: number };\n const count = Number(checkRow.count);\n if (count === 0) {\n logger.debug(LOG_KINDS.AGENT_AUTO_RUN, 'No unprocessed batches, skipping cycle');\n return;\n }\n\n agentRunning = true;\n lastAgentRun = Date.now();\n try {\n logger.info(LOG_KINDS.AGENT_AUTO_RUN, 'Unprocessed batches found, starting agent', { count });\n const { runAgent } = await import('../agent/executor.js');\n const runResult = await runAgent(vaultDir, { embeddingManager });\n logger.info(LOG_KINDS.AGENT_RUN, 'Agent run completed', { status: runResult.status, runId: runResult.runId });\n } catch (err) {\n logger.error(LOG_KINDS.AGENT_ERROR, 'Agent auto-run failed', { error: (err as Error).message });\n } finally {\n agentRunning = false;\n }\n },\n });\n } else {\n logger.info(LOG_KINDS.AGENT_AUTO_RUN, 'Auto-agent disabled (agent.auto_run = false)');\n }\n\n powerManager.register({\n name: 'log-retention',\n runIn: ['idle', 'sleep'],\n fn: async () => {\n const retentionDays = config.daemon.log_retention_days;\n const cutoff = new Date(Date.now() - retentionDays * MS_PER_DAY).toISOString();\n const deleted = deleteOldLogs(cutoff);\n if (deleted > 0) {\n logger.info(LOG_KINDS.LOG_RETENTION, `Deleted ${deleted} log entries older than ${retentionDays} days`, { deleted, retention_days: retentionDays });\n }\n },\n });\n\n // Auto-backup: create a local SQL dump during idle/sleep cycles\n powerManager.register({\n name: 'auto-backup',\n runIn: ['idle', 'sleep'],\n fn: async () => {\n try {\n logger.info(LOG_KINDS.BACKUP_START, 'Auto-backup starting');\n const filePath = createBackup(db, backupDir, machineId);\n logger.info(LOG_KINDS.BACKUP_COMPLETE, 'Auto-backup complete', { file_path: filePath });\n } catch (err) {\n logger.error(LOG_KINDS.BACKUP_ERROR, 'Auto-backup failed', { error: (err as Error).message });\n }\n },\n });\n\n // Team outbox flush: push pending records to the team worker\n if (config.team.enabled) {\n powerManager.register({\n name: 'team-sync-flush',\n runIn: ['active', 'idle'],\n fn: async () => {\n const client = teamClient;\n if (!client) return;\n\n try {\n const pending = listPending();\n if (pending.length === 0) return;\n\n logger.info(LOG_KINDS.TEAM_SYNC_START, 'Flushing outbox', { count: pending.length });\n const result = await client.pushBatch(pending);\n\n // Mark successfully synced records as sent\n if (result.synced > 0 || result.skipped > 0) {\n // Identify which records failed so we only mark the successes\n const failedIds = new Set(result.errors.map((e) => e.id));\n const sentIds = pending.filter((r) => !failedIds.has(String(r.row_id))).map((r) => r.id);\n if (sentIds.length > 0) {\n markSent(sentIds, epochSeconds());\n }\n }\n\n if (result.errors.length > 0) {\n logger.warn(LOG_KINDS.TEAM_SYNC_ERROR, `Sync errors: ${result.errors.length}`, {\n errors: result.errors.slice(0, 5),\n });\n }\n\n pruneOld();\n logger.info(LOG_KINDS.TEAM_SYNC_COMPLETE, 'Outbox flush complete', {\n synced: result.synced, skipped: result.skipped, errors: result.errors.length, total: pending.length,\n });\n } catch (err) {\n logger.error(LOG_KINDS.TEAM_SYNC_ERROR, 'Outbox flush failed', { error: (err as Error).message });\n }\n },\n });\n }\n\n powerManager.start();\n\n // --- Shutdown ---\n\n const shutdown = async (signal: string) => {\n logger.info(LOG_KINDS.DAEMON_START, `${signal} received`);\n powerManager.stop();\n // Wait for any active stop processing to finish before shutting down\n if (activeStopProcessing) {\n logger.info(LOG_KINDS.DAEMON_START, 'Waiting for active stop processing to complete...');\n await activeStopProcessing;\n }\n registry.destroy();\n await server.stop();\n vectorStore.close();\n closeDatabase();\n logger.close();\n process.exit(0);\n };\n\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","path","fs","path","os","path","os","TRANSCRIPT_BASE","fs","path","os","fs","path","os","fs","result","createHash","os","path","DEFAULT_STATUS","path","os","createHash","fs","path","fs","YAML","fs","path","fs","os","path","spawn","fs","path","os","spawn","fs","path","fs","path","path","createHash","fs","path","path","fs","createHash","DEFAULT_PROCESSED","SELECT_COLUMNS","DEFAULT_PROCESSED","SELECT_COLUMNS","SELECT_COLUMNS","SELECT_COLUMNS","DEFAULT_LIST_LIMIT","DEFAULT_LIST_LIMIT","DEFAULT_LIST_OFFSET","createHash","createHash","ACTIVE_STATUS","import_yaml","stringifyYaml","parseYaml","HTTP_OK","HTTP_BAD_REQUEST","fs","path","path","fs","fs","path","resolveDefinitionsDir","contentType"]}
|