@goondocks/myco 0.13.0 → 0.14.1

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.
Files changed (121) hide show
  1. package/dist/{agent-run-YBASQHC7.js → agent-run-VX5MAPRN.js} +4 -4
  2. package/dist/{agent-tasks-WBQFDHWC.js → agent-tasks-FJOBYYC5.js} +4 -4
  3. package/dist/{chunk-NCVR636M.js → chunk-4VF6KQ2Z.js} +55 -4
  4. package/dist/chunk-4VF6KQ2Z.js.map +1 -0
  5. package/dist/{chunk-EUQQVKGQ.js → chunk-CCKPBAKJ.js} +2 -2
  6. package/dist/{chunk-5BK6M6X5.js → chunk-JJXVDCEX.js} +160 -95
  7. package/dist/chunk-JJXVDCEX.js.map +1 -0
  8. package/dist/{chunk-VQEXXS56.js → chunk-NYTEHLVI.js} +2 -2
  9. package/dist/{chunk-W3T3QDBN.js → chunk-PSYLKCWQ.js} +17 -2
  10. package/dist/chunk-PSYLKCWQ.js.map +1 -0
  11. package/dist/{chunk-LPISXFM4.js → chunk-PX5KIOKY.js} +2 -2
  12. package/dist/{chunk-T77674VB.js → chunk-QB2UTRQE.js} +3 -3
  13. package/dist/chunk-QLCD77AN.js +93 -0
  14. package/dist/chunk-QLCD77AN.js.map +1 -0
  15. package/dist/{chunk-TFGGH6UB.js → chunk-RUDOGKWF.js} +2 -2
  16. package/dist/{chunk-NRT2ZRUG.js → chunk-S66YG6QK.js} +19 -1
  17. package/dist/{chunk-NRT2ZRUG.js.map → chunk-S66YG6QK.js.map} +1 -1
  18. package/dist/{chunk-2PWO3WPS.js → chunk-TVV6PZOC.js} +2 -2
  19. package/dist/{chunk-JBFFAGJK.js → chunk-X34OFKYU.js} +2 -2
  20. package/dist/{chunk-YYQWCTF6.js → chunk-XNHHADBK.js} +2 -2
  21. package/dist/{cli-WCBTILMW.js → cli-Z7VO7LIL.js} +36 -36
  22. package/dist/{client-MJUZJ5MC.js → client-CECN26WV.js} +3 -3
  23. package/dist/{config-WBCOTJUE.js → config-H657SF6B.js} +2 -2
  24. package/dist/{doctor-GDCLRJOF.js → doctor-TYMLSW5K.js} +5 -5
  25. package/dist/{executor-TCAXFOIS.js → executor-4DKCQN3A.js} +64 -23
  26. package/dist/executor-4DKCQN3A.js.map +1 -0
  27. package/dist/{init-ZSDMXY4T.js → init-W6VRN5ZZ.js} +10 -10
  28. package/dist/{init-wizard-6LDUVR7C.js → init-wizard-3RJFKAGM.js} +2 -2
  29. package/dist/{loader-L2TCAYCT.js → loader-JQLO6K44.js} +2 -2
  30. package/dist/{main-25MKYYKO.js → main-XUQRWNJ7.js} +255 -39
  31. package/dist/main-XUQRWNJ7.js.map +1 -0
  32. package/dist/{open-4YTUNIP3.js → open-6EC54JEU.js} +4 -4
  33. package/dist/{post-compact-E5V4OZJB.js → post-compact-55ISYIPY.js} +4 -4
  34. package/dist/{post-tool-use-Y6UWKCVD.js → post-tool-use-KNOFQNVX.js} +3 -3
  35. package/dist/{post-tool-use-failure-AHFXMNHX.js → post-tool-use-failure-UIRHWELN.js} +4 -4
  36. package/dist/{pre-compact-EI5EV3N7.js → pre-compact-T3CR4C3Q.js} +4 -4
  37. package/dist/{remove-F63WBELE.js → remove-HIIXTVAK.js} +4 -4
  38. package/dist/{resolution-events-BZYMUQ53.js → resolution-events-5EVUEWHS.js} +3 -3
  39. package/dist/{restart-GULUNCMX.js → restart-5FJYFNIR.js} +5 -5
  40. package/dist/{search-NLZMCEAG.js → search-C3CIHCMP.js} +4 -4
  41. package/dist/{server-CXPWUO6H.js → server-XJTAWCHN.js} +3 -3
  42. package/dist/{session-XXVEDIQZ.js → session-CUGCZWCY.js} +6 -6
  43. package/dist/{session-end-6DP6VTZV.js → session-end-5AI4U3KC.js} +3 -3
  44. package/dist/{session-start-QNAQDF5M.js → session-start-XHH6RN7T.js} +8 -8
  45. package/dist/{setup-llm-ER3B7AZ2.js → setup-llm-3LYRV4KB.js} +5 -5
  46. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +40 -8
  47. package/dist/src/agent/definitions/tasks/skill-generate.yaml +8 -3
  48. package/dist/src/cli.js +1 -1
  49. package/dist/src/daemon/main.js +1 -1
  50. package/dist/src/hooks/post-tool-use.js +1 -1
  51. package/dist/src/hooks/session-end.js +1 -1
  52. package/dist/src/hooks/session-start.js +1 -1
  53. package/dist/src/hooks/stop.js +1 -1
  54. package/dist/src/hooks/user-prompt-submit.js +1 -1
  55. package/dist/src/mcp/server.js +1 -1
  56. package/dist/{stats-VQ7XMOCU.js → stats-BDE5FTO6.js} +6 -6
  57. package/dist/{stop-VTO2KIRG.js → stop-ZIKA2LGJ.js} +3 -3
  58. package/dist/{stop-failure-C5T6LJQR.js → stop-failure-MF22OJ7R.js} +4 -4
  59. package/dist/{subagent-start-NZF42NKF.js → subagent-start-XNAZKYVD.js} +4 -4
  60. package/dist/{subagent-stop-UV5ECFVU.js → subagent-stop-QTJSJYIT.js} +4 -4
  61. package/dist/{task-completed-3SV6TL3V.js → task-completed-24BVLKOC.js} +4 -4
  62. package/dist/{team-XSJXLBZX.js → team-SJPDXELY.js} +2 -2
  63. package/dist/turns-3ZQAF6HF.js +16 -0
  64. package/dist/ui/assets/index-BmsHIwjl.css +1 -0
  65. package/dist/ui/assets/index-Cn6cQwJy.js +842 -0
  66. package/dist/ui/index.html +2 -2
  67. package/dist/{update-5GXOQIY5.js → update-LRPXOWMZ.js} +4 -4
  68. package/dist/{user-prompt-submit-VLQG77A6.js → user-prompt-submit-HPBZOZHM.js} +3 -3
  69. package/dist/{verify-WF7U3NQW.js → verify-JHIMXTY5.js} +2 -2
  70. package/dist/{version-RGX7TZ7V.js → version-ELM3BK4H.js} +2 -2
  71. package/dist/version-ELM3BK4H.js.map +1 -0
  72. package/package.json +1 -1
  73. package/dist/chunk-5BK6M6X5.js.map +0 -1
  74. package/dist/chunk-NCVR636M.js.map +0 -1
  75. package/dist/chunk-W3T3QDBN.js.map +0 -1
  76. package/dist/executor-TCAXFOIS.js.map +0 -1
  77. package/dist/main-25MKYYKO.js.map +0 -1
  78. package/dist/ui/assets/index-BeygBZGu.css +0 -1
  79. package/dist/ui/assets/index-CbGC0T_o.js +0 -822
  80. /package/dist/{agent-run-YBASQHC7.js.map → agent-run-VX5MAPRN.js.map} +0 -0
  81. /package/dist/{agent-tasks-WBQFDHWC.js.map → agent-tasks-FJOBYYC5.js.map} +0 -0
  82. /package/dist/{chunk-EUQQVKGQ.js.map → chunk-CCKPBAKJ.js.map} +0 -0
  83. /package/dist/{chunk-VQEXXS56.js.map → chunk-NYTEHLVI.js.map} +0 -0
  84. /package/dist/{chunk-LPISXFM4.js.map → chunk-PX5KIOKY.js.map} +0 -0
  85. /package/dist/{chunk-T77674VB.js.map → chunk-QB2UTRQE.js.map} +0 -0
  86. /package/dist/{chunk-TFGGH6UB.js.map → chunk-RUDOGKWF.js.map} +0 -0
  87. /package/dist/{chunk-2PWO3WPS.js.map → chunk-TVV6PZOC.js.map} +0 -0
  88. /package/dist/{chunk-JBFFAGJK.js.map → chunk-X34OFKYU.js.map} +0 -0
  89. /package/dist/{chunk-YYQWCTF6.js.map → chunk-XNHHADBK.js.map} +0 -0
  90. /package/dist/{cli-WCBTILMW.js.map → cli-Z7VO7LIL.js.map} +0 -0
  91. /package/dist/{client-MJUZJ5MC.js.map → client-CECN26WV.js.map} +0 -0
  92. /package/dist/{config-WBCOTJUE.js.map → config-H657SF6B.js.map} +0 -0
  93. /package/dist/{doctor-GDCLRJOF.js.map → doctor-TYMLSW5K.js.map} +0 -0
  94. /package/dist/{init-ZSDMXY4T.js.map → init-W6VRN5ZZ.js.map} +0 -0
  95. /package/dist/{init-wizard-6LDUVR7C.js.map → init-wizard-3RJFKAGM.js.map} +0 -0
  96. /package/dist/{loader-L2TCAYCT.js.map → loader-JQLO6K44.js.map} +0 -0
  97. /package/dist/{open-4YTUNIP3.js.map → open-6EC54JEU.js.map} +0 -0
  98. /package/dist/{post-compact-E5V4OZJB.js.map → post-compact-55ISYIPY.js.map} +0 -0
  99. /package/dist/{post-tool-use-Y6UWKCVD.js.map → post-tool-use-KNOFQNVX.js.map} +0 -0
  100. /package/dist/{post-tool-use-failure-AHFXMNHX.js.map → post-tool-use-failure-UIRHWELN.js.map} +0 -0
  101. /package/dist/{pre-compact-EI5EV3N7.js.map → pre-compact-T3CR4C3Q.js.map} +0 -0
  102. /package/dist/{remove-F63WBELE.js.map → remove-HIIXTVAK.js.map} +0 -0
  103. /package/dist/{resolution-events-BZYMUQ53.js.map → resolution-events-5EVUEWHS.js.map} +0 -0
  104. /package/dist/{restart-GULUNCMX.js.map → restart-5FJYFNIR.js.map} +0 -0
  105. /package/dist/{search-NLZMCEAG.js.map → search-C3CIHCMP.js.map} +0 -0
  106. /package/dist/{server-CXPWUO6H.js.map → server-XJTAWCHN.js.map} +0 -0
  107. /package/dist/{session-XXVEDIQZ.js.map → session-CUGCZWCY.js.map} +0 -0
  108. /package/dist/{session-end-6DP6VTZV.js.map → session-end-5AI4U3KC.js.map} +0 -0
  109. /package/dist/{session-start-QNAQDF5M.js.map → session-start-XHH6RN7T.js.map} +0 -0
  110. /package/dist/{setup-llm-ER3B7AZ2.js.map → setup-llm-3LYRV4KB.js.map} +0 -0
  111. /package/dist/{stats-VQ7XMOCU.js.map → stats-BDE5FTO6.js.map} +0 -0
  112. /package/dist/{stop-VTO2KIRG.js.map → stop-ZIKA2LGJ.js.map} +0 -0
  113. /package/dist/{stop-failure-C5T6LJQR.js.map → stop-failure-MF22OJ7R.js.map} +0 -0
  114. /package/dist/{subagent-start-NZF42NKF.js.map → subagent-start-XNAZKYVD.js.map} +0 -0
  115. /package/dist/{subagent-stop-UV5ECFVU.js.map → subagent-stop-QTJSJYIT.js.map} +0 -0
  116. /package/dist/{task-completed-3SV6TL3V.js.map → task-completed-24BVLKOC.js.map} +0 -0
  117. /package/dist/{team-XSJXLBZX.js.map → team-SJPDXELY.js.map} +0 -0
  118. /package/dist/{version-RGX7TZ7V.js.map → turns-3ZQAF6HF.js.map} +0 -0
  119. /package/dist/{update-5GXOQIY5.js.map → update-LRPXOWMZ.js.map} +0 -0
  120. /package/dist/{user-prompt-submit-VLQG77A6.js.map → user-prompt-submit-HPBZOZHM.js.map} +0 -0
  121. /package/dist/{verify-WF7U3NQW.js.map → verify-JHIMXTY5.js.map} +0 -0
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Myco</title>
8
- <script type="module" crossorigin src="/assets/index-CbGC0T_o.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BeygBZGu.css">
8
+ <script type="module" crossorigin src="/assets/index-Cn6cQwJy.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BmsHIwjl.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -2,13 +2,13 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  VAULT_GITIGNORE,
4
4
  registerSymbionts
