@goondocks/myco 0.20.0 → 0.20.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-4HUXVRHW.js → agent-run-X25Q2A6T.js} +6 -6
- package/dist/{agent-tasks-JF45ELB6.js → agent-tasks-7B6OFERB.js} +6 -6
- package/dist/{chunk-POR75WM6.js → chunk-2PDWCDKY.js} +2 -2
- package/dist/{chunk-57O67XVF.js → chunk-6X2ERTQV.js} +2 -2
- package/dist/{chunk-4M7EWPIA.js → chunk-CCRGY3QW.js} +4 -3
- package/dist/{chunk-4M7EWPIA.js.map → chunk-CCRGY3QW.js.map} +1 -1
- package/dist/{chunk-P3DN5EWW.js → chunk-JZGN33AY.js} +5 -5
- package/dist/chunk-JZGN33AY.js.map +1 -0
- package/dist/{chunk-YSNIAJ5D.js → chunk-KESLPBKV.js} +3 -3
- package/dist/{chunk-SRXTSI25.js → chunk-OD4AA7PV.js} +2 -2
- package/dist/chunk-OD4AA7PV.js.map +1 -0
- package/dist/{chunk-4LCIKVDM.js → chunk-Q36VMZST.js} +4 -4
- package/dist/chunk-Q36VMZST.js.map +1 -0
- package/dist/{chunk-L6XFAJIF.js → chunk-UYMFCYBF.js} +57 -7
- package/dist/chunk-UYMFCYBF.js.map +1 -0
- package/dist/{chunk-UOQQENDW.js → chunk-VVNL26WX.js} +2 -2
- package/dist/{chunk-QS5TWZBL.js → chunk-XATDZX7U.js} +2 -2
- package/dist/{chunk-ACQ2AIEM.js → chunk-XG5RRUYF.js} +2 -2
- package/dist/{cli-AHTINAHY.js → cli-GGPWH4UO.js} +38 -38
- package/dist/{client-LHENCAV3.js → client-YXQUTXVZ.js} +3 -3
- package/dist/{config-XPV5GDE4.js → config-OMCYHG2S.js} +2 -2
- package/dist/{doctor-XPCF5HV5.js → doctor-5JXJ36KA.js} +9 -9
- package/dist/doctor-5JXJ36KA.js.map +1 -0
- package/dist/{executor-ACDHGTRH.js → executor-HWW2QNZQ.js} +10 -10
- package/dist/{init-V3KCC36O.js → init-LMYOVZAV.js} +12 -16
- package/dist/init-LMYOVZAV.js.map +1 -0
- package/dist/{installer-ZNK4JSQA.js → installer-FS257JRZ.js} +3 -3
- package/dist/{loader-H7OFASVC.js → loader-CQYTFHEW.js} +2 -2
- package/dist/{loader-TSB5M7FD.js → loader-NOMBJUPW.js} +2 -2
- package/dist/{main-5S4MDCIO.js → main-YTBVRTBI.js} +234 -115
- package/dist/main-YTBVRTBI.js.map +1 -0
- package/dist/{open-AB5ULZIB.js → open-HG2DX6RN.js} +6 -6
- package/dist/{post-compact-P2B7C7FE.js → post-compact-JSECI44W.js} +4 -4
- package/dist/{post-tool-use-LXL6NXDS.js → post-tool-use-POGPTJBA.js} +3 -3
- package/dist/{post-tool-use-failure-WAYVVKGR.js → post-tool-use-failure-OT7BFWQW.js} +4 -4
- package/dist/{pre-compact-BCXUCF4V.js → pre-compact-OXVODKH4.js} +4 -4
- package/dist/{registry-MGJSJBAS.js → registry-U4CHXK6R.js} +3 -3
- package/dist/{remove-KAPX5NT2.js → remove-N7ZPELFU.js} +6 -6
- package/dist/{restart-HQO36FTG.js → restart-ADG5GBTB.js} +7 -7
- package/dist/{search-YOMOKAAI.js → search-AHZEUNRR.js} +6 -6
- package/dist/{server-2N23P6F2.js → server-AGVYZVP5.js} +3 -3
- package/dist/{session-WW2JLHPX.js → session-6IU4AXYP.js} +6 -6
- package/dist/{session-end-4WRTIBVQ.js → session-end-FT27DWYZ.js} +3 -3
- package/dist/{session-start-HRWTZXQR.js → session-start-LAFICHII.js} +6 -6
- package/dist/{session-start-HRWTZXQR.js.map → session-start-LAFICHII.js.map} +1 -1
- package/dist/{setup-llm-HFWSBUAF.js → setup-llm-77MP4I2G.js} +6 -6
- package/dist/src/agent/definitions/tasks/full-intelligence.yaml +1 -1
- package/dist/src/agent/definitions/tasks/skill-evolve.yaml +1 -0
- package/dist/src/agent/definitions/tasks/skill-generate.yaml +1 -0
- package/dist/src/agent/definitions/tasks/skill-survey.yaml +23 -7
- 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/{stats-7A4CJ4MS.js → stats-NVPWOYTE.js} +7 -7
- package/dist/{stop-R2GDHMRA.js → stop-ZPIKVLH4.js} +3 -3
- package/dist/{stop-failure-773KR4VZ.js → stop-failure-2PX67YJC.js} +4 -4
- package/dist/{subagent-start-IDECNBHW.js → subagent-start-UUE6EHQD.js} +4 -4
- package/dist/{subagent-stop-3JH7DR2S.js → subagent-stop-KQWWWPE6.js} +4 -4
- package/dist/{task-completed-AYVHPHDR.js → task-completed-WMHOFQ7B.js} +4 -4
- package/dist/{team-3JKF7VAD.js → team-LRZ6GTQK.js} +3 -3
- package/dist/{update-YWYW55JM.js → update-O6V4RC4W.js} +6 -6
- package/dist/{user-prompt-submit-YELSR6XI.js → user-prompt-submit-N36KUPHI.js} +3 -3
- package/dist/{verify-JS44DVKJ.js → verify-LXPV7NYG.js} +4 -4
- package/dist/verify-LXPV7NYG.js.map +1 -0
- package/dist/{version-K5NETYIL.js → version-XMPPJQHR.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-4LCIKVDM.js.map +0 -1
- package/dist/chunk-L6XFAJIF.js.map +0 -1
- package/dist/chunk-P3DN5EWW.js.map +0 -1
- package/dist/chunk-SRXTSI25.js.map +0 -1
- package/dist/doctor-XPCF5HV5.js.map +0 -1
- package/dist/init-V3KCC36O.js.map +0 -1
- package/dist/main-5S4MDCIO.js.map +0 -1
- package/dist/verify-JS44DVKJ.js.map +0 -1
- /package/dist/{agent-run-4HUXVRHW.js.map → agent-run-X25Q2A6T.js.map} +0 -0
- /package/dist/{agent-tasks-JF45ELB6.js.map → agent-tasks-7B6OFERB.js.map} +0 -0
- /package/dist/{chunk-POR75WM6.js.map → chunk-2PDWCDKY.js.map} +0 -0
- /package/dist/{chunk-57O67XVF.js.map → chunk-6X2ERTQV.js.map} +0 -0
- /package/dist/{chunk-YSNIAJ5D.js.map → chunk-KESLPBKV.js.map} +0 -0
- /package/dist/{chunk-UOQQENDW.js.map → chunk-VVNL26WX.js.map} +0 -0
- /package/dist/{chunk-QS5TWZBL.js.map → chunk-XATDZX7U.js.map} +0 -0
- /package/dist/{chunk-ACQ2AIEM.js.map → chunk-XG5RRUYF.js.map} +0 -0
- /package/dist/{cli-AHTINAHY.js.map → cli-GGPWH4UO.js.map} +0 -0
- /package/dist/{client-LHENCAV3.js.map → client-YXQUTXVZ.js.map} +0 -0
- /package/dist/{config-XPV5GDE4.js.map → config-OMCYHG2S.js.map} +0 -0
- /package/dist/{executor-ACDHGTRH.js.map → executor-HWW2QNZQ.js.map} +0 -0
- /package/dist/{installer-ZNK4JSQA.js.map → installer-FS257JRZ.js.map} +0 -0
- /package/dist/{loader-H7OFASVC.js.map → loader-CQYTFHEW.js.map} +0 -0
- /package/dist/{loader-TSB5M7FD.js.map → loader-NOMBJUPW.js.map} +0 -0
- /package/dist/{open-AB5ULZIB.js.map → open-HG2DX6RN.js.map} +0 -0
- /package/dist/{post-compact-P2B7C7FE.js.map → post-compact-JSECI44W.js.map} +0 -0
- /package/dist/{post-tool-use-LXL6NXDS.js.map → post-tool-use-POGPTJBA.js.map} +0 -0
- /package/dist/{post-tool-use-failure-WAYVVKGR.js.map → post-tool-use-failure-OT7BFWQW.js.map} +0 -0
- /package/dist/{pre-compact-BCXUCF4V.js.map → pre-compact-OXVODKH4.js.map} +0 -0
- /package/dist/{registry-MGJSJBAS.js.map → registry-U4CHXK6R.js.map} +0 -0
- /package/dist/{remove-KAPX5NT2.js.map → remove-N7ZPELFU.js.map} +0 -0
- /package/dist/{restart-HQO36FTG.js.map → restart-ADG5GBTB.js.map} +0 -0
- /package/dist/{search-YOMOKAAI.js.map → search-AHZEUNRR.js.map} +0 -0
- /package/dist/{server-2N23P6F2.js.map → server-AGVYZVP5.js.map} +0 -0
- /package/dist/{session-WW2JLHPX.js.map → session-6IU4AXYP.js.map} +0 -0
- /package/dist/{session-end-4WRTIBVQ.js.map → session-end-FT27DWYZ.js.map} +0 -0
- /package/dist/{setup-llm-HFWSBUAF.js.map → setup-llm-77MP4I2G.js.map} +0 -0
- /package/dist/{stats-7A4CJ4MS.js.map → stats-NVPWOYTE.js.map} +0 -0
- /package/dist/{stop-R2GDHMRA.js.map → stop-ZPIKVLH4.js.map} +0 -0
- /package/dist/{stop-failure-773KR4VZ.js.map → stop-failure-2PX67YJC.js.map} +0 -0
- /package/dist/{subagent-start-IDECNBHW.js.map → subagent-start-UUE6EHQD.js.map} +0 -0
- /package/dist/{subagent-stop-3JH7DR2S.js.map → subagent-stop-KQWWWPE6.js.map} +0 -0
- /package/dist/{task-completed-AYVHPHDR.js.map → task-completed-WMHOFQ7B.js.map} +0 -0
- /package/dist/{team-3JKF7VAD.js.map → team-LRZ6GTQK.js.map} +0 -0
- /package/dist/{update-YWYW55JM.js.map → update-O6V4RC4W.js.map} +0 -0
- /package/dist/{user-prompt-submit-YELSR6XI.js.map → user-prompt-submit-N36KUPHI.js.map} +0 -0
- /package/dist/{version-K5NETYIL.js.map → version-XMPPJQHR.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/notifications/notify.ts","../src/db/queries/notifications.ts","../src/notifications/registry.ts","../src/utils/error-message.ts","../src/db/queries/runs.ts","../src/agent/config-resolver.ts","../src/agent/instruction-builders.ts","../src/db/queries/skill-candidates.ts","../src/db/queries/skill-records.ts","../src/db/queries/digest-extracts.ts","../src/db/queries/agent-state.ts","../src/agent/tools/skill-validator.ts","../src/db/queries/skill-lineage.ts","../src/db/queries/batches.ts","../src/db/queries/graph-edges.ts","../src/db/queries/entities.ts","../src/db/queries/lineage.ts","../src/db/queries/resolution-events.ts","../src/db/queries/reports.ts"],"sourcesContent":["/**\n * Internal notification emitter.\n *\n * Domain systems call `notify()` to emit notifications. It checks\n * global + domain config, resolves mode/level from registry defaults,\n * and inserts directly into the DB (no HTTP round-trip).\n */\n\nimport crypto from 'node:crypto';\nimport { loadMergedConfig } from '@myco/config/loader.js';\nimport { insertNotification } from '@myco/db/queries/notifications.js';\nimport { getType } from './registry.js';\nimport type { MycoConfig } from '@myco/config/schema.js';\nimport type { NotificationMode, NotificationLevel, CreateNotificationPayload } from './types.js';\n\n/**\n * Emit a notification. Returns the notification ID if inserted,\n * or null if suppressed by config or undefined vaultDir.\n *\n * Best-effort — catches and logs errors so callers don't need to handle failures.\n *\n * @param vaultDir — vault directory; pass undefined to no-op (avoids if-guards at call sites).\n * @param payload — notification content.\n * @param config — pre-loaded merged config to avoid redundant disk reads.\n */\nexport function notify(\n vaultDir: string | undefined,\n payload: CreateNotificationPayload,\n config?: MycoConfig,\n): string | null {\n if (!vaultDir) return null;\n\n try {\n const cfg = config ?? loadMergedConfig(vaultDir);\n\n if (!cfg.notifications.enabled) return null;\n\n const domainConfig = cfg.notifications.domains[payload.domain];\n if (domainConfig && !domainConfig.enabled) return null;\n\n // Resolve mode: payload > domain config > global default > type registry default.\n // The project config is the user's explicit preference; registry defaults are\n // only a fallback for older configs and unconfigured callers.\n const registeredType = getType(payload.type);\n const mode: NotificationMode = payload.mode\n ?? domainConfig?.mode\n ?? cfg.notifications.default_mode\n ?? registeredType?.type.defaultMode;\n const level: NotificationLevel = payload.level\n ?? registeredType?.type.defaultLevel\n ?? 'info';\n\n const id = crypto.randomUUID();\n\n insertNotification({\n id,\n domain: payload.domain,\n type: payload.type,\n level,\n title: payload.title,\n message: payload.message ?? null,\n mode,\n link: payload.link ?? null,\n metadata: payload.metadata ? JSON.stringify(payload.metadata) : null,\n });\n\n return id;\n } catch (err) {\n console.warn('[notify] Failed to emit notification:', err instanceof Error ? err.message : err);\n return null;\n }\n}\n","/**\n * Notification CRUD query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport { epochSeconds } from '@myco/constants.js';\nimport type { NotificationStatus, NotificationMode, NotificationLevel } from '@myco/notifications/types.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of notifications per list query. */\nconst DEFAULT_LIMIT = 50;\n\n/** Max age for auto-pruning dismissed notifications (30 days). */\nexport const NOTIFICATION_PRUNE_AGE_SECONDS = 30 * 24 * 60 * 60;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required when inserting a notification. */\nexport interface NotificationInsert {\n id: string;\n domain: string;\n type: string;\n level: NotificationLevel;\n title: string;\n message: string | null;\n mode: NotificationMode;\n link: string | null;\n metadata: string | null;\n}\n\n/** Row shape returned from notifications queries. */\nexport interface NotificationRow {\n id: string;\n domain: string;\n type: string;\n level: string;\n title: string;\n message: string | null;\n mode: string;\n status: string;\n link: string | null;\n metadata: string | null;\n created_at: number;\n}\n\n// ---------------------------------------------------------------------------\n// Queries\n// ---------------------------------------------------------------------------\n\n/** Insert a new notification. */\nexport function insertNotification(n: NotificationInsert): void {\n const db = getDatabase();\n db.prepare(\n `INSERT INTO notifications (id, domain, type, level, title, message, mode, status, link, metadata, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, 'unread', ?, ?, ?)`,\n ).run(n.id, n.domain, n.type, n.level, n.title, n.message, n.mode, n.link, n.metadata, epochSeconds());\n}\n\n/** List notifications, newest first. Optionally filter by status and/or domain. */\nexport function listNotifications(opts: {\n status?: NotificationStatus;\n domain?: string;\n mode?: NotificationMode;\n limit?: number;\n offset?: number;\n} = {}): NotificationRow[] {\n const db = getDatabase();\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (opts.status) {\n conditions.push('status = ?');\n params.push(opts.status);\n }\n if (opts.domain) {\n conditions.push('domain = ?');\n params.push(opts.domain);\n }\n if (opts.mode) {\n conditions.push('mode = ?');\n params.push(opts.mode);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = opts.limit ?? DEFAULT_LIMIT;\n const offset = opts.offset ?? 0;\n\n return db.prepare(\n `SELECT * FROM notifications ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`,\n ).all(...params, limit, offset) as NotificationRow[];\n}\n\n/** Count notifications by status. */\nexport function countNotifications(status?: NotificationStatus): number {\n const db = getDatabase();\n if (status) {\n const row = db.prepare('SELECT COUNT(*) as count FROM notifications WHERE status = ?').get(status) as { count: number };\n return row.count;\n }\n const row = db.prepare('SELECT COUNT(*) as count FROM notifications').get() as { count: number };\n return row.count;\n}\n\n/** Get a single notification by ID. */\nexport function getNotification(id: string): NotificationRow | undefined {\n const db = getDatabase();\n return db.prepare('SELECT * FROM notifications WHERE id = ?').get(id) as NotificationRow | undefined;\n}\n\n/** Update notification status (read, dismissed). */\nexport function updateNotificationStatus(id: string, status: NotificationStatus): boolean {\n const db = getDatabase();\n const result = db.prepare('UPDATE notifications SET status = ? WHERE id = ?').run(status, id);\n return result.changes > 0;\n}\n\n/** Dismiss all notifications (or all within a domain). */\nexport function dismissAllNotifications(domain?: string): number {\n const db = getDatabase();\n if (domain) {\n const result = db.prepare(\"UPDATE notifications SET status = 'dismissed' WHERE domain = ? AND status != 'dismissed'\").run(domain);\n return result.changes;\n }\n const result = db.prepare(\"UPDATE notifications SET status = 'dismissed' WHERE status != 'dismissed'\").run();\n return result.changes;\n}\n\n/** Mark all unread notifications as read (or within a domain). */\nexport function markAllRead(domain?: string): number {\n const db = getDatabase();\n if (domain) {\n const result = db.prepare(\"UPDATE notifications SET status = 'read' WHERE domain = ? AND status = 'unread'\").run(domain);\n return result.changes;\n }\n const result = db.prepare(\"UPDATE notifications SET status = 'read' WHERE status = 'unread'\").run();\n return result.changes;\n}\n\n/** Prune dismissed notifications older than the given threshold. */\nexport function pruneOldNotifications(maxAgeSeconds: number = NOTIFICATION_PRUNE_AGE_SECONDS): number {\n const db = getDatabase();\n const cutoff = epochSeconds() - maxAgeSeconds;\n const result = db.prepare(\"DELETE FROM notifications WHERE status = 'dismissed' AND created_at < ?\").run(cutoff);\n return result.changes;\n}\n","/**\n * Notification registry — domains register their notification descriptors here.\n *\n * Each subsystem (agents, sessions, skills, mycelium) calls `register()`\n * with its descriptor at module load time. The registry is then consumed by:\n * - Config UI: to render per-domain toggle sections\n * - Notification API: to validate incoming notification types\n * - Settings: to discover all configurable domains dynamically\n */\n\nimport type { NotificationDomainDescriptor, NotificationTypeDescriptor } from './types.js';\n\nconst domains = new Map<string, NotificationDomainDescriptor>();\n\n/** Register a domain's notification descriptor. Idempotent — re-registering the same domain replaces it. */\nexport function register(descriptor: NotificationDomainDescriptor): void {\n domains.set(descriptor.domain, descriptor);\n}\n\n/** Get a specific domain descriptor. */\nexport function getDomain(domain: string): NotificationDomainDescriptor | undefined {\n return domains.get(domain);\n}\n\n/** Get all registered domain descriptors, sorted by domain name. */\nexport function getAllDomains(): NotificationDomainDescriptor[] {\n return [...domains.values()].sort((a, b) => a.domain.localeCompare(b.domain));\n}\n\n/** Look up a specific notification type across all domains. */\nexport function getType(typeId: string): { domain: NotificationDomainDescriptor; type: NotificationTypeDescriptor } | undefined {\n for (const descriptor of domains.values()) {\n const match = descriptor.types.find((t) => t.id === typeId);\n if (match) return { domain: descriptor, type: match };\n }\n return undefined;\n}\n\n/** Check if a domain is registered. */\nexport function hasDomain(domain: string): boolean {\n return domains.has(domain);\n}\n\n/** Clear all registrations (for testing). */\nexport function clearAll(): void {\n domains.clear();\n}\n","/**\n * Extract a human-readable error message from an unknown thrown value.\n *\n * Handles Error instances, strings, and arbitrary objects. Never throws.\n */\nexport function errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message || err.constructor.name || 'Error';\n if (typeof err === 'string') return err || 'Empty string error';\n try { return JSON.stringify(err); } catch { return 'Unserializable error'; }\n}\n","/**\n * Agent run 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// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of runs returned by listRuns when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Default run status for new runs. */\nconst DEFAULT_STATUS = 'pending';\n\n/** Run status indicating the run is currently executing. */\nexport const STATUS_RUNNING = 'running';\n\n/** Run status for a successfully completed run. */\nexport const STATUS_COMPLETED = 'completed';\n\n/** Run status for a run that encountered an error. */\nexport const STATUS_FAILED = 'failed';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a run. */\nexport interface RunInsert {\n id: string;\n agent_id: string;\n task?: string | null;\n instruction?: string | null;\n status?: string;\n started_at?: number | null;\n completed_at?: number | null;\n tokens_used?: number | null;\n cost_usd?: number | null;\n actions_taken?: string | null;\n error?: string | null;\n}\n\n/** Row shape returned from agent_runs queries (all columns). */\nexport interface RunRow {\n id: string;\n agent_id: string;\n task: string | null;\n instruction: string | null;\n status: string;\n started_at: number | null;\n completed_at: number | null;\n tokens_used: number | null;\n cost_usd: number | null;\n actions_taken: string | null;\n error: string | null;\n}\n\n/** Completion data passed to updateRunStatus. */\nexport interface RunCompletion {\n completed_at?: number;\n tokens_used?: number;\n cost_usd?: number;\n actions_taken?: string;\n error?: string;\n}\n\n/** Filter options for `listRuns`. */\nexport interface ListRunsOptions {\n limit?: number;\n offset?: number;\n agent_id?: string;\n status?: string;\n task?: string;\n search?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst RUN_COLUMNS = [\n 'id',\n 'agent_id',\n 'task',\n 'instruction',\n 'status',\n 'started_at',\n 'completed_at',\n 'tokens_used',\n 'cost_usd',\n 'actions_taken',\n 'error',\n] as const;\n\nconst SELECT_COLUMNS = RUN_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed RunRow. */\nfunction toRunRow(row: Record<string, unknown>): RunRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n task: (row.task as string) ?? null,\n instruction: (row.instruction as string) ?? null,\n status: row.status as string,\n started_at: (row.started_at as number) ?? null,\n completed_at: (row.completed_at as number) ?? null,\n tokens_used: (row.tokens_used as number) ?? null,\n cost_usd: (row.cost_usd as number) ?? null,\n actions_taken: (row.actions_taken as string) ?? null,\n error: (row.error as string) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new agent run.\n */\nexport function insertRun(data: RunInsert): RunRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO agent_runs (\n id, agent_id, task, instruction, status,\n started_at, completed_at, tokens_used, cost_usd,\n actions_taken, error\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n data.id,\n data.agent_id,\n data.task ?? null,\n data.instruction ?? null,\n data.status ?? DEFAULT_STATUS,\n data.started_at ?? null,\n data.completed_at ?? null,\n data.tokens_used ?? null,\n data.cost_usd ?? null,\n data.actions_taken ?? null,\n data.error ?? null,\n );\n\n return toRunRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM agent_runs WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n}\n\n/**\n * Retrieve a single run by id.\n *\n * @returns the run row, or null if not found.\n */\nexport function getRun(id: string): RunRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM agent_runs WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toRunRow(row);\n}\n\n/** Build a WHERE clause and params array from ListRunsOptions filter fields. */\nfunction buildRunsWhere(\n options: Omit<ListRunsOptions, 'limit' | 'offset'>,\n): { where: string; params: unknown[] } {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.agent_id !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agent_id);\n }\n if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n }\n if (options.task !== undefined) {\n conditions.push(`task = ?`);\n params.push(options.task);\n }\n if (options.search !== undefined && options.search.length > 0) {\n conditions.push(`task LIKE ?`);\n params.push(`%${options.search}%`);\n }\n\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n params,\n };\n}\n\n/**\n * List runs with optional filters, ordered by started_at DESC (nulls last).\n */\nexport function listRuns(\n options: ListRunsOptions = {},\n): RunRow[] {\n const db = getDatabase();\n const { where, params } = buildRunsWhere(options);\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n const offset = options.offset ?? 0;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_runs\n ${where}\n ORDER BY started_at DESC NULLS LAST\n LIMIT ?\n OFFSET ?`,\n ).all(...params, limit, offset) as Record<string, unknown>[];\n\n return rows.map(toRunRow);\n}\n\n/**\n * Count runs matching the given filters (no limit/offset).\n */\nexport function countRuns(\n options: Omit<ListRunsOptions, 'limit' | 'offset'> = {},\n): number {\n const db = getDatabase();\n const { where, params } = buildRunsWhere(options);\n\n const row = db.prepare(\n `SELECT COUNT(*) as count FROM agent_runs ${where}`,\n ).get(...params) as { count: number };\n\n return row.count;\n}\n\n/**\n * Update a run's status, with optional completion data.\n *\n * @returns the updated row, or null if the run does not exist.\n */\nexport function updateRunStatus(\n id: string,\n status: string,\n completion?: RunCompletion,\n): RunRow | null {\n const db = getDatabase();\n\n const setClauses: string[] = ['status = ?'];\n const params: unknown[] = [status];\n\n if (completion?.completed_at !== undefined) {\n setClauses.push(`completed_at = ?`);\n params.push(completion.completed_at);\n }\n\n if (completion?.tokens_used !== undefined) {\n setClauses.push(`tokens_used = ?`);\n params.push(completion.tokens_used);\n }\n\n if (completion?.cost_usd !== undefined) {\n setClauses.push(`cost_usd = ?`);\n params.push(completion.cost_usd);\n }\n\n if (completion?.actions_taken !== undefined) {\n setClauses.push(`actions_taken = ?`);\n params.push(completion.actions_taken);\n }\n\n if (completion?.error !== undefined) {\n setClauses.push(`error = ?`);\n params.push(completion.error);\n }\n\n params.push(id);\n\n const info = db.prepare(\n `UPDATE agent_runs\n SET ${setClauses.join(', ')}\n WHERE id = ?`,\n ).run(...params);\n\n if (info.changes === 0) return null;\n\n return toRunRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM agent_runs WHERE id = ?`).get(id) as Record<string, unknown>,\n );\n}\n\n/**\n * Get the currently running run for an agent, if any.\n *\n * @returns the running run row, or null if no run is active.\n */\nexport function getRunningRun(\n agentId: string,\n): RunRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_runs\n WHERE agent_id = ? AND status = ?\n ORDER BY started_at DESC NULLS LAST\n LIMIT 1`,\n ).get(agentId, STATUS_RUNNING) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toRunRow(row);\n}\n\n/**\n * Check if a specific task is already running for an agent.\n *\n * @returns the run ID if running, or null.\n */\nexport function getRunningRunForTask(\n agentId: string,\n taskName: string,\n): string | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT id FROM agent_runs\n WHERE agent_id = ? AND task = ? AND status = ?\n LIMIT 1`,\n ).get(agentId, taskName, STATUS_RUNNING) as { id: string } | undefined;\n\n return row?.id ?? null;\n}\n\n/**\n * Get the most recently started run for an agent, optionally filtered by task.\n *\n * @returns the run ID if found, or null.\n */\nexport function getLatestRunId(\n agentId: string,\n taskName?: string,\n): string | null {\n const db = getDatabase();\n\n if (taskName) {\n const row = db.prepare(\n `SELECT id FROM agent_runs\n WHERE agent_id = ? AND task = ?\n ORDER BY started_at DESC\n LIMIT 1`,\n ).get(agentId, taskName) as { id: string } | undefined;\n return row?.id ?? null;\n }\n\n const row = db.prepare(\n `SELECT id FROM agent_runs\n WHERE agent_id = ?\n ORDER BY started_at DESC\n LIMIT 1`,\n ).get(agentId) as { id: string } | undefined;\n return row?.id ?? null;\n}\n","/**\n * Config resolution for agent runs.\n *\n * Centralizes the multi-source config merging that was previously scattered\n * through `runAgent()`:\n * 1. Agent definition from YAML (via loader)\n * 2. Agent DB row overrides\n * 3. Task DB row + registry YAML (structural fields)\n * 4. myco.yaml provider overrides (global, per-task, per-phase)\n *\n * Export: `resolveRunConfig()` — produces the fully resolved config with all\n * overrides applied in a single call.\n */\n\nimport { getAgent } from '@myco/db/queries/agents.js';\nimport { getTask, getDefaultTask } from '@myco/db/queries/tasks.js';\nimport {\n resolveDefinitionsDir,\n loadAgentDefinition,\n resolveEffectiveConfig,\n} from './loader.js';\nimport { loadAllTasks } from './registry.js';\nimport { loadMergedConfig } from '@myco/config/loader.js';\nimport type { MycoConfig } from '@myco/config/schema.js';\nimport type { ProviderConfig, EffectiveConfig } from './types.js';\n\n/**\n * Returns true when an agent provider is configured for the given task —\n * either via a per-task override (`agent.tasks[name].provider`) or the\n * global default (`agent.provider`). Per-task overrides take precedence,\n * matching the resolution order in `resolveRunConfig` below.\n */\nexport function hasConfiguredProvider(mycoConfig: MycoConfig, taskName?: string): boolean {\n const taskProvider = taskName ? mycoConfig.agent.tasks?.[taskName]?.provider : undefined;\n return !!(taskProvider ?? mycoConfig.agent.provider);\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fully resolved run configuration with all overrides applied. */\nexport interface ResolvedRunConfig {\n /** Merged effective config (definition + DB + task + execution). */\n config: EffectiveConfig;\n /** Resolved definitions directory path. */\n definitionsDir: string;\n /** Task-level provider override from myco.yaml (global or per-task). */\n taskProviderOverride?: ProviderConfig;\n /** Per-phase provider/maxTurns overrides from myco.yaml. */\n phaseProviderOverrides: Record<string, { provider?: ProviderConfig; maxTurns?: number }>;\n /** Effective task name (from DB or options). */\n taskName?: string;\n /** Resolved task params — YAML defaults merged with myco.yaml overrides. */\n taskParams?: Record<string, string | number | boolean>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a myco.yaml snake_case provider to the runtime camelCase ProviderConfig.\n *\n * API keys are NOT stored in myco.yaml — they flow via env vars\n * (settings.json -> hooks -> daemon).\n */\nfunction toProviderConfig(p: {\n type: 'anthropic' | 'ollama' | 'lmstudio';\n base_url?: string;\n model?: string;\n context_length?: number;\n}): ProviderConfig {\n return {\n type: p.type,\n baseUrl: p.base_url,\n model: p.model,\n contextLength: p.context_length,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the full run configuration from all sources.\n *\n * Merges:\n * 1. Built-in AgentDefinition (YAML)\n * 2. Agent DB row overrides\n * 3. Task DB row + YAML registry structural fields (phases, execution, etc.)\n * 4. myco.yaml provider overrides (global, per-task, per-phase)\n *\n * @param agentId — agent identifier.\n * @param requestedTask — optional task name from RunOptions.\n * @param vaultDir — absolute path to the vault directory.\n * @returns the fully resolved config bundle.\n */\nexport function resolveRunConfig(\n agentId: string,\n requestedTask: string | undefined,\n vaultDir: string,\n): ResolvedRunConfig {\n const definitionsDir = resolveDefinitionsDir();\n const definition = loadAgentDefinition(definitionsDir);\n\n // Load agent and task — both are sync DB lookups\n const agentRow = getAgent(agentId);\n const taskRow = requestedTask\n ? getTask(requestedTask)\n : getDefaultTask(agentId);\n\n // Structural fields (phases, execution, contextQueries) come from the registry\n // (built-in YAML merged with user vault tasks) rather than the DB flat columns.\n const allTasks = loadAllTasks(definitionsDir, vaultDir);\n const taskName = taskRow?.id ?? requestedTask;\n const yamlTask = taskName ? allTasks.get(taskName) : undefined;\n\n const taskOverrides = taskRow\n ? {\n name: taskRow.id,\n displayName: taskRow.display_name ?? taskRow.id,\n description: taskRow.description ?? '',\n agent: taskRow.agent_id,\n prompt: taskRow.prompt,\n isDefault: taskRow.is_default === 1,\n ...(taskRow.tool_overrides\n ? { toolOverrides: JSON.parse(taskRow.tool_overrides) as string[] }\n : {}),\n // Scalar config from YAML (model, turns, timeout) — DB doesn't store these\n ...(yamlTask?.model ? { model: yamlTask.model } : {}),\n ...(yamlTask?.maxTurns ? { maxTurns: yamlTask.maxTurns } : {}),\n ...(yamlTask?.timeoutSeconds ? { timeoutSeconds: yamlTask.timeoutSeconds } : {}),\n // Structural config from YAML\n ...(yamlTask?.phases ? { phases: yamlTask.phases } : {}),\n ...(yamlTask?.execution ? { execution: yamlTask.execution } : {}),\n ...(yamlTask?.contextQueries ? { contextQueries: yamlTask.contextQueries } : {}),\n ...(yamlTask?.orchestrator ? { orchestrator: yamlTask.orchestrator } : {}),\n }\n : undefined;\n\n const config = resolveEffectiveConfig(definition, agentRow, taskOverrides);\n\n // Load myco.yaml for provider overrides (global, per-task, per-phase)\n let taskProviderOverride: ProviderConfig | undefined;\n let phaseProviderOverrides: Record<string, { provider?: ProviderConfig; maxTurns?: number }> = {};\n let taskParams: Record<string, string | number | boolean> | undefined;\n try {\n const mycoConfig = loadMergedConfig(vaultDir);\n\n // Per-task override takes priority over global\n const taskConfig = taskName ? mycoConfig.agent.tasks?.[taskName] : undefined;\n const globalProvider = mycoConfig.agent.provider;\n\n if (taskConfig?.provider) {\n taskProviderOverride = toProviderConfig(taskConfig.provider);\n } else if (globalProvider) {\n taskProviderOverride = toProviderConfig(globalProvider);\n }\n\n // Per-phase overrides from myco.yaml\n if (taskConfig?.phases) {\n for (const [phaseName, phaseConfig] of Object.entries(taskConfig.phases)) {\n phaseProviderOverrides[phaseName] = {\n ...(phaseConfig.provider ? { provider: toProviderConfig(phaseConfig.provider) } : {}),\n ...(phaseConfig.maxTurns != null ? { maxTurns: phaseConfig.maxTurns } : {}),\n };\n }\n }\n\n // Resolve task params: YAML defaults merged with myco.yaml overrides\n const yamlParams = yamlTask?.params;\n const configParams = taskConfig?.params;\n if (yamlParams || configParams) {\n taskParams = { ...yamlParams, ...configParams };\n }\n } catch {\n // Config load failure is non-fatal — proceed without overrides\n }\n\n return {\n config,\n definitionsDir,\n taskProviderOverride,\n phaseProviderOverrides,\n taskName,\n taskParams,\n };\n}\n","/**\n * Instruction builders for agent tasks.\n *\n * Pre-assembles data from the vault DB into instruction strings that are\n * injected into agent run prompts. This moves deterministic data assembly\n * out of the LLM loop — the agent receives the material it needs without\n * needing to discover it via tool calls.\n *\n * Each builder corresponds to a task that needs pre-assembled context.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { listCandidates } from '@myco/db/queries/skill-candidates.js';\nimport { countSpores, getSpore, listSporeIdsSince, listSpores } from '@myco/db/queries/spores.js';\nimport { countSessions, getSession, listSessions } from '@myco/db/queries/sessions.js';\nimport { listSkillRecords } from '@myco/db/queries/skill-records.js';\nimport { listLineageForSkill } from '@myco/db/queries/skill-lineage.js';\nimport { listDigestExtracts } from '@myco/db/queries/digest-extracts.js';\nimport { getState, setState } from '@myco/db/queries/agent-state.js';\nimport { epochSeconds } from '@myco/constants.js';\nimport {\n descriptionSimilarity,\n DESCRIPTION_DUPLICATE_THRESHOLD,\n} from './tools/skill-validator.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Task name that gets special candidate-injection handling. */\nexport const SKILL_GENERATE_TASK = 'skill-generate';\n\n/**\n * Structured run context dispatchers emit alongside the instruction\n * string. Lets the executor and tools react to task metadata without\n * re-parsing the prose instruction.\n */\nexport interface TaskRunContext {\n candidate_id?: string;\n}\n\n/**\n * Instruction + structured context bundle returned from the task\n * dispatcher. The string portion is what the LLM sees; the context\n * portion flows to the executor for hooks like skill-generate's\n * staging cleanup.\n */\nexport interface BuiltTaskInstruction {\n instruction: string;\n context?: TaskRunContext;\n}\n\n/** Task name for the skill-evolve pipeline step. */\nexport const SKILL_EVOLVE_TASK = 'skill-evolve';\n\n/** Task name for the skill-survey pipeline step. */\nexport const SKILL_SURVEY_TASK = 'skill-survey';\n\n/** Caps for pre-assembled survey context. */\nconst SURVEY_MAX_WISDOM_SPORES = 30;\nconst SURVEY_MAX_SESSIONS = 15;\nconst SURVEY_MIN_SETTLED_SESSIONS = 2;\nconst SURVEY_MIN_SETTLED_ACTIVE_SPORES = 3;\n\n/** State key for the survey watermark. */\nconst SURVEY_WATERMARK_KEY = 'skill-survey-watermark';\n\nexport interface SkillSurveyEligibility {\n eligible: boolean;\n reason: 'insufficient-settled-sessions' | 'insufficient-settled-spores' | 'no-new-settled-knowledge' | null;\n}\n\n/**\n * Determine whether skill-survey has enough settled knowledge to produce\n * meaningful, project-specific candidates.\n */\nexport function getSkillSurveyEligibility(agentId?: string): SkillSurveyEligibility {\n const settledSessionCount = countSessions({ includeActive: false });\n if (settledSessionCount < SURVEY_MIN_SETTLED_SESSIONS) {\n return { eligible: false, reason: 'insufficient-settled-sessions' };\n }\n\n const settledSporeCount = countSpores({ includeActive: false, status: 'active' });\n if (settledSporeCount < SURVEY_MIN_SETTLED_ACTIVE_SPORES) {\n return { eligible: false, reason: 'insufficient-settled-spores' };\n }\n\n if (!agentId) {\n return { eligible: true, reason: null };\n }\n\n const watermarkState = getState(agentId, SURVEY_WATERMARK_KEY);\n const watermarkEpoch = watermarkState ? Number(watermarkState.value) : 0;\n if (watermarkEpoch <= 0) {\n return { eligible: true, reason: null };\n }\n\n const hasNewSettledSessions = countSessions({\n includeActive: false,\n since: watermarkEpoch,\n }) > 0;\n if (hasNewSettledSessions) {\n return { eligible: true, reason: null };\n }\n\n const hasNewSettledSpores = countSpores({\n includeActive: false,\n status: 'active',\n since: watermarkEpoch,\n }) > 0;\n if (hasNewSettledSpores) {\n return { eligible: true, reason: null };\n }\n\n return { eligible: false, reason: 'no-new-settled-knowledge' };\n}\n\n// ---------------------------------------------------------------------------\n// skill-generate\n// ---------------------------------------------------------------------------\n\n/**\n * Build the instruction for a skill-generate run.\n *\n * Fetches the first approved candidate and assembles the full source\n * material (spore content, session summaries) into the instruction text.\n * The draft phase receives this as context — no gathering tool calls needed.\n *\n * Returns both the prose instruction and a structured context bundle\n * so the executor can read `candidate_id` without regex-parsing the\n * instruction string.\n */\nexport function buildSkillGenerateInstruction(): BuiltTaskInstruction | undefined {\n const candidates = listCandidates({ status: 'approved', limit: 1 });\n if (candidates.length === 0) return undefined;\n const c = candidates[0];\n\n const parts = [\n `candidate_id: ${c.id}`,\n `topic: ${c.topic}`,\n `confidence: ${c.confidence}`,\n `rationale: ${c.rationale}`,\n '',\n '## Source Material',\n ];\n\n let sourceIds: Array<{ id: string; type: string }> = [];\n try { sourceIds = JSON.parse(c.source_ids || '[]'); } catch { /* malformed */ }\n\n for (const src of sourceIds) {\n if (src.type === 'spore') {\n const spore = getSpore(src.id);\n if (spore) {\n parts.push(`\\n### Spore: ${src.id} (${spore.observation_type}, importance ${spore.importance})`);\n parts.push(spore.content);\n if (spore.context) parts.push(`Context: ${spore.context}`);\n if (spore.tags) parts.push(`Tags: ${spore.tags}`);\n }\n } else if (src.type === 'session') {\n const session = getSession(src.id);\n if (session) {\n parts.push(`\\n### Session: ${src.id}`);\n if (session.title) parts.push(`Title: ${session.title}`);\n if (session.summary) parts.push(session.summary);\n }\n }\n }\n\n return {\n instruction: parts.join('\\n'),\n context: { candidate_id: c.id },\n };\n}\n\n// ---------------------------------------------------------------------------\n// skill-survey\n// ---------------------------------------------------------------------------\n\n/**\n * Build the instruction for a skill-survey run.\n *\n * Pre-assembles a baseline context document from the vault so the explore\n * phase gets zero-turn orientation. Caps the input to keep turn budgets\n * predictable regardless of time gaps between runs. Tracks a watermark\n * via agent_state so subsequent runs process incrementally.\n */\nexport function buildSkillSurveyInstruction(\n agentId: string,\n): BuiltTaskInstruction | undefined {\n const eligibility = getSkillSurveyEligibility(agentId);\n if (!eligibility.eligible) {\n return undefined;\n }\n\n const now = epochSeconds();\n\n // Read watermark — 0 means \"never surveyed, scan everything\"\n const watermarkState = getState(agentId, SURVEY_WATERMARK_KEY);\n const watermarkEpoch = watermarkState ? Number(watermarkState.value) : 0;\n const sinceFilter = watermarkEpoch > 0 ? { since: watermarkEpoch } : {};\n\n const parts: string[] = [\n '## Pre-assembled Vault Context',\n '',\n `Survey watermark: ${watermarkEpoch === 0 ? 'first run (full scan)' : new Date(watermarkEpoch * 1000).toISOString()}`,\n `Eligibility gate: requires ${SURVEY_MIN_SETTLED_SESSIONS}+ settled sessions and ${SURVEY_MIN_SETTLED_ACTIVE_SPORES}+ active spores from settled work.`,\n '',\n 'CRITICAL: only propose project-specific procedural domains.',\n '- A valid domain must be anchored to this repository\\'s components, files, commands, or conventions.',\n '- Generic engineering topics that could apply to any Node/TypeScript/React repo are not candidates.',\n '- If a domain fails repo-specificity or cross-session evidence, reject it instead of creating or updating a candidate.',\n '',\n ];\n\n // 1. Digest — smallest tier only (landscape overview without flooding context).\n // Full digests can be 50K+ chars across tiers; the smallest tier provides\n // sufficient orientation for the explore phase to direct follow-up queries.\n const digests = listDigestExtracts(agentId);\n if (digests.length > 0) {\n const smallest = digests.reduce((a, b) => a.tier < b.tier ? a : b);\n parts.push('### Digest');\n parts.push(`**Tier ${smallest.tier}** (${smallest.content.length} chars):`);\n parts.push(smallest.content);\n parts.push('');\n }\n\n // 2. Wisdom spores — highest signal observations.\n // Skill-survey runs against settled work only so spores from in-flight\n // sessions don't bait candidates for procedures that haven't stabilized.\n const wisdomSpores = listSpores({\n observation_type: 'wisdom',\n limit: SURVEY_MAX_WISDOM_SPORES,\n includeActive: false,\n ...sinceFilter,\n });\n if (wisdomSpores.length > 0) {\n parts.push(`### Wisdom Spores (${wisdomSpores.length})`);\n for (const s of wisdomSpores) {\n parts.push(`- **${s.id}** (importance ${s.importance}): ${s.content.slice(0, 300)}`);\n }\n parts.push('');\n }\n\n // 3. Recent decisions and gotchas\n const decisions = listSpores({\n observation_type: 'decision',\n limit: 20,\n includeActive: false,\n ...sinceFilter,\n });\n const gotchas = listSpores({\n observation_type: 'gotcha',\n limit: 10,\n includeActive: false,\n ...sinceFilter,\n });\n if (decisions.length > 0 || gotchas.length > 0) {\n parts.push(`### Decisions (${decisions.length}) & Gotchas (${gotchas.length})`);\n for (const s of [...decisions, ...gotchas]) {\n parts.push(`- **${s.observation_type}** ${s.id}: ${s.content.slice(0, 200)}`);\n }\n parts.push('');\n }\n\n // 4. Recent sessions\n const sessions = listSessions({\n limit: SURVEY_MAX_SESSIONS,\n includeActive: false,\n ...sinceFilter,\n });\n if (sessions.length > 0) {\n parts.push(`### Recent Sessions (${sessions.length})`);\n for (const s of sessions) {\n parts.push(`- **${s.id}**: ${s.title ?? '(untitled)'} — ${(s.summary ?? '').slice(0, 200)}`);\n }\n parts.push('');\n }\n\n // 5. Current skill inventory (for dedup awareness)\n const activeSkills = listSkillRecords({ status: 'active', limit: 100 });\n parts.push(`### Active Skills (${activeSkills.length})`);\n for (const s of activeSkills) {\n parts.push(`- **${s.name}**: ${s.description.slice(0, 150)}`);\n }\n parts.push('');\n\n // Advance watermark\n setState(agentId, SURVEY_WATERMARK_KEY, String(now), now);\n\n return { instruction: parts.join('\\n') };\n}\n\n// ---------------------------------------------------------------------------\n// skill-evolve\n// ---------------------------------------------------------------------------\n\nexport const SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS = 24;\nexport const SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN = 8;\n\nconst SECONDS_PER_HOUR = 3600;\n\n/** A skill that needs assessment — assembled by the instruction builder. */\ninterface SkillAssessmentEntry {\n id: string;\n name: string;\n generation: number;\n description: string;\n newSporeIds: string[];\n}\n\n/** Pre-computed overlap between two skills for the inventory phase. */\ninterface SkillOverlapPair {\n skillA: string;\n skillB: string;\n descriptionJaccard: number;\n headingOverlap: number;\n sharedHeadings: string[];\n verdict: 'potential-merge' | 'potential-narrow' | 'distinct';\n}\n\n/** Per-skill structural analysis for the inventory phase. */\ninterface SkillStructure {\n name: string;\n sectionCount: number;\n headings: string[];\n narrow: boolean;\n}\n\n/** Minimum H2 section count for a skill to be considered broad enough. */\nconst MIN_SECTIONS_FOR_STANDALONE = 2;\n\n/**\n * Extract H2 headings from SKILL.md content (lines starting with \"## \").\n * Excludes frontmatter and the top-level H1 title.\n */\nfunction extractHeadings(content: string): string[] {\n // Skip frontmatter\n const bodyMatch = content.match(/^---[\\s\\S]*?---\\n([\\s\\S]*)$/);\n const body = bodyMatch ? bodyMatch[1] : content;\n return body\n .split('\\n')\n .filter(line => line.startsWith('## '))\n .map(line => line.slice(3).trim());\n}\n\n/**\n * Compute heading overlap between two skills using tokenized Jaccard\n * on H2 heading text. Returns { score, sharedHeadings }.\n */\nfunction headingOverlap(\n headingsA: string[],\n headingsB: string[],\n): { score: number; shared: string[] } {\n if (headingsA.length === 0 || headingsB.length === 0) return { score: 0, shared: [] };\n\n // Tokenize each heading into significant words (reusing the same stemming logic)\n const tokenize = (h: string) => new Set(\n h.toLowerCase().replace(/[^a-z0-9\\s]/g, ' ').split(/\\s+/).filter(w => w.length >= 4),\n );\n\n const shared: string[] = [];\n for (const a of headingsA) {\n const aTokens = tokenize(a);\n for (const b of headingsB) {\n const bTokens = tokenize(b);\n const intersection = [...aTokens].filter(t => bTokens.has(t)).length;\n const union = new Set([...aTokens, ...bTokens]).size;\n if (union > 0 && intersection / union >= 0.5) {\n shared.push(`\"${a}\" ~ \"${b}\"`);\n }\n }\n }\n\n // Score: fraction of the smaller skill's headings that have a match\n const smaller = Math.min(headingsA.length, headingsB.length);\n return { score: smaller > 0 ? shared.length / smaller : 0, shared };\n}\n\n/**\n * Build the instruction for a skill-evolve run.\n *\n * Pre-filters active skills that need assessment based on:\n * - Time since last assessment (assess_interval_hours)\n * - Whether new spores exist since the skill's knowledge_watermark\n *\n * Skills that pass both checks are assembled with their current content and\n * new spore IDs into a single instruction string. The evolve phase receives\n * this as context — no gathering tool calls needed.\n *\n * @param params - Optional overrides for assess_interval_hours and max_skills_per_run.\n */\n/**\n * Optional embedding similarity provider. Decoupled from EmbeddingManager\n * so the instruction builder doesn't depend on the daemon's embedding module.\n */\nexport interface SkillSimilarityProvider {\n pairwiseSimilarity(namespace: string, threshold?: number): Array<{ idA: string; idB: string; similarity: number }>;\n}\n\nexport function buildSkillEvolveInstruction(\n params?: Record<string, string | number | boolean>,\n projectRoot?: string,\n similarityProvider?: SkillSimilarityProvider,\n): string {\n const assessIntervalHours = Number(params?.assess_interval_hours ?? SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS);\n const maxSkillsPerRun = Number(params?.max_skills_per_run ?? SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN);\n\n const now = epochSeconds();\n const intervalSeconds = assessIntervalHours * 3600;\n\n const allSkills = listSkillRecords({ status: 'active', limit: 100 });\n const needsAssessment: SkillAssessmentEntry[] = [];\n\n for (const skill of allSkills) {\n let props: Record<string, unknown> = {};\n try {\n props = JSON.parse(skill.properties || '{}');\n } catch {\n props = {};\n }\n\n const lastAssessedAt = typeof props.last_assessed_at === 'number' ? props.last_assessed_at : 0;\n const knowledgeWatermark = typeof props.knowledge_watermark === 'number' ? props.knowledge_watermark : 0;\n\n if (lastAssessedAt > 0 && (now - lastAssessedAt) < intervalSeconds) continue;\n\n const newSporeIds = listSporeIdsSince(knowledgeWatermark, 10);\n if (newSporeIds.length === 0) continue;\n\n needsAssessment.push({\n id: skill.id,\n name: skill.name,\n generation: skill.generation,\n description: skill.description,\n newSporeIds,\n });\n\n if (needsAssessment.length >= maxSkillsPerRun) {\n break;\n }\n }\n\n if (needsAssessment.length === 0) {\n return 'No skills need assessment. All active skills are current or were recently assessed. Report skip via vault_report and finish.';\n }\n\n // ----- Structural analysis: section counts + heading extraction -----\n // Read each skill's content from disk and extract H2 headings.\n // This gives the inventory phase mechanical signals for narrow/merge\n // detection that don't depend on LLM judgment.\n const structures: SkillStructure[] = [];\n const skillHeadings = new Map<string, string[]>();\n for (const skill of allSkills) {\n let content = '';\n if (projectRoot && skill.path) {\n try { content = readFileSync(resolve(projectRoot, skill.path), 'utf-8'); } catch { /* missing */ }\n }\n const headings = extractHeadings(content);\n skillHeadings.set(skill.name, headings);\n structures.push({\n name: skill.name,\n sectionCount: headings.length,\n headings,\n narrow: headings.length < MIN_SECTIONS_FOR_STANDALONE,\n });\n }\n\n // ----- Pairwise similarity: description + heading overlap -----\n // Runs after the early-exit so we don't compute O(n²) scores for no-op runs.\n const overlaps: SkillOverlapPair[] = [];\n for (let i = 0; i < allSkills.length; i++) {\n for (let j = i + 1; j < allSkills.length; j++) {\n const a = allSkills[i];\n const b = allSkills[j];\n const descJaccard = descriptionSimilarity(a.description, b.description);\n const aHeadings = skillHeadings.get(a.name) ?? [];\n const bHeadings = skillHeadings.get(b.name) ?? [];\n const ho = headingOverlap(aHeadings, bHeadings);\n\n // Flag if EITHER description similarity OR heading overlap is significant\n const descFlag = descJaccard >= DESCRIPTION_DUPLICATE_THRESHOLD * 0.75;\n const headingFlag = ho.score >= 0.4;\n if (!descFlag && !headingFlag) continue;\n\n const verdict = (descJaccard >= DESCRIPTION_DUPLICATE_THRESHOLD || ho.score >= 0.5)\n ? 'potential-merge'\n : 'potential-narrow';\n\n overlaps.push({\n skillA: a.name,\n skillB: b.name,\n descriptionJaccard: Math.round(descJaccard * 100) / 100,\n headingOverlap: Math.round(ho.score * 100) / 100,\n sharedHeadings: ho.shared,\n verdict,\n });\n }\n }\n\n const parts: string[] = [\n `${needsAssessment.length} skill(s) need assessment.`,\n `assess_interval_hours: ${assessIntervalHours}`,\n `max_skills_per_run: ${maxSkillsPerRun}`,\n ];\n\n for (const skill of needsAssessment) {\n parts.push('');\n parts.push('---');\n parts.push(`## Skill: ${skill.name} (gen ${skill.generation})`);\n parts.push(`id: ${skill.id}`);\n parts.push(`description: ${skill.description}`);\n parts.push(`new_spore_ids: ${JSON.stringify(skill.newSporeIds)}`);\n // Full content is NOT included here to keep the instruction lean.\n // Use vault_skill_records (action: get, id: \"<name>\") to read\n // the full SKILL.md content when you need to verify code references.\n }\n\n // Inventory section — all active skills with structural signals\n parts.push('');\n parts.push('## All Active Skills (for inventory analysis)');\n for (const s of structures) {\n const skill = allSkills.find(sk => sk.name === s.name)!;\n const narrowTag = s.narrow ? ' **[NARROW — <2 sections]**' : '';\n parts.push(`- **${skill.name}** (gen ${skill.generation}, ${s.sectionCount} sections${narrowTag}): ${skill.description.slice(0, 200)}`);\n if (s.headings.length > 0) {\n parts.push(` Headings: ${s.headings.join(' | ')}`);\n }\n }\n\n // Mechanically narrow skills — explicit flags for the inventory phase\n const narrowSkills = structures.filter(s => s.narrow);\n if (narrowSkills.length > 0) {\n parts.push('');\n parts.push('## Mechanically Narrow Skills (<2 H2 sections)');\n parts.push('These skills have insufficient section breadth for domain-level standalone status.');\n parts.push('Determine which broader skill each should be absorbed into.');\n for (const s of narrowSkills) {\n parts.push(`- **${s.name}**: ${s.sectionCount} section(s). Headings: ${s.headings.length > 0 ? s.headings.join(' | ') : '(none)'}`);\n }\n }\n\n // Pairwise overlap analysis (description tokens + heading overlap)\n if (overlaps.length > 0) {\n parts.push('');\n parts.push('## Pre-computed Token Overlap');\n parts.push('Pairs flagged by description token similarity AND/OR heading overlap:');\n for (const o of overlaps) {\n parts.push(`- **${o.skillA}** <-> **${o.skillB}**: desc=${o.descriptionJaccard}, headings=${o.headingOverlap} (${o.verdict})`);\n if (o.sharedHeadings.length > 0) {\n parts.push(` Shared headings: ${o.sharedHeadings.join('; ')}`);\n }\n }\n }\n\n // Semantic similarity from embeddings — strongest signal for overlap detection.\n // Uses cosine similarity on embedded skill descriptions. Catches semantic\n // overlap that token-based methods miss (\"adding agent integration\" ~= \"onboarding a symbiont\").\n if (similarityProvider) {\n // Build a name→id lookup for resolving embedding results\n const idToName = new Map(allSkills.map(s => [s.id, s.name]));\n\n try {\n const semanticPairs = similarityProvider.pairwiseSimilarity('skill_records', 0.65);\n if (semanticPairs.length > 0) {\n parts.push('');\n parts.push('## Semantic Similarity (embedding cosine distance)');\n parts.push('Pairs with cosine similarity >= 0.65. This is the STRONGEST overlap signal.');\n parts.push('High similarity (>0.8) means the skills describe nearly identical procedures.');\n for (const p of semanticPairs) {\n const nameA = idToName.get(p.idA) ?? p.idA;\n const nameB = idToName.get(p.idB) ?? p.idB;\n parts.push(`- **${nameA}** <-> **${nameB}**: cosine=${p.similarity}`);\n }\n }\n } catch {\n // Embeddings not available — fall through to token-based signals only\n }\n }\n\n return parts.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Unified dispatch\n// ---------------------------------------------------------------------------\n\n/**\n * Build the pre-assembled instruction for a task that needs one.\n *\n * Returns undefined if the task doesn't need a custom instruction\n * (generic tasks use their default prompt) OR if no work is available\n * (e.g., no approved candidates for skill-generate, no skills due for\n * assessment for skill-evolve). Dispatchers should combine this with\n * `isInstructionRequiredTask` to distinguish the two cases — see the\n * scheduler's short-circuit for the \"no work to do\" path.\n *\n * Single dispatch point used by both the scheduler and the API handler.\n */\nexport function buildTaskInstruction(\n taskName: string,\n taskParams?: Record<string, string | number | boolean>,\n agentId?: string,\n projectRoot?: string,\n similarityProvider?: SkillSimilarityProvider,\n): BuiltTaskInstruction | undefined {\n switch (taskName) {\n case SKILL_GENERATE_TASK:\n return buildSkillGenerateInstruction();\n case SKILL_SURVEY_TASK:\n return agentId ? buildSkillSurveyInstruction(agentId) : undefined;\n case SKILL_EVOLVE_TASK: {\n const instruction = buildSkillEvolveInstruction(taskParams, projectRoot, similarityProvider);\n return instruction ? { instruction } : undefined;\n }\n default:\n return undefined;\n }\n}\n\n/**\n * True when the task cannot run meaningfully without a pre-assembled\n * instruction — skill-generate needs an approved candidate,\n * skill-evolve needs at least one skill due for assessment. When\n * buildTaskInstruction returns undefined for one of these, the\n * dispatcher must skip the run rather than falling through to the\n * bare default prompt.\n *\n * Generic tasks like full-intelligence never call buildTaskInstruction,\n * so this returns false for them.\n */\nexport function isInstructionRequiredTask(taskName: string): boolean {\n return taskName === SKILL_GENERATE_TASK\n || taskName === SKILL_EVOLVE_TASK\n || taskName === SKILL_SURVEY_TASK;\n}\n","/**\n * Skill candidate 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_LIST_LIMIT } from '@myco/constants.js';\nimport { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n\n/** Default confidence score for new candidates. */\nconst DEFAULT_CONFIDENCE = 0.0;\n\n/** Default status for new candidates. */\nconst DEFAULT_STATUS = 'identified';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a skill candidate. */\nexport interface CandidateInsert {\n id: string;\n agent_id: string;\n machine_id?: string;\n topic: string;\n rationale: string;\n confidence?: number;\n status?: string;\n source_ids?: string;\n skill_id?: string | null;\n supersedes?: string | null;\n approved_at?: number | null;\n created_at: number;\n updated_at: number;\n}\n\n/**\n * Fields that may be updated on a skill candidate.\n *\n * `approved_at` is normally auto-managed by `updateCandidate` on first\n * transition into `'approved'` — callers should not set it manually.\n * It is exposed here so the backfill migration and tests can seed it.\n */\nexport interface CandidateUpdate {\n topic?: string;\n rationale?: string;\n confidence?: number;\n status?: string;\n source_ids?: string;\n skill_id?: string | null;\n supersedes?: string | null;\n approved_at?: number | null;\n updated_at: number;\n}\n\n/** Row shape returned from skill candidate queries (all columns). */\nexport interface CandidateRow {\n id: string;\n agent_id: string;\n machine_id: string;\n topic: string;\n rationale: string;\n confidence: number;\n status: string;\n source_ids: string;\n skill_id: string | null;\n supersedes: string | null;\n approved_at: number | null;\n created_at: number;\n updated_at: number;\n synced_at: number | null;\n}\n\n/** Filter options for `listCandidates`. */\nexport interface ListCandidatesOptions {\n agent_id?: string;\n /** Exact-match status filter. Ignored when `statuses` is provided. */\n status?: string;\n /**\n * Multi-status filter emitted as `status IN (?, ?, ...)`. Takes\n * precedence over `status` when both are set. An empty array is\n * treated as \"no filter\" so REST callers can forward user input\n * without branching.\n */\n statuses?: string[];\n limit?: number;\n offset?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nexport const CANDIDATE_COLUMNS = [\n 'id',\n 'agent_id',\n 'machine_id',\n 'topic',\n 'rationale',\n 'confidence',\n 'status',\n 'source_ids',\n 'skill_id',\n 'supersedes',\n 'approved_at',\n 'created_at',\n 'updated_at',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = CANDIDATE_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed CandidateRow. */\nfunction toCandidateRow(row: Record<string, unknown>): CandidateRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n machine_id: (row.machine_id as string) ?? getTeamMachineId(),\n topic: row.topic as string,\n rationale: row.rationale as string,\n confidence: row.confidence as number,\n status: row.status as string,\n source_ids: (row.source_ids as string) ?? '[]',\n skill_id: (row.skill_id as string) ?? null,\n supersedes: (row.supersedes as string) ?? null,\n approved_at: (row.approved_at as number) ?? null,\n created_at: row.created_at as number,\n updated_at: row.updated_at as number,\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n/** Build WHERE clause and bound params from candidate filter options. */\nfunction buildWhere(\n options: Omit<ListCandidatesOptions, 'limit' | 'offset'>,\n): { where: string; params: unknown[] } {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.agent_id !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agent_id);\n }\n\n // Multi-status wins over single-status when both are provided. Empty\n // array is treated as \"no status filter\" so REST handlers can forward\n // user input without branching on presence.\n if (options.statuses !== undefined && options.statuses.length > 0) {\n const placeholders = options.statuses.map(() => '?').join(', ');\n conditions.push(`status IN (${placeholders})`);\n params.push(...options.statuses);\n } else if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n }\n\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n params,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new skill candidate.\n *\n * Requires a valid `agent_id` (foreign key to agents table).\n */\nexport function insertCandidate(data: CandidateInsert): CandidateRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO skill_candidates (\n id, agent_id, machine_id, topic, rationale,\n confidence, status, source_ids, skill_id, supersedes, approved_at,\n created_at, updated_at\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n data.id,\n data.agent_id,\n data.machine_id ?? getTeamMachineId(),\n data.topic,\n data.rationale,\n data.confidence ?? DEFAULT_CONFIDENCE,\n data.status ?? DEFAULT_STATUS,\n data.source_ids ?? '[]',\n data.skill_id ?? null,\n data.supersedes ?? null,\n data.approved_at ?? null,\n data.created_at,\n data.updated_at,\n );\n\n const raw = db.prepare(`SELECT ${SELECT_COLUMNS} FROM skill_candidates WHERE id = ?`).get(data.id) as Record<string, unknown> | undefined;\n if (!raw) throw new Error(`Failed to insert skill candidate: ${data.id}`);\n const row = toCandidateRow(raw);\n\n syncRow('skill_candidates', row);\n\n return row;\n}\n\n/**\n * Retrieve a single skill candidate by id.\n *\n * @returns the candidate row, or null if not found.\n */\nexport function getCandidate(id: string): CandidateRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM skill_candidates WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toCandidateRow(row);\n}\n\n/**\n * List skill candidates with optional filters, ordered by confidence DESC,\n * created_at DESC.\n */\nexport function listCandidates(\n options: ListCandidatesOptions = {},\n): CandidateRow[] {\n const db = getDatabase();\n const { where, params } = buildWhere(options);\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n const offset = options.offset ?? 0;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM skill_candidates\n ${where}\n ORDER BY confidence DESC, created_at DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...params, limit, offset) as Record<string, unknown>[];\n\n return rows.map(toCandidateRow);\n}\n\n/**\n * Update specific fields on an existing skill candidate.\n *\n * @returns the updated row, or null if the candidate does not exist.\n */\nexport function updateCandidate(\n id: string,\n updates: CandidateUpdate,\n): CandidateRow | null {\n const db = getDatabase();\n\n // Auto-manage approved_at: stamp on the FIRST transition into 'approved'\n // and never overwrite thereafter. Single audit trail source of truth —\n // callers should not set approved_at directly (the field is exposed on\n // CandidateUpdate only so the backfill migration and tests can seed it).\n //\n // Compute the auto-stamped value as a local, then fold it into the\n // fieldMap iteration below alongside the caller-supplied fields. No\n // defensive clone of `updates` needed.\n let autoApprovedAt: number | undefined;\n if (\n updates.status === 'approved' &&\n updates.approved_at === undefined\n ) {\n const existing = getCandidate(id);\n if (existing && existing.approved_at === null) {\n autoApprovedAt = updates.updated_at;\n }\n }\n\n const fieldMap: Record<string, string> = {\n topic: 'topic',\n rationale: 'rationale',\n confidence: 'confidence',\n status: 'status',\n source_ids: 'source_ids',\n skill_id: 'skill_id',\n supersedes: 'supersedes',\n approved_at: 'approved_at',\n updated_at: 'updated_at',\n };\n\n const setClauses: string[] = [];\n const params: unknown[] = [];\n const updateValues = updates as unknown as Record<string, unknown>;\n\n for (const [key, column] of Object.entries(fieldMap)) {\n if (key in updates) {\n setClauses.push(`${column} = ?`);\n params.push(updateValues[key] ?? null);\n } else if (key === 'approved_at' && autoApprovedAt !== undefined) {\n setClauses.push(`${column} = ?`);\n params.push(autoApprovedAt);\n }\n }\n\n if (setClauses.length === 0) return getCandidate(id);\n\n params.push(id);\n\n db.prepare(\n `UPDATE skill_candidates\n SET ${setClauses.join(', ')}\n WHERE id = ?`,\n ).run(...params);\n\n const updated = getCandidate(id);\n\n if (updated) syncRow('skill_candidates', updated);\n\n return updated;\n}\n\n/**\n * List candidates and return the unpaginated total count in a single\n * SQL round-trip. The `COUNT(*) OVER ()` window function computes the\n * total against the full filter set, and then `LIMIT`/`OFFSET` clip\n * the result set — so `total` always reflects the count before\n * pagination, matching the two-query implementation this replaces.\n *\n * When the filter matches zero rows, the result set is empty and we\n * fall back to a bare COUNT query for the total. In practice this is\n * a fast index lookup and happens only on empty pages.\n */\nexport function listCandidatesWithCount(\n options: ListCandidatesOptions = {},\n): { items: CandidateRow[]; total: number } {\n const db = getDatabase();\n const { where, params } = buildWhere(options);\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n const offset = options.offset ?? 0;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}, COUNT(*) OVER () AS __total\n FROM skill_candidates\n ${where}\n ORDER BY confidence DESC, created_at DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...params, limit, offset) as Array<Record<string, unknown> & { __total: number }>;\n\n if (rows.length === 0) {\n // Empty page — fall back to COUNT for the total. This keeps\n // callers that query beyond the last page (offset > total) from\n // losing visibility into the true count.\n return { items: [], total: countCandidates(options) };\n }\n\n const total = Number(rows[0].__total);\n const items = rows.map((row) => {\n // Strip the window-function column before the row mapper sees it.\n const { __total: _drop, ...rest } = row;\n return toCandidateRow(rest);\n });\n return { items, total };\n}\n\n/**\n * Count skill candidates matching optional filters (for pagination totals).\n */\nexport function countCandidates(\n options: Omit<ListCandidatesOptions, 'limit' | 'offset'> = {},\n): number {\n const db = getDatabase();\n const { where, params } = buildWhere(options);\n\n const row = db.prepare(\n `SELECT COUNT(*) as count FROM skill_candidates ${where}`,\n ).get(...params) as { count: number };\n\n return row.count;\n}\n\n/**\n * Delete a skill candidate by id.\n *\n * @returns true if a row was deleted, false if not found.\n */\nexport function deleteCandidate(id: string): boolean {\n const db = getDatabase();\n const info = db.prepare('DELETE FROM skill_candidates WHERE id = ?').run(id);\n return info.changes > 0;\n}\n","/**\n * Skill record 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_LIST_LIMIT } from '@myco/constants.js';\nimport { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n\n/** Default status for new skill records. */\nconst DEFAULT_STATUS = 'active';\n\n/** Default generation for new skill records. */\nconst DEFAULT_GENERATION = 1;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a skill record. */\nexport interface SkillRecordInsert {\n id: string;\n agent_id: string;\n machine_id?: string;\n name: string;\n display_name: string;\n description: string;\n status?: string;\n generation?: number;\n candidate_id?: string | null;\n source_ids?: string;\n path: string;\n created_at: number;\n updated_at: number;\n properties?: string;\n}\n\n/** Fields that may be updated on a skill record. */\nexport interface SkillRecordUpdate {\n display_name?: string;\n description?: string;\n status?: string;\n generation?: number;\n source_ids?: string;\n path?: string;\n usage_count?: number;\n last_used_at?: number | null;\n updated_at: number;\n properties?: string;\n}\n\n/** Row shape returned from skill record queries (all columns). */\nexport interface SkillRecordRow {\n id: string;\n agent_id: string;\n machine_id: string;\n name: string;\n display_name: string;\n description: string;\n status: string;\n generation: number;\n candidate_id: string | null;\n source_ids: string;\n path: string;\n usage_count: number;\n last_used_at: number | null;\n created_at: number;\n updated_at: number;\n properties: string;\n synced_at: number | null;\n}\n\n/** Filter options for `listSkillRecords`. */\nexport interface ListSkillRecordsOptions {\n agent_id?: string;\n status?: string;\n limit?: number;\n offset?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nexport const RECORD_COLUMNS = [\n 'id',\n 'agent_id',\n 'machine_id',\n 'name',\n 'display_name',\n 'description',\n 'status',\n 'generation',\n 'candidate_id',\n 'source_ids',\n 'path',\n 'usage_count',\n 'last_used_at',\n 'created_at',\n 'updated_at',\n 'properties',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = RECORD_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed SkillRecordRow. */\nfunction toSkillRecordRow(row: Record<string, unknown>): SkillRecordRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n machine_id: (row.machine_id as string) ?? getTeamMachineId(),\n name: row.name as string,\n display_name: row.display_name as string,\n description: row.description as string,\n status: row.status as string,\n generation: row.generation as number,\n candidate_id: (row.candidate_id as string) ?? null,\n source_ids: (row.source_ids as string) ?? '[]',\n path: row.path as string,\n usage_count: row.usage_count as number,\n last_used_at: (row.last_used_at as number) ?? null,\n created_at: row.created_at as number,\n updated_at: row.updated_at as number,\n properties: (row.properties as string) ?? '{}',\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n/** Build WHERE clause and bound params from skill record filter options. */\nfunction buildWhere(\n options: Omit<ListSkillRecordsOptions, 'limit' | 'offset'>,\n): { where: string; params: unknown[] } {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.agent_id !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agent_id);\n }\n\n if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n }\n\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n params,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new skill record.\n *\n * Requires a valid `agent_id` (foreign key to agents table).\n */\nexport function insertSkillRecord(data: SkillRecordInsert): SkillRecordRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO skill_records (\n id, agent_id, machine_id, name, display_name,\n description, status, generation, candidate_id,\n source_ids, path, created_at, updated_at, properties\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?, ?\n )`,\n ).run(\n data.id,\n data.agent_id,\n data.machine_id ?? getTeamMachineId(),\n data.name,\n data.display_name,\n data.description,\n data.status ?? DEFAULT_STATUS,\n data.generation ?? DEFAULT_GENERATION,\n data.candidate_id ?? null,\n data.source_ids ?? '[]',\n data.path,\n data.created_at,\n data.updated_at,\n data.properties ?? '{}',\n );\n\n const raw = db.prepare(`SELECT ${SELECT_COLUMNS} FROM skill_records WHERE id = ?`).get(data.id) as Record<string, unknown> | undefined;\n if (!raw) throw new Error(`Failed to insert skill record: ${data.id}`);\n const row = toSkillRecordRow(raw);\n\n syncRow('skill_records', row);\n\n return row;\n}\n\n/**\n * Retrieve a single skill record by id.\n *\n * @returns the skill record row, or null if not found.\n */\nexport function getSkillRecord(id: string): SkillRecordRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM skill_records WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toSkillRecordRow(row);\n}\n\n/**\n * Retrieve a single skill record by its unique name.\n *\n * @returns the skill record row, or null if not found.\n */\nexport function getSkillRecordByName(name: string): SkillRecordRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM skill_records WHERE name = ?`,\n ).get(name) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toSkillRecordRow(row);\n}\n\n/**\n * List skill records with optional filters, ordered by updated_at DESC.\n */\nexport function listSkillRecords(\n options: ListSkillRecordsOptions = {},\n): SkillRecordRow[] {\n const db = getDatabase();\n const { where, params } = buildWhere(options);\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n const offset = options.offset ?? 0;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM skill_records\n ${where}\n ORDER BY updated_at DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...params, limit, offset) as Record<string, unknown>[];\n\n return rows.map(toSkillRecordRow);\n}\n\n/**\n * Update specific fields on an existing skill record.\n *\n * @returns the updated row, or null if the record does not exist.\n */\nexport function updateSkillRecord(\n id: string,\n updates: SkillRecordUpdate,\n): SkillRecordRow | null {\n const db = getDatabase();\n\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n const fieldMap: Record<string, string> = {\n display_name: 'display_name',\n description: 'description',\n status: 'status',\n generation: 'generation',\n source_ids: 'source_ids',\n path: 'path',\n usage_count: 'usage_count',\n last_used_at: 'last_used_at',\n updated_at: 'updated_at',\n properties: 'properties',\n };\n\n for (const [key, column] of Object.entries(fieldMap)) {\n if (key in updates) {\n setClauses.push(`${column} = ?`);\n params.push((updates as unknown as Record<string, unknown>)[key] ?? null);\n }\n }\n\n if (setClauses.length === 0) return getSkillRecord(id);\n\n params.push(id);\n\n db.prepare(\n `UPDATE skill_records\n SET ${setClauses.join(', ')}\n WHERE id = ?`,\n ).run(...params);\n\n const updated = getSkillRecord(id);\n\n if (updated) syncRow('skill_records', updated);\n\n return updated;\n}\n\n/**\n * Atomically increment the usage_count for a skill record and update last_used_at.\n *\n * Uses a direct SQL increment (`usage_count + 1`) to avoid read-modify-write\n * races when multiple detections could run concurrently.\n */\nexport function incrementSkillUsageCount(id: string, now: number): void {\n const db = getDatabase();\n db.prepare(\n `UPDATE skill_records SET usage_count = usage_count + 1, last_used_at = ?, updated_at = ? WHERE id = ?`,\n ).run(now, now, id);\n // Note: syncRow omitted for atomic increment — synced via next full record read\n}\n\n/**\n * List skill records and return the total count in a single call.\n *\n * Runs listSkillRecords and countSkillRecords with the same filter options.\n * Saves callers from issuing two separate function calls.\n */\nexport function listSkillRecordsWithCount(\n options: ListSkillRecordsOptions = {},\n): { items: SkillRecordRow[]; total: number } {\n const items = listSkillRecords(options);\n const total = countSkillRecords(options);\n return { items, total };\n}\n\n/**\n * Count skill records matching optional filters (for pagination totals).\n */\nexport function countSkillRecords(\n options: Omit<ListSkillRecordsOptions, 'limit' | 'offset'> = {},\n): number {\n const db = getDatabase();\n const { where, params } = buildWhere(options);\n\n const row = db.prepare(\n `SELECT COUNT(*) as count FROM skill_records ${where}`,\n ).get(...params) as { count: number };\n\n return row.count;\n}\n\n/**\n * Delete a skill record and cascade to lineage, usage, and linked candidates.\n * Runs in a transaction. Does NOT handle disk/symlink cleanup — callers must\n * handle filesystem operations separately.\n *\n * @returns the deleted record's name (for disk cleanup) or null if not found.\n */\nexport function deleteSkillRecordCascade(idOrName: string): { id: string; name: string } | null {\n const db = getDatabase();\n const record = getSkillRecord(idOrName) ?? getSkillRecordByName(idOrName);\n if (!record) return null;\n\n db.transaction(() => {\n db.prepare('DELETE FROM skill_lineage WHERE skill_id = ?').run(record.id);\n db.prepare('DELETE FROM skill_usage WHERE skill_id = ?').run(record.id);\n // Dismiss linked candidates so they don't regenerate\n if (record.candidate_id) {\n db.prepare(\n `UPDATE skill_candidates SET status = 'dismissed', skill_id = NULL, updated_at = ? WHERE id = ?`,\n ).run(Math.floor(Date.now() / 1000), record.candidate_id);\n }\n db.prepare(\n `UPDATE skill_candidates SET status = 'dismissed', skill_id = NULL, updated_at = ? WHERE skill_id = ?`,\n ).run(Math.floor(Date.now() / 1000), record.id);\n db.prepare('DELETE FROM skill_records WHERE id = ?').run(record.id);\n })();\n\n return { id: record.id, name: record.name };\n}\n","/**\n * Digest extract 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 { DIGEST_TIERS } from '@myco/constants.js';\nimport { getTeamMachineId } from '@myco/daemon/team-context.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required when upserting a digest extract. */\nexport interface DigestExtractUpsert {\n agent_id: string;\n tier: number;\n content: string;\n generated_at: number;\n machine_id?: string;\n}\n\n/** Row shape returned from digest_extracts queries (all columns). */\nexport interface DigestExtractRow {\n id: number;\n agent_id: string;\n tier: number;\n content: string;\n substrate_hash: string | null;\n generated_at: number;\n machine_id: string;\n synced_at: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst EXTRACT_COLUMNS = [\n 'id',\n 'agent_id',\n 'tier',\n 'content',\n 'substrate_hash',\n 'generated_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = EXTRACT_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed DigestExtractRow. */\nfunction toDigestExtractRow(row: Record<string, unknown>): DigestExtractRow {\n return {\n id: row.id as number,\n agent_id: row.agent_id as string,\n tier: row.tier as number,\n content: row.content as string,\n substrate_hash: (row.substrate_hash as string) ?? null,\n generated_at: row.generated_at as number,\n machine_id: (row.machine_id as string) ?? 'local',\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Upsert a digest extract. Uses ON CONFLICT on (agent_id, tier).\n *\n * Creates or updates the extract for the given agent and token tier.\n * Uses lastInsertRowid for SERIAL PK on insert, or falls back to\n * SELECT for the conflict (update) case.\n */\nexport function upsertDigestExtract(\n data: DigestExtractUpsert,\n): DigestExtractRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO digest_extracts (agent_id, tier, content, generated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT (agent_id, tier) DO UPDATE SET\n content = EXCLUDED.content,\n generated_at = EXCLUDED.generated_at`,\n ).run(data.agent_id, data.tier, data.content, data.generated_at);\n\n // Always look up by composite unique key — works for both insert and update cases.\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM digest_extracts WHERE agent_id = ? AND tier = ?`,\n ).get(data.agent_id, data.tier);\n\n return toDigestExtractRow(row as Record<string, unknown>);\n}\n\n/**\n * Get a digest extract for a specific agent and tier.\n *\n * @returns the extract row, or null if not found.\n */\nexport function getDigestExtract(\n agentId: string,\n tier: number,\n): DigestExtractRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM digest_extracts\n WHERE agent_id = ? AND tier = ?`,\n ).get(agentId, tier) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toDigestExtractRow(row);\n}\n\n/**\n * List digest extracts for an agent, filtered to configured tiers, ordered by tier ASC.\n */\nexport function listDigestExtracts(\n agentId: string,\n): DigestExtractRow[] {\n const db = getDatabase();\n const tierPlaceholders = DIGEST_TIERS.map(() => '?').join(', ');\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM digest_extracts\n WHERE agent_id = ? AND tier IN (${tierPlaceholders})\n ORDER BY tier ASC`,\n ).all(agentId, ...DIGEST_TIERS) as Record<string, unknown>[];\n\n return rows.map(toDigestExtractRow);\n}\n","/**\n * Agent state key-value 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/** Row shape returned from agent_state queries. */\nexport interface AgentStateRow {\n agent_id: string;\n key: string;\n value: string;\n updated_at: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst STATE_COLUMNS = [\n 'agent_id',\n 'key',\n 'value',\n 'updated_at',\n] as const;\n\nconst SELECT_COLUMNS = STATE_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed AgentStateRow. */\nfunction toAgentStateRow(row: Record<string, unknown>): AgentStateRow {\n return {\n agent_id: row.agent_id as string,\n key: row.key as string,\n value: row.value as string,\n updated_at: row.updated_at as number,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Get a single state value for an agent by key.\n *\n * @returns the state row, or null if not found.\n */\nexport function getState(\n agentId: string,\n key: string,\n): AgentStateRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM agent_state WHERE agent_id = ? AND key = ?`,\n ).get(agentId, key) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toAgentStateRow(row);\n}\n\n/**\n * Set a state value for an agent. Inserts or updates on conflict.\n *\n * The composite primary key (agent_id, key) ensures each agent\n * has at most one value per key. On conflict, the value and updated_at\n * are overwritten.\n */\nexport function setState(\n agentId: string,\n key: string,\n value: string,\n updatedAt: number,\n): AgentStateRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO agent_state (agent_id, key, value, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT (agent_id, key) DO UPDATE SET\n value = EXCLUDED.value,\n updated_at = EXCLUDED.updated_at`,\n ).run(agentId, key, value, updatedAt);\n\n return toAgentStateRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM agent_state WHERE agent_id = ? AND key = ?`).get(agentId, key) as Record<string, unknown>,\n );\n}\n\n/**\n * Get all state key-value pairs for an agent, ordered by key ASC.\n */\nexport function getStatesForAgent(\n agentId: string,\n): AgentStateRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_state\n WHERE agent_id = ?\n ORDER BY key ASC`,\n ).all(agentId) as Record<string, unknown>[];\n\n return rows.map(toAgentStateRow);\n}\n","/**\n * Skill content validation.\n *\n * Deterministic quality gate -- the agent must fix all issues before\n * a skill is accepted.\n */\n\nimport { parse as parseYaml } from 'yaml';\n\n/** Maximum lines for a generated skill. */\nexport const MAX_SKILL_LINES = 800;\n\n/** Maximum description length accepted by Codex skill frontmatter. */\nexport const MAX_SKILL_DESCRIPTION_CHARS = 1024;\n\n/** Frontmatter delimiters at the top of a SKILL.md file. */\nconst FRONTMATTER_PATTERN = /^---\\n([\\s\\S]*?)\\n---/;\n\n/** Required frontmatter fields for Myco-managed skills. */\nexport const REQUIRED_FRONTMATTER_FIELDS = ['name', 'description', 'managed_by', 'user-invocable', 'allowed-tools'] as const;\n\n/** Frontmatter fields that must not change when updating an existing skill. */\nexport const PROTECTED_FRONTMATTER_FIELDS = ['user-invocable', 'allowed-tools'] as const;\n\n/**\n * Whitelist of Claude Code tool names that may appear in `allowed-tools`.\n * Myco-managed skills run in developer Claude Code sessions — anything not\n * on this list is almost certainly a model confabulation (e.g. \"[None]\",\n * \"ReadFile\", \"search\", or a vault_* tool copied from the agent's own context).\n *\n * Kept intentionally narrow: add entries when a legitimate tool is rejected.\n */\nexport const ALLOWED_CLAUDE_CODE_TOOLS: ReadonlySet<string> = new Set([\n 'Read', 'Edit', 'Write', 'MultiEdit', 'Bash', 'Grep', 'Glob',\n 'NotebookRead', 'NotebookEdit', 'WebFetch', 'WebSearch',\n 'Task', 'TodoWrite',\n]);\n\ntype ParsedFrontmatter = Record<string, unknown>;\n\nfunction extractFrontmatterBlock(content: string): string | undefined {\n return content.match(FRONTMATTER_PATTERN)?.[1];\n}\n\nfunction parseFrontmatter(content: string): ParsedFrontmatter | null {\n const block = extractFrontmatterBlock(content);\n if (!block) return null;\n const parsed = parseYaml(block);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n return parsed as ParsedFrontmatter;\n}\n\nfunction normalizeFrontmatterValue(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (typeof value === 'boolean' || typeof value === 'number') return String(value);\n if (Array.isArray(value)) {\n const items = value\n .map((item) => (typeof item === 'string' ? item : null))\n .filter((item): item is string => item !== null);\n return items.length > 0 ? items.join(', ') : undefined;\n }\n return undefined;\n}\n\n/**\n * Extract a frontmatter field value from SKILL.md content.\n * Returns the raw value string, or undefined if not found.\n */\nexport function extractFrontmatterField(content: string, field: string): string | undefined {\n try {\n const parsed = parseFrontmatter(content);\n const normalized = normalizeFrontmatterValue(parsed?.[field]);\n if (normalized !== undefined) return normalized;\n } catch {\n // Fall back to the legacy line-based extraction so callers can still\n // compare or repair malformed frontmatter already on disk.\n }\n\n const fm = extractFrontmatterBlock(content);\n if (!fm) return undefined;\n\n // Single-line value: `field: value`\n const match = fm.match(new RegExp(`^${field}:\\\\s*(.+)$`, 'm'));\n if (match) return match[1].trim();\n\n // Multi-line YAML block-list: `field:\\n - item1\\n - item2`\n // Collects indented `- ` entries into a comma-separated string.\n const blockMatch = fm.match(new RegExp(`^${field}:\\\\s*$`, 'm'));\n if (blockMatch) {\n const startIdx = fm.indexOf(blockMatch[0]) + blockMatch[0].length;\n const remaining = fm.slice(startIdx);\n const items: string[] = [];\n for (const line of remaining.split('\\n')) {\n const itemMatch = line.match(/^\\s+-\\s+(.+)$/);\n if (!itemMatch) break;\n items.push(itemMatch[1].trim());\n }\n if (items.length > 0) return items.join(', ');\n }\n\n return undefined;\n}\n\n/**\n * Parse the `allowed-tools` frontmatter value into a list of tool names.\n *\n * Accepts either of these YAML shapes:\n * allowed-tools: Read, Edit, Write\n * allowed-tools: [Read, Edit, Write]\n *\n * Returns null if the value is absent, empty, or clearly malformed\n * (e.g. a bare word like `None`, or a list containing `None` / `null`).\n * Caller treats null as a validation failure.\n */\nexport function parseAllowedTools(rawValue: string | undefined): string[] | null {\n if (!rawValue) return null;\n let stripped = rawValue.trim();\n if (stripped.length === 0) return null;\n\n // Strip YAML inline-list brackets if present: [Read, Edit] -> Read, Edit\n if (stripped.startsWith('[') && stripped.endsWith(']')) {\n stripped = stripped.slice(1, -1).trim();\n }\n if (stripped.length === 0) return null;\n\n const parts = stripped\n .split(',')\n .map((s) => s.trim().replace(/^['\"]|['\"]$/g, '')) // strip quotes\n .filter((s) => s.length > 0);\n\n if (parts.length === 0) return null;\n\n // Reject literal \"None\" / \"null\" / \"~\" — common model confabulations\n // when the model means \"no tools needed.\"\n const sentinels = new Set(['None', 'none', 'null', 'Null', '~']);\n if (parts.some((p) => sentinels.has(p))) return null;\n\n return parts;\n}\n\n/**\n * Light English stemmer applied to every significant token before set\n * comparison. Collapses common suffix variants so that `task`/`tasks`,\n * `gate`/`gates`, `model`/`models`, `configure`/`configuring`, and\n * `implement`/`implemented` all share a stem.\n *\n * The order matters: strip -ing / -ed before trailing -s so \"parking\"\n * doesn't become \"parkin\" then \"parki\"; strip trailing -e last so\n * \"validate\" and \"validating\" both land on \"validat\".\n *\n * Length guards prevent degenerate stems on short words (e.g. \"ring\"\n * must not become empty, \"cod\" must not become \"c\").\n */\nfunction stemToken(word: string): string {\n let w = word;\n if (w.length > 5 && w.endsWith('ing')) w = w.slice(0, -3);\n else if (w.length > 5 && w.endsWith('ed')) w = w.slice(0, -2);\n if (w.length > 4 && w.endsWith('s')) w = w.slice(0, -1);\n if (w.length > 4 && w.endsWith('e')) w = w.slice(0, -1);\n return w;\n}\n\n/**\n * Lowercase-word token set from a string, excluding stopwords and\n * short/noise tokens, with light stemming applied. Used for all\n * similarity metrics in this file.\n */\n/** Stopwords excluded from token sets — hoisted to module level to avoid\n * re-allocating on every `tokenSet()` call (matters in O(n²) loops). */\nconst STOPWORDS = new Set([\n 'the', 'a', 'an', 'and', 'or', 'but', 'is', 'are', 'was', 'were',\n 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did',\n 'will', 'would', 'should', 'could', 'may', 'might', 'must', 'can',\n 'this', 'that', 'these', 'those', 'with', 'from', 'into', 'onto',\n 'for', 'when', 'where', 'which', 'what', 'who', 'how', 'why',\n 'use', 'uses', 'used', 'using', 'not', 'also', 'than', 'then',\n 'ensure', 'ensures', 'make', 'makes',\n]);\n\nfunction tokenSet(text: string): Set<string> {\n return new Set(\n text\n .toLowerCase()\n .replace(/[^a-z0-9_\\s]/g, ' ')\n .split(/\\s+/)\n .filter((w) => w.length >= 4 && !STOPWORDS.has(w))\n .map(stemToken),\n );\n}\n\n/** Internal helper: count shared stems between two token sets. */\nfunction intersectionSize(a: Set<string>, b: Set<string>): number {\n let count = 0;\n for (const token of a) {\n if (b.has(token)) count++;\n }\n return count;\n}\n\n/**\n * Jaccard similarity between two text strings on their significant-word\n * token sets. Returns a value in [0, 1].\n *\n * Purpose: detect near-duplicate skill descriptions so `vault_write_skill`\n * can refuse to create sibling skills covering the same topic. This is a\n * deterministic gate — unlike asking the model to self-check for conflicts,\n * which is known to hallucinate \"no overlap\" on visibly overlapping pairs.\n */\nexport function descriptionSimilarity(a: string, b: string): number {\n const aTokens = tokenSet(a);\n const bTokens = tokenSet(b);\n if (aTokens.size === 0 || bTokens.size === 0) return 0;\n\n const intersection = intersectionSize(aTokens, bTokens);\n const union = aTokens.size + bTokens.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\n/**\n * Overlap coefficient (Szymkiewicz–Simpson) between two topic strings:\n * `|A ∩ B| / min(|A|, |B|)`. Returns 0 when either side has fewer than\n * `minTokens` significant tokens — 1-2 token topics are dominated by\n * single-token collisions under this metric and must fall back to\n * Jaccard for duplicate detection.\n *\n * Why this exists alongside `descriptionSimilarity`: candidate topics are\n * often short kebab-case labels like `add-idle-skip-watermark-to-agent-task`\n * (5 tokens) paired against longer natural-language topics like\n * `Implementing DB Watermark Prefilters for Incremental Agent Tasks`\n * (6 tokens). The asymmetric token-count inflates Jaccard's union and\n * pushes real duplicates below the 0.4 threshold. Overlap coefficient is\n * robust to that asymmetry because the denominator is the smaller set.\n *\n * The 3-token minimum is calibrated to catch cases like\n * `publish-npm-package-with-oidc-in-ci` (3 significant tokens) against\n * `npm OIDC Trusted Publishing in GitHub Actions` (5 tokens) while\n * leaving 2-token topic pairs to the Jaccard path. Two-token topics\n * with a single shared word would score 0.5 under naive overlap —\n * noisy — so they are deliberately excluded.\n */\nexport function topicOverlapSimilarity(\n a: string,\n b: string,\n minTokens: number = 3,\n): number {\n const aTokens = tokenSet(a);\n const bTokens = tokenSet(b);\n if (aTokens.size < minTokens || bTokens.size < minTokens) return 0;\n\n const intersection = intersectionSize(aTokens, bTokens);\n const smaller = Math.min(aTokens.size, bTokens.size);\n return smaller === 0 ? 0 : intersection / smaller;\n}\n\n/**\n * Similarity threshold above which two descriptions are treated as\n * covering the same topic. Chosen empirically from the unifi-mcp incident\n * that motivated this gate: the real duplicate pair\n * (`unifi-validator-coercion-pattern` vs `unifi-validator-registry-coercion`)\n * scored ~0.42 on this metric — lower than intuition suggests because each\n * description reframes roughly half its content with different wording.\n * Clearly-distinct skill pairs score under 0.2 on the same metric, so 0.4\n * is a comfortable middle that catches real duplicates without flagging\n * incidentally-adjacent topics.\n *\n * Tuning note: the cost of a false positive here is \"agent must reframe\n * its description and try again\" (low); the cost of a false negative is\n * \"sibling skill on disk for the same topic\" (high). Lean aggressive.\n */\nexport const DESCRIPTION_DUPLICATE_THRESHOLD = 0.4;\n\n/**\n * Threshold for the overlap-coefficient topic dedup path. At 0.6 and\n * with a 4-token minimum, this catches recent false negatives where\n * short kebab-case topics were re-identifying dismissed natural-language\n * candidates (watermark/agent/task overlap, structural/enforcement/gate\n * overlap, configure/local/model overlap) while leaving genuinely-new\n * candidates like `implement-spa-sub-navigation-with-browser-history`\n * unaffected.\n */\nexport const TOPIC_OVERLAP_THRESHOLD = 0.6;\n\n/**\n * Compare protected frontmatter fields between existing and new content.\n * Returns an array of violation descriptions (empty = all preserved).\n *\n * Also guards against description shortening — the description is the\n * primary triggering mechanism for skills, so losing content degrades\n * skill activation quality.\n */\nexport function checkFrontmatterPreservation(existing: string, incoming: string): string[] {\n const violations: string[] = [];\n for (const field of PROTECTED_FRONTMATTER_FIELDS) {\n const oldValue = extractFrontmatterField(existing, field);\n const newValue = extractFrontmatterField(incoming, field);\n if (oldValue === undefined || newValue === undefined) continue;\n\n // For allowed-tools, normalize before comparing — YAML block-list syntax\n // (- Read\\n- Edit) and inline syntax (Read, Edit) are semantically identical.\n if (field === 'allowed-tools') {\n const oldParsed = parseAllowedTools(oldValue);\n const newParsed = parseAllowedTools(newValue);\n if (oldParsed && newParsed) {\n const oldSet = new Set(oldParsed);\n const newSet = new Set(newParsed);\n const changed = oldSet.size !== newSet.size || [...oldSet].some(t => !newSet.has(t));\n if (changed) {\n violations.push(`${field}: was [${oldParsed.join(', ')}], changed to [${newParsed.join(', ')}]`);\n }\n continue;\n }\n }\n\n if (oldValue !== newValue) {\n violations.push(`${field}: was \"${oldValue}\", changed to \"${newValue}\"`);\n }\n }\n\n // Guard against description shortening — descriptions drive skill triggering.\n // Lengthening is allowed (adding context), shortening is not (losing trigger keywords).\n const oldDesc = extractFrontmatterField(existing, 'description');\n const newDesc = extractFrontmatterField(incoming, 'description');\n if (oldDesc && newDesc && newDesc.length < oldDesc.length * 0.9) {\n violations.push(\n `description shortened from ${oldDesc.length} to ${newDesc.length} chars (${Math.round((1 - newDesc.length / oldDesc.length) * 100)}% reduction). ` +\n 'Descriptions are the primary triggering mechanism — do not shorten them.',\n );\n }\n\n return violations;\n}\n\n/**\n * Validate skill content before writing. Returns an array of issues\n * (empty = valid). This is a deterministic quality gate -- the agent\n * must fix all issues before the skill is accepted.\n */\nexport function validateSkillContent(content: string, dirName: string): string[] {\n const issues: string[] = [];\n\n // Check for frontmatter delimiters\n const frontmatter = extractFrontmatterBlock(content);\n if (!frontmatter) {\n issues.push('Missing YAML frontmatter (must start with --- and end with ---)');\n return issues; // Can't check fields without frontmatter\n }\n\n let parsedFrontmatter: ParsedFrontmatter | null = null;\n try {\n parsedFrontmatter = parseFrontmatter(content);\n } catch (error) {\n const message = error instanceof Error ? error.message.split('\\n')[0] : String(error);\n issues.push(`Invalid YAML frontmatter: ${message}`);\n return issues;\n }\n if (!parsedFrontmatter) {\n issues.push('Invalid YAML frontmatter: top-level frontmatter must be a mapping/object');\n return issues;\n }\n\n // Check required fields\n for (const field of REQUIRED_FRONTMATTER_FIELDS) {\n if (!(field in parsedFrontmatter)) {\n issues.push(`Missing required frontmatter field: ${field}`);\n }\n }\n\n // Check myco: prefix on name\n const nameValue = normalizeFrontmatterValue(parsedFrontmatter.name);\n if (nameValue && !nameValue.startsWith('myco:')) {\n issues.push(`Skill name must start with \"myco:\" prefix. Got: \"${nameValue}\"`);\n }\n if (nameValue && nameValue !== `myco:${dirName}`) {\n issues.push(`Skill name must match directory name. Expected \"myco:${dirName}\", got \"${nameValue}\"`);\n }\n\n // Check managed_by: myco\n const managedByValue = normalizeFrontmatterValue(parsedFrontmatter.managed_by);\n if (managedByValue && managedByValue !== 'myco') {\n issues.push(`managed_by must be \"myco\". Got: \"${managedByValue}\"`);\n }\n\n // Codex and other agents reject overly long descriptions.\n const descriptionValue = normalizeFrontmatterValue(parsedFrontmatter.description);\n if (descriptionValue && descriptionValue.length > MAX_SKILL_DESCRIPTION_CHARS) {\n issues.push(\n `description exceeds maximum length of ${MAX_SKILL_DESCRIPTION_CHARS} characters ` +\n `(got ${descriptionValue.length})`,\n );\n }\n\n // Check allowed-tools values -- must be Claude Code tool names, not vault agent tools.\n // These skills run in developer Claude Code sessions, not the agent pipeline.\n // Uses extractFrontmatterField to handle both inline and YAML block-list formats.\n const rawAllowedTools = normalizeFrontmatterValue(parsedFrontmatter['allowed-tools']);\n if (rawAllowedTools) {\n const rawValue = rawAllowedTools;\n // Reject vault_* tool names first — most informative message for the\n // common LLM mistake of copying its own agent tool context into the skill.\n if (rawValue.includes('vault_')) {\n issues.push(\n 'allowed-tools contains vault agent tool names (vault_*). ' +\n 'Skills run in Claude Code sessions -- use Claude Code tool names instead: ' +\n 'Read, Edit, Write, Bash, Grep, Glob',\n );\n } else {\n // Positive whitelist check — catches \"[None]\", \"None\", invented tool\n // names, and other confabulations that the vault_* reject misses.\n const parsed = parseAllowedTools(rawValue);\n if (parsed === null) {\n issues.push(\n `allowed-tools value is malformed or empty: \"${rawValue}\". ` +\n 'Provide a comma-separated list of Claude Code tools, e.g. ' +\n '\"Read, Edit, Write, Bash, Grep, Glob\". Use the narrowest set ' +\n 'the skill actually needs.',\n );\n } else {\n const unknown = parsed.filter((t) => !ALLOWED_CLAUDE_CODE_TOOLS.has(t));\n if (unknown.length > 0) {\n issues.push(\n `allowed-tools contains unknown tool name(s): ${unknown.join(', ')}. ` +\n `Valid Claude Code tools: ${[...ALLOWED_CLAUDE_CODE_TOOLS].join(', ')}.`,\n );\n }\n }\n }\n }\n // Also check YAML list format (- vault_search_fts etc.)\n const listToolLines = frontmatter.match(/^\\s+-\\s+vault_\\w+/gm);\n if (listToolLines) {\n issues.push(\n 'allowed-tools contains vault agent tool names (vault_*). ' +\n 'Skills run in Claude Code sessions -- use Claude Code tool names instead: ' +\n 'Read, Edit, Write, Bash, Grep, Glob',\n );\n }\n\n // Check line count\n const lineCount = content.split('\\n').length;\n if (lineCount > MAX_SKILL_LINES) {\n issues.push(`Skill is ${lineCount} lines (max ${MAX_SKILL_LINES})`);\n }\n\n return issues;\n}\n","/**\n * Skill lineage query helpers.\n *\n * Lineage is append-only (no update). Each row records a generation event\n * for a skill — what changed, why, and a snapshot of the content at that\n * point in time.\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 a skill lineage entry. */\nexport interface LineageInsert {\n id: string;\n skill_id: string;\n generation: number;\n action: string;\n rationale: string;\n source_ids_added?: string;\n content_snapshot: string;\n created_at: number;\n}\n\n/** Row shape returned from skill lineage queries (all columns). */\nexport interface LineageRow {\n id: string;\n skill_id: string;\n generation: number;\n action: string;\n rationale: string;\n source_ids_added: string;\n content_snapshot: string;\n created_at: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nexport const LINEAGE_COLUMNS = [\n 'id',\n 'skill_id',\n 'generation',\n 'action',\n 'rationale',\n 'source_ids_added',\n 'content_snapshot',\n 'created_at',\n] as const;\n\nconst SELECT_COLUMNS = LINEAGE_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed LineageRow. */\nfunction toLineageRow(row: Record<string, unknown>): LineageRow {\n return {\n id: row.id as string,\n skill_id: row.skill_id as string,\n generation: row.generation as number,\n action: row.action as string,\n rationale: row.rationale as string,\n source_ids_added: (row.source_ids_added as string) ?? '[]',\n content_snapshot: row.content_snapshot as string,\n created_at: row.created_at as number,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new skill lineage entry.\n *\n * Lineage is derived data — there is no syncRow call here.\n * Requires a valid `skill_id` (foreign key to skill_records table).\n */\nexport function insertLineage(data: LineageInsert): LineageRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO skill_lineage (\n id, skill_id, generation, action, rationale,\n source_ids_added, content_snapshot, created_at\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?\n )`,\n ).run(\n data.id,\n data.skill_id,\n data.generation,\n data.action,\n data.rationale,\n data.source_ids_added ?? '[]',\n data.content_snapshot,\n data.created_at,\n );\n\n return toLineageRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM skill_lineage WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n}\n\n/**\n * List all lineage entries for a skill, ordered by generation DESC\n * (newest generation first).\n */\nexport function listLineageForSkill(skillId: string, limit = 50): LineageRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM skill_lineage\n WHERE skill_id = ?\n ORDER BY generation DESC\n LIMIT ?`,\n ).all(skillId, limit) as Record<string, unknown>[];\n\n return rows.map(toLineageRow);\n}\n","/**\n * Prompt batch 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 { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of unprocessed batches returned when no limit given. */\nconst DEFAULT_UNPROCESSED_LIMIT = 100;\n\n/** Default number of batches returned by listBatchesBySession when no limit given. */\nexport const BATCHES_DEFAULT_LIMIT = 200;\n\n/** Batch status value when a batch is closed normally. */\nconst STATUS_COMPLETED = 'completed';\n\n/** Default batch status for new batches. */\nconst DEFAULT_STATUS = 'active';\n\n/** Default activity count for new batches. */\nconst DEFAULT_ACTIVITY_COUNT = 0;\n\n/** Default processed flag for new batches. */\nconst DEFAULT_PROCESSED = 0;\n\n/** Processed flag value indicating a batch has been processed. */\nconst PROCESSED_FLAG = 1;\n\n/** Number of characters used for prompt prefix matching. */\nconst PROMPT_PREFIX_MATCH_CHARS = 60;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Filter options for `listBatchesBySession`. */\nexport interface ListBatchesBySessionOptions {\n limit?: number;\n offset?: number;\n}\n\n/** Fields required (or optional) when inserting a prompt batch. */\nexport interface BatchInsert {\n session_id: string;\n created_at: number;\n prompt_number?: number | null;\n user_prompt?: string | null;\n response_summary?: string | null;\n classification?: string | null;\n started_at?: number | null;\n ended_at?: number | null;\n status?: string;\n activity_count?: number;\n processed?: number;\n content_hash?: string | null;\n machine_id?: string;\n}\n\n/** Row shape returned from batch queries. */\nexport interface BatchRow {\n id: number;\n session_id: string;\n prompt_number: number | null;\n user_prompt: string | null;\n response_summary: string | null;\n classification: string | null;\n started_at: number | null;\n ended_at: number | null;\n status: string;\n activity_count: number;\n processed: number;\n content_hash: string | null;\n created_at: number;\n machine_id: string;\n synced_at: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst BATCH_COLUMNS = [\n 'id',\n 'session_id',\n 'prompt_number',\n 'user_prompt',\n 'response_summary',\n 'classification',\n 'started_at',\n 'ended_at',\n 'status',\n 'activity_count',\n 'processed',\n 'content_hash',\n 'created_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = BATCH_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed BatchRow. */\nfunction toBatchRow(row: Record<string, unknown>): BatchRow {\n return {\n id: row.id as number,\n session_id: row.session_id as string,\n prompt_number: (row.prompt_number as number) ?? null,\n user_prompt: (row.user_prompt as string) ?? null,\n response_summary: (row.response_summary as string) ?? null,\n classification: (row.classification as string) ?? null,\n started_at: (row.started_at as number) ?? null,\n ended_at: (row.ended_at as number) ?? null,\n status: row.status as string,\n activity_count: row.activity_count 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 machine_id: (row.machine_id as string) ?? 'local',\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new prompt batch.\n *\n * The `id` is auto-generated by the INTEGER PRIMARY KEY (AUTOINCREMENT).\n * FTS5 index is kept in sync automatically via database triggers.\n */\nexport function insertBatch(data: BatchInsert): BatchRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO prompt_batches (\n session_id, prompt_number, user_prompt, response_summary,\n classification, started_at, ended_at, status,\n activity_count, processed, content_hash, created_at, machine_id\n ) VALUES (\n ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?, ?, ?, ?\n )`,\n ).run(\n data.session_id,\n data.prompt_number ?? null,\n data.user_prompt ?? null,\n data.response_summary ?? null,\n data.classification ?? null,\n data.started_at ?? null,\n data.ended_at ?? null,\n data.status ?? DEFAULT_STATUS,\n data.activity_count ?? DEFAULT_ACTIVITY_COUNT,\n data.processed ?? DEFAULT_PROCESSED,\n data.content_hash ?? null,\n data.created_at,\n data.machine_id ?? getTeamMachineId(),\n );\n\n const batchId = Number(info.lastInsertRowid);\n\n const row = toBatchRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM prompt_batches WHERE id = ?`).get(batchId) as Record<string, unknown>,\n );\n\n syncRow('prompt_batches', row);\n\n return row;\n}\n\n/**\n * Close a batch — set status to 'completed' and record the end time.\n *\n * @returns the updated row, or null if the batch does not exist.\n */\nexport function closeBatch(\n id: number,\n endedAt: number,\n): BatchRow | null {\n const db = getDatabase();\n\n const info = db.prepare(\n `UPDATE prompt_batches\n SET status = ?, ended_at = ?\n WHERE id = ?`,\n ).run(STATUS_COMPLETED, endedAt, id);\n\n if (info.changes === 0) return null;\n\n return toBatchRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM prompt_batches WHERE id = ?`).get(id) as Record<string, unknown>,\n );\n}\n\n/**\n * Populate response_summary on batches from transcript turns.\n *\n * Matches transcript turns (ordered by position) to batches (ordered by id ASC).\n * This is resilient to prompt_number duplicates caused by daemon restarts.\n * Only updates batches that don't already have a response_summary.\n *\n * @param sessionId — the session to update\n * @param responses — array of { response } ordered by turn position (1-indexed)\n */\nexport function populateBatchResponses(\n sessionId: string,\n responses: Array<{ turnIndex: number; response: string }>,\n): void {\n const db = getDatabase();\n\n // Get all batches for this session ordered by id (insertion order = true order)\n const batches = db.prepare(\n `SELECT id FROM prompt_batches WHERE session_id = ? ORDER BY id ASC`,\n ).all(sessionId) as Array<{ id: number }>;\n\n // Map each response to the batch at the same position\n for (const { turnIndex, response } of responses) {\n const batchIndex = turnIndex - 1; // turns are 1-indexed\n if (batchIndex >= 0 && batchIndex < batches.length) {\n const batchId = batches[batchIndex].id;\n db.prepare(\n `UPDATE prompt_batches SET response_summary = ? WHERE id = ? AND response_summary IS NULL`,\n ).run(response, batchId);\n }\n }\n}\n\n/**\n * Get unprocessed batches, ordered by id ASC (insertion order).\n *\n * Supports cursor-based pagination via `after_id` and a `limit` cap.\n *\n * When `includeActive` is explicitly `false`, batches from sessions still\n * in `status = 'active'` are excluded — intelligence tasks opt in to this\n * so they don't reason over in-flight work. The default is permissive to\n * preserve behavior for tests and any non-agent caller.\n */\nexport function getUnprocessedBatches(\n options: { after_id?: number; limit?: number; includeActive?: boolean } = {},\n): BatchRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [`processed = ?`];\n const params: unknown[] = [DEFAULT_PROCESSED];\n\n if (options.after_id !== undefined) {\n conditions.push(`id > ?`);\n params.push(options.after_id);\n }\n\n if (options.includeActive === false) {\n conditions.push(\n `EXISTS (SELECT 1 FROM sessions s WHERE s.id = prompt_batches.session_id AND s.status != 'active')`,\n );\n }\n\n const limit = options.limit ?? DEFAULT_UNPROCESSED_LIMIT;\n params.push(limit);\n\n const where = conditions.join(' AND ');\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM prompt_batches\n WHERE ${where}\n ORDER BY id ASC\n LIMIT ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toBatchRow);\n}\n\n/**\n * Increment the activity_count for a batch by 1.\n *\n * @returns the updated row, or null if the batch does not exist.\n */\nexport function incrementActivityCount(\n id: number,\n): BatchRow | null {\n const db = getDatabase();\n\n const info = db.prepare(\n `UPDATE prompt_batches\n SET activity_count = activity_count + 1\n WHERE id = ?`,\n ).run(id);\n\n if (info.changes === 0) return null;\n\n return toBatchRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM prompt_batches WHERE id = ?`).get(id) as Record<string, unknown>,\n );\n}\n\n/**\n * Mark a batch as processed (processed = 1).\n *\n * @returns the updated row, or null if the batch does not exist.\n */\nexport function markBatchProcessed(\n id: number,\n): BatchRow | null {\n const db = getDatabase();\n\n const info = db.prepare(\n `UPDATE prompt_batches\n SET processed = ?\n WHERE id = ?`,\n ).run(PROCESSED_FLAG, id);\n\n if (info.changes === 0) return null;\n\n return toBatchRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM prompt_batches WHERE id = ?`).get(id) as Record<string, unknown>,\n );\n}\n\n/**\n * Get a batch's ID by session and prompt number.\n * Used to link attachments to their prompt batch at stop time.\n */\nexport function getBatchIdByPromptNumber(\n sessionId: string,\n promptNumber: number,\n): number | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT id FROM prompt_batches WHERE session_id = ? AND prompt_number = ? LIMIT 1`,\n ).get(sessionId, promptNumber) as { id: number } | undefined;\n\n return row ? row.id : null;\n}\n\n/**\n * Find a batch by matching the start of its user_prompt text.\n * Used for attachment matching after transcript compaction where turn indices no longer\n * align with prompt_numbers.\n */\nexport function findBatchByPromptPrefix(\n sessionId: string,\n promptPrefix: string,\n): { id: number; prompt_number: number } | null {\n const db = getDatabase();\n // Match first N chars — enough to be unique, tolerant of minor differences\n const prefix = promptPrefix.slice(0, PROMPT_PREFIX_MATCH_CHARS);\n const row = db.prepare(\n `SELECT id, prompt_number FROM prompt_batches\n WHERE session_id = ? AND user_prompt LIKE ? || '%'\n LIMIT 1`,\n ).get(sessionId, prefix) as { id: number; prompt_number: number } | undefined;\n return row ?? null;\n}\n\n/** Fields required when inserting a batch statelessly (prompt_number derived from DB). */\nexport interface StatelessBatchInsert {\n session_id: string;\n created_at: number;\n user_prompt?: string | null;\n started_at?: number | null;\n status?: string;\n machine_id?: string;\n}\n\n/**\n * Insert a new prompt batch with prompt_number derived from an inline subquery.\n *\n * The prompt_number is set to `COALESCE(MAX(prompt_number), 0) + 1` for the\n * session, so the caller never needs a separate SELECT. This makes the insert\n * stateless — no in-memory counter required.\n *\n * FTS5 index is kept in sync automatically via database triggers.\n */\nexport function insertBatchStateless(data: StatelessBatchInsert): BatchRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO prompt_batches (\n session_id, prompt_number, user_prompt, response_summary,\n classification, started_at, ended_at, status,\n activity_count, processed, content_hash, created_at, machine_id\n ) VALUES (\n ?,\n (SELECT COALESCE(MAX(prompt_number), 0) + 1 FROM prompt_batches WHERE session_id = ?),\n ?, NULL,\n NULL, ?, NULL, ?,\n ?, ?, NULL, ?, ?\n )`,\n ).run(\n data.session_id,\n data.session_id,\n data.user_prompt ?? null,\n data.started_at ?? null,\n data.status ?? DEFAULT_STATUS,\n DEFAULT_ACTIVITY_COUNT,\n DEFAULT_PROCESSED,\n data.created_at,\n data.machine_id ?? getTeamMachineId(),\n );\n\n const batchId = Number(info.lastInsertRowid);\n\n return toBatchRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM prompt_batches WHERE id = ?`).get(batchId) as Record<string, unknown>,\n );\n}\n\n/**\n * Close all open batches for a session — blind UPDATE, no prior SELECT needed.\n *\n * Sets `status = 'completed'` and `ended_at` on every batch that has no\n * `ended_at` value yet. Returns the number of batches closed.\n */\nexport function closeOpenBatches(\n sessionId: string,\n endedAt: number,\n): number {\n const db = getDatabase();\n\n const info = db.prepare(\n `UPDATE prompt_batches\n SET status = ?, ended_at = ?\n WHERE session_id = ? AND ended_at IS NULL`,\n ).run(STATUS_COMPLETED, endedAt, sessionId);\n\n return info.changes;\n}\n\n/**\n * Set response_summary on a batch if it doesn't already have one.\n *\n * Idempotent — only updates NULL response_summary.\n */\nexport function setResponseSummary(\n batchId: number,\n summary: string,\n): void {\n const db = getDatabase();\n db.prepare(\n `UPDATE prompt_batches SET response_summary = ? WHERE id = ? AND response_summary IS NULL`,\n ).run(summary, batchId);\n}\n\n/**\n * Get the most recent batch for a session (by id DESC), regardless of status.\n *\n * Used by processStopEvent to attach the AI response and images to the\n * correct batch without positional turn mapping.\n */\nexport function getLatestBatch(\n sessionId: string,\n): BatchRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM prompt_batches\n WHERE session_id = ?\n ORDER BY id DESC LIMIT 1`,\n ).get(sessionId) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toBatchRow(row);\n}\n\nexport function listBatchesBySession(\n sessionId: string,\n options: ListBatchesBySessionOptions = {},\n): BatchRow[] {\n const db = getDatabase();\n\n const limit = options.limit ?? BATCHES_DEFAULT_LIMIT;\n const offset = options.offset ?? 0;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM prompt_batches\n WHERE session_id = ?\n ORDER BY prompt_number ASC\n LIMIT ?\n OFFSET ?`,\n ).all(sessionId, limit, offset) as Record<string, unknown>[];\n\n return rows.map(toBatchRow);\n}\n\n/**\n * Count prompt batches for a session — authoritative prompt count.\n */\nexport function countBatchesBySession(sessionId: string): number {\n const db = getDatabase();\n const row = db.prepare(\n `SELECT COUNT(*) as count FROM prompt_batches WHERE session_id = ?`,\n ).get(sessionId) as { count: number };\n return row.count;\n}\n","/**\n * Graph edge CRUD query helpers.\n *\n * Unlike the `edges` table (which has FK constraints to entities), `graph_edges`\n * supports edges between any node types (session, batch, spore, entity).\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport crypto from 'node:crypto';\nimport { getDatabase } from '@myco/db/client.js';\nimport { QUERY_DEFAULT_LIST_LIMIT, GRAPH_EDGE_DEFAULT_CONFIDENCE } from '@myco/constants.js';\nimport { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default BFS traversal depth. */\nconst DEFAULT_BFS_DEPTH = 2;\n\n/** Maximum BFS traversal depth (capped for performance). */\nconst MAX_BFS_DEPTH = 5;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Valid node types in the graph. */\nexport type GraphNodeType = 'session' | 'batch' | 'spore' | 'entity';\n\n/** Lineage edge types (auto-created by daemon, no LLM). */\nexport type LineageEdgeType = 'FROM_SESSION' | 'EXTRACTED_FROM' | 'DERIVED_FROM' | 'HAS_BATCH';\n\n/** Semantic edge types (created by intelligence agent, LLM-driven). */\nexport type SemanticEdgeType = 'RELATES_TO' | 'SUPERSEDED_BY' | 'REFERENCES' | 'DEPENDS_ON' | 'AFFECTS';\n\n/** All valid graph edge types. */\nexport type GraphEdgeType = LineageEdgeType | SemanticEdgeType;\n\n/** Fields required (or optional) when inserting a graph edge. */\nexport interface GraphEdgeInsert {\n agent_id: string;\n source_id: string;\n source_type: GraphNodeType;\n target_id: string;\n target_type: GraphNodeType;\n type: GraphEdgeType;\n created_at: number;\n session_id?: string;\n confidence?: number;\n properties?: string;\n machine_id?: string;\n}\n\n/** Row shape returned from graph edge queries. */\nexport interface GraphEdgeRow {\n id: string;\n agent_id: string;\n source_id: string;\n source_type: string;\n target_id: string;\n target_type: string;\n type: string;\n session_id: string | null;\n confidence: number;\n properties: string | null;\n created_at: number;\n machine_id: string;\n synced_at: number | null;\n}\n\n/** Filter options for `listGraphEdges`. */\nexport interface ListGraphEdgesOptions {\n sourceId?: string;\n targetId?: string;\n type?: string;\n agentId?: string;\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst GRAPH_EDGE_COLUMNS = [\n 'id',\n 'agent_id',\n 'source_id',\n 'source_type',\n 'target_id',\n 'target_type',\n 'type',\n 'session_id',\n 'confidence',\n 'properties',\n 'created_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = GRAPH_EDGE_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed GraphEdgeRow. */\nfunction toGraphEdgeRow(row: Record<string, unknown>): GraphEdgeRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n source_id: row.source_id as string,\n source_type: row.source_type as string,\n target_id: row.target_id as string,\n target_type: row.target_type as string,\n type: row.type as string,\n session_id: (row.session_id as string) ?? null,\n confidence: row.confidence as number,\n properties: (row.properties as string) ?? null,\n created_at: row.created_at as number,\n machine_id: (row.machine_id as string) ?? getTeamMachineId(),\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new graph edge.\n *\n * Generates a UUID id automatically.\n */\nexport function insertGraphEdge(data: GraphEdgeInsert): GraphEdgeRow {\n const db = getDatabase();\n const id = crypto.randomUUID();\n\n db.prepare(\n `INSERT INTO graph_edges (\n id, agent_id, source_id, source_type, target_id, target_type,\n type, session_id, confidence, properties, created_at, machine_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n data.agent_id,\n data.source_id,\n data.source_type,\n data.target_id,\n data.target_type,\n data.type,\n data.session_id ?? null,\n data.confidence ?? GRAPH_EDGE_DEFAULT_CONFIDENCE,\n data.properties ?? null,\n data.created_at,\n data.machine_id ?? getTeamMachineId(),\n );\n\n const row = toGraphEdgeRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM graph_edges WHERE id = ?`).get(id) as Record<string, unknown>,\n );\n\n syncRow('graph_edges', row);\n\n return row;\n}\n\n/**\n * List graph edges with optional filters, ordered by created_at DESC.\n */\nexport function listGraphEdges(\n options: ListGraphEdgesOptions = {},\n): GraphEdgeRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.sourceId !== undefined) {\n conditions.push(`source_id = ?`);\n params.push(options.sourceId);\n }\n\n if (options.targetId !== undefined) {\n conditions.push(`target_id = ?`);\n params.push(options.targetId);\n }\n\n if (options.type !== undefined) {\n conditions.push(`type = ?`);\n params.push(options.type);\n }\n\n if (options.agentId !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agentId);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = options.limit ?? QUERY_DEFAULT_LIST_LIMIT;\n\n params.push(limit);\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM graph_edges\n ${where}\n ORDER BY created_at DESC\n LIMIT ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toGraphEdgeRow);\n}\n\n/**\n * BFS traversal from a node across graph edges.\n *\n * Returns all edges reachable within `depth` hops from the starting node.\n *\n * @param nodeId - The starting node ID.\n * @param nodeType - The starting node type.\n * @param options - Optional depth limit (default 2, max 5).\n */\nexport function getGraphForNode(\n nodeId: string,\n nodeType: GraphNodeType,\n options?: { depth?: number },\n): { edges: GraphEdgeRow[] } {\n const db = getDatabase();\n const depth = Math.min(Math.max(options?.depth ?? DEFAULT_BFS_DEPTH, 1), MAX_BFS_DEPTH);\n\n const seenEdgeIds = new Set<string>();\n const collectedEdges: GraphEdgeRow[] = [];\n const visited = new Set<string>([`${nodeType}:${nodeId}`]);\n let frontier = new Set<string>([nodeId]);\n\n for (let hop = 0; hop < depth; hop++) {\n if (frontier.size === 0) break;\n\n const frontierArray = Array.from(frontier);\n const placeholders = frontierArray.map(() => `?`).join(', ');\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM graph_edges\n WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})`,\n ).all(...frontierArray, ...frontierArray) as Record<string, unknown>[];\n\n const nextFrontier = new Set<string>();\n\n for (const row of rows) {\n const edge = toGraphEdgeRow(row);\n if (!seenEdgeIds.has(edge.id)) {\n seenEdgeIds.add(edge.id);\n collectedEdges.push(edge);\n }\n const sourceKey = `${edge.source_type}:${edge.source_id}`;\n const targetKey = `${edge.target_type}:${edge.target_id}`;\n if (!visited.has(sourceKey)) {\n visited.add(sourceKey);\n nextFrontier.add(edge.source_id);\n }\n if (!visited.has(targetKey)) {\n visited.add(targetKey);\n nextFrontier.add(edge.target_id);\n }\n }\n\n frontier = nextFrontier;\n }\n\n return { edges: collectedEdges };\n}\n","/**\n * Entity CRUD query helpers for the knowledge graph.\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 { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { getGraphForNode, type GraphEdgeRow } from '@myco/db/queries/graph-edges.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of entities returned by listEntities when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting an entity. */\nexport interface EntityInsert {\n id: string;\n agent_id: string;\n type: string;\n name: string;\n first_seen: number;\n last_seen: number;\n properties?: string | null;\n machine_id?: string;\n}\n\n/** Row shape returned from entity queries (all columns). */\nexport interface EntityRow {\n id: string;\n agent_id: string;\n type: string;\n name: string;\n properties: string | null;\n first_seen: number;\n last_seen: number;\n status: string;\n machine_id: string;\n synced_at: number | null;\n}\n\n/** Filter options for `listEntities`. */\nexport interface ListEntitiesOptions {\n agent_id?: string;\n type?: string;\n /** Filter by exact entity name. */\n name?: string;\n /** Filter by status (default 'active'). */\n status?: string;\n /** Filter by entity_mentions subquery — must be paired with note_type. */\n mentioned_in?: string;\n /** Required when mentioned_in is provided. */\n note_type?: string;\n limit?: number;\n offset?: number;\n}\n\n/** Return type for `getEntityWithEdges`. */\nexport interface EntityGraph {\n center: EntityRow;\n nodes: EntityRow[];\n edges: GraphEdgeRow[];\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst ENTITY_COLUMNS = [\n 'id',\n 'agent_id',\n 'type',\n 'name',\n 'properties',\n 'first_seen',\n 'last_seen',\n 'status',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = ENTITY_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed EntityRow. */\nfunction toEntityRow(row: Record<string, unknown>): EntityRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n type: row.type as string,\n name: row.name as string,\n properties: (row.properties as string) ?? null,\n first_seen: row.first_seen as number,\n last_seen: row.last_seen as number,\n status: (row.status as string) ?? 'active',\n machine_id: (row.machine_id as string) ?? 'local',\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert or update an entity. Uses UPSERT on (agent_id, type, name).\n *\n * On conflict, updates properties (if provided) and last_seen.\n */\nexport function insertEntity(data: EntityInsert): EntityRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO entities (id, agent_id, type, name, properties, first_seen, last_seen, machine_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (agent_id, type, name) DO UPDATE SET\n properties = COALESCE(EXCLUDED.properties, entities.properties),\n last_seen = EXCLUDED.last_seen`,\n ).run(\n data.id,\n data.agent_id,\n data.type,\n data.name,\n data.properties ?? null,\n data.first_seen,\n data.last_seen,\n data.machine_id ?? getTeamMachineId(),\n );\n\n // On conflict, the passed-in id may not be the actual row id. Look up by unique key.\n const row = toEntityRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM entities WHERE agent_id = ? AND type = ? AND name = ?`).get(\n data.agent_id,\n data.type,\n data.name,\n ) as Record<string, unknown>,\n );\n\n syncRow('entities', row);\n\n return row;\n}\n\n/**\n * Retrieve a single entity by id.\n *\n * @returns the entity row, or null if not found.\n */\nexport function getEntity(id: string): EntityRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM entities WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toEntityRow(row);\n}\n\n/**\n * List entities with optional filters, ordered by last_seen DESC.\n *\n * Defaults to `status = 'active'` — archived entities are excluded unless\n * `status` is explicitly provided. Pass `status: undefined` in options to\n * get only active entities (the default), or set a specific status string.\n *\n * When both `mentioned_in` and `note_type` are provided, filters to entities\n * referenced in a specific note via the entity_mentions subquery.\n */\nexport function listEntities(\n options: ListEntitiesOptions = {},\n): EntityRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.agent_id !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agent_id);\n }\n\n if (options.type !== undefined) {\n conditions.push(`type = ?`);\n params.push(options.type);\n }\n\n if (options.name !== undefined) {\n conditions.push(`name = ?`);\n params.push(options.name);\n }\n\n // Default: only show active entities (status column added in v5)\n if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n } else {\n conditions.push(`status = ?`);\n params.push('active');\n }\n\n if (options.mentioned_in !== undefined && options.note_type !== undefined) {\n conditions.push(\n `id IN (SELECT entity_id FROM entity_mentions WHERE note_id = ? AND note_type = ?)`,\n );\n params.push(options.mentioned_in);\n params.push(options.note_type);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n const offset = options.offset ?? 0;\n\n params.push(limit);\n params.push(offset);\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM entities\n ${where}\n ORDER BY last_seen DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toEntityRow);\n}\n\n/**\n * Fetch an entity and its surrounding graph via BFS traversal.\n *\n * Delegates to `getGraphForNode` (graph_edges table) for the BFS,\n * then fetches entity rows for all connected entity nodes.\n *\n * @param entityId - The center entity to expand from.\n * @param depth - Number of hops to traverse (1-3, default 1).\n * @returns `{ center, nodes, edges }` where nodes are all connected entities\n * (excluding center) and edges are deduplicated across BFS iterations.\n */\nexport function getEntityWithEdges(\n entityId: string,\n depth = 1,\n): EntityGraph | null {\n const db = getDatabase();\n\n const center = getEntity(entityId);\n if (center === null) return null;\n\n const clampedDepth = Math.min(Math.max(depth, 1), 3);\n const graph = getGraphForNode(entityId, 'entity', { depth: clampedDepth });\n\n // Collect all entity node IDs from edges (excluding center)\n const nodeIdSet = new Set<string>();\n for (const edge of graph.edges) {\n if (edge.source_type === 'entity' && edge.source_id !== entityId) nodeIdSet.add(edge.source_id);\n if (edge.target_type === 'entity' && edge.target_id !== entityId) nodeIdSet.add(edge.target_id);\n }\n\n // Fetch all connected entity nodes\n const nodeIds = Array.from(nodeIdSet);\n let nodes: EntityRow[] = [];\n if (nodeIds.length > 0) {\n const placeholders = nodeIds.map(() => `?`).join(', ');\n const nodeRows = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM entities WHERE id IN (${placeholders})`,\n ).all(...nodeIds) as Record<string, unknown>[];\n nodes = nodeRows.map(toEntityRow);\n }\n\n return { center, nodes, edges: graph.edges };\n}\n","/**\n * Lineage edge creation helpers.\n *\n * Creates automatic graph edges when spores and batches are inserted.\n * These are structural (no LLM needed) — the daemon layer calls them.\n *\n * Edge types created:\n * - FROM_SESSION: spore -> session (the session it was extracted from)\n * - EXTRACTED_FROM: spore -> batch (the prompt batch it was extracted from)\n * - DERIVED_FROM: wisdom spore -> source spore (consolidation provenance)\n * - HAS_BATCH: session -> batch (prompt batch belongs to session)\n */\n\nimport { insertGraphEdge } from './graph-edges.js';\nimport {\n EDGE_TYPE_FROM_SESSION,\n EDGE_TYPE_EXTRACTED_FROM,\n EDGE_TYPE_DERIVED_FROM,\n EDGE_TYPE_HAS_BATCH,\n} from '@myco/constants.js';\n\n// ---------------------------------------------------------------------------\n// Spore lineage\n// ---------------------------------------------------------------------------\n\n/** Create lineage edges for a newly inserted spore. */\nexport function createSporeLineage(spore: {\n id: string;\n agent_id: string;\n session_id?: string | null;\n prompt_batch_id?: number | null;\n observation_type?: string;\n properties?: string | null;\n created_at: number;\n}): void {\n if (spore.session_id) {\n insertGraphEdge({\n agent_id: spore.agent_id,\n source_id: spore.id,\n source_type: 'spore',\n target_id: spore.session_id,\n target_type: 'session',\n type: EDGE_TYPE_FROM_SESSION,\n created_at: spore.created_at,\n });\n }\n\n if (spore.prompt_batch_id != null) {\n insertGraphEdge({\n agent_id: spore.agent_id,\n source_id: spore.id,\n source_type: 'spore',\n target_id: String(spore.prompt_batch_id),\n target_type: 'batch',\n type: EDGE_TYPE_EXTRACTED_FROM,\n created_at: spore.created_at,\n });\n }\n\n // DERIVED_FROM edges for wisdom spores\n if (spore.observation_type === 'wisdom' && spore.properties) {\n try {\n const props = JSON.parse(spore.properties);\n if (Array.isArray(props.consolidated_from)) {\n for (const sourceId of props.consolidated_from) {\n insertGraphEdge({\n agent_id: spore.agent_id,\n source_id: spore.id,\n source_type: 'spore',\n target_id: sourceId,\n target_type: 'spore',\n type: EDGE_TYPE_DERIVED_FROM,\n created_at: spore.created_at,\n });\n }\n }\n } catch { /* ignore malformed properties */ }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Batch lineage\n// ---------------------------------------------------------------------------\n\n/** Create a HAS_BATCH lineage edge from session to batch. */\nexport function createBatchLineage(\n agentId: string,\n sessionId: string,\n batchId: number,\n createdAt: number,\n): void {\n insertGraphEdge({\n agent_id: agentId,\n source_id: sessionId,\n source_type: 'session',\n target_id: String(batchId),\n target_type: 'batch',\n type: EDGE_TYPE_HAS_BATCH,\n created_at: createdAt,\n });\n}\n","/**\n * Resolution event 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 { getTeamMachineId } from '@myco/daemon/team-context.js';\nimport { syncRow } from '@myco/db/queries/team-outbox.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of events returned by listResolutionEvents when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a resolution event. */\nexport interface ResolutionEventInsert {\n id: string;\n agent_id: string;\n spore_id: string;\n action: string;\n created_at: number;\n new_spore_id?: string | null;\n reason?: string | null;\n session_id?: string | null;\n machine_id?: string;\n}\n\n/** Row shape returned from resolution_events queries (all columns). */\nexport interface ResolutionEventRow {\n id: string;\n agent_id: string;\n spore_id: string;\n action: string;\n new_spore_id: string | null;\n reason: string | null;\n session_id: string | null;\n created_at: number;\n machine_id: string;\n synced_at: number | null;\n}\n\n/** Filter options for `listResolutionEvents`. */\nexport interface ListResolutionEventsOptions {\n agent_id?: string;\n spore_id?: string;\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst EVENT_COLUMNS = [\n 'id',\n 'agent_id',\n 'spore_id',\n 'action',\n 'new_spore_id',\n 'reason',\n 'session_id',\n 'created_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = EVENT_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed ResolutionEventRow. */\nfunction toResolutionEventRow(row: Record<string, unknown>): ResolutionEventRow {\n return {\n id: row.id as string,\n agent_id: row.agent_id as string,\n spore_id: row.spore_id as string,\n action: row.action as string,\n new_spore_id: (row.new_spore_id as string) ?? null,\n reason: (row.reason as string) ?? null,\n session_id: (row.session_id as string) ?? null,\n created_at: row.created_at as number,\n machine_id: (row.machine_id as string) ?? 'local',\n synced_at: (row.synced_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new resolution event.\n */\nexport function insertResolutionEvent(\n data: ResolutionEventInsert,\n): ResolutionEventRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO resolution_events (\n id, agent_id, spore_id, action, new_spore_id, reason, session_id, created_at, machine_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n data.id,\n data.agent_id,\n data.spore_id,\n data.action,\n data.new_spore_id ?? null,\n data.reason ?? null,\n data.session_id ?? null,\n data.created_at,\n data.machine_id ?? getTeamMachineId(),\n );\n\n const row = toResolutionEventRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM resolution_events WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n\n syncRow('resolution_events', row);\n\n return row;\n}\n\n/**\n * List resolution events with optional filters, ordered by created_at DESC.\n */\nexport function listResolutionEvents(\n options: ListResolutionEventsOptions = {},\n): ResolutionEventRow[] {\n const db = getDatabase();\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.agent_id !== undefined) {\n conditions.push(`agent_id = ?`);\n params.push(options.agent_id);\n }\n\n if (options.spore_id !== undefined) {\n conditions.push(`spore_id = ?`);\n params.push(options.spore_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 resolution_events\n ${where}\n ORDER BY created_at DESC\n LIMIT ?`,\n ).all(...params) as Record<string, unknown>[];\n\n return rows.map(toResolutionEventRow);\n}\n","/**\n * Agent report 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// Constants\n// ---------------------------------------------------------------------------\n\n/** Default number of reports returned by list queries when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a report. */\nexport interface ReportInsert {\n run_id: string;\n agent_id: string;\n action: string;\n summary: string;\n details?: string | null;\n created_at: number;\n}\n\n/** Row shape returned from agent_reports queries (all columns). */\nexport interface ReportRow {\n id: number;\n run_id: string;\n agent_id: string;\n action: string;\n summary: string;\n details: string | null;\n created_at: number;\n}\n\n/** Filter options for `listReportsByAgent`. */\nexport interface ListReportsByAgentOptions {\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst REPORT_COLUMNS = [\n 'id',\n 'run_id',\n 'agent_id',\n 'action',\n 'summary',\n 'details',\n 'created_at',\n] as const;\n\nconst SELECT_COLUMNS = REPORT_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed ReportRow. */\nfunction toReportRow(row: Record<string, unknown>): ReportRow {\n return {\n id: row.id as number,\n run_id: row.run_id as string,\n agent_id: row.agent_id as string,\n action: row.action as string,\n summary: row.summary as string,\n details: (row.details 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 agent report.\n *\n * The `id` is auto-generated by the INTEGER PRIMARY KEY (AUTOINCREMENT).\n */\nexport function insertReport(data: ReportInsert): ReportRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO agent_reports (\n run_id, agent_id, action, summary, details, created_at\n ) VALUES (\n ?, ?, ?, ?, ?, ?\n )`,\n ).run(\n data.run_id,\n data.agent_id,\n data.action,\n data.summary,\n data.details ?? null,\n data.created_at,\n );\n\n const reportId = Number(info.lastInsertRowid);\n\n return toReportRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM agent_reports WHERE id = ?`).get(reportId) as Record<string, unknown>,\n );\n}\n\n/**\n * List all reports for a specific run, ordered by created_at ASC.\n */\nexport function listReports(runId: string): ReportRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_reports\n WHERE run_id = ?\n ORDER BY created_at ASC`,\n ).all(runId) as Record<string, unknown>[];\n\n return rows.map(toReportRow);\n}\n\n/**\n * List reports by agent, ordered by created_at DESC.\n */\nexport function listReportsByAgent(\n agentId: string,\n options: ListReportsByAgentOptions = {},\n): ReportRow[] {\n const db = getDatabase();\n\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_reports\n WHERE agent_id = ?\n ORDER BY created_at DESC\n LIMIT ?`,\n ).all(agentId, limit) as Record<string, unknown>[];\n\n return rows.map(toReportRow);\n}\n"],"mappings":"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","names":["row","result","DEFAULT_LIST_LIMIT","row","DEFAULT_STATUS","SELECT_COLUMNS","DEFAULT_STATUS","SELECT_COLUMNS","buildWhere","SELECT_COLUMNS","SELECT_COLUMNS","parseYaml","SELECT_COLUMNS","STATUS_COMPLETED","DEFAULT_STATUS","SELECT_COLUMNS","SELECT_COLUMNS","DEFAULT_STATUS","SELECT_COLUMNS","STATUS_COMPLETED","crypto","SELECT_COLUMNS","crypto","DEFAULT_LIST_LIMIT","SELECT_COLUMNS","SELECT_COLUMNS","SELECT_COLUMNS"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
getPluginVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XG5RRUYF.js";
|
|
5
5
|
import {
|
|
6
6
|
DAEMON_CLIENT_TIMEOUT_MS,
|
|
7
7
|
DAEMON_HEALTH_CHECK_TIMEOUT_MS,
|
|
@@ -185,4 +185,4 @@ export {
|
|
|
185
185
|
resolveCliEntryPath,
|
|
186
186
|
DaemonClient
|
|
187
187
|
};
|
|
188
|
-
//# sourceMappingURL=chunk-
|
|
188
|
+
//# sourceMappingURL=chunk-VVNL26WX.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
loadConfig,
|
|
7
7
|
updateTeamConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OD4AA7PV.js";
|
|
9
9
|
import {
|
|
10
10
|
TEAM_API_KEY_SECRET,
|
|
11
11
|
TEAM_MCP_TOKEN_SECRET,
|
|
@@ -595,4 +595,4 @@ export {
|
|
|
595
595
|
upgradeWorker,
|
|
596
596
|
teamUpgrade
|
|
597
597
|
};
|
|
598
|
-
//# sourceMappingURL=chunk-
|
|
598
|
+
//# sourceMappingURL=chunk-XATDZX7U.js.map
|
|
@@ -11,7 +11,7 @@ var cached;
|
|
|
11
11
|
function getPluginVersion() {
|
|
12
12
|
if (cached) return cached;
|
|
13
13
|
if (true) {
|
|
14
|
-
cached = "0.20.
|
|
14
|
+
cached = "0.20.2";
|
|
15
15
|
return cached;
|
|
16
16
|
}
|
|
17
17
|
const root = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
@@ -32,4 +32,4 @@ function getPluginVersion() {
|
|
|
32
32
|
export {
|
|
33
33
|
getPluginVersion
|
|
34
34
|
};
|
|
35
|
-
//# sourceMappingURL=chunk-
|
|
35
|
+
//# sourceMappingURL=chunk-XG5RRUYF.js.map
|