@goondocks/myco 0.17.2 → 0.18.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 (196) hide show
  1. package/README.md +14 -22
  2. package/bin/myco-run +15 -2
  3. package/dist/{agent-run-7AYHXIEF.js → agent-run-I4O2K2CK.js} +7 -7
  4. package/dist/{agent-tasks-UUIFKBD4.js → agent-tasks-UOW5BQIB.js} +7 -7
  5. package/dist/{chunk-XD3NEN3Q.js → chunk-2V7HR7HB.js} +2 -2
  6. package/dist/chunk-44PZCAYS.js +107 -0
  7. package/dist/chunk-44PZCAYS.js.map +1 -0
  8. package/dist/{chunk-DT42247G.js → chunk-75AZFBFW.js} +3 -3
  9. package/dist/{chunk-RMJPQZGF.js → chunk-C3EGL5JX.js} +755 -266
  10. package/dist/chunk-C3EGL5JX.js.map +1 -0
  11. package/dist/{chunk-7DAH5GLC.js → chunk-CKJAWZQE.js} +5 -1
  12. package/dist/chunk-CKJAWZQE.js.map +1 -0
  13. package/dist/{chunk-ML6GTPZU.js → chunk-CML4MCYF.js} +2 -2
  14. package/dist/{chunk-UTLWSKDV.js → chunk-CURS2TNP.js} +45 -4
  15. package/dist/chunk-CURS2TNP.js.map +1 -0
  16. package/dist/{chunk-EBIYONNZ.js → chunk-DPSLJ242.js} +34 -2
  17. package/dist/chunk-DPSLJ242.js.map +1 -0
  18. package/dist/{chunk-BZDZORVP.js → chunk-LSP5HYOO.js} +19 -16
  19. package/dist/chunk-LSP5HYOO.js.map +1 -0
  20. package/dist/{chunk-NUSTG3BH.js → chunk-N75GMQGA.js} +3 -3
  21. package/dist/{chunk-F6C4IC6R.js → chunk-NI23QCHB.js} +3 -3
  22. package/dist/{chunk-C3C5QVLK.js → chunk-O3TRN3RC.js} +2 -2
  23. package/dist/{chunk-3NCVCGUZ.js → chunk-RAV5YMRU.js} +3 -3
  24. package/dist/{chunk-25WHTV4N.js → chunk-RIDSOQDR.js} +21 -7
  25. package/dist/chunk-RIDSOQDR.js.map +1 -0
  26. package/dist/{chunk-HPZ7YAMA.js → chunk-TCSVDQF5.js} +1130 -195
  27. package/dist/chunk-TCSVDQF5.js.map +1 -0
  28. package/dist/{chunk-CTF7TQMJ.js → chunk-TLK46KKD.js} +14 -4
  29. package/dist/chunk-TLK46KKD.js.map +1 -0
  30. package/dist/{chunk-IGBHLFV5.js → chunk-TOER6RNC.js} +22 -2
  31. package/dist/chunk-TOER6RNC.js.map +1 -0
  32. package/dist/{chunk-ZSJPI5MS.js → chunk-TZAXQKO6.js} +6 -2
  33. package/dist/chunk-TZAXQKO6.js.map +1 -0
  34. package/dist/{chunk-RKPTMHED.js → chunk-U3J2DDSR.js} +2 -2
  35. package/dist/{chunk-SI5BBQAT.js → chunk-W7WENJ6F.js} +2 -2
  36. package/dist/{chunk-VVGZL2HX.js → chunk-WYOE4IAX.js} +153 -15
  37. package/dist/{chunk-VVGZL2HX.js.map → chunk-WYOE4IAX.js.map} +1 -1
  38. package/dist/{chunk-XZWFMMJR.js → chunk-XWOQL4XN.js} +3 -3
  39. package/dist/{chunk-5ZISXCDC.js → chunk-YZPI2Y3E.js} +39 -5
  40. package/dist/chunk-YZPI2Y3E.js.map +1 -0
  41. package/dist/{cli-WJVYP2QT.js → cli-D3TJYJ2U.js} +40 -40
  42. package/dist/{client-LZ3ZR4HC.js → client-4LLEXLVK.js} +4 -4
  43. package/dist/{config-ZQIMG3FB.js → config-DA4IUVFL.js} +3 -3
  44. package/dist/{detect-NJ2OREDP.js → detect-SZ2KDUF4.js} +2 -2
  45. package/dist/{detect-providers-C64L3QET.js → detect-providers-PSVKXTWE.js} +4 -4
  46. package/dist/{doctor-XEPBNHM3.js → doctor-KCTXPX5D.js} +12 -12
  47. package/dist/{executor-NXKJU5KW.js → executor-UYIZC3L5.js} +93 -285
  48. package/dist/executor-UYIZC3L5.js.map +1 -0
  49. package/dist/{init-BHVQAQ27.js → init-QFNBKKDC.js} +13 -13
  50. package/dist/{installer-45ZLP2RP.js → installer-BWJED3ED.js} +2 -2
  51. package/dist/{llm-KTD6SR55.js → llm-SMA5ZEAW.js} +4 -4
  52. package/dist/{loader-SHRKUKOS.js → loader-Q3P3R4UP.js} +3 -3
  53. package/dist/{loader-NEX3UF6U.js → loader-SKKUMT5C.js} +3 -3
  54. package/dist/{main-YFVBIRRK.js → main-5THODR77.js} +751 -257
  55. package/dist/main-5THODR77.js.map +1 -0
  56. package/dist/{open-2U7ZRGA3.js → open-7737CSPN.js} +7 -7
  57. package/dist/{post-compact-QIBMEWL3.js → post-compact-2TJ5FPZH.js} +7 -7
  58. package/dist/{post-tool-use-ICGFXDVY.js → post-tool-use-FRTSICC3.js} +6 -6
  59. package/dist/{post-tool-use-failure-C7TLH3XQ.js → post-tool-use-failure-KYO2NCNB.js} +7 -7
  60. package/dist/{pre-compact-IF7K4TQK.js → pre-compact-J6GCJEJR.js} +7 -7
  61. package/dist/{provider-check-LTLQ6BUZ.js → provider-check-AE3L5Z6R.js} +4 -4
  62. package/dist/{registry-TFQ22Z7N.js → registry-O2NZLO3V.js} +4 -4
  63. package/dist/{remove-FBGM2QVJ.js → remove-3WZZC7AX.js} +9 -9
  64. package/dist/{resolution-events-HGKIJOTA.js → resolution-events-XWYLLDRK.js} +4 -4
  65. package/dist/{restart-TQEECRNW.js → restart-HUHEFOXU.js} +8 -8
  66. package/dist/{search-NN5FC4Z6.js → search-ZGN3LDXG.js} +8 -8
  67. package/dist/{server-XMWJ4GF7.js → server-PTXLVVEE.js} +4 -4
  68. package/dist/{session-GLPAFYPO.js → session-7VV3IQMO.js} +9 -9
  69. package/dist/{session-end-TI3ILRBC.js → session-end-SMU55UCM.js} +6 -6
  70. package/dist/{session-start-PJLJDVJJ.js → session-start-NIMWEOIZ.js} +29 -13
  71. package/dist/session-start-NIMWEOIZ.js.map +1 -0
  72. package/dist/{setup-llm-AQSWLXCZ.js → setup-llm-7S3VPAPN.js} +8 -8
  73. package/dist/src/agent/definitions/tasks/extract-only.yaml +1 -1
  74. package/dist/src/agent/definitions/tasks/full-intelligence.yaml +10 -0
  75. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +163 -49
  76. package/dist/src/agent/definitions/tasks/skill-generate.yaml +44 -27
  77. package/dist/src/agent/definitions/tasks/skill-survey.yaml +132 -138
  78. package/dist/src/agent/definitions/tasks/supersession-sweep.yaml +1 -1
  79. package/dist/src/cli.js +1 -1
  80. package/dist/src/daemon/main.js +1 -1
  81. package/dist/src/hooks/post-tool-use.js +1 -1
  82. package/dist/src/hooks/session-end.js +1 -1
  83. package/dist/src/hooks/session-start.js +1 -1
  84. package/dist/src/hooks/stop.js +1 -1
  85. package/dist/src/hooks/user-prompt-submit.js +1 -1
  86. package/dist/src/mcp/server.js +1 -1
  87. package/dist/src/symbionts/manifests/codex.yaml +85 -0
  88. package/dist/src/symbionts/templates/claude-code/hooks.json +12 -12
  89. package/dist/src/symbionts/templates/claude-code/settings.json +3 -3
  90. package/dist/src/symbionts/templates/codex/hooks.json +4 -4
  91. package/dist/src/symbionts/templates/cursor/hooks.json +9 -9
  92. package/dist/src/symbionts/templates/cursor/settings.json +2 -2
  93. package/dist/src/symbionts/templates/gemini/hooks.json +6 -6
  94. package/dist/src/symbionts/templates/gemini/settings.json +2 -2
  95. package/dist/src/symbionts/templates/myco-run.cjs +44 -0
  96. package/dist/src/symbionts/templates/opencode/settings.json +2 -2
  97. package/dist/src/symbionts/templates/vscode-copilot/hooks.json +7 -7
  98. package/dist/src/symbionts/templates/vscode-copilot/settings.json +2 -2
  99. package/dist/src/symbionts/templates/windsurf/hooks.json +4 -4
  100. package/dist/src/symbionts/templates/windsurf/settings.json +2 -2
  101. package/dist/src/worker/package-lock.json +4338 -0
  102. package/dist/src/worker/package.json +5 -0
  103. package/dist/src/worker/src/index.ts +58 -65
  104. package/dist/src/worker/src/mcp/auth.ts +65 -0
  105. package/dist/src/worker/src/mcp/server.ts +53 -0
  106. package/dist/src/worker/src/mcp/tools/context.ts +13 -0
  107. package/dist/src/worker/src/mcp/tools/get.ts +15 -0
  108. package/dist/src/worker/src/mcp/tools/graph.ts +35 -0
  109. package/dist/src/worker/src/mcp/tools/search.ts +32 -0
  110. package/dist/src/worker/src/mcp/tools/sessions.ts +24 -0
  111. package/dist/src/worker/src/mcp/tools/skills.ts +16 -0
  112. package/dist/src/worker/src/mcp/tools/team.ts +9 -0
  113. package/dist/src/worker/src/schema.ts +5 -1
  114. package/dist/src/worker/src/search-helpers.ts +70 -0
  115. package/dist/src/worker/wrangler.toml +9 -0
  116. package/dist/{stats-BISBIBXZ.js → stats-GEOQ2DFF.js} +9 -9
  117. package/dist/{stop-47BJ42EO.js → stop-7AKYBJJ2.js} +6 -6
  118. package/dist/{stop-failure-VU5BTLWX.js → stop-failure-NLE2EURG.js} +7 -7
  119. package/dist/{subagent-start-SPTKQRHU.js → subagent-start-LBNZF2TG.js} +7 -7
  120. package/dist/{subagent-stop-UU75BYLC.js → subagent-stop-B2Z5GYAB.js} +7 -7
  121. package/dist/{task-completed-MVDO7TZF.js → task-completed-PO5TETJ7.js} +7 -7
  122. package/dist/{team-7X64J4Y6.js → team-DPNP2RN7.js} +97 -14
  123. package/dist/team-DPNP2RN7.js.map +1 -0
  124. package/dist/ui/assets/{index-rpmSpJpm.js → index-CiI1fwas.js} +120 -120
  125. package/dist/ui/index.html +1 -1
  126. package/dist/{update-DA7VEXOS.js → update-WBWB5URU.js} +18 -9
  127. package/dist/update-WBWB5URU.js.map +1 -0
  128. package/dist/{user-prompt-submit-ADZ4NTVO.js → user-prompt-submit-IZJC3NV7.js} +30 -7
  129. package/dist/user-prompt-submit-IZJC3NV7.js.map +1 -0
  130. package/dist/{verify-QYSERHF7.js → verify-FNSP62I3.js} +5 -5
  131. package/dist/{version-A72TAL2J.js → version-QEVU66NT.js} +2 -2
  132. package/package.json +7 -7
  133. package/dist/chunk-25WHTV4N.js.map +0 -1
  134. package/dist/chunk-5ZISXCDC.js.map +0 -1
  135. package/dist/chunk-7DAH5GLC.js.map +0 -1
  136. package/dist/chunk-BZDZORVP.js.map +0 -1
  137. package/dist/chunk-CTF7TQMJ.js.map +0 -1
  138. package/dist/chunk-EBIYONNZ.js.map +0 -1
  139. package/dist/chunk-HPZ7YAMA.js.map +0 -1
  140. package/dist/chunk-IGBHLFV5.js.map +0 -1
  141. package/dist/chunk-RMJPQZGF.js.map +0 -1
  142. package/dist/chunk-UTLWSKDV.js.map +0 -1
  143. package/dist/chunk-ZSJPI5MS.js.map +0 -1
  144. package/dist/executor-NXKJU5KW.js.map +0 -1
  145. package/dist/main-YFVBIRRK.js.map +0 -1
  146. package/dist/session-start-PJLJDVJJ.js.map +0 -1
  147. package/dist/src/symbionts/templates/hook-guard.cjs +0 -19
  148. package/dist/team-7X64J4Y6.js.map +0 -1
  149. package/dist/update-DA7VEXOS.js.map +0 -1
  150. package/dist/user-prompt-submit-ADZ4NTVO.js.map +0 -1
  151. /package/dist/{agent-run-7AYHXIEF.js.map → agent-run-I4O2K2CK.js.map} +0 -0
  152. /package/dist/{agent-tasks-UUIFKBD4.js.map → agent-tasks-UOW5BQIB.js.map} +0 -0
  153. /package/dist/{chunk-XD3NEN3Q.js.map → chunk-2V7HR7HB.js.map} +0 -0
  154. /package/dist/{chunk-DT42247G.js.map → chunk-75AZFBFW.js.map} +0 -0
  155. /package/dist/{chunk-ML6GTPZU.js.map → chunk-CML4MCYF.js.map} +0 -0
  156. /package/dist/{chunk-NUSTG3BH.js.map → chunk-N75GMQGA.js.map} +0 -0
  157. /package/dist/{chunk-F6C4IC6R.js.map → chunk-NI23QCHB.js.map} +0 -0
  158. /package/dist/{chunk-C3C5QVLK.js.map → chunk-O3TRN3RC.js.map} +0 -0
  159. /package/dist/{chunk-3NCVCGUZ.js.map → chunk-RAV5YMRU.js.map} +0 -0
  160. /package/dist/{chunk-RKPTMHED.js.map → chunk-U3J2DDSR.js.map} +0 -0
  161. /package/dist/{chunk-SI5BBQAT.js.map → chunk-W7WENJ6F.js.map} +0 -0
  162. /package/dist/{chunk-XZWFMMJR.js.map → chunk-XWOQL4XN.js.map} +0 -0
  163. /package/dist/{cli-WJVYP2QT.js.map → cli-D3TJYJ2U.js.map} +0 -0
  164. /package/dist/{client-LZ3ZR4HC.js.map → client-4LLEXLVK.js.map} +0 -0
  165. /package/dist/{config-ZQIMG3FB.js.map → config-DA4IUVFL.js.map} +0 -0
  166. /package/dist/{detect-NJ2OREDP.js.map → detect-SZ2KDUF4.js.map} +0 -0
  167. /package/dist/{detect-providers-C64L3QET.js.map → detect-providers-PSVKXTWE.js.map} +0 -0
  168. /package/dist/{doctor-XEPBNHM3.js.map → doctor-KCTXPX5D.js.map} +0 -0
  169. /package/dist/{init-BHVQAQ27.js.map → init-QFNBKKDC.js.map} +0 -0
  170. /package/dist/{installer-45ZLP2RP.js.map → installer-BWJED3ED.js.map} +0 -0
  171. /package/dist/{llm-KTD6SR55.js.map → llm-SMA5ZEAW.js.map} +0 -0
  172. /package/dist/{loader-NEX3UF6U.js.map → loader-Q3P3R4UP.js.map} +0 -0
  173. /package/dist/{loader-SHRKUKOS.js.map → loader-SKKUMT5C.js.map} +0 -0
  174. /package/dist/{open-2U7ZRGA3.js.map → open-7737CSPN.js.map} +0 -0
  175. /package/dist/{post-compact-QIBMEWL3.js.map → post-compact-2TJ5FPZH.js.map} +0 -0
  176. /package/dist/{post-tool-use-ICGFXDVY.js.map → post-tool-use-FRTSICC3.js.map} +0 -0
  177. /package/dist/{post-tool-use-failure-C7TLH3XQ.js.map → post-tool-use-failure-KYO2NCNB.js.map} +0 -0
  178. /package/dist/{pre-compact-IF7K4TQK.js.map → pre-compact-J6GCJEJR.js.map} +0 -0
  179. /package/dist/{provider-check-LTLQ6BUZ.js.map → provider-check-AE3L5Z6R.js.map} +0 -0
  180. /package/dist/{registry-TFQ22Z7N.js.map → registry-O2NZLO3V.js.map} +0 -0
  181. /package/dist/{remove-FBGM2QVJ.js.map → remove-3WZZC7AX.js.map} +0 -0
  182. /package/dist/{resolution-events-HGKIJOTA.js.map → resolution-events-XWYLLDRK.js.map} +0 -0
  183. /package/dist/{restart-TQEECRNW.js.map → restart-HUHEFOXU.js.map} +0 -0
  184. /package/dist/{search-NN5FC4Z6.js.map → search-ZGN3LDXG.js.map} +0 -0
  185. /package/dist/{server-XMWJ4GF7.js.map → server-PTXLVVEE.js.map} +0 -0
  186. /package/dist/{session-GLPAFYPO.js.map → session-7VV3IQMO.js.map} +0 -0
  187. /package/dist/{session-end-TI3ILRBC.js.map → session-end-SMU55UCM.js.map} +0 -0
  188. /package/dist/{setup-llm-AQSWLXCZ.js.map → setup-llm-7S3VPAPN.js.map} +0 -0
  189. /package/dist/{stats-BISBIBXZ.js.map → stats-GEOQ2DFF.js.map} +0 -0
  190. /package/dist/{stop-47BJ42EO.js.map → stop-7AKYBJJ2.js.map} +0 -0
  191. /package/dist/{stop-failure-VU5BTLWX.js.map → stop-failure-NLE2EURG.js.map} +0 -0
  192. /package/dist/{subagent-start-SPTKQRHU.js.map → subagent-start-LBNZF2TG.js.map} +0 -0
  193. /package/dist/{subagent-stop-UU75BYLC.js.map → subagent-stop-B2Z5GYAB.js.map} +0 -0
  194. /package/dist/{task-completed-MVDO7TZF.js.map → task-completed-PO5TETJ7.js.map} +0 -0
  195. /package/dist/{verify-QYSERHF7.js.map → verify-FNSP62I3.js.map} +0 -0
  196. /package/dist/{version-A72TAL2J.js.map → version-QEVU66NT.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  loadAllTasks
4
- } from "./chunk-3NCVCGUZ.js";
4
+ } from "./chunk-RAV5YMRU.js";
5
5
  import {
6
6
  getAgent,
7
7
  getDefaultTask,
@@ -9,21 +9,23 @@ import {
9
9
  loadAgentDefinition,
10
10
  resolveDefinitionsDir,
11
11
  resolveEffectiveConfig
12
- } from "./chunk-F6C4IC6R.js";
12
+ } from "./chunk-NI23QCHB.js";
13
13
  import {
14
14
  getSpore,
15
- listSporeIdsSince
16
- } from "./chunk-ZSJPI5MS.js";
15
+ listSporeIdsSince,
16
+ listSpores
17
+ } from "./chunk-TZAXQKO6.js";
17
18
  import {
18
- getSession
19
- } from "./chunk-25WHTV4N.js";
19
+ getSession,
20
+ listSessions
21
+ } from "./chunk-RIDSOQDR.js";
20
22
  import {
21
23
  getTeamMachineId,
22
24
  syncRow
23
- } from "./chunk-C3C5QVLK.js";
25
+ } from "./chunk-O3TRN3RC.js";
24
26
  import {
25
27
  loadConfig
26
- } from "./chunk-XD3NEN3Q.js";
28
+ } from "./chunk-2V7HR7HB.js";
27
29
  import {
28
30
  getDatabase
29
31
  } from "./chunk-MYX5NCRH.js";
