@goondocks/myco 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +17 -130
  2. package/dist/{agent-run-2NFYMQXW.js → agent-run-EADUYYAS.js} +6 -6
  3. package/dist/{agent-tasks-MEIYLXGN.js → agent-tasks-GC77JXQB.js} +6 -6
  4. package/dist/{chunk-EO2RQW4S.js → chunk-2CKDAFSX.js} +2 -2
  5. package/dist/{chunk-NZI7WBZI.js → chunk-2DF4OZ2D.js} +22 -2
  6. package/dist/chunk-2DF4OZ2D.js.map +1 -0
  7. package/dist/{chunk-OW433Q4C.js → chunk-2LN2BBKA.js} +45 -4
  8. package/dist/chunk-2LN2BBKA.js.map +1 -0
  9. package/dist/{chunk-U7GJTVSX.js → chunk-2OO3BRFK.js} +21 -7
  10. package/dist/chunk-2OO3BRFK.js.map +1 -0
  11. package/dist/{chunk-RAV5YMRU.js → chunk-3TPD6HEF.js} +4 -4
  12. package/dist/{chunk-JMOUFG6Y.js → chunk-44PZCAYS.js} +47 -5
  13. package/dist/chunk-44PZCAYS.js.map +1 -0
  14. package/dist/{chunk-D7TYRPRM.js → chunk-6LQIMRTC.js} +145 -145
  15. package/dist/chunk-6LQIMRTC.js.map +1 -0
  16. package/dist/{chunk-NI23QCHB.js → chunk-AELJ4PS5.js} +5 -5
  17. package/dist/{chunk-BUIR3JWM.js → chunk-CYBC2HZ3.js} +3 -3
  18. package/dist/chunk-EM63ZFKA.js +166 -0
  19. package/dist/chunk-EM63ZFKA.js.map +1 -0
  20. package/dist/{chunk-O3TRN3RC.js → chunk-INWD6AIQ.js} +2 -2
  21. package/dist/{chunk-CML4MCYF.js → chunk-KSXTNYXO.js} +2 -2
  22. package/dist/{chunk-KWTOCJLB.js → chunk-LLJMDXO2.js} +1176 -241
  23. package/dist/chunk-LLJMDXO2.js.map +1 -0
  24. package/dist/{chunk-2V7HR7HB.js → chunk-MDEUXYJG.js} +4 -4
  25. package/dist/{chunk-PFWIPRF6.js → chunk-MS6FDV45.js} +3 -3
  26. package/dist/{chunk-55QEICRO.js → chunk-N77K772N.js} +3 -3
  27. package/dist/{chunk-E4VLWIJC.js → chunk-ODXLRR4U.js} +1 -1
  28. package/dist/{chunk-DLFDBKEV.js → chunk-OZF5EURR.js} +19 -16
  29. package/dist/chunk-OZF5EURR.js.map +1 -0
  30. package/dist/{chunk-IB76KGBY.js → chunk-POEPHBQK.js} +1 -1
  31. package/dist/{chunk-7OYXB2NM.js → chunk-REN37KYI.js} +6 -2
  32. package/dist/chunk-REN37KYI.js.map +1 -0
  33. package/dist/{chunk-JDI4DPWD.js → chunk-RXROZBSK.js} +637 -150
  34. package/dist/chunk-RXROZBSK.js.map +1 -0
  35. package/dist/{chunk-U3J2DDSR.js → chunk-SCI55NKY.js} +2 -2
  36. package/dist/{chunk-GDY63YAW.js → chunk-U6PF3YII.js} +79 -79
  37. package/dist/chunk-U6PF3YII.js.map +1 -0
  38. package/dist/{chunk-FABWUX5G.js → chunk-UVKQ62II.js} +18 -4
  39. package/dist/chunk-UVKQ62II.js.map +1 -0
  40. package/dist/{chunk-VOCGURV7.js → chunk-UW6DGPSV.js} +3 -3
  41. package/dist/{chunk-CKJAWZQE.js → chunk-W4VHC2ES.js} +11 -3
  42. package/dist/chunk-W4VHC2ES.js.map +1 -0
  43. package/dist/{chunk-75AZFBFW.js → chunk-YPWF322W.js} +3 -3
  44. package/dist/{cli-IIMBALPV.js → cli-X7CFP4YD.js} +39 -39
  45. package/dist/{client-VZCUISHZ.js → client-YA33HUFY.js} +4 -4
  46. package/dist/{config-DA4IUVFL.js → config-RFB2DJC6.js} +6 -6
  47. package/dist/{detect-GEM3NVK6.js → detect-BEOIHGBC.js} +5 -5
  48. package/dist/{detect-providers-PSVKXTWE.js → detect-providers-2OQBU4VX.js} +4 -4
  49. package/dist/{doctor-QYD34X7Q.js → doctor-FAH7N66M.js} +11 -11
  50. package/dist/{executor-NSPRTH4M.js → executor-ICTRRUBY.js} +93 -285
  51. package/dist/executor-ICTRRUBY.js.map +1 -0
  52. package/dist/{init-WYYL44KZ.js → init-PTJEOTJV.js} +15 -15
  53. package/dist/{llm-KEDHK3TQ.js → llm-7D2OGDEK.js} +4 -4
  54. package/dist/{loader-Q3P3R4UP.js → loader-O2JFO2UC.js} +6 -6
  55. package/dist/{loader-SKKUMT5C.js → loader-VPE4RCIF.js} +6 -6
  56. package/dist/{main-6PY3ITQ5.js → main-EIKBLOUL.js} +752 -264
  57. package/dist/main-EIKBLOUL.js.map +1 -0
  58. package/dist/{open-HRFMJDQX.js → open-2JCSOLZS.js} +6 -6
  59. package/dist/{post-compact-HT24YMAN.js → post-compact-2HPPWPBI.js} +10 -10
  60. package/dist/{post-tool-use-DENRI5WB.js → post-tool-use-TWBBBABS.js} +9 -9
  61. package/dist/{post-tool-use-failure-A6SNJX42.js → post-tool-use-failure-LIJYR4KL.js} +10 -10
  62. package/dist/{pre-compact-3Q4BALCL.js → pre-compact-II2CMNTG.js} +10 -10
  63. package/dist/{provider-check-AE3L5Z6R.js → provider-check-KEQNQ6LO.js} +4 -4
  64. package/dist/{registry-O2NZLO3V.js → registry-X5FDGYXT.js} +7 -7
  65. package/dist/{remove-YB5A6HY2.js → remove-L5MVYBOY.js} +11 -11
  66. package/dist/{resolution-events-XWYLLDRK.js → resolution-events-MVIZMONR.js} +4 -4
  67. package/dist/{restart-RGDVHELZ.js → restart-VIT3JBD6.js} +7 -7
  68. package/dist/{search-WOHT3G55.js → search-O6BB5MTO.js} +7 -7
  69. package/dist/{server-6SUNYDV7.js → server-O3UPJVBR.js} +258 -173
  70. package/dist/server-O3UPJVBR.js.map +1 -0
  71. package/dist/{session-W3SKRFRV.js → session-5JV3DQIK.js} +8 -8
  72. package/dist/{session-end-OUTY7AFF.js → session-end-PZ2OXBGG.js} +9 -9
  73. package/dist/{session-start-5MB3LFOA.js → session-start-FDGM56BX.js} +22 -17
  74. package/dist/{session-start-5MB3LFOA.js.map → session-start-FDGM56BX.js.map} +1 -1
  75. package/dist/{setup-llm-ZMYGIQX5.js → setup-llm-MQK557BB.js} +10 -10
  76. package/dist/src/agent/definitions/tasks/extract-only.yaml +1 -1
  77. package/dist/src/agent/definitions/tasks/full-intelligence.yaml +10 -0
  78. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +163 -49
  79. package/dist/src/agent/definitions/tasks/skill-generate.yaml +44 -27
  80. package/dist/src/agent/definitions/tasks/skill-survey.yaml +132 -138
  81. package/dist/src/agent/definitions/tasks/supersession-sweep.yaml +1 -1
  82. package/dist/src/cli.js +1 -1
  83. package/dist/src/daemon/main.js +1 -1
  84. package/dist/src/hooks/post-tool-use.js +1 -1
  85. package/dist/src/hooks/session-end.js +1 -1
  86. package/dist/src/hooks/session-start.js +1 -1
  87. package/dist/src/hooks/stop.js +1 -1
  88. package/dist/src/hooks/user-prompt-submit.js +1 -1
  89. package/dist/src/mcp/server.js +1 -1
  90. package/dist/src/symbionts/manifests/codex.yaml +45 -7
  91. package/dist/{stats-DGI6B3HX.js → stats-2STTARTC.js} +11 -11
  92. package/dist/{stop-YGHODSP7.js → stop-WNKCMCGO.js} +9 -9
  93. package/dist/{stop-failure-7IJTPJ6W.js → stop-failure-6GTOBVTN.js} +10 -10
  94. package/dist/{subagent-start-ZBQ5PJB5.js → subagent-start-VJF5YKVX.js} +10 -10
  95. package/dist/{subagent-stop-N2TDQU2D.js → subagent-stop-UW6HMICY.js} +10 -10
  96. package/dist/{task-completed-BDLMRSBB.js → task-completed-U4Q3XXLX.js} +10 -10
  97. package/dist/{team-2ZFGTSIN.js → team-N6TXS2PF.js} +148 -103
  98. package/dist/team-N6TXS2PF.js.map +1 -0
  99. package/dist/ui/assets/{index-DtT9_nlT.js → index-CHIm98OP.js} +48 -48
  100. package/dist/ui/index.html +1 -1
  101. package/dist/{update-STLAN7LR.js → update-ZYCOWKMD.js} +11 -11
  102. package/dist/{user-prompt-submit-4IBFUYQ3.js → user-prompt-submit-SOYL4OWF.js} +15 -12
  103. package/dist/user-prompt-submit-SOYL4OWF.js.map +1 -0
  104. package/dist/{verify-EJYPO7QA.js → verify-P37PQ4YM.js} +8 -8
  105. package/dist/{version-YPBIKH77.js → version-XAWC277D.js} +2 -2
  106. package/package.json +25 -8
  107. package/CONTRIBUTING.md +0 -132
  108. package/dist/chunk-7OYXB2NM.js.map +0 -1
  109. package/dist/chunk-CKJAWZQE.js.map +0 -1
  110. package/dist/chunk-D7TYRPRM.js.map +0 -1
  111. package/dist/chunk-DLFDBKEV.js.map +0 -1
  112. package/dist/chunk-FABWUX5G.js.map +0 -1
  113. package/dist/chunk-GDY63YAW.js.map +0 -1
  114. package/dist/chunk-JDI4DPWD.js.map +0 -1
  115. package/dist/chunk-JMOUFG6Y.js.map +0 -1
  116. package/dist/chunk-KWTOCJLB.js.map +0 -1
  117. package/dist/chunk-NZI7WBZI.js.map +0 -1
  118. package/dist/chunk-OW433Q4C.js.map +0 -1
  119. package/dist/chunk-RJMXDUMA.js +0 -40
  120. package/dist/chunk-RJMXDUMA.js.map +0 -1
  121. package/dist/chunk-U7GJTVSX.js.map +0 -1
  122. package/dist/executor-NSPRTH4M.js.map +0 -1
  123. package/dist/main-6PY3ITQ5.js.map +0 -1
  124. package/dist/server-6SUNYDV7.js.map +0 -1
  125. package/dist/src/worker/package-lock.json +0 -4338
  126. package/dist/src/worker/package.json +0 -22
  127. package/dist/src/worker/src/auth.ts +0 -31
  128. package/dist/src/worker/src/index.ts +0 -470
  129. package/dist/src/worker/src/mcp/auth.ts +0 -65
  130. package/dist/src/worker/src/mcp/server.ts +0 -53
  131. package/dist/src/worker/src/mcp/tools/context.ts +0 -13
  132. package/dist/src/worker/src/mcp/tools/get.ts +0 -15
  133. package/dist/src/worker/src/mcp/tools/graph.ts +0 -35
  134. package/dist/src/worker/src/mcp/tools/search.ts +0 -32
  135. package/dist/src/worker/src/mcp/tools/sessions.ts +0 -24
  136. package/dist/src/worker/src/mcp/tools/skills.ts +0 -16
  137. package/dist/src/worker/src/mcp/tools/team.ts +0 -9
  138. package/dist/src/worker/src/schema.ts +0 -324
  139. package/dist/src/worker/src/search-helpers.ts +0 -70
  140. package/dist/src/worker/tsconfig.json +0 -16
  141. package/dist/src/worker/wrangler.toml +0 -30
  142. package/dist/team-2ZFGTSIN.js.map +0 -1
  143. package/dist/user-prompt-submit-4IBFUYQ3.js.map +0 -1
  144. /package/dist/{agent-run-2NFYMQXW.js.map → agent-run-EADUYYAS.js.map} +0 -0
  145. /package/dist/{agent-tasks-MEIYLXGN.js.map → agent-tasks-GC77JXQB.js.map} +0 -0
  146. /package/dist/{chunk-EO2RQW4S.js.map → chunk-2CKDAFSX.js.map} +0 -0
  147. /package/dist/{chunk-RAV5YMRU.js.map → chunk-3TPD6HEF.js.map} +0 -0
  148. /package/dist/{chunk-NI23QCHB.js.map → chunk-AELJ4PS5.js.map} +0 -0
  149. /package/dist/{chunk-BUIR3JWM.js.map → chunk-CYBC2HZ3.js.map} +0 -0
  150. /package/dist/{chunk-O3TRN3RC.js.map → chunk-INWD6AIQ.js.map} +0 -0
  151. /package/dist/{chunk-CML4MCYF.js.map → chunk-KSXTNYXO.js.map} +0 -0
  152. /package/dist/{chunk-2V7HR7HB.js.map → chunk-MDEUXYJG.js.map} +0 -0
  153. /package/dist/{chunk-PFWIPRF6.js.map → chunk-MS6FDV45.js.map} +0 -0
  154. /package/dist/{chunk-55QEICRO.js.map → chunk-N77K772N.js.map} +0 -0
  155. /package/dist/{chunk-E4VLWIJC.js.map → chunk-ODXLRR4U.js.map} +0 -0
  156. /package/dist/{chunk-IB76KGBY.js.map → chunk-POEPHBQK.js.map} +0 -0
  157. /package/dist/{chunk-U3J2DDSR.js.map → chunk-SCI55NKY.js.map} +0 -0
  158. /package/dist/{chunk-VOCGURV7.js.map → chunk-UW6DGPSV.js.map} +0 -0
  159. /package/dist/{chunk-75AZFBFW.js.map → chunk-YPWF322W.js.map} +0 -0
  160. /package/dist/{cli-IIMBALPV.js.map → cli-X7CFP4YD.js.map} +0 -0
  161. /package/dist/{client-VZCUISHZ.js.map → client-YA33HUFY.js.map} +0 -0
  162. /package/dist/{config-DA4IUVFL.js.map → config-RFB2DJC6.js.map} +0 -0
  163. /package/dist/{detect-GEM3NVK6.js.map → detect-BEOIHGBC.js.map} +0 -0
  164. /package/dist/{detect-providers-PSVKXTWE.js.map → detect-providers-2OQBU4VX.js.map} +0 -0
  165. /package/dist/{doctor-QYD34X7Q.js.map → doctor-FAH7N66M.js.map} +0 -0
  166. /package/dist/{init-WYYL44KZ.js.map → init-PTJEOTJV.js.map} +0 -0
  167. /package/dist/{llm-KEDHK3TQ.js.map → llm-7D2OGDEK.js.map} +0 -0
  168. /package/dist/{loader-Q3P3R4UP.js.map → loader-O2JFO2UC.js.map} +0 -0
  169. /package/dist/{loader-SKKUMT5C.js.map → loader-VPE4RCIF.js.map} +0 -0
  170. /package/dist/{open-HRFMJDQX.js.map → open-2JCSOLZS.js.map} +0 -0
  171. /package/dist/{post-compact-HT24YMAN.js.map → post-compact-2HPPWPBI.js.map} +0 -0
  172. /package/dist/{post-tool-use-DENRI5WB.js.map → post-tool-use-TWBBBABS.js.map} +0 -0
  173. /package/dist/{post-tool-use-failure-A6SNJX42.js.map → post-tool-use-failure-LIJYR4KL.js.map} +0 -0
  174. /package/dist/{pre-compact-3Q4BALCL.js.map → pre-compact-II2CMNTG.js.map} +0 -0
  175. /package/dist/{provider-check-AE3L5Z6R.js.map → provider-check-KEQNQ6LO.js.map} +0 -0
  176. /package/dist/{registry-O2NZLO3V.js.map → registry-X5FDGYXT.js.map} +0 -0
  177. /package/dist/{remove-YB5A6HY2.js.map → remove-L5MVYBOY.js.map} +0 -0
  178. /package/dist/{resolution-events-XWYLLDRK.js.map → resolution-events-MVIZMONR.js.map} +0 -0
  179. /package/dist/{restart-RGDVHELZ.js.map → restart-VIT3JBD6.js.map} +0 -0
  180. /package/dist/{search-WOHT3G55.js.map → search-O6BB5MTO.js.map} +0 -0
  181. /package/dist/{session-W3SKRFRV.js.map → session-5JV3DQIK.js.map} +0 -0
  182. /package/dist/{session-end-OUTY7AFF.js.map → session-end-PZ2OXBGG.js.map} +0 -0
  183. /package/dist/{setup-llm-ZMYGIQX5.js.map → setup-llm-MQK557BB.js.map} +0 -0
  184. /package/dist/{stats-DGI6B3HX.js.map → stats-2STTARTC.js.map} +0 -0
  185. /package/dist/{stop-YGHODSP7.js.map → stop-WNKCMCGO.js.map} +0 -0
  186. /package/dist/{stop-failure-7IJTPJ6W.js.map → stop-failure-6GTOBVTN.js.map} +0 -0
  187. /package/dist/{subagent-start-ZBQ5PJB5.js.map → subagent-start-VJF5YKVX.js.map} +0 -0
  188. /package/dist/{subagent-stop-N2TDQU2D.js.map → subagent-stop-UW6HMICY.js.map} +0 -0
  189. /package/dist/{task-completed-BDLMRSBB.js.map → task-completed-U4Q3XXLX.js.map} +0 -0
  190. /package/dist/{update-STLAN7LR.js.map → update-ZYCOWKMD.js.map} +0 -0
  191. /package/dist/{verify-EJYPO7QA.js.map → verify-P37PQ4YM.js.map} +0 -0
  192. /package/dist/{version-YPBIKH77.js.map → version-XAWC277D.js.map} +0 -0
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  getTeamMachineId,
4
4
  syncRow