5
- } from "./chunk-TFGGH6UB.js";
5
+ } from "./chunk-RUDOGKWF.js";
6
6
  import "./chunk-SAKJMNSR.js";
7
7
  import "./chunk-TNCBMGWB.js";
8
8
  import "./chunk-HHZ3RTEI.js";
9
9
  import "./chunk-MYX5NCRH.js";
10
- import "./chunk-VQEXXS56.js";
11
- import "./chunk-EUQQVKGQ.js";
10
+ import "./chunk-NYTEHLVI.js";
11
+ import "./chunk-CCKPBAKJ.js";
12
12
  import {
13
13
  loadManifests,
14
14
  resolvePackageRoot
@@ -73,4 +73,4 @@ async function run(args) {
73
73
  export {
74
74
  run
75
75
  };
76
- //# sourceMappingURL=update-5GXOQIY5.js.map
76
+ //# sourceMappingURL=update-LRPXOWMZ.js.map
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-OQVKLTQY.js";
9
9
  import {
10
10
  DaemonClient
11
- } from "./chunk-VQEXXS56.js";
12
- import "./chunk-EUQQVKGQ.js";
11
+ } from "./chunk-NYTEHLVI.js";
12
+ import "./chunk-CCKPBAKJ.js";
13
13
  import "./chunk-QFMBZ72S.js";
14
14
  import "./chunk-LPUQPDC2.js";
15
15
  import "./chunk-TRA3R4EC.js";
@@ -63,4 +63,4 @@ ${sessionLine}` : sessionLine;
63
63
  export {
64
64
  main
65
65
  };
66
- //# sourceMappingURL=user-prompt-submit-VLQG77A6.js.map
66
+ //# sourceMappingURL=user-prompt-submit-HPBZOZHM.js.map
@@ -8,7 +8,7 @@ import "./chunk-OKCSSDFC.js";
8
8
  import "./chunk-5YQ6VOFZ.js";
9
9
  import {
10
10
  loadConfig
11
- } from "./chunk-W3T3QDBN.js";
11
+ } from "./chunk-PSYLKCWQ.js";
12
12
  import "./chunk-TRA3R4EC.js";
13
13
  import "./chunk-S6I62FAH.js";
14
14
  import "./chunk-D7TYRPRM.js";
@@ -42,4 +42,4 @@ async function run(_args, vaultDir) {
42
42
  export {
43
43
  run
44
44
  };
45
- //# sourceMappingURL=verify-WF7U3NQW.js.map
45
+ //# sourceMappingURL=verify-JHIMXTY5.js.map
@@ -1,10 +1,10 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  getPluginVersion
4
- } from "./chunk-EUQQVKGQ.js";
4
+ } from "./chunk-CCKPBAKJ.js";
5
5
  import "./chunk-LPUQPDC2.js";
6
6
  import "./chunk-PZUWP5VK.js";
7
7
  export {
8
8
  getPluginVersion
9
9
  };
10
- //# sourceMappingURL=version-RGX7TZ7V.js.map
10
+ //# sourceMappingURL=version-ELM3BK4H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goondocks/myco",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Collective agent intelligence — Claude Code plugin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db/queries/batches.ts","../src/utils/error-message.ts","../src/db/queries/skill-candidates.ts","../src/db/queries/skill-records.ts","../src/db/queries/turns.ts","../src/db/queries/runs.ts","../src/db/queries/reports.ts","../src/db/queries/graph-edges.ts","../src/db/queries/lineage.ts","../src/db/queries/entities.ts","../src/db/queries/digest-extracts.ts","../src/db/queries/skill-lineage.ts"],"sourcesContent":["/**\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 */\nexport function getUnprocessedBatches(\n options: { after_id?: number; limit?: number } = {},\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 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 * 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 * 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 created_at: number;\n updated_at: number;\n}\n\n/** Fields that may be updated on a skill candidate. */\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 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 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 status?: 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 '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 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 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,\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.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 const setClauses: string[] = [];\n const params: unknown[] = [];\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 updated_at: 'updated_at',\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 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 total count in a single call.\n *\n * Runs listCandidates and countCandidates with the same filter options.\n * Saves callers from issuing two separate function calls.\n */\nexport function listCandidatesWithCount(\n options: ListCandidatesOptions = {},\n): { items: CandidateRow[]; total: number } {\n const items = listCandidates(options);\n const total = countCandidates(options);\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 * Agent turn CRUD query helpers.\n *\n * All functions obtain the SQLite instance internally via `getDatabase()`.\n * Queries use positional `?` placeholders throughout (better-sqlite3).\n */\n\nimport { getDatabase } from '@myco/db/client.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting a turn. */\nexport interface TurnInsert {\n run_id: string;\n agent_id: string;\n turn_number: number;\n tool_name: string;\n tool_input?: string | null;\n tool_output_summary?: string | null;\n started_at?: number | null;\n completed_at?: number | null;\n}\n\n/** Row shape returned from agent_turns queries (all columns). */\nexport interface TurnRow {\n id: number;\n run_id: string;\n agent_id: string;\n turn_number: number;\n tool_name: string;\n tool_input: string | null;\n tool_output_summary: string | null;\n started_at: number | null;\n completed_at: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst TURN_COLUMNS = [\n 'id',\n 'run_id',\n 'agent_id',\n 'turn_number',\n 'tool_name',\n 'tool_input',\n 'tool_output_summary',\n 'started_at',\n 'completed_at',\n] as const;\n\nconst SELECT_COLUMNS = TURN_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a SQLite result row into a typed TurnRow. */\nfunction toTurnRow(row: Record<string, unknown>): TurnRow {\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 turn_number: row.turn_number as number,\n tool_name: row.tool_name as string,\n tool_input: (row.tool_input as string) ?? null,\n tool_output_summary: (row.tool_output_summary as string) ?? null,\n started_at: (row.started_at as number) ?? null,\n completed_at: (row.completed_at as number) ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Insert a new agent turn.\n *\n * The `id` is auto-generated by the INTEGER PRIMARY KEY (AUTOINCREMENT).\n */\nexport function insertTurn(data: TurnInsert): TurnRow {\n const db = getDatabase();\n\n const info = db.prepare(\n `INSERT INTO agent_turns (\n run_id, agent_id, turn_number, tool_name,\n tool_input, tool_output_summary, started_at, completed_at\n ) VALUES (\n ?, ?, ?, ?,\n ?, ?, ?, ?\n )`,\n ).run(\n data.run_id,\n data.agent_id,\n data.turn_number,\n data.tool_name,\n data.tool_input ?? null,\n data.tool_output_summary ?? null,\n data.started_at ?? null,\n data.completed_at ?? null,\n );\n\n const turnId = Number(info.lastInsertRowid);\n\n return toTurnRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM agent_turns WHERE id = ?`).get(turnId) as Record<string, unknown>,\n );\n}\n\n/**\n * List all turns for a specific run, ordered by turn_number ASC.\n */\nexport function listTurns(runId: string): TurnRow[] {\n const db = getDatabase();\n\n const rows = db.prepare(\n `SELECT ${SELECT_COLUMNS}\n FROM agent_turns\n WHERE run_id = ?\n ORDER BY turn_number ASC`,\n ).all(runId) as Record<string, unknown>[];\n\n return rows.map(toTurnRow);\n}\n\n/**\n * List all agent turns for a run, ordered by turn_number ASC.\n *\n * Alias for `listTurns` with an explicit \"by run\" naming convention used\n * by the dashboard API layer.\n */\nexport function listTurnsByRun(runId: string): TurnRow[] {\n return listTurns(runId);\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 * 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","/**\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 * 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 * 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 * 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 * 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"],"mappings":"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","names":["DEFAULT_STATUS","SELECT_COLUMNS","DEFAULT_STATUS","SELECT_COLUMNS","buildWhere","SELECT_COLUMNS","DEFAULT_LIST_LIMIT","DEFAULT_STATUS","STATUS_COMPLETED","SELECT_COLUMNS","row","SELECT_COLUMNS","SELECT_COLUMNS","DEFAULT_LIST_LIMIT","SELECT_COLUMNS","SELECT_COLUMNS","SELECT_COLUMNS"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db/schema.ts"],"sourcesContent":["/**\n * SQLite database schema -- all capture, intelligence, and agent state tables.\n *\n * Uses `CREATE TABLE IF NOT EXISTS` and `CREATE INDEX IF NOT EXISTS` throughout\n * for idempotency. Running `createSchema()` multiple times is always safe.\n *\n * Timestamp convention: all timestamps are INTEGER (Unix epoch seconds).\n * Content hashing: all `content_hash` columns are TEXT with UNIQUE constraint.\n * Embedding dimensions: 1024 (bge-m3 default) -- used by external sqlite-vec store.\n *\n * Vector columns live in a separate sqlite-vec virtual table, not inline.\n * Tables that participate in vector search carry an `embedded INTEGER DEFAULT 0`\n * flag so the embedder knows which rows still need vectors.\n */\n\nimport type { Database } from 'better-sqlite3';\nimport { epochSeconds, DEFAULT_MACHINE_ID } from '@myco/constants.js';\n\n/** Current schema version -- fresh start for the SQLite era. */\nexport const SCHEMA_VERSION = 7;\n\n// Re-export for backwards compat (other modules import from schema.ts)\nexport { DEFAULT_MACHINE_ID };\n\n/** Embedding vector dimensions (bge-m3 default). */\nexport const EMBEDDING_DIMENSIONS = 1024;\n\n// ---------------------------------------------------------------------------\n// DDL statements\n// ---------------------------------------------------------------------------\n\nconst SCHEMA_VERSION_TABLE = `\n CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at INTEGER NOT NULL\n )`;\n\n// -- Capture Layer ----------------------------------------------------------\n\nconst SESSIONS_TABLE = `\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent TEXT NOT NULL,\n \"user\" TEXT,\n project_root TEXT,\n branch TEXT,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n status TEXT DEFAULT 'active',\n prompt_count INTEGER DEFAULT 0,\n tool_count INTEGER DEFAULT 0,\n title TEXT,\n summary TEXT,\n transcript_path TEXT,\n parent_session_id TEXT,\n parent_session_reason TEXT,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst PROMPT_BATCHES_TABLE = `\n CREATE TABLE IF NOT EXISTS prompt_batches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n prompt_number INTEGER,\n user_prompt TEXT,\n response_summary TEXT,\n classification TEXT,\n started_at INTEGER,\n ended_at INTEGER,\n status TEXT DEFAULT 'active',\n activity_count INTEGER DEFAULT 0,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ACTIVITIES_TABLE = `\n CREATE TABLE IF NOT EXISTS activities (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n tool_name TEXT NOT NULL,\n tool_input TEXT,\n tool_output_summary TEXT,\n file_path TEXT,\n files_affected TEXT,\n duration_ms INTEGER,\n success INTEGER DEFAULT 1,\n error_message TEXT,\n timestamp INTEGER NOT NULL,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL\n )`;\n\nconst PLANS_TABLE = `\n CREATE TABLE IF NOT EXISTS plans (\n id TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n author TEXT,\n title TEXT,\n content TEXT,\n source_path TEXT,\n tags TEXT,\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n content_hash TEXT,\n processed INTEGER DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ARTIFACTS_TABLE = `\n CREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n artifact_type TEXT,\n source_path TEXT NOT NULL,\n title TEXT NOT NULL,\n content TEXT,\n last_captured_by TEXT,\n tags TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst TEAM_MEMBERS_TABLE = `\n CREATE TABLE IF NOT EXISTS team_members (\n id TEXT PRIMARY KEY,\n \"user\" TEXT NOT NULL,\n role TEXT,\n joined TEXT,\n tags TEXT,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ATTACHMENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS attachments (\n id TEXT PRIMARY KEY,\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n file_path TEXT NOT NULL,\n media_type TEXT,\n description TEXT,\n data BLOB,\n content_hash TEXT,\n created_at INTEGER NOT NULL\n )`;\n\n// -- Intelligence Layer -----------------------------------------------------\n\nconst AGENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS agents (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n provider TEXT,\n model TEXT,\n system_prompt_hash TEXT,\n config TEXT,\n source TEXT NOT NULL DEFAULT 'built-in',\n system_prompt TEXT,\n max_turns INTEGER,\n timeout_seconds INTEGER,\n tool_access TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n updated_at INTEGER\n )`;\n\nconst SPORES_TABLE = `\n CREATE TABLE IF NOT EXISTS spores (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n observation_type TEXT NOT NULL,\n status TEXT DEFAULT 'active',\n content TEXT NOT NULL,\n context TEXT,\n importance INTEGER DEFAULT 5,\n file_path TEXT,\n tags TEXT,\n content_hash TEXT UNIQUE,\n properties TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ENTITIES_TABLE = `\n CREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n type TEXT NOT NULL,\n name TEXT NOT NULL,\n properties TEXT,\n first_seen INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n status TEXT DEFAULT 'active',\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (agent_id, type, name)\n )`;\n\nconst GRAPH_EDGES_TABLE = `\n CREATE TABLE IF NOT EXISTS graph_edges (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n source_id TEXT NOT NULL,\n source_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n target_type TEXT NOT NULL,\n type TEXT NOT NULL,\n session_id TEXT,\n confidence REAL DEFAULT 1.0,\n properties TEXT,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ENTITY_MENTIONS_TABLE = `\n CREATE TABLE IF NOT EXISTS entity_mentions (\n entity_id TEXT NOT NULL REFERENCES entities(id),\n note_id TEXT NOT NULL,\n note_type TEXT NOT NULL,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (entity_id, note_id, note_type, agent_id)\n )`;\n\nconst RESOLUTION_EVENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS resolution_events (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n spore_id TEXT NOT NULL REFERENCES spores(id),\n action TEXT NOT NULL,\n new_spore_id TEXT,\n reason TEXT,\n session_id TEXT,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst DIGEST_EXTRACTS_TABLE = `\n CREATE TABLE IF NOT EXISTS digest_extracts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n tier INTEGER NOT NULL,\n content TEXT NOT NULL,\n substrate_hash TEXT,\n generated_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (agent_id, tier)\n )`;\n\n// -- Agent State Layer ------------------------------------------------------\n\nconst AGENT_RUNS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_runs (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n task TEXT,\n instruction TEXT,\n status TEXT DEFAULT 'pending',\n started_at INTEGER,\n completed_at INTEGER,\n tokens_used INTEGER,\n cost_usd REAL,\n actions_taken TEXT,\n error TEXT\n )`;\n\nconst AGENT_REPORTS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_reports (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL REFERENCES agent_runs(id),\n agent_id TEXT NOT NULL REFERENCES agents(id),\n action TEXT NOT NULL,\n summary TEXT NOT NULL,\n details TEXT,\n created_at INTEGER NOT NULL\n )`;\n\nconst AGENT_TURNS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_turns (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL REFERENCES agent_runs(id),\n agent_id TEXT NOT NULL REFERENCES agents(id),\n turn_number INTEGER NOT NULL,\n tool_name TEXT NOT NULL,\n tool_input TEXT,\n tool_output_summary TEXT,\n started_at INTEGER,\n completed_at INTEGER\n )`;\n\nconst AGENT_TASKS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_tasks (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n source TEXT NOT NULL DEFAULT 'built-in',\n display_name TEXT,\n description TEXT,\n prompt TEXT NOT NULL,\n is_default INTEGER DEFAULT 0,\n tool_overrides TEXT,\n model TEXT,\n config TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER\n )`;\n\nconst AGENT_STATE_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_state (\n agent_id TEXT NOT NULL REFERENCES agents(id),\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (agent_id, key)\n )`;\n\n// -- Sync Layer -------------------------------------------------------------\n\nconst TEAM_OUTBOX_TABLE = `\n CREATE TABLE IF NOT EXISTS team_outbox (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n table_name TEXT NOT NULL,\n row_id TEXT NOT NULL,\n operation TEXT NOT NULL DEFAULT 'upsert',\n payload TEXT NOT NULL,\n machine_id TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n sent_at INTEGER\n )`;\n\n// -- Logging Layer ----------------------------------------------------------\n\nconst LOG_ENTRIES_TABLE = `\n CREATE TABLE IF NOT EXISTS log_entries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT NOT NULL,\n level TEXT NOT NULL,\n component TEXT NOT NULL,\n kind TEXT NOT NULL,\n message TEXT NOT NULL,\n data TEXT,\n session_id TEXT\n )`;\n\n// -- Skills Layer -----------------------------------------------------------\n\nconst SKILL_CANDIDATES_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_candidates (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n topic TEXT NOT NULL,\n rationale TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.0,\n status TEXT NOT NULL DEFAULT 'identified',\n source_ids TEXT NOT NULL DEFAULT '[]',\n skill_id TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n synced_at INTEGER\n )`;\n\nconst SKILL_RECORDS_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_records (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n description TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n generation INTEGER NOT NULL DEFAULT 1,\n candidate_id TEXT REFERENCES skill_candidates(id),\n source_ids TEXT NOT NULL DEFAULT '[]',\n path TEXT NOT NULL,\n usage_count INTEGER NOT NULL DEFAULT 0,\n last_used_at INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n properties TEXT NOT NULL DEFAULT '{}',\n synced_at INTEGER\n )`;\n\nconst SKILL_LINEAGE_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_lineage (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL REFERENCES skill_records(id),\n generation INTEGER NOT NULL,\n action TEXT NOT NULL,\n rationale TEXT NOT NULL,\n source_ids_added TEXT NOT NULL DEFAULT '[]',\n content_snapshot TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`;\n\nconst SKILL_USAGE_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_usage (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL REFERENCES skill_records(id),\n session_id TEXT NOT NULL REFERENCES sessions(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n detected_at INTEGER NOT NULL\n )`;\n\n// -- FTS5 Virtual Tables ----------------------------------------------------\n\nconst FTS_TABLES = [\n `CREATE VIRTUAL TABLE IF NOT EXISTS prompt_batches_fts\n USING fts5(user_prompt, response_summary, content='prompt_batches', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS activities_fts\n USING fts5(tool_name, tool_input, file_path, content='activities', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS log_entries_fts\n USING fts5(message, content='log_entries', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS spores_fts\n USING fts5(content, content='spores', content_rowid='rowid')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts\n USING fts5(title, summary, content='sessions', content_rowid='rowid')`,\n\n // FTS5 sync triggers for log_entries (external-content table)\n `CREATE TRIGGER IF NOT EXISTS log_entries_ai AFTER INSERT ON log_entries BEGIN\n INSERT INTO log_entries_fts(rowid, message) VALUES (new.id, new.message);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS log_entries_ad AFTER DELETE ON log_entries BEGIN\n INSERT INTO log_entries_fts(log_entries_fts, rowid, message) VALUES('delete', old.id, old.message);\n END`,\n\n // FTS5 sync triggers for prompt_batches\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ai AFTER INSERT ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_au AFTER UPDATE OF user_prompt, response_summary ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ad AFTER DELETE ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n END`,\n\n // FTS5 sync triggers for spores\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ai AFTER INSERT ON spores BEGIN\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS spores_fts_au AFTER UPDATE OF content ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ad AFTER DELETE ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n END`,\n\n // FTS5 sync triggers for sessions\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ai AFTER INSERT ON sessions BEGIN\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_au AFTER UPDATE OF title, summary ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ad AFTER DELETE ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n END`,\n];\n\n// -- Indexes ----------------------------------------------------------------\n\nconst SECONDARY_INDEXES = [\n // Sessions\n 'CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions (status)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_processed ON sessions (processed)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_started_at ON sessions (started_at)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions (agent)',\n\n // Prompt batches\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_session_id ON prompt_batches (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_processed ON prompt_batches (processed)',\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_status ON prompt_batches (status)',\n\n // Activities\n 'CREATE INDEX IF NOT EXISTS idx_activities_session_id ON activities (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_prompt_batch_id ON activities (prompt_batch_id)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_tool_name ON activities (tool_name)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_timestamp ON activities (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_processed ON activities (processed)',\n\n // Spores\n 'CREATE INDEX IF NOT EXISTS idx_spores_agent_id ON spores (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_session_id ON spores (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_status ON spores (status)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_observation_type ON spores (observation_type)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_created_at ON spores (created_at)',\n\n // Entities\n 'CREATE INDEX IF NOT EXISTS idx_entities_agent_id ON entities (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_entities_type ON entities (type)',\n\n // Graph edges\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges (source_id, source_type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges (target_id, target_type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_type ON graph_edges (type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_agent ON graph_edges (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_source_type ON graph_edges (source_id, type)',\n\n // Entity mentions\n 'CREATE INDEX IF NOT EXISTS idx_entity_mentions_entity_id ON entity_mentions (entity_id)',\n 'CREATE INDEX IF NOT EXISTS idx_entity_mentions_agent_id ON entity_mentions (agent_id)',\n\n // Resolution events\n 'CREATE INDEX IF NOT EXISTS idx_resolution_events_spore_id ON resolution_events (spore_id)',\n 'CREATE INDEX IF NOT EXISTS idx_resolution_events_agent_id ON resolution_events (agent_id)',\n\n // Digest extracts\n 'CREATE INDEX IF NOT EXISTS idx_digest_extracts_agent_id ON digest_extracts (agent_id)',\n\n // Agent runs\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent_id ON agent_runs (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_status ON agent_runs (status)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent_status ON agent_runs (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_task_completed ON agent_runs (task, status, completed_at)',\n\n // Agent reports\n 'CREATE INDEX IF NOT EXISTS idx_agent_reports_run_id ON agent_reports (run_id)',\n\n // Agent turns\n 'CREATE INDEX IF NOT EXISTS idx_agent_turns_run_id ON agent_turns (run_id)',\n\n // Agent tasks\n 'CREATE INDEX IF NOT EXISTS idx_agent_tasks_agent_id ON agent_tasks (agent_id)',\n\n // Plans\n 'CREATE INDEX IF NOT EXISTS idx_plans_session_id ON plans (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_source_path ON plans (source_path)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans (content_hash)',\n // Attachments\n 'CREATE INDEX IF NOT EXISTS idx_attachments_file_path ON attachments (file_path)',\n\n // Team outbox\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_pending ON team_outbox (sent_at, created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_table_name ON team_outbox (table_name)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_row_lookup ON team_outbox (table_name, row_id)',\n\n // Machine ID (synced tables)\n 'CREATE INDEX IF NOT EXISTS idx_sessions_machine_id ON sessions (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_machine_id ON spores (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_machine_id ON graph_edges (machine_id)',\n\n // Skill candidates\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_id ON skill_candidates (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_status ON skill_candidates (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_machine_id ON skill_candidates (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_status ON skill_candidates (agent_id, status)',\n\n // Skill records\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_id ON skill_records (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_status ON skill_records (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_name ON skill_records (name)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_machine_id ON skill_records (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_status ON skill_records (agent_id, status)',\n\n // Skill lineage\n 'CREATE INDEX IF NOT EXISTS idx_skill_lineage_skill_id ON skill_lineage (skill_id)',\n\n // Skill usage\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_id ON skill_usage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_session_id ON skill_usage (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_session ON skill_usage (skill_id, session_id)',\n\n // Log entries\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_timestamp ON log_entries (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_level ON log_entries (level)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_component ON log_entries (component)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_kind ON log_entries (kind)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_session_id ON log_entries (session_id)',\n];\n\n// -- Ordered table creation -------------------------------------------------\n\nconst TABLE_DDLS = [\n SCHEMA_VERSION_TABLE,\n // Capture layer (order matters for FK references)\n SESSIONS_TABLE,\n PROMPT_BATCHES_TABLE,\n ACTIVITIES_TABLE,\n PLANS_TABLE,\n ARTIFACTS_TABLE,\n TEAM_MEMBERS_TABLE,\n ATTACHMENTS_TABLE,\n // Intelligence layer\n AGENTS_TABLE,\n SPORES_TABLE,\n ENTITIES_TABLE,\n GRAPH_EDGES_TABLE,\n ENTITY_MENTIONS_TABLE,\n RESOLUTION_EVENTS_TABLE,\n DIGEST_EXTRACTS_TABLE,\n // Agent state layer\n AGENT_RUNS_TABLE,\n AGENT_REPORTS_TABLE,\n AGENT_TURNS_TABLE,\n AGENT_TASKS_TABLE,\n AGENT_STATE_TABLE,\n // Skills layer\n SKILL_CANDIDATES_TABLE,\n SKILL_RECORDS_TABLE,\n SKILL_LINEAGE_TABLE,\n SKILL_USAGE_TABLE,\n // Sync layer\n TEAM_OUTBOX_TABLE,\n // Logging layer\n LOG_ENTRIES_TABLE,\n];\n\n// ---------------------------------------------------------------------------\n// Migrations\n// ---------------------------------------------------------------------------\n\n/**\n * Migrate a version-1 database to version-2.\n *\n * Version 2 adds:\n * - plans.session_id, plans.prompt_batch_id, plans.content_hash\n * - attachments.data, attachments.content_hash\n * - indexes: idx_plans_session_id, idx_plans_source_path, idx_plans_content_hash\n *\n * Each ALTER TABLE is wrapped in try/catch so re-running is safe -- SQLite\n * throws \"duplicate column name\" if the column already exists, which we ignore.\n */\nfunction migrateV1ToV2(db: Database): void {\n db.exec('BEGIN');\n try {\n const alterStatements = [\n 'ALTER TABLE plans ADD COLUMN session_id TEXT REFERENCES sessions(id)',\n 'ALTER TABLE plans ADD COLUMN prompt_batch_id INTEGER REFERENCES prompt_batches(id)',\n 'ALTER TABLE plans ADD COLUMN content_hash TEXT',\n 'ALTER TABLE attachments ADD COLUMN data BLOB',\n 'ALTER TABLE attachments ADD COLUMN content_hash TEXT',\n ];\n\n for (const stmt of alterStatements) {\n try {\n db.exec(stmt);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n }\n\n // Indexes use IF NOT EXISTS so they are idempotent\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_plans_session_id ON plans (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_source_path ON plans (source_path)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans (content_hash)',\n 'CREATE INDEX IF NOT EXISTS idx_attachments_file_path ON attachments (file_path)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(2, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-2 database to version-3.\n *\n * Version 3 adds:\n * - log_entries table\n * - log_entries_fts virtual table (FTS5)\n * - indexes: idx_log_entries_timestamp, _level, _component, _kind, _session_id\n *\n * Uses `CREATE ... IF NOT EXISTS` throughout for idempotency.\n */\nfunction migrateV2ToV3(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec(LOG_ENTRIES_TABLE);\n\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS log_entries_fts\n USING fts5(message, content='log_entries', content_rowid='id')`\n );\n\n // FTS5 sync triggers for log_entries\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS log_entries_ai AFTER INSERT ON log_entries BEGIN\n INSERT INTO log_entries_fts(rowid, message) VALUES (new.id, new.message);\n END`\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS log_entries_ad AFTER DELETE ON log_entries BEGIN\n INSERT INTO log_entries_fts(log_entries_fts, rowid, message) VALUES('delete', old.id, old.message);\n END`\n );\n\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_timestamp ON log_entries (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_level ON log_entries (level)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_component ON log_entries (component)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_kind ON log_entries (kind)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_session_id ON log_entries (session_id)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(3, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-3 database to version-4.\n *\n * Version 4 adds multi-machine support:\n * - machine_id TEXT NOT NULL DEFAULT 'local' on all synced tables\n * - synced_at INTEGER on all synced tables\n * - team_outbox table + indexes\n * - machine_id indexes on high-traffic tables\n *\n * Backfills existing rows with the provided machineId.\n */\nfunction migrateV3ToV4(db: Database, machineId: string): void {\n db.exec('BEGIN');\n try {\n // Tables that need machine_id + synced_at columns\n const syncedTables = [\n 'sessions',\n 'prompt_batches',\n 'spores',\n 'entities',\n 'graph_edges',\n 'entity_mentions',\n 'resolution_events',\n 'plans',\n 'artifacts',\n 'digest_extracts',\n 'team_members',\n ];\n\n for (const table of syncedTables) {\n try {\n db.exec(`ALTER TABLE ${table} ADD COLUMN machine_id TEXT NOT NULL DEFAULT 'local'`);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n try {\n db.exec(`ALTER TABLE ${table} ADD COLUMN synced_at INTEGER`);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n }\n\n // Backfill machine_id on existing rows\n for (const table of syncedTables) {\n db.prepare(`UPDATE ${table} SET machine_id = ? WHERE machine_id = 'local'`).run(machineId);\n }\n\n // Create team_outbox table\n db.exec(TEAM_OUTBOX_TABLE);\n\n // Create new indexes (IF NOT EXISTS for idempotency)\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_pending ON team_outbox (sent_at, created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_table_name ON team_outbox (table_name)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_row_lookup ON team_outbox (table_name, row_id)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_machine_id ON sessions (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_machine_id ON spores (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_machine_id ON graph_edges (machine_id)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(4, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-5 database to version-6.\n *\n * Version 6 expands FTS5 coverage:\n * - prompt_batches_fts gains response_summary column (drop + recreate)\n * - spores_fts new virtual table (content column, hidden rowid)\n * - sessions_fts new virtual table (title + summary, hidden rowid)\n * - sync triggers for all three tables (insert / update / delete)\n * - backfills FTS from existing data\n *\n * Uses `IF NOT EXISTS` throughout for idempotency where possible.\n * The prompt_batches_fts table must be dropped first since its column\n * definition changed.\n */\nfunction migrateV5ToV6(db: Database): void {\n db.exec('BEGIN');\n try {\n // Drop old prompt_batches_fts (column definition changed)\n db.exec('DROP TABLE IF EXISTS prompt_batches_fts');\n\n // Recreate with response_summary added\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS prompt_batches_fts\n USING fts5(user_prompt, response_summary, content='prompt_batches', content_rowid='id')`,\n );\n\n // New FTS tables\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS spores_fts\n USING fts5(content, content='spores', content_rowid='rowid')`,\n );\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts\n USING fts5(title, summary, content='sessions', content_rowid='rowid')`,\n );\n\n // Triggers for prompt_batches\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ai AFTER INSERT ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_au AFTER UPDATE OF user_prompt, response_summary ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ad AFTER DELETE ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n END`,\n );\n\n // Triggers for spores\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ai AFTER INSERT ON spores BEGIN\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_au AFTER UPDATE OF content ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ad AFTER DELETE ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n END`,\n );\n\n // Triggers for sessions\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ai AFTER INSERT ON sessions BEGIN\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_au AFTER UPDATE OF title, summary ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ad AFTER DELETE ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n END`,\n );\n\n // Backfill FTS from existing data\n db.exec(\n `INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary)\n SELECT rowid, user_prompt, response_summary FROM prompt_batches`,\n );\n db.exec(\n `INSERT INTO spores_fts(rowid, content)\n SELECT rowid, content FROM spores`,\n );\n db.exec(\n `INSERT INTO sessions_fts(rowid, title, summary)\n SELECT rowid, title, summary FROM sessions`,\n );\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(6, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-4 database to version-5.\n *\n * Version 5 adds the Skills layer:\n * - skill_candidates table\n * - skill_records table\n * - skill_lineage table\n * - skill_usage table\n * - indexes for all new tables\n *\n * Uses `CREATE TABLE IF NOT EXISTS` throughout for idempotency.\n */\nfunction migrateV4ToV5(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec(SKILL_CANDIDATES_TABLE);\n db.exec(SKILL_RECORDS_TABLE);\n db.exec(SKILL_LINEAGE_TABLE);\n db.exec(SKILL_USAGE_TABLE);\n\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_id ON skill_candidates (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_status ON skill_candidates (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_machine_id ON skill_candidates (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_status ON skill_candidates (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_id ON skill_records (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_status ON skill_records (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_name ON skill_records (name)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_machine_id ON skill_records (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_status ON skill_records (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_lineage_skill_id ON skill_lineage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_id ON skill_usage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_session_id ON skill_usage (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_session ON skill_usage (skill_id, session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_task_completed ON agent_runs (task, status, completed_at)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(5, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create all database tables, indexes, and record the schema version.\n *\n * Fully idempotent -- safe to call on every startup. Uses `IF NOT EXISTS`\n * for all DDL and `ON CONFLICT DO NOTHING` for the version row.\n */\n\n/**\n * Migrate v6 → v7: fix stale 'local' machine_id on ALL synced tables.\n *\n * The agent vault tools historically used DEFAULT_MACHINE_ID ('local')\n * instead of the resolved machine identity. This one-time data migration\n * fixes all affected records and re-queues them for team sync.\n */\nfunction migrateV6ToV7(db: Database, machineId: string): void {\n if (machineId === 'local' || machineId === DEFAULT_MACHINE_ID) return; // Nothing to fix\n\n db.exec('BEGIN');\n try {\n // entity_mentions excluded — no `id` column (composite key: entity_id, note_id, note_type)\n const tables = [\n 'sessions', 'prompt_batches', 'spores', 'entities', 'graph_edges',\n 'resolution_events', 'plans', 'artifacts',\n 'digest_extracts', 'skill_candidates', 'skill_records',\n ];\n\n for (const table of tables) {\n try {\n // Find rows that need fixing BEFORE updating\n const staleRows = db.prepare(\n `SELECT id FROM ${table} WHERE machine_id = 'local'`,\n ).all() as Array<{ id: string }>;\n\n if (staleRows.length === 0) continue;\n\n // Fix machine_id and clear synced_at\n db.prepare(\n `UPDATE ${table} SET machine_id = ?, synced_at = NULL WHERE machine_id = 'local'`,\n ).run(machineId);\n\n // Clear stale outbox entries for affected rows only\n for (const row of staleRows) {\n db.prepare(\n `DELETE FROM team_outbox WHERE table_name = ? AND row_id = ?`,\n ).run(table, String(row.id));\n }\n\n // Re-enqueue only the fixed rows with full payload\n const enqueueStmt = db.prepare(\n `INSERT INTO team_outbox (table_name, row_id, operation, payload, machine_id, created_at)\n VALUES (?, ?, 'upsert', ?, ?, ?)`,\n );\n const now = epochSeconds();\n for (const stale of staleRows) {\n const fresh = db.prepare(`SELECT * FROM ${table} WHERE id = ?`).get(stale.id) as Record<string, unknown>;\n if (fresh) {\n enqueueStmt.run(table, String(stale.id), JSON.stringify(fresh), machineId, now);\n }\n }\n } catch (tableErr) {\n // Skip if table doesn't exist; re-throw for other errors (I/O, constraint)\n const msg = tableErr instanceof Error ? tableErr.message : String(tableErr);\n if (!msg.includes('no such table')) throw tableErr;\n }\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(7, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * @param db — better-sqlite3 Database instance.\n * @param machineId — machine identifier for backfilling existing rows during\n * v3→v4 and v6→v7 migrations. Defaults to `'local'` (tests, init).\n */\nexport function createSchema(db: Database, machineId: string = DEFAULT_MACHINE_ID): void {\n // Fast-path: skip if already at current version\n try {\n const row = db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined;\n if (row?.version === SCHEMA_VERSION) return;\n // Migration path: version 1 → 2 (then fall through to check for 2 → 3)\n if (row?.version === 1) {\n migrateV1ToV2(db);\n }\n // Migration path: version 2 → 3\n const afterV1Migration = (db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined)?.version ?? 0;\n if (afterV1Migration < 3) {\n migrateV2ToV3(db);\n }\n // Migration path: version 3 → 4\n const afterV2Migration = (db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined)?.version ?? 0;\n if (afterV2Migration < 4) {\n migrateV3ToV4(db, machineId);\n }\n // Migration path: version 4 → 5\n const afterV3Migration = (db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined)?.version ?? 0;\n if (afterV3Migration < 5) {\n migrateV4ToV5(db);\n }\n // Migration path: version 5 → 6\n const afterV4Migration = (db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined)?.version ?? 0;\n if (afterV4Migration < 6) {\n migrateV5ToV6(db);\n }\n // Migration path: version 6 → 7\n const afterV5Migration = (db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined)?.version ?? 0;\n if (afterV5Migration < 7) {\n migrateV6ToV7(db, machineId);\n }\n return;\n } catch {\n // Table doesn't exist yet -- first run\n }\n\n for (const ddl of TABLE_DDLS) {\n db.exec(ddl);\n }\n\n for (const ddl of FTS_TABLES) {\n db.exec(ddl);\n }\n\n for (const idx of SECONDARY_INDEXES) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(SCHEMA_VERSION, epochSeconds());\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/loader.ts","../src/config/schema.ts","../src/config/migrations.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { MycoConfigSchema, type MycoConfig, type BackupConfig, type TeamConfig } from './schema.js';\nimport { runMigrations, CURRENT_MIGRATION_VERSION } from './migrations.js';\n\nexport const CONFIG_FILENAME = 'myco.yaml';\n\nexport function loadConfig(vaultDir: string): MycoConfig {\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n throw new Error(`myco.yaml not found in ${vaultDir}`);\n }\n\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = YAML.parse(raw) as Record<string, unknown>;\n\n // Detect v1 config and guide migration\n if (parsed.version === 1 || (parsed.intelligence as Record<string, unknown>)?.backend) {\n throw new Error(\n 'Myco config uses v1 format. Run /myco:setup-llm to reconfigure for v2.',\n );\n }\n\n // --- v2 → v3 migration ---\n let v2Migrated = false;\n if (parsed.version === 2) {\n // Extract intelligence.embedding to top-level embedding\n const intel = parsed.intelligence as Record<string, unknown> | undefined;\n const embeddingConfig = intel?.embedding as Record<string, unknown> | undefined;\n if (embeddingConfig && !parsed.embedding) {\n // Map v2 'lm-studio' to v3 'openai-compatible' for embedding provider\n if (embeddingConfig.provider === 'lm-studio') {\n embeddingConfig.provider = 'openai-compatible';\n }\n parsed.embedding = embeddingConfig;\n }\n\n // Keep daemon.port and daemon.log_level, drop grace_period and max_log_size\n const daemon = parsed.daemon as Record<string, unknown> | undefined;\n if (daemon) {\n const { port, log_level } = daemon;\n parsed.daemon = { port: port ?? null, log_level: log_level ?? 'info' };\n }\n\n // Keep capture basics, drop token-related fields; migrate artifact_watch → plan_dirs\n const capture = parsed.capture as Record<string, unknown> | undefined;\n if (capture) {\n const { transcript_paths, artifact_watch, plan_dirs, artifact_extensions, buffer_max_events } = capture;\n parsed.capture = {\n transcript_paths,\n plan_dirs: plan_dirs ?? artifact_watch,\n artifact_extensions,\n buffer_max_events,\n };\n }\n\n // Drop removed top-level sections\n delete parsed.intelligence;\n delete parsed.context;\n delete parsed.team;\n delete parsed.digest;\n delete parsed.pipeline;\n\n // Set version to 3\n parsed.version = 3;\n v2Migrated = true;\n\n process.stderr.write('[myco migration] Migrated config from v2 to v3\\n');\n }\n\n // Run numbered migrations (for v3+ forward migrations)\n const migrationsRan = runMigrations(parsed, vaultDir, (msg) => {\n process.stderr.write(`[myco migration] ${msg}\\n`);\n });\n\n // Parse with Zod to fill in defaults for new config sections\n const config = MycoConfigSchema.parse(parsed);\n\n // Write back if v2→v3 migration ran, numbered migrations ran, or new defaults were added\n const needsWrite = v2Migrated\n || migrationsRan\n || (parsed.config_version as number ?? 0) < CURRENT_MIGRATION_VERSION\n || parsed.version !== config.version;\n\n if (needsWrite) {\n const fullConfig = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;\n fs.writeFileSync(configPath, YAML.stringify(fullConfig), 'utf-8');\n }\n\n return config;\n}\n\nexport function saveConfig(vaultDir: string, config: MycoConfig): void {\n // Validate before writing — OAK lesson: validate on write, not just read\n const validated = MycoConfigSchema.parse(config);\n\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n fs.mkdirSync(vaultDir, { recursive: true });\n fs.writeFileSync(configPath, YAML.stringify(validated), 'utf-8');\n}\n\nexport function updateConfig(\n vaultDir: string,\n fn: (config: MycoConfig) => MycoConfig,\n): MycoConfig {\n const current = loadConfig(vaultDir);\n const updated = fn(current);\n saveConfig(vaultDir, updated);\n return updated;\n}\n\nexport function updateBackupConfig(\n vaultDir: string,\n backup: Partial<BackupConfig>,\n): MycoConfig {\n return updateConfig(vaultDir, (config) => ({\n ...config,\n backup: { ...config.backup, ...backup },\n }));\n}\n\nexport function updateTeamConfig(\n vaultDir: string,\n team: Partial<TeamConfig>,\n): MycoConfig {\n return updateConfig(vaultDir, (config) => ({\n ...config,\n team: { ...config.team, ...team },\n }));\n}\n","import { z } from 'zod';\nimport { SCHEDULABLE_POWER_STATES } from '@myco/constants.js';\n\nconst EmbeddingProviderSchema = z.object({\n provider: z.enum(['ollama', 'openai-compatible', 'openrouter', 'openai']).default('ollama'),\n model: z.string().default('bge-m3'),\n base_url: z.string().url().optional(),\n});\n\nconst DaemonSchema = z.object({\n port: z.number().int().min(1024).max(65535).nullable().default(null),\n log_level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n log_retention_days: z.number().int().min(1).max(365).default(30),\n});\n\nconst CaptureSchema = z.object({\n transcript_paths: z.array(z.string()).default([]),\n plan_dirs: z.array(z.string()).default([]),\n artifact_extensions: z.array(z.string()).default(['.md']),\n buffer_max_events: z.number().int().positive().default(500),\n});\n\n/** Provider config shape used in both task-level and phase-level overrides. */\nconst ProviderOverrideSchema = z.object({\n type: z.enum(['cloud', 'ollama', 'lmstudio']),\n base_url: z.string().optional(),\n model: z.string().optional(),\n /** Context window size for local models (Ollama num_ctx, LM Studio context_length). */\n context_length: z.number().int().positive().optional(),\n});\n\n/** Per-phase overrides within a task — keyed by phase name. */\nconst PhaseOverrideSchema = z.object({\n provider: ProviderOverrideSchema.optional(),\n model: z.string().optional(),\n maxTurns: z.number().int().positive().optional(),\n});\n\n/** Per-task schedule override — partial, merges with YAML defaults. */\nconst ScheduleOverrideSchema = z.object({\n enabled: z.boolean().optional(),\n intervalSeconds: z.number().int().positive().optional(),\n runIn: z.array(z.enum([...SCHEDULABLE_POWER_STATES])).optional(),\n preCondition: z.enum(['has-unprocessed-batches', 'has-active-skills', 'has-approved-candidates']).optional(),\n}).optional();\n\n/** Per-task config override — stored in myco.yaml under agent.tasks. */\nconst TaskProviderOverrideSchema = z.object({\n provider: ProviderOverrideSchema.optional(),\n model: z.string().optional(),\n maxTurns: z.number().int().positive().optional(),\n timeoutSeconds: z.number().int().positive().optional(),\n phases: z.record(z.string(), PhaseOverrideSchema).optional(),\n schedule: ScheduleOverrideSchema,\n});\n\nconst ContextSchema = z.object({\n /** Which digest tier to inject at session start. */\n digest_tier: z.number().int().default(5000),\n /** Enable semantic spore search on each user prompt. */\n prompt_search: z.boolean().default(true),\n /** Max spores to inject per prompt (0-10). */\n prompt_max_spores: z.number().int().min(0).max(10).default(3),\n});\n\nconst AgentSchema = z.object({\n /** Number of batches between event-driven summary triggers (0 to disable). */\n summary_batch_interval: z.number().int().min(0).default(5),\n /** Global default provider — applies to all tasks unless overridden per-task. */\n provider: ProviderOverrideSchema.optional(),\n /** Global default model — applies to all tasks unless overridden per-task. */\n model: z.string().optional(),\n /** Per-task overrides keyed by task name. */\n tasks: z.record(z.string(), TaskProviderOverrideSchema).optional(),\n});\n\nconst BackupSchema = z.object({\n /** Override directory for backup files. Supports ~ for home directory. When unset, defaults to .myco/backups. */\n dir: z.string().optional(),\n});\n\nconst TeamSchema = z.object({\n /** Whether team sync is enabled. */\n enabled: z.boolean().default(false),\n /** Cloudflare Worker URL for team sync. */\n worker_url: z.string().url().optional(),\n /** Team identifier for sync grouping. */\n team_id: z.string().optional(),\n /** Sync interval in minutes. */\n interval_minutes: z.number().int().min(1).max(1440).default(15),\n});\n\nconst SkillsSchema = z.object({\n /** Auto-generate candidates above this confidence score. */\n confidence_threshold: z.number().min(0).max(1).default(0.7),\n /** Flag unused skills after this many days. */\n usage_stale_days: z.number().int().positive().default(30),\n});\n\nexport const MycoConfigSchema = z.preprocess(\n (raw: unknown) => {\n if (raw && typeof raw === 'object' && 'curation' in raw && !('agent' in raw)) {\n const { curation, ...rest } = raw as Record<string, unknown>;\n return { ...rest, agent: curation };\n }\n return raw;\n },\n z.object({\n version: z.literal(3),\n config_version: z.number().int().nonnegative().default(0),\n embedding: EmbeddingProviderSchema.default(() => EmbeddingProviderSchema.parse({})),\n daemon: DaemonSchema.default(() => DaemonSchema.parse({})),\n capture: CaptureSchema.default(() => CaptureSchema.parse({})),\n agent: AgentSchema.default(() => AgentSchema.parse({})),\n context: ContextSchema.default(() => ContextSchema.parse({})),\n backup: BackupSchema.default(() => BackupSchema.parse({})),\n team: TeamSchema.default(() => TeamSchema.parse({})),\n skills: SkillsSchema.default(() => SkillsSchema.parse({})),\n }),\n);\n\nexport type MycoConfig = z.output<typeof MycoConfigSchema>;\nexport type EmbeddingProviderConfig = z.infer<typeof EmbeddingProviderSchema>;\nexport type TaskProviderOverride = z.infer<typeof TaskProviderOverrideSchema>;\nexport type PhaseOverride = z.infer<typeof PhaseOverrideSchema>;\nexport type ScheduleOverride = z.infer<typeof ScheduleOverrideSchema>;\nexport type ContextConfig = z.infer<typeof ContextSchema>;\nexport type BackupConfig = z.infer<typeof BackupSchema>;\nexport type TeamConfig = z.infer<typeof TeamSchema>;\nexport type SkillsConfig = z.infer<typeof SkillsSchema>;\n","/**\n * Config and vault migrations — run once per version, tracked by config_version.\n *\n * Each migration has a version number, a name, and a function that receives\n * the raw parsed YAML doc and the vault directory. Migrations run in order\n * and are skipped if config_version is already past them.\n *\n * To add a new migration:\n * 1. Add an entry to MIGRATIONS with the next version number\n * 2. Write the migrate function — it receives the mutable doc and vaultDir\n * 3. The framework handles version tracking and writing the config back\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface Migration {\n version: number;\n name: string;\n migrate: (doc: Record<string, unknown>, vaultDir: string) => void;\n}\n\n/** Regex matching both quoted and unquoted YAML: type: memory, type: \"memory\", type: 'memory' */\nconst MEMORY_TYPE_PATTERN = /type:\\s*[\"']?memory[\"']?/g;\n\nexport const MIGRATIONS: Migration[] = [\n {\n version: 1,\n name: 'rename-memories-to-spores',\n migrate: (doc, vaultDir) => {\n // Config: rename context.layers.memories → context.layers.spores\n const context = doc.context as Record<string, unknown> | undefined;\n const layers = context?.layers as Record<string, unknown> | undefined;\n if (layers && 'memories' in layers && !('spores' in layers)) {\n layers.spores = layers.memories;\n delete layers.memories;\n }\n\n // Vault: rename memories/ directory → spores/\n const memoriesDir = path.join(vaultDir, 'memories');\n const sporesDir = path.join(vaultDir, 'spores');\n\n if (!fs.existsSync(memoriesDir)) return;\n\n if (fs.existsSync(sporesDir)) {\n // Both exist (interrupted migration) — merge remaining files\n const moveRemaining = (srcDir: string, destDir: string): void => {\n for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {\n const srcPath = path.join(srcDir, entry.name);\n const destPath = path.join(destDir, entry.name);\n if (entry.isDirectory()) {\n if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });\n moveRemaining(srcPath, destPath);\n } else if (!fs.existsSync(destPath)) {\n fs.renameSync(srcPath, destPath);\n }\n }\n };\n moveRemaining(memoriesDir, sporesDir);\n fs.rmSync(memoriesDir, { recursive: true, force: true });\n } else {\n fs.renameSync(memoriesDir, sporesDir);\n }\n\n // Update frontmatter type: memory → type: spore (handles quoted and unquoted)\n const walkUpdate = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkUpdate(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n if (MEMORY_TYPE_PATTERN.test(content)) {\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n fs.writeFileSync(fullPath, content.replace(MEMORY_TYPE_PATTERN, 'type: spore'));\n }\n }\n };\n walkUpdate(sporesDir);\n\n // Legacy: update wikilink references in Markdown files (pre-SQLite migration): [[memories/...]] → [[spores/...]]\n const walkLinks = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkLinks(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n if (content.includes('memories/')) {\n fs.writeFileSync(fullPath, content.replace(/memories\\//g, 'spores/'));\n }\n }\n };\n walkLinks(vaultDir);\n },\n },\n {\n version: 2,\n name: 'consolidation-boolean-to-object',\n migrate: (doc) => {\n const digest = doc.digest as Record<string, unknown> | undefined;\n if (!digest) return;\n\n const consolidation = digest.consolidation;\n if (typeof consolidation === 'boolean') {\n digest.consolidation = { enabled: consolidation, max_tokens: 2048 };\n }\n },\n },\n {\n version: 3,\n name: 'schedule-to-task-level',\n migrate(doc: Record<string, unknown>, _vaultDir: string): void {\n const agent = (doc.agent ?? {}) as Record<string, unknown>;\n const skills = (doc.skills ?? {}) as Record<string, unknown>;\n const tasks = ((agent.tasks ?? {}) as Record<string, Record<string, unknown>>);\n\n /** Default interval for full-intelligence task (5 minutes). */\n const DEFAULT_INTELLIGENCE_INTERVAL_SECONDS = 300;\n\n const VALID_SCHEDULE_STATES = ['active', 'idle', 'sleep'] as const;\n\n // Migrate agent.auto_run + interval_seconds → full-intelligence schedule\n if ('auto_run' in agent || 'interval_seconds' in agent) {\n const fiTask = tasks['full-intelligence'] ?? {};\n fiTask.schedule = {\n enabled: agent.auto_run ?? true,\n intervalSeconds: agent.interval_seconds ?? DEFAULT_INTELLIGENCE_INTERVAL_SECONDS,\n };\n tasks['full-intelligence'] = fiTask;\n delete agent.auto_run;\n delete agent.interval_seconds;\n }\n\n // Migrate skills.auto_survey → skill-survey schedule\n if ('auto_survey' in skills) {\n const ssTask = tasks['skill-survey'] ?? {};\n ssTask.schedule = {\n enabled: skills.auto_survey ?? false,\n };\n tasks['skill-survey'] = ssTask;\n delete skills.auto_survey;\n }\n\n // Migrate skills.auto_evolve + evolve_cadence → skill-evolve schedule\n if ('auto_evolve' in skills || 'evolve_cadence' in skills) {\n const seTask = tasks['skill-evolve'] ?? {};\n const schedule: Record<string, unknown> = {\n enabled: skills.auto_evolve ?? false,\n };\n if ('evolve_cadence' in skills) {\n const cadence = String(skills.evolve_cadence);\n schedule.runIn = VALID_SCHEDULE_STATES.includes(cadence as typeof VALID_SCHEDULE_STATES[number])\n ? [cadence]\n : ['idle']; // fallback to safe default\n }\n seTask.schedule = schedule;\n tasks['skill-evolve'] = seTask;\n delete skills.auto_evolve;\n delete skills.evolve_cadence;\n }\n\n // Write back tasks if any were created\n if (Object.keys(tasks).length > 0) {\n agent.tasks = tasks;\n }\n doc.agent = agent;\n doc.skills = skills;\n },\n },\n];\n\n/** Current migration version — the highest version in MIGRATIONS. */\nexport const CURRENT_MIGRATION_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;\n\n/**\n * Run all pending migrations on the raw config doc.\n * Returns true if any migrations ran (caller should reindex).\n */\nexport function runMigrations(\n doc: Record<string, unknown>,\n vaultDir: string,\n log?: (message: string) => void,\n): boolean {\n const currentVersion = (doc.config_version as number) ?? 0;\n let ran = false;\n\n for (const migration of MIGRATIONS) {\n if (migration.version <= currentVersion) continue;\n\n migration.migrate(doc, vaultDir);\n doc.config_version = migration.version;\n ran = true;\n }\n\n if (ran) {\n const from = currentVersion;\n const to = (doc.config_version as number) ?? 0;\n log?.(`Migrated config from v${from} to v${to}`);\n }\n\n return ran;\n}\n"],"mappings":"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","names":["fs","path","path","fs","YAML"]}