@@ -37,7 +39,126 @@ import {
37
39
  GRAPH_EDGE_DEFAULT_CONFIDENCE,
38
40
  QUERY_DEFAULT_LIST_LIMIT,
39
41
  epochSeconds
40
- } from "./chunk-7DAH5GLC.js";
42
+ } from "./chunk-CKJAWZQE.js";
43
+
44
+ // src/notifications/notify.ts
45
+ import crypto from "crypto";
46
+
47
+ // src/db/queries/notifications.ts
48
+ var DEFAULT_LIMIT = 50;
49
+ var NOTIFICATION_PRUNE_AGE_SECONDS = 30 * 24 * 60 * 60;
50
+ function insertNotification(n) {
51
+ const db = getDatabase();
52
+ db.prepare(
53
+ `INSERT INTO notifications (id, domain, type, level, title, message, mode, status, link, metadata, created_at)
54
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'unread', ?, ?, ?)`
55
+ ).run(n.id, n.domain, n.type, n.level, n.title, n.message, n.mode, n.link, n.metadata, epochSeconds());
56
+ }
57
+ function listNotifications(opts = {}) {
58
+ const db = getDatabase();
59
+ const conditions = [];
60
+ const params = [];
61
+ if (opts.status) {
62
+ conditions.push("status = ?");
63
+ params.push(opts.status);
64
+ }
65
+ if (opts.domain) {
66
+ conditions.push("domain = ?");
67
+ params.push(opts.domain);
68
+ }
69
+ if (opts.mode) {
70
+ conditions.push("mode = ?");
71
+ params.push(opts.mode);
72
+ }
73
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
74
+ const limit = opts.limit ?? DEFAULT_LIMIT;
75
+ const offset = opts.offset ?? 0;
76
+ return db.prepare(
77
+ `SELECT * FROM notifications ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`
78
+ ).all(...params, limit, offset);
79
+ }
80
+ function countNotifications(status) {
81
+ const db = getDatabase();
82
+ if (status) {
83
+ const row2 = db.prepare("SELECT COUNT(*) as count FROM notifications WHERE status = ?").get(status);
84
+ return row2.count;
85
+ }
86
+ const row = db.prepare("SELECT COUNT(*) as count FROM notifications").get();
87
+ return row.count;
88
+ }
89
+ function getNotification(id) {
90
+ const db = getDatabase();
91
+ return db.prepare("SELECT * FROM notifications WHERE id = ?").get(id);
92
+ }
93
+ function updateNotificationStatus(id, status) {
94
+ const db = getDatabase();
95
+ const result = db.prepare("UPDATE notifications SET status = ? WHERE id = ?").run(status, id);
96
+ return result.changes > 0;
97
+ }
98
+ function dismissAllNotifications(domain) {
99
+ const db = getDatabase();
100
+ if (domain) {
101
+ const result2 = db.prepare("UPDATE notifications SET status = 'dismissed' WHERE domain = ? AND status != 'dismissed'").run(domain);
102
+ return result2.changes;
103
+ }
104
+ const result = db.prepare("UPDATE notifications SET status = 'dismissed' WHERE status != 'dismissed'").run();
105
+ return result.changes;
106
+ }
107
+ function markAllRead(domain) {
108
+ const db = getDatabase();
109
+ if (domain) {
110
+ const result2 = db.prepare("UPDATE notifications SET status = 'read' WHERE domain = ? AND status = 'unread'").run(domain);
111
+ return result2.changes;
112
+ }
113
+ const result = db.prepare("UPDATE notifications SET status = 'read' WHERE status = 'unread'").run();
114
+ return result.changes;
115
+ }
116
+
117
+ // src/notifications/registry.ts
118
+ var domains = /* @__PURE__ */ new Map();
119
+ function register(descriptor) {
120
+ domains.set(descriptor.domain, descriptor);
121
+ }
122
+ function getAllDomains() {
123
+ return [...domains.values()].sort((a, b) => a.domain.localeCompare(b.domain));
124
+ }
125
+ function getType(typeId) {
126
+ for (const descriptor of domains.values()) {
127
+ const match = descriptor.types.find((t) => t.id === typeId);
128
+ if (match) return { domain: descriptor, type: match };
129
+ }
130
+ return void 0;
131
+ }
132
+
133
+ // src/notifications/notify.ts
134
+ function notify(vaultDir, payload, config) {
135
+ if (!vaultDir) return null;
136
+ try {
137
+ const cfg = config ?? loadConfig(vaultDir);
138
+ if (!cfg.notifications.enabled) return null;
139
+ const domainConfig = cfg.notifications.domains[payload.domain];
140
+ if (domainConfig && !domainConfig.enabled) return null;
141
+ const registeredType = getType(payload.type);
142
+ const mode = payload.mode ?? domainConfig?.mode ?? registeredType?.type.defaultMode ?? cfg.notifications.default_mode;
143
+ const level = payload.level ?? registeredType?.type.defaultLevel ?? "info";
144
+ const id = crypto.randomUUID();
145
+ insertNotification({
146
+ id,
147
+ domain: payload.domain,
148
+ type: payload.type,
149
+ level,
150
+ title: payload.title,
151
+ message: payload.message ?? null,
152
+ mode,
153
+ link: payload.link ?? null,
154
+ metadata: payload.metadata ? JSON.stringify(payload.metadata) : null
155
+ });
156
+ return id;
157
+ } catch (err) {
158
+ console.warn("[notify] Failed to emit notification:", err instanceof Error ? err.message : err);
159
+ return null;
160
+ }
161
+ }
41
162
 