5
- } from "./chunk-O3TRN3RC.js";
5
+ } from "./chunk-INWD6AIQ.js";
6
6
  import {
7
7
  getDatabase
8
8
  } from "./chunk-MYX5NCRH.js";
@@ -149,6 +149,10 @@ function buildSessionsWhere(options) {
149
149
  const pattern = `%${options.search}%`;
150
150
  params.push(pattern, pattern);
151
151
  }
152
+ if (options.since !== void 0) {
153
+ conditions.push("created_at > ?");
154
+ params.push(options.since);
155
+ }
152
156
  return {
153
157
  where: conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "",
154
158
  params
@@ -254,8 +258,10 @@ function deleteSessionCascade(sessionId) {
254
258
  const exists = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);
255
259
  if (!exists) return zeroCounts;
256
260
  const sporeIds = db.prepare(
257
- `SELECT id FROM spores WHERE session_id = ?`
258
- ).all(sessionId).map((r) => r.id);
261
+ `SELECT id FROM spores
262
+ WHERE session_id = ?
263
+ OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)`
264
+ ).all(sessionId, sessionId).map((r) => r.id);
259
265
  const attachmentPaths = db.prepare(
260
266
  `SELECT file_path FROM attachments WHERE session_id = ?`
261
267
  ).all(sessionId).map((r) => r.file_path);
@@ -267,10 +273,18 @@ function deleteSessionCascade(sessionId) {
267
273
  const resEvents = db.prepare(
268
274
  `DELETE FROM resolution_events
269
275
  WHERE session_id = ?
270
- OR spore_id IN (SELECT id FROM spores WHERE session_id = ?)`
271
- ).run(sessionId, sessionId);
276
+ OR spore_id IN (
277
+ SELECT id FROM spores
278
+ WHERE session_id = ?
279
+ OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)
280
+ )`
281
+ ).run(sessionId, sessionId, sessionId);
272
282
  const edges = db.prepare(`DELETE FROM graph_edges WHERE session_id = ?`).run(sessionId);
273
- const spores = db.prepare(`DELETE FROM spores WHERE session_id = ?`).run(sessionId);
283
+ const spores = db.prepare(
284
+ `DELETE FROM spores
285
+ WHERE session_id = ?
286
+ OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)`
287
+ ).run(sessionId, sessionId);
274
288
  const prompts = db.prepare(`DELETE FROM prompt_batches WHERE session_id = ?`).run(sessionId);
275
289
  const session = db.prepare(`DELETE FROM sessions WHERE id = ?`).run(sessionId);
276
290
  return {
@@ -302,4 +316,4 @@ export {
302
316
  getSessionImpact,
303
317
  deleteSessionCascade
304
318
  };
305
- //# sourceMappingURL=chunk-U7GJTVSX.js.map
319
+ //# sourceMappingURL=chunk-2OO3BRFK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/queries/sessions.ts"],"sourcesContent":["/**\n * Session 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 sessions returned by listSessions when no limit given. */\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Session status value when a session is closed normally. */\nconst STATUS_COMPLETED = 'completed';\n\n/** Default session status for new sessions. */\nconst DEFAULT_STATUS = 'active';\n\n/** Default prompt count for new sessions. */\nconst DEFAULT_PROMPT_COUNT = 0;\n\n/** Default tool count for new sessions. */\nconst DEFAULT_TOOL_COUNT = 0;\n\n/** Default processed flag for new sessions. */\nconst DEFAULT_PROCESSED = 0;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Fields required (or optional) when inserting/upserting a session. */\nexport interface SessionInsert {\n id: string;\n agent: string;\n started_at: number;\n created_at: number;\n user?: string | null;\n project_root?: string | null;\n branch?: string | null;\n ended_at?: number | null;\n status?: string;\n prompt_count?: number;\n tool_count?: number;\n title?: string | null;\n summary?: string | null;\n transcript_path?: string | null;\n parent_session_id?: string | null;\n parent_session_reason?: string | null;\n processed?: number;\n content_hash?: string | null;\n machine_id?: string;\n}\n\n/** Row shape returned from session queries (all columns). */\nexport interface SessionRow {\n id: string;\n agent: string;\n user: string | null;\n project_root: string | null;\n branch: string | null;\n started_at: number;\n ended_at: number | null;\n status: string;\n prompt_count: number;\n tool_count: number;\n title: string | null;\n summary: string | null;\n transcript_path: string | null;\n parent_session_id: string | null;\n parent_session_reason: string | null;\n processed: number;\n content_hash: string | null;\n embedded: number;\n created_at: number;\n machine_id: string;\n synced_at: number | null;\n}\n\n/** Updatable fields for `updateSession`. */\nexport interface SessionUpdate {\n agent?: string;\n user?: string | null;\n project_root?: string | null;\n branch?: string | null;\n ended_at?: number | null;\n status?: string;\n prompt_count?: number;\n tool_count?: number;\n title?: string | null;\n summary?: string | null;\n transcript_path?: string | null;\n parent_session_id?: string | null;\n parent_session_reason?: string | null;\n processed?: number;\n content_hash?: string | null;\n}\n\n/** Filter options for `listSessions`. */\nexport interface ListSessionsOptions {\n limit?: number;\n offset?: number;\n status?: string;\n agent?: string;\n search?: string;\n /** Only return sessions created after this epoch-seconds timestamp. */\n since?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Column list\n// ---------------------------------------------------------------------------\n\nconst SESSION_COLUMNS = [\n 'id',\n 'agent',\n '\"user\"',\n 'project_root',\n 'branch',\n 'started_at',\n 'ended_at',\n 'status',\n 'prompt_count',\n 'tool_count',\n 'title',\n 'summary',\n 'transcript_path',\n 'parent_session_id',\n 'parent_session_reason',\n 'processed',\n 'content_hash',\n 'embedded',\n 'created_at',\n 'machine_id',\n 'synced_at',\n] as const;\n\nconst SELECT_COLUMNS = SESSION_COLUMNS.join(', ');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a SQLite result row into a typed SessionRow.\n *\n * The quoted \"user\" column comes back as `user` in the result object.\n */\nfunction toSessionRow(row: Record<string, unknown>): SessionRow {\n return {\n id: row.id as string,\n agent: row.agent as string,\n user: (row.user as string) ?? null,\n project_root: (row.project_root as string) ?? null,\n branch: (row.branch as string) ?? null,\n started_at: row.started_at as number,\n ended_at: (row.ended_at as number) ?? null,\n status: row.status as string,\n prompt_count: row.prompt_count as number,\n tool_count: row.tool_count as number,\n title: (row.title as string) ?? null,\n summary: (row.summary as string) ?? null,\n transcript_path: (row.transcript_path as string) ?? null,\n parent_session_id: (row.parent_session_id as string) ?? null,\n parent_session_reason: (row.parent_session_reason as string) ?? null,\n processed: row.processed as number,\n content_hash: (row.content_hash as string) ?? null,\n embedded: (row.embedded as number) ?? 0,\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 session or update it if the id already exists.\n *\n * On conflict the row is updated with the values from `data`, preserving\n * any columns not supplied via COALESCE with EXCLUDED values.\n */\nexport function upsertSession(data: SessionInsert): SessionRow {\n const db = getDatabase();\n\n db.prepare(\n `INSERT INTO sessions (\n id, agent, \"user\", project_root, branch,\n started_at, ended_at, status, prompt_count, tool_count,\n title, summary, transcript_path,\n parent_session_id, parent_session_reason,\n processed, content_hash, created_at, machine_id\n ) VALUES (\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?,\n ?, ?,\n ?, ?, ?, ?\n )\n ON CONFLICT (id) DO UPDATE SET\n agent = EXCLUDED.agent,\n \"user\" = EXCLUDED.\"user\",\n project_root = EXCLUDED.project_root,\n branch = EXCLUDED.branch,\n started_at = EXCLUDED.started_at,\n ended_at = COALESCE(EXCLUDED.ended_at, sessions.ended_at),\n status = COALESCE(EXCLUDED.status, sessions.status),\n prompt_count = CASE WHEN ? THEN EXCLUDED.prompt_count ELSE sessions.prompt_count END,\n tool_count = CASE WHEN ? THEN EXCLUDED.tool_count ELSE sessions.tool_count END,\n title = COALESCE(EXCLUDED.title, sessions.title),\n summary = COALESCE(EXCLUDED.summary, sessions.summary),\n transcript_path = COALESCE(EXCLUDED.transcript_path, sessions.transcript_path),\n parent_session_id = EXCLUDED.parent_session_id,\n parent_session_reason = EXCLUDED.parent_session_reason,\n processed = COALESCE(EXCLUDED.processed, sessions.processed),\n content_hash = EXCLUDED.content_hash`,\n ).run(\n data.id,\n data.agent,\n data.user ?? null,\n data.project_root ?? null,\n data.branch ?? null,\n data.started_at,\n data.ended_at ?? null,\n data.status ?? DEFAULT_STATUS,\n data.prompt_count ?? DEFAULT_PROMPT_COUNT,\n data.tool_count ?? DEFAULT_TOOL_COUNT,\n data.title ?? null,\n data.summary ?? null,\n data.transcript_path ?? null,\n data.parent_session_id ?? null,\n data.parent_session_reason ?? null,\n data.processed ?? DEFAULT_PROCESSED,\n data.content_hash ?? null,\n data.created_at,\n data.machine_id ?? getTeamMachineId(),\n data.prompt_count !== undefined ? 1 : 0,\n data.tool_count !== undefined ? 1 : 0,\n );\n\n const row = toSessionRow(\n db.prepare(`SELECT ${SELECT_COLUMNS} FROM sessions WHERE id = ?`).get(data.id) as Record<string, unknown>,\n );\n\n syncRow('sessions', row);\n\n return row;\n}\n\n/**\n * Retrieve a single session by id.\n *\n * @returns the session row, or null if not found.\n */\nexport function getSession(id: string): SessionRow | null {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT ${SELECT_COLUMNS} FROM sessions WHERE id = ?`,\n ).get(id) as Record<string, unknown> | undefined;\n\n if (!row) return null;\n return toSessionRow(row);\n}\n\n/** Build WHERE clause and bound params from session filter options. */\nfunction buildSessionsWhere(\n options: Omit<ListSessionsOptions, 'limit' | 'offset'>,\n): { where: string; params: unknown[] } {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.status !== undefined) {\n conditions.push(`status = ?`);\n params.push(options.status);\n }\n\n if (options.agent !== undefined) {\n conditions.push(`agent = ?`);\n params.push(options.agent);\n }\n\n if (options.search !== undefined && options.search.length > 0) {\n conditions.push(`(title LIKE ? OR id LIKE ?)`);\n const pattern = `%${options.search}%`;\n params.push(pattern, pattern);\n }\n if (options.since !== undefined) {\n conditions.push('created_at > ?');\n params.push(options.since);\n }\n\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n params,\n };\n}\n\n/**\n * List sessions with optional filters, ordered by created_at DESC.\n */\nexport function listSessions(\n options: ListSessionsOptions = {},\n): SessionRow[] {\n const db = getDatabase();\n const { where, params } = buildSessionsWhere(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 sessions\n ${where}\n ORDER BY created_at DESC\n LIMIT ?\n OFFSET ?`,\n ).all(...params, limit, offset) as Record<string, unknown>[];\n\n return rows.map(toSessionRow);\n}\n\n/**\n * Count sessions matching optional filters (for pagination totals).\n */\nexport function countSessions(\n options: Omit<ListSessionsOptions, 'limit' | 'offset'> = {},\n): number {\n const db = getDatabase();\n const { where, params } = buildSessionsWhere(options);\n\n const row = db.prepare(\n `SELECT COUNT(*) as count FROM sessions ${where}`,\n ).get(...params) as { count: number };\n\n return row.count;\n}\n\n/**\n * Update specific fields on an existing session.\n *\n * @returns the updated row, or null if the session does not exist.\n */\nexport function updateSession(\n id: string,\n updates: SessionUpdate,\n): SessionRow | null {\n const db = getDatabase();\n\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n const fieldMap: Record<string, string> = {\n agent: 'agent',\n user: '\"user\"',\n project_root: 'project_root',\n branch: 'branch',\n ended_at: 'ended_at',\n status: 'status',\n prompt_count: 'prompt_count',\n tool_count: 'tool_count',\n title: 'title',\n summary: 'summary',\n transcript_path: 'transcript_path',\n parent_session_id: 'parent_session_id',\n parent_session_reason: 'parent_session_reason',\n processed: 'processed',\n content_hash: 'content_hash',\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 Record<string, unknown>)[key] ?? null);\n }\n }\n\n if (setClauses.length === 0) return getSession(id);\n\n params.push(id);\n\n db.prepare(\n `UPDATE sessions\n SET ${setClauses.join(', ')}\n WHERE id = ?`,\n ).run(...params);\n\n const updated = getSession(id);\n\n if (updated) syncRow('sessions', updated);\n\n return updated;\n}\n\n/**\n * Atomically increment tool_count for a session.\n *\n * Uses SQL `tool_count + 1` to avoid read-modify-write races.\n */\nexport function incrementSessionToolCount(id: string): void {\n const db = getDatabase();\n db.prepare(\n `UPDATE sessions SET tool_count = COALESCE(tool_count, 0) + 1 WHERE id = ?`,\n ).run(id);\n}\n\n/**\n * Close a session — set status to 'completed' and record the end time.\n *\n * @returns the updated row, or null if the session does not exist.\n */\nexport function closeSession(\n id: string,\n endedAt: number,\n): SessionRow | null {\n const db = getDatabase();\n\n db.prepare(\n `UPDATE sessions\n SET status = ?, ended_at = ?\n WHERE id = ?`,\n ).run(STATUS_COMPLETED, endedAt, id);\n\n const closed = getSession(id);\n\n if (closed) syncRow('sessions', closed);\n\n return closed;\n}\n\n/**\n * Delete a session and all its child rows (batches, activities, attachments).\n *\n * No ON DELETE CASCADE in the schema, so we delete children first.\n * Returns true if the session existed and was deleted.\n */\nexport function deleteSession(id: string): boolean {\n const db = getDatabase();\n\n db.prepare(`DELETE FROM activities WHERE session_id = ?`).run(id);\n db.prepare(`DELETE FROM attachments WHERE session_id = ?`).run(id);\n db.prepare(`DELETE FROM prompt_batches WHERE session_id = ?`).run(id);\n const info = db.prepare(`DELETE FROM sessions WHERE id = ?`).run(id);\n\n return info.changes > 0;\n}\n\n// ---------------------------------------------------------------------------\n// Cascade delete + impact query\n// ---------------------------------------------------------------------------\n\n/** Counts of related data that would be affected by a session delete. */\nexport interface SessionImpact {\n promptCount: number;\n sporeCount: number;\n attachmentCount: number;\n graphEdgeCount: number;\n}\n\n/** Result of a cascade delete operation. */\nexport interface DeleteCascadeResult {\n deleted: boolean;\n counts: {\n prompts: number;\n spores: number;\n attachments: number;\n graphEdges: number;\n resolutionEvents: number;\n };\n /** Spore IDs that were deleted (needed for vault file + vector cleanup). */\n deletedSporeIds: string[];\n /** Attachment file paths that were deleted from DB (needed for disk cleanup). */\n deletedAttachmentPaths: string[];\n}\n\n/**\n * Get counts of all data related to a session, for pre-delete impact display.\n */\nexport function getSessionImpact(sessionId: string): SessionImpact {\n const db = getDatabase();\n\n const row = db.prepare(\n `SELECT\n (SELECT COUNT(*) FROM prompt_batches WHERE session_id = ?) AS promptCount,\n (SELECT COUNT(*) FROM spores WHERE session_id = ?) AS sporeCount,\n (SELECT COUNT(*) FROM attachments WHERE session_id = ?) AS attachmentCount,\n (SELECT COUNT(*) FROM graph_edges WHERE session_id = ?) AS graphEdgeCount`,\n ).get(sessionId, sessionId, sessionId, sessionId) as SessionImpact;\n\n return row;\n}\n\n/**\n * Delete a session and ALL related data in a single transaction.\n *\n * Returns counts of deleted rows and IDs needed for post-transaction\n * cleanup (vault files, embedding vectors).\n */\nexport function deleteSessionCascade(sessionId: string): DeleteCascadeResult {\n const db = getDatabase();\n\n const zeroCounts: DeleteCascadeResult = {\n deleted: false,\n counts: { prompts: 0, spores: 0, attachments: 0, graphEdges: 0, resolutionEvents: 0 },\n deletedSporeIds: [],\n deletedAttachmentPaths: [],\n };\n\n // Check session exists first\n const exists = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);\n if (!exists) return zeroCounts;\n\n // Collect IDs/paths needed for post-transaction cleanup before deleting.\n // Spores can reference prompt_batches from a different session (cross-session\n // spore linkage), so we must also collect spores linked via prompt_batch_id.\n const sporeIds = (db.prepare(\n `SELECT id FROM spores\n WHERE session_id = ?\n OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)`,\n ).all(sessionId, sessionId) as { id: string }[]).map((r) => r.id);\n\n const attachmentPaths = (db.prepare(\n `SELECT file_path FROM attachments WHERE session_id = ?`,\n ).all(sessionId) as { file_path: string }[]).map((r) => r.file_path);\n\n // Run all deletes in a single transaction.\n //\n // Order matters — foreign_keys = ON is set in client.ts, so every DELETE\n // is checked immediately. Child rows must be removed before their parents:\n // - spores.prompt_batch_id → prompt_batches(id) [spores BEFORE prompt_batches]\n // - plans.prompt_batch_id → prompt_batches(id) [plans BEFORE prompt_batches]\n // - resolution_events.spore_id → spores(id) [resolution_events BEFORE spores]\n // - skill_usage.session_id → sessions(id) NOT NULL\n // - plans.session_id → sessions(id)\n // resolution_events can reference spores across sessions (e.g. a later\n // session supersedes an earlier session's spore), so we match by either\n // session_id OR spore_id-in-this-session to catch cross-session references.\n //\n // Spores can also reference prompt_batches from a different session\n // (cross-session prompt_batch_id linkage). We must delete those spores\n // BEFORE deleting prompt_batches to avoid FK violations.\n const result = db.transaction(() => {\n db.prepare(`DELETE FROM activities WHERE session_id = ?`).run(sessionId);\n const attachments = db.prepare(`DELETE FROM attachments WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM plans WHERE session_id = ?`).run(sessionId);\n db.prepare(`DELETE FROM skill_usage WHERE session_id = ?`).run(sessionId);\n const resEvents = db.prepare(\n `DELETE FROM resolution_events\n WHERE session_id = ?\n OR spore_id IN (\n SELECT id FROM spores\n WHERE session_id = ?\n OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)\n )`,\n ).run(sessionId, sessionId, sessionId);\n const edges = db.prepare(`DELETE FROM graph_edges WHERE session_id = ?`).run(sessionId);\n const spores = db.prepare(\n `DELETE FROM spores\n WHERE session_id = ?\n OR prompt_batch_id IN (SELECT id FROM prompt_batches WHERE session_id = ?)`,\n ).run(sessionId, sessionId);\n const prompts = db.prepare(`DELETE FROM prompt_batches WHERE session_id = ?`).run(sessionId);\n const session = db.prepare(`DELETE FROM sessions WHERE id = ?`).run(sessionId);\n\n return {\n deleted: session.changes > 0,\n counts: {\n prompts: prompts.changes,\n spores: spores.changes,\n attachments: attachments.changes,\n graphEdges: edges.changes,\n resolutionEvents: resEvents.changes,\n },\n };\n })();\n\n return {\n ...result,\n deletedSporeIds: sporeIds,\n deletedAttachmentPaths: attachmentPaths,\n };\n}\n"],"mappings":"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","names":[]}
@@ -3,17 +3,17 @@ import {
3
3
  AgentTaskSchema,
4
4
  loadAgentTasks,
5
5
  taskFromParsed
6
- } from "./chunk-NI23QCHB.js";
6
+ } from "./chunk-AELJ4PS5.js";
7
7
  import {
8
8
  BUILT_IN_SOURCE,
9
9
  MAX_TASK_NAME_LENGTH,
10
10
  TASK_NAME_PATTERN,
11
11
  USER_TASKS_DIR,
12
12
  USER_TASK_SOURCE
13
- } from "./chunk-CKJAWZQE.js";
13
+ } from "./chunk-W4VHC2ES.js";
14
14
  import {
15
15
  require_dist
16
- } from "./chunk-D7TYRPRM.js";
16
+ } from "./chunk-6LQIMRTC.js";
17
17
  import {
18
18
  __toESM
19
19
  } from "./chunk-PZUWP5VK.js";