42
163
  // src/utils/error-message.ts
43
164
  function errorMessage(err) {
@@ -50,6 +171,10 @@ function errorMessage(err) {
50
171
  }
51
172
  }
52
173
 
174
+ // src/agent/instruction-builders.ts
175
+ import { readFileSync } from "fs";
176
+ import { resolve } from "path";
177
+
53
178
  // src/db/queries/skill-candidates.ts
54
179
  var DEFAULT_CONFIDENCE = 0;
55
180
  var DEFAULT_STATUS = "identified";
@@ -63,6 +188,7 @@ var CANDIDATE_COLUMNS = [
63
188
  "status",
64
189
  "source_ids",
65
190
  "skill_id",
191
+ "supersedes",
66
192
  "approved_at",
67
193
  "created_at",
68
194
  "updated_at",
@@ -80,6 +206,7 @@ function toCandidateRow(row) {
80
206
  status: row.status,
81
207
  source_ids: row.source_ids ?? "[]",
82
208
  skill_id: row.skill_id ?? null,
209
+ supersedes: row.supersedes ?? null,
83
210
  approved_at: row.approved_at ?? null,
84
211
  created_at: row.created_at,
85
212
  updated_at: row.updated_at,
@@ -111,11 +238,11 @@ function insertCandidate(data) {
111
238
  db.prepare(
112
239
  `INSERT INTO skill_candidates (
113
240
  id, agent_id, machine_id, topic, rationale,
114
- confidence, status, source_ids, skill_id, approved_at,
241
+ confidence, status, source_ids, skill_id, supersedes, approved_at,
115
242
  created_at, updated_at
116
243
  ) VALUES (
117
244
  ?, ?, ?, ?, ?,
118
- ?, ?, ?, ?, ?,
245
+ ?, ?, ?, ?, ?, ?,
119
246
  ?, ?
120
247
  )`
121
248
  ).run(
@@ -128,6 +255,7 @@ function insertCandidate(data) {
128
255
  data.status ?? DEFAULT_STATUS,
129
256
  data.source_ids ?? "[]",
130
257
  data.skill_id ?? null,
258
+ data.supersedes ?? null,
131
259
  data.approved_at ?? null,
132
260
  data.created_at,
133
261
  data.updated_at
@@ -177,6 +305,7 @@ function updateCandidate(id, updates) {
177
305
  status: "status",
178
306
  source_ids: "source_ids",
179
307
  skill_id: "skill_id",
308
+ supersedes: "supersedes",
180
309
  approved_at: "approved_at",
181
310
  updated_at: "updated_at"
182
311
  };
@@ -437,69 +566,357 @@ function deleteSkillRecordCascade(idOrName) {
437
566
  return { id: record.id, name: record.name };
438
567
  }
439
568
 
440
- // src/db/queries/skill-lineage.ts
441
- var LINEAGE_COLUMNS = [
569
+ // src/db/queries/digest-extracts.ts
570
+ var EXTRACT_COLUMNS = [
442
571
  "id",
443
- "skill_id",
444
- "generation",
445
- "action",
446
- "rationale",
447
- "source_ids_added",
448
- "content_snapshot",
449
- "created_at"
572
+ "agent_id",
573
+ "tier",
574
+ "content",
575
+ "substrate_hash",
576
+ "generated_at",
577
+ "machine_id",
578
+ "synced_at"
450
579
  ];
451
- var SELECT_COLUMNS3 = LINEAGE_COLUMNS.join(", ");
452
- function toLineageRow(row) {
580
+ var SELECT_COLUMNS3 = EXTRACT_COLUMNS.join(", ");
581
+ function toDigestExtractRow(row) {
453
582
  return {
454
583
  id: row.id,
455
- skill_id: row.skill_id,
456
- generation: row.generation,
457
- action: row.action,
458
- rationale: row.rationale,
459
- source_ids_added: row.source_ids_added ?? "[]",
460
- content_snapshot: row.content_snapshot,
461
- created_at: row.created_at
584
+ agent_id: row.agent_id,
585
+ tier: row.tier,
586
+ content: row.content,
587
+ substrate_hash: row.substrate_hash ?? null,
588
+ generated_at: row.generated_at,
589
+ machine_id: row.machine_id ?? "local",
590
+ synced_at: row.synced_at ?? null
462
591
  };
463
592
  }
464
- function insertLineage(data) {
593
+ function upsertDigestExtract(data) {
465
594
  const db = getDatabase();
466
595
  db.prepare(
467
- `INSERT INTO skill_lineage (
468
- id, skill_id, generation, action, rationale,
469
- source_ids_added, content_snapshot, created_at
470
- ) VALUES (
471
- ?, ?, ?, ?, ?,
472
- ?, ?, ?
473
- )`
474
- ).run(
475
- data.id,
476
- data.skill_id,
477
- data.generation,
478
- data.action,
479
- data.rationale,
480
- data.source_ids_added ?? "[]",
481
- data.content_snapshot,
482
- data.created_at
483
- );
484
- return toLineageRow(
485
- db.prepare(`SELECT ${SELECT_COLUMNS3} FROM skill_lineage WHERE id = ?`).get(data.id)
486
- );
596
+ `INSERT INTO digest_extracts (agent_id, tier, content, generated_at)
597
+ VALUES (?, ?, ?, ?)
598
+ ON CONFLICT (agent_id, tier) DO UPDATE SET
599
+ content = EXCLUDED.content,
600
+ generated_at = EXCLUDED.generated_at`
601
+ ).run(data.agent_id, data.tier, data.content, data.generated_at);
602
+ const row = db.prepare(
603
+ `SELECT ${SELECT_COLUMNS3} FROM digest_extracts WHERE agent_id = ? AND tier = ?`
604
+ ).get(data.agent_id, data.tier);
605
+ return toDigestExtractRow(row);
487
606
  }
488
- function listLineageForSkill(skillId, limit = 50) {
607
+ function getDigestExtract(agentId, tier) {
608
+ const db = getDatabase();
609
+ const row = db.prepare(
610
+ `SELECT ${SELECT_COLUMNS3} FROM digest_extracts
611
+ WHERE agent_id = ? AND tier = ?`
612
+ ).get(agentId, tier);
613
+ if (!row) return null;
614
+ return toDigestExtractRow(row);
615
+ }
616
+ function listDigestExtracts(agentId) {
489
617
  const db = getDatabase();
618
+ const tierPlaceholders = DIGEST_TIERS.map(() => "?").join(", ");
490
619
  const rows = db.prepare(
491
620
  `SELECT ${SELECT_COLUMNS3}
492
- FROM skill_lineage
493
- WHERE skill_id = ?
494
- ORDER BY generation DESC
495
- LIMIT ?`
496
- ).all(skillId, limit);
497
- return rows.map(toLineageRow);
621
+ FROM digest_extracts
622
+ WHERE agent_id = ? AND tier IN (${tierPlaceholders})
623
+ ORDER BY tier ASC`
624
+ ).all(agentId, ...DIGEST_TIERS);
625
+ return rows.map(toDigestExtractRow);
626
+ }
627
+
628
+ // src/db/queries/agent-state.ts
629
+ var STATE_COLUMNS = [
630
+ "agent_id",
631
+ "key",
632
+ "value",
633
+ "updated_at"
634
+ ];
635
+ var SELECT_COLUMNS4 = STATE_COLUMNS.join(", ");
636
+ function toAgentStateRow(row) {
637
+ return {
638
+ agent_id: row.agent_id,
639
+ key: row.key,
640
+ value: row.value,
641
+ updated_at: row.updated_at
642
+ };
643
+ }
644
+ function getState(agentId, key) {
645
+ const db = getDatabase();
646
+ const row = db.prepare(
647
+ `SELECT ${SELECT_COLUMNS4} FROM agent_state WHERE agent_id = ? AND key = ?`
648
+ ).get(agentId, key);
649
+ if (!row) return null;
650
+ return toAgentStateRow(row);
651
+ }
652
+ function setState(agentId, key, value, updatedAt) {
653
+ const db = getDatabase();
654
+ db.prepare(
655
+ `INSERT INTO agent_state (agent_id, key, value, updated_at)
656
+ VALUES (?, ?, ?, ?)
657
+ ON CONFLICT (agent_id, key) DO UPDATE SET
658
+ value = EXCLUDED.value,
659
+ updated_at = EXCLUDED.updated_at`
660
+ ).run(agentId, key, value, updatedAt);
661
+ return toAgentStateRow(
662
+ db.prepare(`SELECT ${SELECT_COLUMNS4} FROM agent_state WHERE agent_id = ? AND key = ?`).get(agentId, key)
663
+ );
664
+ }
665
+ function getStatesForAgent(agentId) {
666
+ const db = getDatabase();
667
+ const rows = db.prepare(
668
+ `SELECT ${SELECT_COLUMNS4}
669
+ FROM agent_state
670
+ WHERE agent_id = ?
671
+ ORDER BY key ASC`
672
+ ).all(agentId);
673
+ return rows.map(toAgentStateRow);
674
+ }
675
+
676
+ // src/agent/tools/skill-validator.ts
677
+ var MAX_SKILL_LINES = 800;
678
+ var REQUIRED_FRONTMATTER_FIELDS = ["name", "description", "managed_by", "user-invocable", "allowed-tools"];
679
+ var PROTECTED_FRONTMATTER_FIELDS = ["user-invocable", "allowed-tools"];
680
+ var ALLOWED_CLAUDE_CODE_TOOLS = /* @__PURE__ */ new Set([
681
+ "Read",
682
+ "Edit",
683
+ "Write",
684
+ "MultiEdit",
685
+ "Bash",
686
+ "Grep",
687
+ "Glob",
688
+ "NotebookRead",
689
+ "NotebookEdit",
690
+ "WebFetch",
691
+ "WebSearch",
692
+ "Task",
693
+ "TodoWrite"
694
+ ]);
695
+ function extractFrontmatterField(content, field) {
696
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
697
+ if (!fmMatch) return void 0;
698
+ const fm = fmMatch[1];
699
+ const match = fm.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
700
+ if (match) return match[1].trim();
701
+ const blockMatch = fm.match(new RegExp(`^${field}:\\s*$`, "m"));
702
+ if (blockMatch) {
703
+ const startIdx = fm.indexOf(blockMatch[0]) + blockMatch[0].length;
704
+ const remaining = fm.slice(startIdx);
705
+ const items = [];
706
+ for (const line of remaining.split("\n")) {
707
+ const itemMatch = line.match(/^\s+-\s+(.+)$/);
708
+ if (!itemMatch) break;
709
+ items.push(itemMatch[1].trim());
710
+ }
711
+ if (items.length > 0) return items.join(", ");
712
+ }
713
+ return void 0;
714
+ }
715
+ function parseAllowedTools(rawValue) {
716
+ if (!rawValue) return null;
717
+ let stripped = rawValue.trim();
718
+ if (stripped.length === 0) return null;
719
+ if (stripped.startsWith("[") && stripped.endsWith("]")) {
720
+ stripped = stripped.slice(1, -1).trim();
721
+ }
722
+ if (stripped.length === 0) return null;
723
+ const parts = stripped.split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter((s) => s.length > 0);
724
+ if (parts.length === 0) return null;
725
+ const sentinels = /* @__PURE__ */ new Set(["None", "none", "null", "Null", "~"]);
726
+ if (parts.some((p) => sentinels.has(p))) return null;
727
+ return parts;
728
+ }
729
+ function stemToken(word) {
730
+ let w = word;
731
+ if (w.length > 5 && w.endsWith("ing")) w = w.slice(0, -3);
732
+ else if (w.length > 5 && w.endsWith("ed")) w = w.slice(0, -2);
733
+ if (w.length > 4 && w.endsWith("s")) w = w.slice(0, -1);
734
+ if (w.length > 4 && w.endsWith("e")) w = w.slice(0, -1);
735
+ return w;
736
+ }
737
+ var STOPWORDS = /* @__PURE__ */ new Set([
738
+ "the",
739
+ "a",
740
+ "an",
741
+ "and",
742
+ "or",
743
+ "but",
744
+ "is",
745
+ "are",
746
+ "was",
747
+ "were",
748
+ "be",
749
+ "been",
750
+ "being",
751
+ "have",
752
+ "has",
753
+ "had",
754
+ "do",
755
+ "does",
756
+ "did",
757
+ "will",
758
+ "would",
759
+ "should",
760
+ "could",
761
+ "may",
762
+ "might",
763
+ "must",
764
+ "can",
765
+ "this",
766
+ "that",
767
+ "these",
768
+ "those",
769
+ "with",
770
+ "from",
771
+ "into",
772
+ "onto",
773
+ "for",
774
+ "when",
775
+ "where",
776
+ "which",
777
+ "what",
778
+ "who",
779
+ "how",
780
+ "why",
781
+ "use",
782
+ "uses",
783
+ "used",
784
+ "using",
785
+ "not",
786
+ "also",
787
+ "than",
788
+ "then",
789
+ "ensure",
790
+ "ensures",
791
+ "make",
792
+ "makes"
793
+ ]);
794
+ function tokenSet(text) {
795
+ return new Set(
796
+ text.toLowerCase().replace(/[^a-z0-9_\s]/g, " ").split(/\s+/).filter((w) => w.length >= 4 && !STOPWORDS.has(w)).map(stemToken)
797
+ );
798
+ }
799
+ function intersectionSize(a, b) {
800
+ let count = 0;
801
+ for (const token of a) {
802
+ if (b.has(token)) count++;
803
+ }
804
+ return count;
805
+ }
806
+ function descriptionSimilarity(a, b) {
807
+ const aTokens = tokenSet(a);
808
+ const bTokens = tokenSet(b);
809
+ if (aTokens.size === 0 || bTokens.size === 0) return 0;
810
+ const intersection = intersectionSize(aTokens, bTokens);
811
+ const union = aTokens.size + bTokens.size - intersection;
812
+ return union === 0 ? 0 : intersection / union;
813
+ }
814
+ function topicOverlapSimilarity(a, b, minTokens = 3) {
815
+ const aTokens = tokenSet(a);
816
+ const bTokens = tokenSet(b);
817
+ if (aTokens.size < minTokens || bTokens.size < minTokens) return 0;
818
+ const intersection = intersectionSize(aTokens, bTokens);
819
+ const smaller = Math.min(aTokens.size, bTokens.size);
820
+ return smaller === 0 ? 0 : intersection / smaller;
821
+ }
822
+ var DESCRIPTION_DUPLICATE_THRESHOLD = 0.4;
823
+ var TOPIC_OVERLAP_THRESHOLD = 0.6;
824
+ function checkFrontmatterPreservation(existing, incoming) {
825
+ const violations = [];
826
+ for (const field of PROTECTED_FRONTMATTER_FIELDS) {
827
+ const oldValue = extractFrontmatterField(existing, field);
828
+ const newValue = extractFrontmatterField(incoming, field);
829
+ if (oldValue === void 0 || newValue === void 0) continue;
830
+ if (field === "allowed-tools") {
831
+ const oldParsed = parseAllowedTools(oldValue);
832
+ const newParsed = parseAllowedTools(newValue);
833
+ if (oldParsed && newParsed) {
834
+ const oldSet = new Set(oldParsed);
835
+ const newSet = new Set(newParsed);
836
+ const changed = oldSet.size !== newSet.size || [...oldSet].some((t) => !newSet.has(t));
837
+ if (changed) {
838
+ violations.push(`${field}: was [${oldParsed.join(", ")}], changed to [${newParsed.join(", ")}]`);
839
+ }
840
+ continue;
841
+ }
842
+ }
843
+ if (oldValue !== newValue) {
844
+ violations.push(`${field}: was "${oldValue}", changed to "${newValue}"`);
845
+ }
846
+ }
847
+ const oldDesc = extractFrontmatterField(existing, "description");
848
+ const newDesc = extractFrontmatterField(incoming, "description");
849
+ if (oldDesc && newDesc && newDesc.length < oldDesc.length * 0.9) {
850
+ violations.push(
851
+ `description shortened from ${oldDesc.length} to ${newDesc.length} chars (${Math.round((1 - newDesc.length / oldDesc.length) * 100)}% reduction). Descriptions are the primary triggering mechanism \u2014 do not shorten them.`
852
+ );
853
+ }
854
+ return violations;
855
+ }
856
+ function validateSkillContent(content, dirName) {
857
+ const issues = [];
858
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
859
+ if (!fmMatch) {
860
+ issues.push("Missing YAML frontmatter (must start with --- and end with ---)");
861
+ return issues;
862
+ }
863
+ const frontmatter = fmMatch[1];
864
+ for (const field of REQUIRED_FRONTMATTER_FIELDS) {
865
+ if (!frontmatter.includes(`${field}:`)) {
866
+ issues.push(`Missing required frontmatter field: ${field}`);
867
+ }
868
+ }
869
+ const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
870
+ if (nameMatch && !nameMatch[1].trim().startsWith("myco:")) {
871
+ issues.push(`Skill name must start with "myco:" prefix. Got: "${nameMatch[1].trim()}"`);
872
+ }
873
+ const managedMatch = frontmatter.match(/^managed_by:\s*(.+)$/m);
874
+ if (managedMatch && managedMatch[1].trim() !== "myco") {
875
+ issues.push(`managed_by must be "myco". Got: "${managedMatch[1].trim()}"`);
876
+ }
877
+ const rawAllowedTools = extractFrontmatterField(content, "allowed-tools");
878
+ if (rawAllowedTools) {
879
+ const rawValue = rawAllowedTools;
880
+ if (rawValue.includes("vault_")) {
881
+ issues.push(
882
+ "allowed-tools contains vault agent tool names (vault_*). Skills run in Claude Code sessions -- use Claude Code tool names instead: Read, Edit, Write, Bash, Grep, Glob"
883
+ );
884
+ } else {
885
+ const parsed = parseAllowedTools(rawValue);
886
+ if (parsed === null) {
887
+ issues.push(
888
+ `allowed-tools value is malformed or empty: "${rawValue}". Provide a comma-separated list of Claude Code tools, e.g. "Read, Edit, Write, Bash, Grep, Glob". Use the narrowest set the skill actually needs.`
889
+ );
890
+ } else {
891
+ const unknown = parsed.filter((t) => !ALLOWED_CLAUDE_CODE_TOOLS.has(t));
892
+ if (unknown.length > 0) {
893
+ issues.push(
894
+ `allowed-tools contains unknown tool name(s): ${unknown.join(", ")}. Valid Claude Code tools: ${[...ALLOWED_CLAUDE_CODE_TOOLS].join(", ")}.`
895
+ );
896
+ }
897
+ }
898
+ }
899
+ }
900
+ const listToolLines = frontmatter.match(/^\s+-\s+vault_\w+/gm);
901
+ if (listToolLines) {
902
+ issues.push(
903
+ "allowed-tools contains vault agent tool names (vault_*). Skills run in Claude Code sessions -- use Claude Code tool names instead: Read, Edit, Write, Bash, Grep, Glob"
904
+ );
905
+ }
906
+ const lineCount = content.split("\n").length;
907
+ if (lineCount > MAX_SKILL_LINES) {
908
+ issues.push(`Skill is ${lineCount} lines (max ${MAX_SKILL_LINES})`);
909
+ }
910
+ return issues;
498
911
  }
499
912
 
500
913
  // src/agent/instruction-builders.ts
501
914
  var SKILL_GENERATE_TASK = "skill-generate";
502
915
  var SKILL_EVOLVE_TASK = "skill-evolve";
916
+ var SKILL_SURVEY_TASK = "skill-survey";
917
+ var SURVEY_MAX_WISDOM_SPORES = 30;
918
+ var SURVEY_MAX_SESSIONS = 15;
919
+ var SURVEY_WATERMARK_KEY = "skill-survey-watermark";
503
920
  function buildSkillGenerateInstruction() {
504
921
  const candidates = listCandidates({ status: "approved", limit: 1 });
505
922
  if (candidates.length === 0) return void 0;
@@ -537,14 +954,108 @@ function buildSkillGenerateInstruction() {
537
954
  }
538
955
  }
539
956
  }
540
- return {
541
- instruction: parts.join("\n"),
542
- context: { candidate_id: c.id }
543
- };
957
+ return {
958
+ instruction: parts.join("\n"),
959
+ context: { candidate_id: c.id }
960
+ };
961
+ }
962
+ function buildSkillSurveyInstruction(agentId) {
963
+ const now = epochSeconds();
964
+ const watermarkState = getState(agentId, SURVEY_WATERMARK_KEY);
965
+ const watermarkEpoch = watermarkState ? Number(watermarkState.value) : 0;
966
+ const sinceFilter = watermarkEpoch > 0 ? { since: watermarkEpoch } : {};
967
+ const parts = [
968
+ "## Pre-assembled Vault Context",
969
+ "",
970
+ `Survey watermark: ${watermarkEpoch === 0 ? "first run (full scan)" : new Date(watermarkEpoch * 1e3).toISOString()}`,
971
+ ""
972
+ ];
973
+ const digests = listDigestExtracts(agentId);
974
+ if (digests.length > 0) {
975
+ const smallest = digests.reduce((a, b) => a.tier < b.tier ? a : b);
976
+ parts.push("### Digest");
977
+ parts.push(`**Tier ${smallest.tier}** (${smallest.content.length} chars):`);
978
+ parts.push(smallest.content);
979
+ parts.push("");
980
+ }
981
+ const wisdomSpores = listSpores({
982
+ observation_type: "wisdom",
983
+ limit: SURVEY_MAX_WISDOM_SPORES,
984
+ ...sinceFilter
985
+ });
986
+ if (wisdomSpores.length > 0) {
987
+ parts.push(`### Wisdom Spores (${wisdomSpores.length})`);
988
+ for (const s of wisdomSpores) {
989
+ parts.push(`- **${s.id}** (importance ${s.importance}): ${s.content.slice(0, 300)}`);
990
+ }
991
+ parts.push("");
992
+ }
993
+ const decisions = listSpores({
994
+ observation_type: "decision",
995
+ limit: 20,
996
+ ...sinceFilter
997
+ });
998
+ const gotchas = listSpores({
999
+ observation_type: "gotcha",
1000
+ limit: 10,
1001
+ ...sinceFilter
1002
+ });
1003
+ if (decisions.length > 0 || gotchas.length > 0) {
1004
+ parts.push(`### Decisions (${decisions.length}) & Gotchas (${gotchas.length})`);
1005
+ for (const s of [...decisions, ...gotchas]) {
1006
+ parts.push(`- **${s.observation_type}** ${s.id}: ${s.content.slice(0, 200)}`);
1007
+ }
1008
+ parts.push("");
1009
+ }
1010
+ const sessions = listSessions({
1011
+ limit: SURVEY_MAX_SESSIONS,
1012
+ ...sinceFilter
1013
+ });
1014
+ if (sessions.length > 0) {
1015
+ parts.push(`### Recent Sessions (${sessions.length})`);
1016
+ for (const s of sessions) {
1017
+ parts.push(`- **${s.id}**: ${s.title ?? "(untitled)"} \u2014 ${(s.summary ?? "").slice(0, 200)}`);
1018
+ }
1019
+ parts.push("");
1020
+ }
1021
+ const activeSkills = listSkillRecords({ status: "active", limit: 100 });
1022
+ parts.push(`### Active Skills (${activeSkills.length})`);
1023
+ for (const s of activeSkills) {
1024
+ parts.push(`- **${s.name}**: ${s.description.slice(0, 150)}`);
1025
+ }
1026
+ parts.push("");
1027
+ setState(agentId, SURVEY_WATERMARK_KEY, String(now), now);
1028
+ return { instruction: parts.join("\n") };
1029
+ }
1030
+ var SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS = 24;
1031
+ var SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN = 8;
1032
+ var MIN_SECTIONS_FOR_STANDALONE = 2;
1033
+ function extractHeadings(content) {
1034
+ const bodyMatch = content.match(/^---[\s\S]*?---\n([\s\S]*)$/);
1035
+ const body = bodyMatch ? bodyMatch[1] : content;
1036
+ return body.split("\n").filter((line) => line.startsWith("## ")).map((line) => line.slice(3).trim());
1037
+ }
1038
+ function headingOverlap(headingsA, headingsB) {
1039
+ if (headingsA.length === 0 || headingsB.length === 0) return { score: 0, shared: [] };
1040
+ const tokenize = (h) => new Set(
1041
+ h.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length >= 4)
1042
+ );
1043
+ const shared = [];
1044
+ for (const a of headingsA) {
1045
+ const aTokens = tokenize(a);
1046
+ for (const b of headingsB) {
1047
+ const bTokens = tokenize(b);
1048
+ const intersection = [...aTokens].filter((t) => bTokens.has(t)).length;
1049
+ const union = (/* @__PURE__ */ new Set([...aTokens, ...bTokens])).size;
1050
+ if (union > 0 && intersection / union >= 0.5) {
1051
+ shared.push(`"${a}" ~ "${b}"`);
1052
+ }
1053
+ }
1054
+ }
1055
+ const smaller = Math.min(headingsA.length, headingsB.length);
1056
+ return { score: smaller > 0 ? shared.length / smaller : 0, shared };
544
1057
  }
545
- var SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS = 24;
546
- var SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN = 5;
547
- function buildSkillEvolveInstruction(params) {
1058
+ function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
548
1059
  const assessIntervalHours = Number(params?.assess_interval_hours ?? SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS);
549
1060
  const maxSkillsPerRun = Number(params?.max_skills_per_run ?? SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN);
550
1061
  const now = epochSeconds();
@@ -563,14 +1074,11 @@ function buildSkillEvolveInstruction(params) {
563
1074
  if (lastAssessedAt > 0 && now - lastAssessedAt < intervalSeconds) continue;
564
1075
  const newSporeIds = listSporeIdsSince(knowledgeWatermark, 10);
565
1076
  if (newSporeIds.length === 0) continue;
566
- const lineage = listLineageForSkill(skill.id, 1);
567
- if (lineage.length === 0) continue;
568
1077
  needsAssessment.push({
569
1078
  id: skill.id,
570
1079
  name: skill.name,
571
1080
  generation: skill.generation,
572
1081
  description: skill.description,
573
- contentSnapshot: lineage[0].content_snapshot,
574
1082
  newSporeIds
575
1083
  });
576
1084
  if (needsAssessment.length >= maxSkillsPerRun) {
@@ -580,6 +1088,48 @@ function buildSkillEvolveInstruction(params) {
580
1088
  if (needsAssessment.length === 0) {
581
1089
  return "No skills need assessment. All active skills are current or were recently assessed. Report skip via vault_report and finish.";
582
1090
  }
1091
+ const structures = [];
1092
+ const skillHeadings = /* @__PURE__ */ new Map();
1093
+ for (const skill of allSkills) {
1094
+ let content = "";
1095
+ if (projectRoot && skill.path) {
1096
+ try {
1097
+ content = readFileSync(resolve(projectRoot, skill.path), "utf-8");
1098
+ } catch {
1099
+ }
1100
+ }
1101
+ const headings = extractHeadings(content);
1102
+ skillHeadings.set(skill.name, headings);
1103
+ structures.push({
1104
+ name: skill.name,
1105
+ sectionCount: headings.length,
1106
+ headings,
1107
+ narrow: headings.length < MIN_SECTIONS_FOR_STANDALONE
1108
+ });
1109
+ }
1110
+ const overlaps = [];
1111
+ for (let i = 0; i < allSkills.length; i++) {
1112
+ for (let j = i + 1; j < allSkills.length; j++) {
1113
+ const a = allSkills[i];
1114
+ const b = allSkills[j];
1115
+ const descJaccard = descriptionSimilarity(a.description, b.description);
1116
+ const aHeadings = skillHeadings.get(a.name) ?? [];
1117
+ const bHeadings = skillHeadings.get(b.name) ?? [];
1118
+ const ho = headingOverlap(aHeadings, bHeadings);
1119
+ const descFlag = descJaccard >= DESCRIPTION_DUPLICATE_THRESHOLD * 0.75;
1120
+ const headingFlag = ho.score >= 0.4;
1121
+ if (!descFlag && !headingFlag) continue;
1122
+ const verdict = descJaccard >= DESCRIPTION_DUPLICATE_THRESHOLD || ho.score >= 0.5 ? "potential-merge" : "potential-narrow";
1123
+ overlaps.push({
1124
+ skillA: a.name,
1125
+ skillB: b.name,
1126
+ descriptionJaccard: Math.round(descJaccard * 100) / 100,
1127
+ headingOverlap: Math.round(ho.score * 100) / 100,
1128
+ sharedHeadings: ho.shared,
1129
+ verdict
1130
+ });
1131
+ }
1132
+ }
583
1133
  const parts = [
584
1134
  `${needsAssessment.length} skill(s) need assessment.`,
585
1135
  `assess_interval_hours: ${assessIntervalHours}`,
@@ -592,19 +1142,66 @@ function buildSkillEvolveInstruction(params) {
592
1142
  parts.push(`id: ${skill.id}`);
593
1143
  parts.push(`description: ${skill.description}`);
594
1144
  parts.push(`new_spore_ids: ${JSON.stringify(skill.newSporeIds)}`);
1145
+ }
1146
+ parts.push("");
1147
+ parts.push("## All Active Skills (for inventory analysis)");
1148
+ for (const s of structures) {
1149
+ const skill = allSkills.find((sk) => sk.name === s.name);
1150
+ const narrowTag = s.narrow ? " **[NARROW \u2014 <2 sections]**" : "";
1151
+ parts.push(`- **${skill.name}** (gen ${skill.generation}, ${s.sectionCount} sections${narrowTag}): ${skill.description.slice(0, 200)}`);
1152
+ if (s.headings.length > 0) {
1153
+ parts.push(` Headings: ${s.headings.join(" | ")}`);
1154
+ }
1155
+ }
1156
+ const narrowSkills = structures.filter((s) => s.narrow);
1157
+ if (narrowSkills.length > 0) {
595
1158
  parts.push("");
596
- parts.push("### Current Content");
1159
+ parts.push("## Mechanically Narrow Skills (<2 H2 sections)");
1160
+ parts.push("These skills have insufficient section breadth for domain-level standalone status.");
1161
+ parts.push("Determine which broader skill each should be absorbed into.");
1162
+ for (const s of narrowSkills) {
1163
+ parts.push(`- **${s.name}**: ${s.sectionCount} section(s). Headings: ${s.headings.length > 0 ? s.headings.join(" | ") : "(none)"}`);
1164
+ }
1165
+ }
1166
+ if (overlaps.length > 0) {
597
1167
  parts.push("");
598
- parts.push(skill.contentSnapshot);
1168
+ parts.push("## Pre-computed Token Overlap");
1169
+ parts.push("Pairs flagged by description token similarity AND/OR heading overlap:");
1170
+ for (const o of overlaps) {
1171
+ parts.push(`- **${o.skillA}** <-> **${o.skillB}**: desc=${o.descriptionJaccard}, headings=${o.headingOverlap} (${o.verdict})`);
1172
+ if (o.sharedHeadings.length > 0) {
1173
+ parts.push(` Shared headings: ${o.sharedHeadings.join("; ")}`);
1174
+ }
1175
+ }
1176
+ }
1177
+ if (similarityProvider) {
1178
+ const idToName = new Map(allSkills.map((s) => [s.id, s.name]));
1179
+ try {
1180
+ const semanticPairs = similarityProvider.pairwiseSimilarity("skill_records", 0.65);
1181
+ if (semanticPairs.length > 0) {
1182
+ parts.push("");
1183
+ parts.push("## Semantic Similarity (embedding cosine distance)");
1184
+ parts.push("Pairs with cosine similarity >= 0.65. This is the STRONGEST overlap signal.");
1185
+ parts.push("High similarity (>0.8) means the skills describe nearly identical procedures.");
1186
+ for (const p of semanticPairs) {
1187
+ const nameA = idToName.get(p.idA) ?? p.idA;
1188
+ const nameB = idToName.get(p.idB) ?? p.idB;
1189
+ parts.push(`- **${nameA}** <-> **${nameB}**: cosine=${p.similarity}`);
1190
+ }
1191
+ }
1192
+ } catch {
1193
+ }
599
1194
  }
600
1195
  return parts.join("\n");
601
1196
  }
602
- function buildTaskInstruction(taskName, taskParams) {
1197
+ function buildTaskInstruction(taskName, taskParams, agentId, projectRoot, similarityProvider) {
603
1198
  switch (taskName) {
604
1199
  case SKILL_GENERATE_TASK:
605
1200
  return buildSkillGenerateInstruction();
1201
+ case SKILL_SURVEY_TASK:
1202
+ return agentId ? buildSkillSurveyInstruction(agentId) : void 0;
606
1203
  case SKILL_EVOLVE_TASK: {
607
- const instruction = buildSkillEvolveInstruction(taskParams);
1204
+ const instruction = buildSkillEvolveInstruction(taskParams, projectRoot, similarityProvider);
608
1205
  return instruction ? { instruction } : void 0;
609
1206
  }
610
1207
  default:
@@ -612,7 +1209,7 @@ function buildTaskInstruction(taskName, taskParams) {
612
1209
  }
613
1210
  }
614
1211
  function isInstructionRequiredTask(taskName) {
615
- return taskName === SKILL_GENERATE_TASK || taskName === SKILL_EVOLVE_TASK;
1212
+ return taskName === SKILL_GENERATE_TASK || taskName === SKILL_EVOLVE_TASK || taskName === SKILL_SURVEY_TASK;
616
1213
  }
617
1214
 
618
1215
  // src/db/queries/runs.ts
@@ -634,7 +1231,7 @@ var RUN_COLUMNS = [
634
1231
  "actions_taken",
635
1232
  "error"
636
1233
  ];
637
- var SELECT_COLUMNS4 = RUN_COLUMNS.join(", ");
1234
+ var SELECT_COLUMNS5 = RUN_COLUMNS.join(", ");
638
1235
  function toRunRow(row) {
639
1236
  return {
640
1237
  id: row.id,
@@ -676,13 +1273,13 @@ function insertRun(data) {
676
1273
  data.error ?? null
677
1274
  );
678
1275
  return toRunRow(
679
- db.prepare(`SELECT ${SELECT_COLUMNS4} FROM agent_runs WHERE id = ?`).get(data.id)
1276
+ db.prepare(`SELECT ${SELECT_COLUMNS5} FROM agent_runs WHERE id = ?`).get(data.id)
680
1277
  );
681
1278
  }
682
1279
  function getRun(id) {
683
1280
  const db = getDatabase();
684
1281
  const row = db.prepare(
685
- `SELECT ${SELECT_COLUMNS4} FROM agent_runs WHERE id = ?`
1282
+ `SELECT ${SELECT_COLUMNS5} FROM agent_runs WHERE id = ?`
686
1283
  ).get(id);
687
1284
  if (!row) return null;
688
1285
  return toRunRow(row);
@@ -717,7 +1314,7 @@ function listRuns(options = {}) {
717
1314
  const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
718
1315
  const offset = options.offset ?? 0;
719
1316
  const rows = db.prepare(
720
- `SELECT ${SELECT_COLUMNS4}
1317
+ `SELECT ${SELECT_COLUMNS5}
721
1318
  FROM agent_runs
722
1319
  ${where}
723
1320
  ORDER BY started_at DESC NULLS LAST
@@ -766,7 +1363,7 @@ function updateRunStatus(id, status, completion) {
766
1363
  ).run(...params);
767
1364
  if (info.changes === 0) return null;
768
1365
  return toRunRow(
769
- db.prepare(`SELECT ${SELECT_COLUMNS4} FROM agent_runs WHERE id = ?`).get(id)
1366
+ db.prepare(`SELECT ${SELECT_COLUMNS5} FROM agent_runs WHERE id = ?`).get(id)
770
1367
  );
771
1368
  }
772
1369
  function getRunningRunForTask(agentId, taskName) {
@@ -875,121 +1472,64 @@ function resolveRunConfig(agentId, requestedTask, vaultDir) {
875
1472
  };
876
1473
  }
877
1474
 
878
- // src/db/queries/notifications.ts
879
- var DEFAULT_LIMIT = 50;
880
- var NOTIFICATION_PRUNE_AGE_SECONDS = 30 * 24 * 60 * 60;
881
- function insertNotification(n) {
882
- const db = getDatabase();
883
- db.prepare(
884
- `INSERT INTO notifications (id, domain, type, level, title, message, mode, status, link, metadata, created_at)
885
- VALUES (?, ?, ?, ?, ?, ?, ?, 'unread', ?, ?, ?)`
886
- ).run(n.id, n.domain, n.type, n.level, n.title, n.message, n.mode, n.link, n.metadata, epochSeconds());
887
- }
888
- function listNotifications(opts = {}) {
889
- const db = getDatabase();
890
- const conditions = [];
891
- const params = [];
892
- if (opts.status) {
893
- conditions.push("status = ?");
894
- params.push(opts.status);
895
- }
896
- if (opts.domain) {
897
- conditions.push("domain = ?");
898
- params.push(opts.domain);
899
- }
900
- if (opts.mode) {
901
- conditions.push("mode = ?");
902
- params.push(opts.mode);
903
- }
904
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
905
- const limit = opts.limit ?? DEFAULT_LIMIT;
906
- const offset = opts.offset ?? 0;
907
- return db.prepare(
908
- `SELECT * FROM notifications ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`
909
- ).all(...params, limit, offset);
910
- }
911
- function countNotifications(status) {
912
- const db = getDatabase();
913
- if (status) {
914
- const row2 = db.prepare("SELECT COUNT(*) as count FROM notifications WHERE status = ?").get(status);
915
- return row2.count;
916
- }
917
- const row = db.prepare("SELECT COUNT(*) as count FROM notifications").get();
918
- return row.count;
919
- }
920
- function getNotification(id) {
921
- const db = getDatabase();
922
- return db.prepare("SELECT * FROM notifications WHERE id = ?").get(id);
923
- }
924
- function updateNotificationStatus(id, status) {
925
- const db = getDatabase();
926
- const result = db.prepare("UPDATE notifications SET status = ? WHERE id = ?").run(status, id);
927
- return result.changes > 0;
1475
+ // src/db/queries/skill-lineage.ts
1476
+ var LINEAGE_COLUMNS = [
1477
+ "id",
1478
+ "skill_id",
1479
+ "generation",
1480
+ "action",
1481
+ "rationale",
1482
+ "source_ids_added",
1483
+ "content_snapshot",
1484
+ "created_at"
1485
+ ];
1486
+ var SELECT_COLUMNS6 = LINEAGE_COLUMNS.join(", ");
1487
+ function toLineageRow(row) {
1488
+ return {
1489
+ id: row.id,
1490
+ skill_id: row.skill_id,
1491
+ generation: row.generation,
1492
+ action: row.action,
1493
+ rationale: row.rationale,
1494
+ source_ids_added: row.source_ids_added ?? "[]",
1495
+ content_snapshot: row.content_snapshot,
1496
+ created_at: row.created_at
1497
+ };
928
1498
  }
929
- function dismissAllNotifications(domain) {
1499
+ function insertLineage(data) {
930
1500
  const db = getDatabase();
931
- if (domain) {
932
- const result2 = db.prepare("UPDATE notifications SET status = 'dismissed' WHERE domain = ? AND status != 'dismissed'").run(domain);
933
- return result2.changes;
934
- }
935
- const result = db.prepare("UPDATE notifications SET status = 'dismissed' WHERE status != 'dismissed'").run();
936
- return result.changes;
1501
+ db.prepare(
1502
+ `INSERT INTO skill_lineage (
1503
+ id, skill_id, generation, action, rationale,
1504
+ source_ids_added, content_snapshot, created_at
1505
+ ) VALUES (
1506
+ ?, ?, ?, ?, ?,
1507
+ ?, ?, ?
1508
+ )`
1509
+ ).run(
1510
+ data.id,
1511
+ data.skill_id,
1512
+ data.generation,
1513
+ data.action,
1514
+ data.rationale,
1515
+ data.source_ids_added ?? "[]",
1516
+ data.content_snapshot,
1517
+ data.created_at
1518
+ );
1519
+ return toLineageRow(
1520
+ db.prepare(`SELECT ${SELECT_COLUMNS6} FROM skill_lineage WHERE id = ?`).get(data.id)
1521
+ );
937
1522
  }
938
- function markAllRead(domain) {
1523
+ function listLineageForSkill(skillId, limit = 50) {
939
1524
  const db = getDatabase();
940
- if (domain) {
941
- const result2 = db.prepare("UPDATE notifications SET status = 'read' WHERE domain = ? AND status = 'unread'").run(domain);
942
- return result2.changes;
943
- }
944
- const result = db.prepare("UPDATE notifications SET status = 'read' WHERE status = 'unread'").run();
945
- return result.changes;
946
- }
947
-
948
- // src/notifications/registry.ts
949
- var domains = /* @__PURE__ */ new Map();
950
- function register(descriptor) {
951
- domains.set(descriptor.domain, descriptor);
952
- }
953
- function getAllDomains() {
954
- return [...domains.values()].sort((a, b) => a.domain.localeCompare(b.domain));
955
- }
956
- function getType(typeId) {
957
- for (const descriptor of domains.values()) {
958
- const match = descriptor.types.find((t) => t.id === typeId);
959
- if (match) return { domain: descriptor, type: match };
960
- }
961
- return void 0;
962
- }
963
-
964
- // src/notifications/notify.ts
965
- import crypto from "crypto";
966
- function notify(vaultDir, payload, config) {
967
- if (!vaultDir) return null;
968
- try {
969
- const cfg = config ?? loadConfig(vaultDir);
970
- if (!cfg.notifications.enabled) return null;
971
- const domainConfig = cfg.notifications.domains[payload.domain];
972
- if (domainConfig && !domainConfig.enabled) return null;
973
- const registeredType = getType(payload.type);
974
- const mode = payload.mode ?? domainConfig?.mode ?? registeredType?.type.defaultMode ?? cfg.notifications.default_mode;
975
- const level = payload.level ?? registeredType?.type.defaultLevel ?? "info";
976
- const id = crypto.randomUUID();
977
- insertNotification({
978
- id,
979
- domain: payload.domain,
980
- type: payload.type,
981
- level,
982
- title: payload.title,
983
- message: payload.message ?? null,
984
- mode,
985
- link: payload.link ?? null,
986
- metadata: payload.metadata ? JSON.stringify(payload.metadata) : null
987
- });
988
- return id;
989
- } catch (err) {
990
- console.warn("[notify] Failed to emit notification:", err instanceof Error ? err.message : err);
991
- return null;
992
- }
1525
+ const rows = db.prepare(
1526
+ `SELECT ${SELECT_COLUMNS6}
1527
+ FROM skill_lineage
1528
+ WHERE skill_id = ?
1529
+ ORDER BY generation DESC
1530
+ LIMIT ?`
1531
+ ).all(skillId, limit);
1532
+ return rows.map(toLineageRow);
993
1533
  }
994
1534
 
995
1535
  // src/db/queries/batches.ts
@@ -1018,7 +1558,7 @@ var BATCH_COLUMNS = [
1018
1558
  "machine_id",
1019
1559
  "synced_at"
1020
1560
  ];
1021
- var SELECT_COLUMNS5 = BATCH_COLUMNS.join(", ");
1561
+ var SELECT_COLUMNS7 = BATCH_COLUMNS.join(", ");
1022
1562
  function toBatchRow(row) {
1023
1563
  return {
1024
1564
  id: row.id,
@@ -1065,7 +1605,7 @@ function getUnprocessedBatches(options = {}) {
1065
1605
  params.push(limit);
1066
1606
  const where = conditions.join(" AND ");
1067
1607
  const rows = db.prepare(
1068
- `SELECT ${SELECT_COLUMNS5}
1608
+ `SELECT ${SELECT_COLUMNS7}
1069
1609
  FROM prompt_batches
1070
1610
  WHERE ${where}
1071
1611
  ORDER BY id ASC
@@ -1082,7 +1622,7 @@ function incrementActivityCount(id) {
1082
1622
  ).run(id);
1083
1623
  if (info.changes === 0) return null;
1084
1624
  return toBatchRow(
1085
- db.prepare(`SELECT ${SELECT_COLUMNS5} FROM prompt_batches WHERE id = ?`).get(id)
1625
+ db.prepare(`SELECT ${SELECT_COLUMNS7} FROM prompt_batches WHERE id = ?`).get(id)
1086
1626
  );
1087
1627
  }
1088
1628
  function markBatchProcessed(id) {
@@ -1094,7 +1634,7 @@ function markBatchProcessed(id) {
1094
1634
  ).run(PROCESSED_FLAG, id);
1095
1635
  if (info.changes === 0) return null;
1096
1636
  return toBatchRow(
1097
- db.prepare(`SELECT ${SELECT_COLUMNS5} FROM prompt_batches WHERE id = ?`).get(id)
1637
+ db.prepare(`SELECT ${SELECT_COLUMNS7} FROM prompt_batches WHERE id = ?`).get(id)
1098
1638
  );
1099
1639
  }
1100
1640
  function findBatchByPromptPrefix(sessionId, promptPrefix) {
@@ -1134,7 +1674,7 @@ function insertBatchStateless(data) {
1134
1674
  );
1135
1675
  const batchId = Number(info.lastInsertRowid);
1136
1676
  return toBatchRow(
1137
- db.prepare(`SELECT ${SELECT_COLUMNS5} FROM prompt_batches WHERE id = ?`).get(batchId)
1677
+ db.prepare(`SELECT ${SELECT_COLUMNS7} FROM prompt_batches WHERE id = ?`).get(batchId)
1138
1678
  );
1139
1679
  }
1140
1680
  function closeOpenBatches(sessionId, endedAt) {
@@ -1155,7 +1695,7 @@ function setResponseSummary(batchId, summary) {
1155
1695
  function getLatestBatch(sessionId) {
1156
1696
  const db = getDatabase();
1157
1697
  const row = db.prepare(
1158
- `SELECT ${SELECT_COLUMNS5} FROM prompt_batches
1698
+ `SELECT ${SELECT_COLUMNS7} FROM prompt_batches
1159
1699
  WHERE session_id = ?
1160
1700
  ORDER BY id DESC LIMIT 1`
1161
1701
  ).get(sessionId);
@@ -1167,7 +1707,7 @@ function listBatchesBySession(sessionId, options = {}) {
1167
1707
  const limit = options.limit ?? BATCHES_DEFAULT_LIMIT;
1168
1708
  const offset = options.offset ?? 0;
1169
1709
  const rows = db.prepare(
1170
- `SELECT ${SELECT_COLUMNS5}
1710
+ `SELECT ${SELECT_COLUMNS7}
1171
1711
  FROM prompt_batches
1172
1712
  WHERE session_id = ?
1173
1713
  ORDER BY prompt_number ASC
@@ -1203,7 +1743,7 @@ var GRAPH_EDGE_COLUMNS = [
1203
1743
  "machine_id",
1204
1744
  "synced_at"
1205
1745
  ];
1206
- var SELECT_COLUMNS6 = GRAPH_EDGE_COLUMNS.join(", ");
1746
+ var SELECT_COLUMNS8 = GRAPH_EDGE_COLUMNS.join(", ");
1207
1747
  function toGraphEdgeRow(row) {
1208
1748
  return {
1209
1749
  id: row.id,
@@ -1244,7 +1784,7 @@ function insertGraphEdge(data) {
1244
1784
  data.machine_id ?? getTeamMachineId()
1245
1785
  );
1246
1786
  const row = toGraphEdgeRow(
1247
- db.prepare(`SELECT ${SELECT_COLUMNS6} FROM graph_edges WHERE id = ?`).get(id)
1787
+ db.prepare(`SELECT ${SELECT_COLUMNS8} FROM graph_edges WHERE id = ?`).get(id)
1248
1788
  );
1249
1789
  syncRow("graph_edges", row);
1250
1790
  return row;
@@ -1273,7 +1813,7 @@ function listGraphEdges(options = {}) {
1273
1813
  const limit = options.limit ?? QUERY_DEFAULT_LIST_LIMIT;
1274
1814
  params.push(limit);
1275
1815
  const rows = db.prepare(
1276
- `SELECT ${SELECT_COLUMNS6}
1816
+ `SELECT ${SELECT_COLUMNS8}
1277
1817
  FROM graph_edges
1278
1818
  ${where}
1279
1819
  ORDER BY created_at DESC
@@ -1293,7 +1833,7 @@ function getGraphForNode(nodeId, nodeType, options) {
1293
1833
  const frontierArray = Array.from(frontier);
1294
1834
  const placeholders = frontierArray.map(() => `?`).join(", ");
1295
1835
  const rows = db.prepare(
1296
- `SELECT ${SELECT_COLUMNS6}
1836
+ `SELECT ${SELECT_COLUMNS8}
1297
1837
  FROM graph_edges
1298
1838
  WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})`
1299
1839
  ).all(...frontierArray, ...frontierArray);
@@ -1334,7 +1874,7 @@ var ENTITY_COLUMNS = [
1334
1874
  "machine_id",
1335
1875
  "synced_at"
1336
1876
  ];
1337
- var SELECT_COLUMNS7 = ENTITY_COLUMNS.join(", ");
1877
+ var SELECT_COLUMNS9 = ENTITY_COLUMNS.join(", ");
1338
1878
  function toEntityRow(row) {
1339
1879
  return {
1340
1880
  id: row.id,
@@ -1368,7 +1908,7 @@ function insertEntity(data) {
1368
1908
  data.machine_id ?? getTeamMachineId()
1369
1909
  );
1370
1910
  const row = toEntityRow(
1371
- db.prepare(`SELECT ${SELECT_COLUMNS7} FROM entities WHERE agent_id = ? AND type = ? AND name = ?`).get(
1911
+ db.prepare(`SELECT ${SELECT_COLUMNS9} FROM entities WHERE agent_id = ? AND type = ? AND name = ?`).get(
1372
1912
  data.agent_id,
1373
1913
  data.type,
1374
1914
  data.name
@@ -1380,7 +1920,7 @@ function insertEntity(data) {
1380
1920
  function getEntity(id) {
1381
1921
  const db = getDatabase();
1382
1922
  const row = db.prepare(
1383
- `SELECT ${SELECT_COLUMNS7} FROM entities WHERE id = ?`
1923
+ `SELECT ${SELECT_COLUMNS9} FROM entities WHERE id = ?`
1384
1924
  ).get(id);
1385
1925
  if (!row) return null;
1386
1926
  return toEntityRow(row);
@@ -1421,7 +1961,7 @@ function listEntities(options = {}) {
1421
1961
  params.push(limit);
1422
1962
  params.push(offset);
1423
1963
  const rows = db.prepare(
1424
- `SELECT ${SELECT_COLUMNS7}
1964
+ `SELECT ${SELECT_COLUMNS9}
1425
1965
  FROM entities
1426
1966
  ${where}
1427
1967
  ORDER BY last_seen DESC
@@ -1431,65 +1971,6 @@ function listEntities(options = {}) {
1431
1971
  return rows.map(toEntityRow);
1432
1972
  }
1433
1973
 
1434
- // src/db/queries/digest-extracts.ts
1435
- var EXTRACT_COLUMNS = [
1436
- "id",
1437
- "agent_id",
1438
- "tier",
1439
- "content",
1440
- "substrate_hash",
1441
- "generated_at",
1442
- "machine_id",
1443
- "synced_at"
1444
- ];
1445
- var SELECT_COLUMNS8 = EXTRACT_COLUMNS.join(", ");
1446
- function toDigestExtractRow(row) {
1447
- return {
1448
- id: row.id,
1449
- agent_id: row.agent_id,
1450
- tier: row.tier,
1451
- content: row.content,
1452
- substrate_hash: row.substrate_hash ?? null,
1453
- generated_at: row.generated_at,
1454
- machine_id: row.machine_id ?? "local",
1455
- synced_at: row.synced_at ?? null
1456
- };
1457
- }
1458
- function upsertDigestExtract(data) {
1459
- const db = getDatabase();
1460
- db.prepare(
1461
- `INSERT INTO digest_extracts (agent_id, tier, content, generated_at)
1462
- VALUES (?, ?, ?, ?)
1463
- ON CONFLICT (agent_id, tier) DO UPDATE SET
1464
- content = EXCLUDED.content,
1465
- generated_at = EXCLUDED.generated_at`
1466
- ).run(data.agent_id, data.tier, data.content, data.generated_at);
1467
- const row = db.prepare(
1468
- `SELECT ${SELECT_COLUMNS8} FROM digest_extracts WHERE agent_id = ? AND tier = ?`
1469
- ).get(data.agent_id, data.tier);
1470
- return toDigestExtractRow(row);
1471
- }
1472
- function getDigestExtract(agentId, tier) {
1473
- const db = getDatabase();
1474
- const row = db.prepare(
1475
- `SELECT ${SELECT_COLUMNS8} FROM digest_extracts
1476
- WHERE agent_id = ? AND tier = ?`
1477
- ).get(agentId, tier);
1478
- if (!row) return null;
1479
- return toDigestExtractRow(row);
1480
- }
1481
- function listDigestExtracts(agentId) {
1482
- const db = getDatabase();
1483
- const tierPlaceholders = DIGEST_TIERS.map(() => "?").join(", ");
1484
- const rows = db.prepare(
1485
- `SELECT ${SELECT_COLUMNS8}
1486
- FROM digest_extracts
1487
- WHERE agent_id = ? AND tier IN (${tierPlaceholders})
1488
- ORDER BY tier ASC`
1489
- ).all(agentId, ...DIGEST_TIERS);
1490
- return rows.map(toDigestExtractRow);
1491
- }
1492
-
1493
1974
  // src/db/queries/lineage.ts
1494
1975
  function createSporeLineage(spore) {
1495
1976
  if (spore.session_id) {
@@ -1556,7 +2037,7 @@ var REPORT_COLUMNS = [
1556
2037
  "details",
1557
2038
  "created_at"
1558
2039
  ];
1559
- var SELECT_COLUMNS9 = REPORT_COLUMNS.join(", ");
2040
+ var SELECT_COLUMNS10 = REPORT_COLUMNS.join(", ");
1560
2041
  function toReportRow(row) {
1561
2042
  return {
1562
2043
  id: row.id,
@@ -1586,13 +2067,13 @@ function insertReport(data) {
1586
2067
  );
1587
2068
  const reportId = Number(info.lastInsertRowid);
1588
2069
  return toReportRow(
1589
- db.prepare(`SELECT ${SELECT_COLUMNS9} FROM agent_reports WHERE id = ?`).get(reportId)
2070
+ db.prepare(`SELECT ${SELECT_COLUMNS10} FROM agent_reports WHERE id = ?`).get(reportId)
1590
2071
  );
1591
2072
  }
1592
2073
  function listReports(runId) {
1593
2074
  const db = getDatabase();
1594
2075
  const rows = db.prepare(
1595
- `SELECT ${SELECT_COLUMNS9}
2076
+ `SELECT ${SELECT_COLUMNS10}
1596
2077
  FROM agent_reports
1597
2078
  WHERE run_id = ?
1598
2079
  ORDER BY created_at ASC`
@@ -1649,6 +2130,14 @@ export {
1649
2130
  getDigestExtract,
1650
2131
  listDigestExtracts,
1651
2132
  errorMessage,
2133
+ setState,
2134
+ getStatesForAgent,
2135
+ descriptionSimilarity,
2136
+ topicOverlapSimilarity,
2137
+ DESCRIPTION_DUPLICATE_THRESHOLD,
2138
+ TOPIC_OVERLAP_THRESHOLD,
2139
+ checkFrontmatterPreservation,
2140
+ validateSkillContent,
1652
2141
  SKILL_GENERATE_TASK,
1653
2142
  buildTaskInstruction,
1654
2143
  isInstructionRequiredTask,
@@ -1669,4 +2158,4 @@ export {
1669
2158
  hasConfiguredProvider,
1670
2159
  resolveRunConfig
1671
2160
  };
1672
- //# sourceMappingURL=chunk-RMJPQZGF.js.map
2161
+ //# sourceMappingURL=chunk-C3EGL5JX.js.map