@@ -100,4 +100,4 @@ export {
100
100
  deleteUserTask,
101
101
  copyTaskToUser
102
102
  };
103
- //# sourceMappingURL=chunk-RAV5YMRU.js.map
103
+ //# sourceMappingURL=chunk-3TPD6HEF.js.map
@@ -19,7 +19,7 @@ function evaluateSessionStartRules(manifests, detectedAgent, ctx) {
19
19
  for (const rule of rules) {
20
20
  if (rule.event !== "session_start") continue;
21
21
  if (!scopePermits(rule, manifest.name, detectedAgent)) continue;
22
- if (!whenMatches(rule, { prompt: "", transcriptPath: ctx.transcriptPath })) continue;
22
+ if (!whenMatches(rule, { prompt: "", transcriptPath: ctx.transcriptPath, transcriptMeta: ctx.transcriptMeta })) continue;
23
23
  if (rule.action === "drop") {
24
24
  return { action: "drop", reason: rule.reason };
25
25
  }
@@ -32,8 +32,8 @@ function scopePermits(rule, owningAgent, detectedAgent) {
32
32
  return owningAgent === detectedAgent;
33
33
  }
34
34
  function whenMatches(rule, ctx) {
35
- const { prompt_starts_with, prompt_contains, transcript_path_missing } = rule.when;
36
- const hasAnyCondition = prompt_starts_with !== void 0 || prompt_contains !== void 0 || transcript_path_missing !== void 0;
35
+ const { prompt_starts_with, prompt_contains, transcript_path_missing, transcript_meta_field_exists } = rule.when;
36
+ const hasAnyCondition = prompt_starts_with !== void 0 || prompt_contains !== void 0 || transcript_path_missing !== void 0 || transcript_meta_field_exists !== void 0;
37
37
  if (!hasAnyCondition) return false;
38
38
  if (prompt_starts_with && !ctx.prompt.startsWith(prompt_starts_with)) return false;
39
39
  if (prompt_contains && !ctx.prompt.includes(prompt_contains)) return false;
@@ -42,8 +42,20 @@ function whenMatches(rule, ctx) {
42
42
  if (transcript_path_missing && !missing) return false;
43
43
  if (!transcript_path_missing && missing) return false;
44
44
  }
45
+ if (transcript_meta_field_exists !== void 0) {
46
+ if (!ctx.transcriptMeta) return false;
47
+ if (!resolveMetaField(ctx.transcriptMeta, transcript_meta_field_exists)) return false;
48
+ }
45
49
  return true;
46
50
  }
51
+ function resolveMetaField(meta, fieldPath) {
52
+ let current = meta;
53
+ for (const part of fieldPath.split(".")) {
54
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
55
+ current = current[part];
56
+ }
57
+ return current;
58
+ }
47
59
  function applyAction(rule, ctx) {
48
60
  if (rule.action === "drop") {
49
61
  return { action: "drop", reason: rule.reason };
@@ -58,8 +70,38 @@ function applyAction(rule, ctx) {
58
70
  return { action: "rewrite", prompt: next, reason: rule.reason };
59
71
  }
60
72
 
73
+ // src/hooks/transcript-meta.ts
74
+ import fs from "fs";
75
+ function readTranscriptMeta(transcriptPath) {
76
+ try {
77
+ const fd = fs.openSync(transcriptPath, "r");
78
+ try {
79
+ const buf = Buffer.alloc(131072);
80
+ const bytesRead = fs.readSync(fd, buf, 0, buf.length, 0);
81
+ if (bytesRead === 0) return null;
82
+ const chunk = buf.toString("utf-8", 0, bytesRead);
83
+ const newlineIdx = chunk.indexOf("\n");
84
+ const firstLine = newlineIdx >= 0 ? chunk.slice(0, newlineIdx) : chunk;
85
+ if (!firstLine) return null;
86
+ const entry = JSON.parse(firstLine);
87
+ if (entry?.type === "session_meta" && typeof entry.payload === "object") {
88
+ return entry.payload;
89
+ }
90
+ if (typeof entry === "object" && entry !== null) {
91
+ return entry;
92
+ }
93
+ return null;
94
+ } finally {
95
+ fs.closeSync(fd);
96
+ }
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+
61
102
  export {
62
103
  evaluateUserPromptRules,
63
- evaluateSessionStartRules
104
+ evaluateSessionStartRules,
105
+ readTranscriptMeta
64
106
  };
65
- //# sourceMappingURL=chunk-JMOUFG6Y.js.map
107
+ //# sourceMappingURL=chunk-44PZCAYS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/capture-rules.ts","../src/hooks/transcript-meta.ts"],"sourcesContent":["/**\n * Generic capture-rule evaluator.\n *\n * Each symbiont manifest declares `capture.rules` — a list of `{ event,\n * when, action }` records that describe how Myco should filter captured\n * events for that agent. This module loads rules from every manifest\n * in one place and exposes a pure evaluator the hook handlers call\n * without knowing anything symbiont-specific.\n *\n * Adding a new symbiont's capture behavior is a YAML-only change: edit\n * that agent's manifest file, no hook or evaluator changes needed.\n *\n * Rule scope (`this_agent` vs `any_agent`) lets rules opt into running\n * even when agent detection itself fails — useful for ephemeral\n * sub-invocations that legitimately lack the signals we key on.\n *\n * Conditions should prefer structural signals (e.g.,\n * `transcript_path_missing`) over text matching so rules stay robust\n * across upstream agent updates.\n */\n\nimport type { CaptureRule, SymbiontManifest } from '../symbionts/manifest-schema.js';\n\n/** Structured context a rule can match against at UserPromptSubmit time. */\nexport interface UserPromptRuleContext {\n /** The user prompt text as received from the hook. */\n prompt: string;\n /** Transcript path from the hook payload, if any. Empty/undefined signals an ephemeral session. */\n transcriptPath?: string;\n /** Parsed first JSON line (session_meta) from the transcript, if available. */\n transcriptMeta?: Record<string, unknown>;\n}\n\n/** Structured context a rule can match against at SessionStart time. */\nexport interface SessionStartRuleContext {\n /** Transcript path from the hook payload, if any. Empty/undefined signals an ephemeral session. */\n transcriptPath?: string;\n /** Parsed first JSON line (session_meta) from the transcript, if available. */\n transcriptMeta?: Record<string, unknown>;\n}\n\n/** Outcome of evaluating user_prompt rules. */\nexport type UserPromptDecision =\n | { action: 'pass'; prompt: string }\n | { action: 'rewrite'; prompt: string; reason?: string }\n | { action: 'drop'; reason?: string };\n\n/** Outcome of evaluating session_start rules. No rewrite — there's no prompt text yet. */\nexport type SessionStartDecision =\n | { action: 'pass' }\n | { action: 'drop'; reason?: string };\n\n/**\n * Evaluate all user_prompt rules from every manifest against one context.\n *\n * Rules are checked in declaration order, first-match-wins. A rule only\n * fires when:\n * 1. its `event` is `user_prompt`,\n * 2. its scope permits it (see scope semantics in manifest-schema.ts),\n * 3. every condition in its `when` block matches the context.\n *\n * If no rule matches, the prompt passes through unchanged.\n */\nexport function evaluateUserPromptRules(\n manifests: SymbiontManifest[],\n detectedAgent: string,\n ctx: UserPromptRuleContext,\n): UserPromptDecision {\n for (const manifest of manifests) {\n const rules = manifest.capture?.rules ?? [];\n for (const rule of rules) {\n if (rule.event !== 'user_prompt') continue;\n if (!scopePermits(rule, manifest.name, detectedAgent)) continue;\n if (!whenMatches(rule, ctx)) continue;\n return applyAction(rule, ctx);\n }\n }\n return { action: 'pass', prompt: ctx.prompt };\n}\n\n/**\n * Evaluate all session_start rules from every manifest.\n *\n * Same first-match-wins semantics as user_prompt rules. The only action\n * session_start rules can take is `drop` — text rewriting doesn't apply\n * because there's no prompt text at SessionStart time. Rules that\n * specify prompt-based conditions (prompt_starts_with / prompt_contains)\n * match against an empty prompt here, so they'll never fire on the\n * session_start pass.\n *\n * Callers should skip session registration when the result is `drop`.\n */\nexport function evaluateSessionStartRules(\n manifests: SymbiontManifest[],\n detectedAgent: string,\n ctx: SessionStartRuleContext,\n): SessionStartDecision {\n for (const manifest of manifests) {\n const rules = manifest.capture?.rules ?? [];\n for (const rule of rules) {\n if (rule.event !== 'session_start') continue;\n if (!scopePermits(rule, manifest.name, detectedAgent)) continue;\n if (!whenMatches(rule, { prompt: '', transcriptPath: ctx.transcriptPath, transcriptMeta: ctx.transcriptMeta })) continue;\n if (rule.action === 'drop') {\n return { action: 'drop', reason: rule.reason };\n }\n // rewrite_prompt is meaningless at session_start — skip and let\n // later rules have a chance to match.\n }\n }\n return { action: 'pass' };\n}\n\nfunction scopePermits(rule: CaptureRule, owningAgent: string, detectedAgent: string): boolean {\n if (rule.scope === 'any_agent') return true;\n return owningAgent === detectedAgent;\n}\n\nfunction whenMatches(rule: CaptureRule, ctx: UserPromptRuleContext): boolean {\n const { prompt_starts_with, prompt_contains, transcript_path_missing, transcript_meta_field_exists } = rule.when;\n\n // Refuse rules with no conditions — prevents a mistyped YAML file from\n // accidentally creating a blanket \"drop everything\" rule.\n const hasAnyCondition =\n prompt_starts_with !== undefined ||\n prompt_contains !== undefined ||\n transcript_path_missing !== undefined ||\n transcript_meta_field_exists !== undefined;\n if (!hasAnyCondition) return false;\n\n if (prompt_starts_with && !ctx.prompt.startsWith(prompt_starts_with)) return false;\n if (prompt_contains && !ctx.prompt.includes(prompt_contains)) return false;\n\n if (transcript_path_missing !== undefined) {\n const missing = !ctx.transcriptPath || ctx.transcriptPath.length === 0;\n if (transcript_path_missing && !missing) return false;\n if (!transcript_path_missing && missing) return false;\n }\n\n if (transcript_meta_field_exists !== undefined) {\n if (!ctx.transcriptMeta) return false;\n if (!resolveMetaField(ctx.transcriptMeta, transcript_meta_field_exists)) return false;\n }\n\n return true;\n}\n\n/**\n * Navigate a dot-path (e.g. \"source.subagent\") into a nested object.\n * Returns the value if it exists and is truthy, undefined otherwise.\n */\nfunction resolveMetaField(meta: Record<string, unknown>, fieldPath: string): unknown {\n let current: unknown = meta;\n for (const part of fieldPath.split('.')) {\n if (current === null || current === undefined || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction applyAction(rule: CaptureRule, ctx: UserPromptRuleContext): UserPromptDecision {\n if (rule.action === 'drop') {\n return { action: 'drop', reason: rule.reason };\n }\n // rewrite_prompt — keep only the substring after the extract_after marker.\n // If the marker isn't in the prompt, fall through to `pass` so we don't\n // accidentally blank out a prompt that turned out not to match after all.\n const marker = rule.extract_after;\n if (!marker) return { action: 'pass', prompt: ctx.prompt };\n const idx = ctx.prompt.indexOf(marker);\n if (idx === -1) return { action: 'pass', prompt: ctx.prompt };\n const after = ctx.prompt.slice(idx + marker.length);\n const next = rule.trim ? after.trim() : after;\n if (!next) return { action: 'pass', prompt: ctx.prompt };\n return { action: 'rewrite', prompt: next, reason: rule.reason };\n}\n","/**\n * Read the first JSON line (session_meta) from an agent's transcript file.\n *\n * Every supported agent writes a JSONL transcript where the first entry\n * is a `session_meta` record containing session identity, source info,\n * model, and other structural signals. This reader extracts that record\n * so capture rules can make decisions based on it — e.g., detecting\n * sub-agent thread spawns that have real transcript files but aren't\n * user-initiated sessions.\n *\n * Returns the parsed `payload` object from the session_meta entry, or\n * null if the file doesn't exist, isn't readable, or doesn't contain\n * valid session_meta JSON.\n */\n\nimport fs from 'node:fs';\n\n/**\n * Read and parse the session_meta payload from a transcript file.\n *\n * @param transcriptPath - Absolute path to the JSONL transcript.\n * @returns The session_meta payload object, or null on any failure.\n */\nexport function readTranscriptMeta(transcriptPath: string): Record<string, unknown> | null {\n try {\n const fd = fs.openSync(transcriptPath, 'r');\n try {\n // Read enough bytes for the first line. Session meta can be large\n // when it embeds the full system prompt (base_instructions) — Codex\n // sessions routinely exceed 16 KB. 128 KB covers all known cases.\n const buf = Buffer.alloc(131072);\n const bytesRead = fs.readSync(fd, buf, 0, buf.length, 0);\n if (bytesRead === 0) return null;\n\n const chunk = buf.toString('utf-8', 0, bytesRead);\n const newlineIdx = chunk.indexOf('\\n');\n const firstLine = newlineIdx >= 0 ? chunk.slice(0, newlineIdx) : chunk;\n if (!firstLine) return null;\n\n const entry = JSON.parse(firstLine);\n\n // session_meta entries have { type: \"session_meta\", payload: {...} }\n if (entry?.type === 'session_meta' && typeof entry.payload === 'object') {\n return entry.payload as Record<string, unknown>;\n }\n\n // Some agents may write the meta directly without the wrapper\n if (typeof entry === 'object' && entry !== null) {\n return entry as Record<string, unknown>;\n }\n\n return null;\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":[]}