@goondocks/myco 0.20.2 → 0.21.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 (196) hide show
  1. package/dist/agent-eval-RJSQI5S2.js +355 -0
  2. package/dist/agent-eval-RJSQI5S2.js.map +1 -0
  3. package/dist/{agent-run-X25Q2A6T.js → agent-run-2JSYFOKU.js} +10 -8
  4. package/dist/{agent-run-X25Q2A6T.js.map → agent-run-2JSYFOKU.js.map} +1 -1
  5. package/dist/{agent-tasks-7B6OFERB.js → agent-tasks-APFJIM2T.js} +10 -8
  6. package/dist/{agent-tasks-7B6OFERB.js.map → agent-tasks-APFJIM2T.js.map} +1 -1
  7. package/dist/{chunk-OD4AA7PV.js → chunk-53RPGOEN.js} +56 -8
  8. package/dist/chunk-53RPGOEN.js.map +1 -0
  9. package/dist/chunk-54SXG5HF.js +26 -0
  10. package/dist/chunk-54SXG5HF.js.map +1 -0
  11. package/dist/{chunk-DCSGJ7W4.js → chunk-5ZG4RMUH.js} +2 -2
  12. package/dist/{chunk-FLLBJLHM.js → chunk-6C6QZ4PM.js} +9 -5
  13. package/dist/chunk-6C6QZ4PM.js.map +1 -0
  14. package/dist/chunk-6LB7XELY.js +406 -0
  15. package/dist/chunk-6LB7XELY.js.map +1 -0
  16. package/dist/{chunk-JZGN33AY.js → chunk-75Z7UKDY.js} +4 -4
  17. package/dist/{chunk-XG5RRUYF.js → chunk-BUTL6IFS.js} +2 -2
  18. package/dist/chunk-CESKJD44.js +586 -0
  19. package/dist/chunk-CESKJD44.js.map +1 -0
  20. package/dist/chunk-CISWUP5W.js +101 -0
  21. package/dist/chunk-CISWUP5W.js.map +1 -0
  22. package/dist/chunk-DJ3IHNYO.js +50 -0
  23. package/dist/chunk-DJ3IHNYO.js.map +1 -0
  24. package/dist/chunk-F3OEQYLS.js +847 -0
  25. package/dist/chunk-F3OEQYLS.js.map +1 -0
  26. package/dist/{chunk-6RFZWV4R.js → chunk-FCJ5JV54.js} +1 -1
  27. package/dist/{chunk-6RFZWV4R.js.map → chunk-FCJ5JV54.js.map} +1 -1
  28. package/dist/{chunk-2PDWCDKY.js → chunk-G6QIBNZM.js} +9 -6
  29. package/dist/{chunk-2PDWCDKY.js.map → chunk-G6QIBNZM.js.map} +1 -1
  30. package/dist/{chunk-6X2ERTQV.js → chunk-ILJPRYES.js} +6 -4
  31. package/dist/{chunk-6X2ERTQV.js.map → chunk-ILJPRYES.js.map} +1 -1
  32. package/dist/{chunk-US4LNCAT.js → chunk-IPPMYQ2Y.js} +5 -1
  33. package/dist/chunk-IPPMYQ2Y.js.map +1 -0
  34. package/dist/{chunk-KESLPBKV.js → chunk-JR54LTPP.js} +4 -4
  35. package/dist/{chunk-CCRGY3QW.js → chunk-JZS6GZ6T.js} +16 -94
  36. package/dist/chunk-JZS6GZ6T.js.map +1 -0
  37. package/dist/{chunk-5XIVBO25.js → chunk-LVIY7P35.js} +2 -2
  38. package/dist/chunk-NGH7U6A3.js +13844 -0
  39. package/dist/chunk-NGH7U6A3.js.map +1 -0
  40. package/dist/chunk-OUJSQSKE.js +113 -0
  41. package/dist/chunk-OUJSQSKE.js.map +1 -0
  42. package/dist/{chunk-VVNL26WX.js → chunk-P66DLD6G.js} +22 -10
  43. package/dist/chunk-P66DLD6G.js.map +1 -0
  44. package/dist/{chunk-XATDZX7U.js → chunk-R2JIJBCL.js} +18 -4
  45. package/dist/{chunk-XATDZX7U.js.map → chunk-R2JIJBCL.js.map} +1 -1
  46. package/dist/{chunk-MYOZLMB2.js → chunk-RL5R4CQU.js} +538 -19
  47. package/dist/chunk-RL5R4CQU.js.map +1 -0
  48. package/dist/{chunk-EVDQKYCG.js → chunk-RQSJLWP4.js} +13 -2
  49. package/dist/chunk-RQSJLWP4.js.map +1 -0
  50. package/dist/{chunk-BPRIYNLE.js → chunk-TKAJ3JVF.js} +3 -3
  51. package/dist/{chunk-Q36VMZST.js → chunk-VHNRMM4O.js} +3 -2
  52. package/dist/{chunk-FMRZ26U5.js → chunk-X3IGT5RV.js} +5 -2
  53. package/dist/{chunk-FMRZ26U5.js.map → chunk-X3IGT5RV.js.map} +1 -1
  54. package/dist/{chunk-KHT24OWC.js → chunk-YDUOSRGD.js} +8 -94
  55. package/dist/{chunk-KHT24OWC.js.map → chunk-YDUOSRGD.js.map} +1 -1
  56. package/dist/{cli-GGPWH4UO.js → cli-LNYSTDQM.js} +49 -42
  57. package/dist/cli-LNYSTDQM.js.map +1 -0
  58. package/dist/{client-YXQUTXVZ.js → client-NWE4TCNO.js} +4 -4
  59. package/dist/{config-OMCYHG2S.js → config-VC4ACP42.js} +6 -4
  60. package/dist/{config-OMCYHG2S.js.map → config-VC4ACP42.js.map} +1 -1
  61. package/dist/{detect-providers-5KOPZ7J2.js → detect-providers-ILLQZROY.js} +4 -4
  62. package/dist/{doctor-5JXJ36KA.js → doctor-TI7EZ3RW.js} +48 -15
  63. package/dist/doctor-TI7EZ3RW.js.map +1 -0
  64. package/dist/executor-F2YU7HXJ.js +44 -0
  65. package/dist/{init-LMYOVZAV.js → init-KG3TYVGE.js} +14 -12
  66. package/dist/{init-LMYOVZAV.js.map → init-KG3TYVGE.js.map} +1 -1
  67. package/dist/{installer-FS257JRZ.js → installer-UMH7OJ5A.js} +6 -4
  68. package/dist/{llm-TH4NLIRM.js → llm-AGVEF5XD.js} +5 -4
  69. package/dist/{loader-CQYTFHEW.js → loader-LX7TFRM6.js} +5 -3
  70. package/dist/{loader-NOMBJUPW.js → loader-NAVVZK63.js} +4 -3
  71. package/dist/{main-YTBVRTBI.js → main-5PRQNEEE.js} +2453 -650
  72. package/dist/main-5PRQNEEE.js.map +1 -0
  73. package/dist/{open-HG2DX6RN.js → open-5A27BCSB.js} +10 -8
  74. package/dist/{open-HG2DX6RN.js.map → open-5A27BCSB.js.map} +1 -1
  75. package/dist/{post-compact-JSECI44W.js → post-compact-USAODKPQ.js} +6 -6
  76. package/dist/{post-tool-use-POGPTJBA.js → post-tool-use-GMMSYBII.js} +9 -7
  77. package/dist/post-tool-use-GMMSYBII.js.map +1 -0
  78. package/dist/{post-tool-use-failure-OT7BFWQW.js → post-tool-use-failure-NZVSL2PO.js} +6 -6
  79. package/dist/{pre-compact-OXVODKH4.js → pre-compact-LZ57DLUS.js} +6 -6
  80. package/dist/{provider-check-43LAMSMH.js → provider-check-ZEV5P4KM.js} +4 -4
  81. package/dist/{registry-U4CHXK6R.js → registry-M2Z5QBWH.js} +5 -4
  82. package/dist/{remove-N7ZPELFU.js → remove-T3KE6C5N.js} +10 -8
  83. package/dist/{remove-N7ZPELFU.js.map → remove-T3KE6C5N.js.map} +1 -1
  84. package/dist/{restart-ADG5GBTB.js → restart-YWDEVZUJ.js} +11 -9
  85. package/dist/{restart-ADG5GBTB.js.map → restart-YWDEVZUJ.js.map} +1 -1
  86. package/dist/{search-AHZEUNRR.js → search-GKFDGELR.js} +11 -9
  87. package/dist/{search-AHZEUNRR.js.map → search-GKFDGELR.js.map} +1 -1
  88. package/dist/{server-AGVYZVP5.js → server-AHUR6CWF.js} +368 -269
  89. package/dist/server-AHUR6CWF.js.map +1 -0
  90. package/dist/{session-6IU4AXYP.js → session-2ZEPLWW6.js} +11 -9
  91. package/dist/{session-6IU4AXYP.js.map → session-2ZEPLWW6.js.map} +1 -1
  92. package/dist/{session-end-FT27DWYZ.js → session-end-LWJYQAXX.js} +5 -5
  93. package/dist/session-start-WTA6GCOQ.js +134 -0
  94. package/dist/session-start-WTA6GCOQ.js.map +1 -0
  95. package/dist/{setup-llm-77MP4I2G.js → setup-llm-E7UU5IO7.js} +11 -9
  96. package/dist/{setup-llm-77MP4I2G.js.map → setup-llm-E7UU5IO7.js.map} +1 -1
  97. package/dist/src/agent/definitions/agent.yaml +9 -5
  98. package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +93 -0
  99. package/dist/src/agent/definitions/tasks/cortex-prompt-builder.yaml +67 -0
  100. package/dist/src/agent/definitions/tasks/digest-only.yaml +1 -1
  101. package/dist/src/agent/definitions/tasks/extract-only.yaml +1 -1
  102. package/dist/src/agent/definitions/tasks/review-session.yaml +10 -39
  103. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +4 -4
  104. package/dist/src/agent/definitions/tasks/skill-generate.yaml +1 -1
  105. package/dist/src/agent/definitions/tasks/skill-survey.yaml +2 -6
  106. package/dist/src/agent/definitions/tasks/supersession-sweep.yaml +1 -1
  107. package/dist/src/agent/definitions/tasks/title-summary.yaml +12 -19
  108. package/dist/src/agent/definitions/tasks/{full-intelligence.yaml → vault-evolve.yaml} +17 -82
  109. package/dist/src/agent/definitions/tasks/vault-seed.yaml +370 -0
  110. package/dist/src/agent/prompts/agent.md +12 -38
  111. package/dist/src/cli.js +1 -1
  112. package/dist/src/daemon/main.js +1 -1
  113. package/dist/src/hooks/post-tool-use.js +1 -1
  114. package/dist/src/hooks/session-end.js +1 -1
  115. package/dist/src/hooks/session-start.js +1 -1
  116. package/dist/src/hooks/stop.js +1 -1
  117. package/dist/src/hooks/user-prompt-submit.js +1 -1
  118. package/dist/src/mcp/server.js +1 -1
  119. package/dist/src/symbionts/manifests/claude-code.yaml +4 -0
  120. package/dist/src/symbionts/manifests/pi.yaml +22 -0
  121. package/dist/src/symbionts/templates/pi/package.json +6 -0
  122. package/dist/src/symbionts/templates/pi/plugin.ts +559 -0
  123. package/dist/{stats-NVPWOYTE.js → stats-DFG6S23S.js} +11 -9
  124. package/dist/{stats-NVPWOYTE.js.map → stats-DFG6S23S.js.map} +1 -1
  125. package/dist/{stop-ZPIKVLH4.js → stop-WRBTXEVT.js} +5 -5
  126. package/dist/{stop-failure-2PX67YJC.js → stop-failure-32MGIG2Q.js} +6 -6
  127. package/dist/{subagent-start-UUE6EHQD.js → subagent-start-VFGHQFVL.js} +6 -6
  128. package/dist/{subagent-stop-KQWWWPE6.js → subagent-stop-663FXG3P.js} +6 -6
  129. package/dist/{task-completed-WMHOFQ7B.js → task-completed-ZCQYEFMZ.js} +6 -6
  130. package/dist/{team-LRZ6GTQK.js → team-JTI5CDUO.js} +7 -5
  131. package/dist/{turns-YFNI5CQC.js → turns-HU2CTZAP.js} +2 -2
  132. package/dist/ui/assets/index-DGf1h-Ha.js +842 -0
  133. package/dist/ui/assets/index-_OP4ifzH.css +1 -0
  134. package/dist/ui/index.html +2 -2
  135. package/dist/{update-O6V4RC4W.js → update-3NBQTG32.js} +10 -8
  136. package/dist/{update-O6V4RC4W.js.map → update-3NBQTG32.js.map} +1 -1
  137. package/dist/{user-prompt-submit-N36KUPHI.js → user-prompt-submit-ME2TBKOS.js} +8 -7
  138. package/dist/{user-prompt-submit-N36KUPHI.js.map → user-prompt-submit-ME2TBKOS.js.map} +1 -1
  139. package/dist/{verify-LXPV7NYG.js → verify-R76ZFJSZ.js} +8 -5
  140. package/dist/{verify-LXPV7NYG.js.map → verify-R76ZFJSZ.js.map} +1 -1
  141. package/dist/{version-XMPPJQHR.js → version-GQAFBBPX.js} +2 -2
  142. package/dist/version-GQAFBBPX.js.map +1 -0
  143. package/package.json +3 -1
  144. package/skills/myco/SKILL.md +16 -1
  145. package/skills/myco/references/cli-usage.md +1 -1
  146. package/skills/myco-curate/SKILL.md +1 -1
  147. package/dist/chunk-4YFKBL3F.js +0 -195
  148. package/dist/chunk-4YFKBL3F.js.map +0 -1
  149. package/dist/chunk-CCRGY3QW.js.map +0 -1
  150. package/dist/chunk-EVDQKYCG.js.map +0 -1
  151. package/dist/chunk-FLLBJLHM.js.map +0 -1
  152. package/dist/chunk-MYOZLMB2.js.map +0 -1
  153. package/dist/chunk-OD4AA7PV.js.map +0 -1
  154. package/dist/chunk-US4LNCAT.js.map +0 -1
  155. package/dist/chunk-UYMFCYBF.js +0 -2326
  156. package/dist/chunk-UYMFCYBF.js.map +0 -1
  157. package/dist/chunk-VVNL26WX.js.map +0 -1
  158. package/dist/cli-GGPWH4UO.js.map +0 -1
  159. package/dist/doctor-5JXJ36KA.js.map +0 -1
  160. package/dist/executor-HWW2QNZQ.js +0 -2472
  161. package/dist/executor-HWW2QNZQ.js.map +0 -1
  162. package/dist/main-YTBVRTBI.js.map +0 -1
  163. package/dist/post-tool-use-POGPTJBA.js.map +0 -1
  164. package/dist/server-AGVYZVP5.js.map +0 -1
  165. package/dist/session-start-LAFICHII.js +0 -189
  166. package/dist/session-start-LAFICHII.js.map +0 -1
  167. package/dist/src/agent/definitions/tasks/graph-maintenance.yaml +0 -93
  168. package/dist/ui/assets/index-C2JuNtRB.css +0 -1
  169. package/dist/ui/assets/index-JLVaQKV2.js +0 -832
  170. /package/dist/{chunk-DCSGJ7W4.js.map → chunk-5ZG4RMUH.js.map} +0 -0
  171. /package/dist/{chunk-JZGN33AY.js.map → chunk-75Z7UKDY.js.map} +0 -0
  172. /package/dist/{chunk-XG5RRUYF.js.map → chunk-BUTL6IFS.js.map} +0 -0
  173. /package/dist/{chunk-KESLPBKV.js.map → chunk-JR54LTPP.js.map} +0 -0
  174. /package/dist/{chunk-5XIVBO25.js.map → chunk-LVIY7P35.js.map} +0 -0
  175. /package/dist/{chunk-BPRIYNLE.js.map → chunk-TKAJ3JVF.js.map} +0 -0
  176. /package/dist/{chunk-Q36VMZST.js.map → chunk-VHNRMM4O.js.map} +0 -0
  177. /package/dist/{client-YXQUTXVZ.js.map → client-NWE4TCNO.js.map} +0 -0
  178. /package/dist/{detect-providers-5KOPZ7J2.js.map → detect-providers-ILLQZROY.js.map} +0 -0
  179. /package/dist/{installer-FS257JRZ.js.map → executor-F2YU7HXJ.js.map} +0 -0
  180. /package/dist/{llm-TH4NLIRM.js.map → installer-UMH7OJ5A.js.map} +0 -0
  181. /package/dist/{loader-CQYTFHEW.js.map → llm-AGVEF5XD.js.map} +0 -0
  182. /package/dist/{loader-NOMBJUPW.js.map → loader-LX7TFRM6.js.map} +0 -0
  183. /package/dist/{provider-check-43LAMSMH.js.map → loader-NAVVZK63.js.map} +0 -0
  184. /package/dist/{post-compact-JSECI44W.js.map → post-compact-USAODKPQ.js.map} +0 -0
  185. /package/dist/{post-tool-use-failure-OT7BFWQW.js.map → post-tool-use-failure-NZVSL2PO.js.map} +0 -0
  186. /package/dist/{pre-compact-OXVODKH4.js.map → pre-compact-LZ57DLUS.js.map} +0 -0
  187. /package/dist/{registry-U4CHXK6R.js.map → provider-check-ZEV5P4KM.js.map} +0 -0
  188. /package/dist/{team-LRZ6GTQK.js.map → registry-M2Z5QBWH.js.map} +0 -0
  189. /package/dist/{session-end-FT27DWYZ.js.map → session-end-LWJYQAXX.js.map} +0 -0
  190. /package/dist/{stop-ZPIKVLH4.js.map → stop-WRBTXEVT.js.map} +0 -0
  191. /package/dist/{stop-failure-2PX67YJC.js.map → stop-failure-32MGIG2Q.js.map} +0 -0
  192. /package/dist/{subagent-start-UUE6EHQD.js.map → subagent-start-VFGHQFVL.js.map} +0 -0
  193. /package/dist/{subagent-stop-KQWWWPE6.js.map → subagent-stop-663FXG3P.js.map} +0 -0
  194. /package/dist/{task-completed-WMHOFQ7B.js.map → task-completed-ZCQYEFMZ.js.map} +0 -0
  195. /package/dist/{turns-YFNI5CQC.js.map → team-JTI5CDUO.js.map} +0 -0
  196. /package/dist/{version-XMPPJQHR.js.map → turns-HU2CTZAP.js.map} +0 -0
@@ -1,11 +1,14 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+ import {
3
+ enumerateMatrixCells
4
+ } from "./chunk-54SXG5HF.js";
2
5
  import {
3
6
  DaemonLogger,
4
7
  LEVEL_ORDER
5
8
  } from "./chunk-3WOS4TAR.js";
6
9
  import {
7
10
  withTaskConfig
8
- } from "./chunk-US4LNCAT.js";
11
+ } from "./chunk-IPPMYQ2Y.js";
9
12
  import {
10
13
  EMBEDDABLE_TABLES,
11
14
  EMBEDDABLE_TEXT_COLUMNS,
@@ -15,16 +18,19 @@ import {
15
18
  getEmbeddingQueueDepth,
16
19
  getUnembedded,
17
20
  markEmbedded
18
- } from "./chunk-JZGN33AY.js";
21
+ } from "./chunk-75Z7UKDY.js";
19
22
  import {
23
+ deleteSecrets,
20
24
  getTeamPackageVersion,
21
25
  loadSecrets,
22
26
  readJsonConfig,
23
27
  readSecrets,
24
28
  resolveVaultConfigPath,
25
29
  writeSecret
26
- } from "./chunk-XATDZX7U.js";
30
+ } from "./chunk-R2JIJBCL.js";
27
31
  import {
32
+ DEFAULT_OPENAI_URL,
33
+ DEFAULT_OPENROUTER_URL,
28
34
  SKILL_SURVEY_TASK,
29
35
  buildTaskInstruction,
30
36
  closeOpenBatches,
@@ -33,19 +39,24 @@ import {
33
39
  countNotifications,
34
40
  countRuns,
35
41
  countSkillRecords,
42
+ countWriteIntents,
43
+ countWriteIntentsByTool,
44
+ countWriteIntentsByToolForEvaluation,
36
45
  createBatchLineage,
46
+ createLocalOpenAIBackend,
37
47
  deleteCandidate,
38
48
  deleteSkillRecordCascade,
39
49
  dismissAllNotifications,
40
- errorMessage,
41
50
  findBatchByPromptPrefix,
42
51
  getAllDomains,
43
52
  getCandidate,
44
- getDigestExtract,
45
- getEntity,
46
53
  getGraphForNode,
47
54
  getLatestBatch,
55
+ getLatestOpenBatch,
56
+ getLatestResumableRunForTask,
48
57
  getLatestRunId,
58
+ getLocalOpenAIBackendDefaultBaseUrl,
59
+ getLocalOpenAIBackendLabel,
49
60
  getNotification,
50
61
  getRun,
51
62
  getSkillRecord,
@@ -54,47 +65,56 @@ import {
54
65
  hasConfiguredProvider,
55
66
  incrementActivityCount,
56
67
  incrementSkillUsageCount,
68
+ inferLocalOpenAIBackendKind,
57
69
  insertBatchStateless,
58
70
  insertResolutionEvent,
59
71
  isInstructionRequiredTask,
60
72
  listBatchesBySession,
61
73
  listCandidatesWithCount,
62
- listDigestExtracts,
63
- listEntities,
64
74
  listLineageForSkill,
65
75
  listNotifications,
66
76
  listReports,
67
77
  listRuns,
78
+ listRunsForEvaluation,
68
79
  listSkillRecords,
69
80
  listSkillRecordsWithCount,
81
+ listWriteIntentTools,
82
+ listWriteIntents,
70
83
  markAllRead,
84
+ markRunningRunsInterrupted,
71
85
  notify,
72
86
  populateBatchResponses,
73
87
  register,
88
+ runAgent,
74
89
  setResponseSummary,
90
+ tryParseJson,
75
91
  updateCandidate,
76
92
  updateNotificationStatus
77
- } from "./chunk-UYMFCYBF.js";
93
+ } from "./chunk-NGH7U6A3.js";
94
+ import {
95
+ errorMessage,
96
+ parseCheckpointState,
97
+ runDurationMs
98
+ } from "./chunk-6LB7XELY.js";
78
99
  import {
79
100
  fullTextSearch,
80
101
  hydrateSearchResults
81
- } from "./chunk-5XIVBO25.js";
102
+ } from "./chunk-LVIY7P35.js";
82
103
  import {
83
104
  copyTaskToUser,
84
105
  deleteUserTask,
85
106
  loadAllTasks,
86
107
  validateTaskName,
87
108
  writeUserTask
88
- } from "./chunk-6X2ERTQV.js";
109
+ } from "./chunk-ILJPRYES.js";
89
110
  import {
90
- AgentTaskSchema,
91
111
  registerAgent,
92
112
  resolveDefinitionsDir,
93
113
  taskFromParsed
94
- } from "./chunk-CCRGY3QW.js";
114
+ } from "./chunk-JZS6GZ6T.js";
95
115
  import {
96
116
  listTurnsByRun
97
- } from "./chunk-6RFZWV4R.js";
117
+ } from "./chunk-FCJ5JV54.js";
98
118
  import {
99
119
  cleanupStagedSkill,
100
120
  listStaleStagingDirs
@@ -102,7 +122,11 @@ import {
102
122
  import {
103
123
  Anthropic,
104
124
  createEmbeddingProvider
105
- } from "./chunk-KHT24OWC.js";
125
+ } from "./chunk-YDUOSRGD.js";
126
+ import {
127
+ OPENAI_API_KEY_ENV,
128
+ OPENROUTER_API_KEY_ENV
129
+ } from "./chunk-CISWUP5W.js";
106
130
  import {
107
131
  getMachineId
108
132
  } from "./chunk-ENWBFX7F.js";
@@ -111,26 +135,42 @@ import {
111
135
  cleanStaleBuffers,
112
136
  listBufferSessionIds
113
137
  } from "./chunk-V7XG6V6C.js";
114
- import "./chunk-POEPHBQK.js";
115
- import "./chunk-KESLPBKV.js";
138
+ import "./chunk-JR54LTPP.js";
116
139
  import "./chunk-SAKJMNSR.js";
117
140
  import {
118
141
  SymbiontInstaller
119
- } from "./chunk-Q36VMZST.js";
142
+ } from "./chunk-VHNRMM4O.js";
120
143
  import {
121
144
  checkLocalProvider
122
- } from "./chunk-BPRIYNLE.js";
145
+ } from "./chunk-TKAJ3JVF.js";
123
146
  import {
124
147
  LmStudioBackend,
125
148
  OllamaBackend
126
- } from "./chunk-FMRZ26U5.js";
149
+ } from "./chunk-X3IGT5RV.js";
150
+ import {
151
+ composeSessionStartContext,
152
+ shouldInjectSessionStartDigest
153
+ } from "./chunk-DJ3IHNYO.js";
127
154
  import {
155
+ buildCortexInstructionsInput,
128
156
  countSpores,
157
+ deletePlan,
158
+ getCortexInstructions,
159
+ getPlan,
160
+ getPlanByLogicalKey,
129
161
  getSpore,
130
162
  insertSpore,
163
+ listDigestExtracts,
164
+ listDigestRevisions,
165
+ listPlans,
166
+ listPlansBySession,
131
167
  listSpores,
132
- updateSporeStatus
133
- } from "./chunk-4YFKBL3F.js";
168
+ resolveInstructionDelivery,
169
+ rollbackDigestExtract,
170
+ shouldInjectCortex,
171
+ updateSporeStatus,
172
+ upsertPlan
173
+ } from "./chunk-F3OEQYLS.js";
134
174
  import {
135
175
  backfillUnsynced,
136
176
  closeSession,
@@ -153,20 +193,29 @@ import {
153
193
  pruneOld,
154
194
  reactivateSessionIfCompleted,
155
195
  retryDeadLettered,
156
- syncRow,
157
196
  updateSession,
158
197
  upsertSession
159
- } from "./chunk-EVDQKYCG.js";
198
+ } from "./chunk-RQSJLWP4.js";
160
199
  import {
161
200
  evaluateSessionCaptureRules,
162
201
  readTranscriptMeta
163
202
  } from "./chunk-XL75KZGI.js";
203
+ import {
204
+ PLAN_STATUSES
205
+ } from "./chunk-CESKJD44.js";
164
206
  import {
165
207
  EMBEDDING_DIMENSIONS,
166
208
  REST_SETTABLE_STATUSES,
167
209
  SCHEMA_VERSION,
168
- createSchema
169
- } from "./chunk-MYOZLMB2.js";
210
+ TRANSCRIPT_SOURCE_PREFIX,
211
+ buildPathPlanLogicalKey,
212
+ buildPlanId,
213
+ buildSessionPlanLogicalKey,
214
+ buildSessionTagPlanLogicalKey,
215
+ createSchema,
216
+ humanizePlanToken,
217
+ normalizePlanSourcePath
218
+ } from "./chunk-RL5R4CQU.js";
170
219
  import {
171
220
  CONFIG_FILENAME,
172
221
  MycoConfigSchema,
@@ -179,7 +228,11 @@ import {
179
228
  updateConfig,
180
229
  updateLocalConfig,
181
230
  updateTeamConfig
182
- } from "./chunk-OD4AA7PV.js";
231
+ } from "./chunk-53RPGOEN.js";
232
+ import {
233
+ AgentTaskSchema
234
+ } from "./chunk-OUJSQSKE.js";
235
+ import "./chunk-POEPHBQK.js";
183
236
  import {
184
237
  closeDatabase,
185
238
  getDatabase,
@@ -191,10 +244,10 @@ import {
191
244
  } from "./chunk-ZXZPJJN3.js";
192
245
  import {
193
246
  resolveCliEntryPath
194
- } from "./chunk-VVNL26WX.js";
247
+ } from "./chunk-P66DLD6G.js";
195
248
  import {
196
249
  getPluginVersion
197
- } from "./chunk-XG5RRUYF.js";
250
+ } from "./chunk-BUTL6IFS.js";
198
251
  import {
199
252
  loadManifests,
200
253
  resolvePackageRoot
@@ -210,6 +263,7 @@ import {
210
263
  DEFAULT_AGENT_ID,
211
264
  DEFAULT_LIST_LIMIT,
212
265
  DEFAULT_RELEASE_CHANNEL,
266
+ DEFAULT_SYMBIONT_NAME,
213
267
  EMBEDDING_BATCH_SIZE,
214
268
  EXCLUDED_SPORE_STATUSES,
215
269
  FEED_DEFAULT_LIMIT,
@@ -243,8 +297,10 @@ import {
243
297
  SYNC_PROTOCOL_VERSION,
244
298
  TEAM_API_KEY_SECRET,
245
299
  TEAM_HEALTH_TIMEOUT_MS,
300
+ TEAM_REQUEST_TIMEOUT_MS,
246
301
  TEAM_SEARCH_TIMEOUT_MS,
247
302
  TEAM_SOURCE_PREFIX,
303
+ TEAM_SYNC_TIMEOUT_MS,
248
304
  UPDATE_CHECK_CACHE_PATH,
249
305
  UPDATE_CHECK_INTERVAL_HOURS,
250
306
  UPDATE_CONFIG_PATH,
@@ -257,7 +313,7 @@ import {
257
313
  USER_TASK_SOURCE,
258
314
  epochSeconds,
259
315
  estimateTokens
260
- } from "./chunk-FLLBJLHM.js";
316
+ } from "./chunk-6C6QZ4PM.js";
261
317
  import {
262
318
  LOG_KINDS,
263
319
  kindToComponent
@@ -428,13 +484,24 @@ var DaemonServer = class {
428
484
  uptime: process.uptime()
429
485
  }
430
486
  }));
487
+ this.registerRoute("GET", "/api/version", async () => ({
488
+ body: { version: this.version }
489
+ }));
431
490
  }
432
491
  async handleRequest(req, res) {
433
492
  const match = this.router.match(req.method, req.url);
434
493
  if (match) {
494
+ const rejection = validateLoopbackRequest(req, this.port);
495
+ if (rejection) {
496
+ res.writeHead(rejection.status, { "Content-Type": "application/json" });
497
+ res.end(JSON.stringify({ error: rejection.error }));
498
+ return;
499
+ }
435
500
  this.onRequest?.();
501
+ const versionHeader = { "X-Myco-Api-Version": this.version };
436
502
  try {
437
- const body = req.method === "POST" || req.method === "PUT" || req.method === "PATCH" ? await readBody(req) : void 0;
503
+ const needsBody = req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE";
504
+ const body = needsBody ? await readBody(req) : void 0;
438
505
  const result = await match.handler({
439
506
  body,
440
507
  query: match.query,
@@ -443,11 +510,11 @@ var DaemonServer = class {
443
510
  });
444
511
  const status = result.status ?? DEFAULT_STATUS;
445
512
  if (Buffer.isBuffer(result.body)) {
446
- res.writeHead(status, result.headers ?? {});
513
+ res.writeHead(status, { ...versionHeader, ...result.headers });
447
514
  res.end(result.body);
448
515
  return;
449
516
  }
450
- const headers = { "Content-Type": "application/json", ...result.headers };
517
+ const headers = { "Content-Type": "application/json", ...versionHeader, ...result.headers };
451
518
  res.writeHead(status, headers);
452
519
  res.end(JSON.stringify(result.body));
453
520
  } catch (error) {
@@ -455,7 +522,7 @@ var DaemonServer = class {
455
522
  path: req.url,
456
523
  error: error.message
457
524
  });
458
- res.writeHead(500, { "Content-Type": "application/json" });
525
+ res.writeHead(500, { "Content-Type": "application/json", ...versionHeader });
459
526
  res.end(JSON.stringify({ error: error.message }));
460
527
  }
461
528
  return;
@@ -578,6 +645,36 @@ function readBody(req) {
578
645
  req.on("error", reject);
579
646
  });
580
647
  }
648
+ function validateLoopbackRequest(req, port) {
649
+ const host = req.headers.host;
650
+ if (host && !isLoopbackHost(host, port)) {
651
+ return { status: 403, error: "forbidden_host" };
652
+ }
653
+ const origin = req.headers.origin;
654
+ if (origin && !isLoopbackOrigin(origin, port)) {
655
+ return { status: 403, error: "forbidden_origin" };
656
+ }
657
+ const method = (req.method ?? "").toUpperCase();
658
+ const isMutating = method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
659
+ if (!isMutating) return null;
660
+ const contentLengthHeader = req.headers["content-length"];
661
+ const contentLength = contentLengthHeader ? Number(contentLengthHeader) : NaN;
662
+ const hasBody = Number.isFinite(contentLength) ? contentLength > 0 : (req.headers["transfer-encoding"] ?? "").length > 0;
663
+ if (!hasBody) return null;
664
+ const contentType = (req.headers["content-type"] ?? "").toLowerCase();
665
+ if (!contentType.includes("application/json")) {
666
+ return { status: 415, error: "unsupported_media_type" };
667
+ }
668
+ return null;
669
+ }
670
+ function isLoopbackHost(host, port) {
671
+ const portStr = String(port);
672
+ return host === `127.0.0.1:${portStr}` || host === `localhost:${portStr}`;
673
+ }
674
+ function isLoopbackOrigin(origin, port) {
675
+ const portStr = String(port);
676
+ return origin === `http://127.0.0.1:${portStr}` || origin === `http://localhost:${portStr}`;
677
+ }
581
678
 
582
679
  // src/daemon/lifecycle.ts
583
680
  var SessionRegistry = class {
@@ -2599,7 +2696,7 @@ var TeamSyncClient = class _TeamSyncClient {
2599
2696
  content_hash: data.content_hash ?? null
2600
2697
  };
2601
2698
  })
2602
- });
2699
+ }, { timeoutMs: TEAM_SYNC_TIMEOUT_MS });
2603
2700
  return res;
2604
2701
  }
2605
2702
  /**
@@ -2699,17 +2796,25 @@ var TeamSyncClient = class _TeamSyncClient {
2699
2796
  "Content-Type": "application/json"
2700
2797
  };
2701
2798
  }
2702
- async request(method, path23, body) {
2703
- const res = await this.fetchFn(`${this.workerUrl}${path23}`, {
2704
- method,
2705
- headers: this.headers(),
2706
- body: body !== void 0 ? JSON.stringify(body) : void 0
2707
- });
2708
- if (!res.ok) {
2709
- const text = await res.text().catch(() => "");
2710
- throw new Error(`Team sync request ${method} ${path23} failed: ${res.status} ${text}`);
2799
+ async request(method, path25, body, options = {}) {
2800
+ const timeoutMs = options.timeoutMs ?? TEAM_REQUEST_TIMEOUT_MS;
2801
+ const controller = new AbortController();
2802
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2803
+ try {
2804
+ const res = await this.fetchFn(`${this.workerUrl}${path25}`, {
2805
+ method,
2806
+ headers: this.headers(),
2807
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
2808
+ signal: controller.signal
2809
+ });
2810
+ if (!res.ok) {
2811
+ const text = await res.text().catch(() => "");
2812
+ throw new Error(`Team sync request ${method} ${path25} failed: ${res.status} ${text}`);
2813
+ }
2814
+ return res.json();
2815
+ } finally {
2816
+ clearTimeout(timer);
2711
2817
  }
2712
- return res.json();
2713
2818
  }
2714
2819
  };
2715
2820
 
@@ -2840,7 +2945,7 @@ function createTeamHandlers(deps) {
2840
2945
  return { body: { retried: count } };
2841
2946
  }
2842
2947
  async function handleUpgradeWorker(_req) {
2843
- const { upgradeWorker } = await import("./team-LRZ6GTQK.js");
2948
+ const { upgradeWorker } = await import("./team-JTI5CDUO.js");
2844
2949
  logger.info("team-sync.upgrade.start", "Starting worker upgrade");
2845
2950
  const result = upgradeWorker(vaultDir);
2846
2951
  if (!result.success) {
@@ -3232,7 +3337,7 @@ function createSkillRecordDeleteHandler(deps) {
3232
3337
  logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill directory", { name: record.name, error: String(err) });
3233
3338
  }
3234
3339
  try {
3235
- const { syncSkillSymlinks } = await import("./installer-FS257JRZ.js");
3340
+ const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
3236
3341
  syncSkillSymlinks(projectRoot, record.name, { remove: true });
3237
3342
  } catch (err) {
3238
3343
  logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill symlinks", { name: record.name, error: String(err) });
@@ -3451,6 +3556,10 @@ async function handleGetProgress(tracker, token) {
3451
3556
 
3452
3557
  // src/daemon/api/models.ts
3453
3558
  var MODEL_LIST_TIMEOUT_MS = 5e3;
3559
+ var REMOTE_MODELS_ENDPOINT = "/models";
3560
+ var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
3561
+ var OPENROUTER_DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
3562
+ var REMOTE_PROVIDER_TIMEOUT_MS = 5e3;
3454
3563
  var ANTHROPIC_MODELS = [
3455
3564
  "claude-sonnet-4-6",
3456
3565
  "claude-opus-4-6",
@@ -3476,22 +3585,60 @@ function filterLlmModels(models) {
3476
3585
  return !EMBEDDING_PATTERNS.some((p) => name.includes(p));
3477
3586
  });
3478
3587
  }
3588
+ var REMOTE_PROVIDER_DEFAULTS = {
3589
+ openai: OPENAI_DEFAULT_BASE_URL,
3590
+ openrouter: OPENROUTER_DEFAULT_BASE_URL
3591
+ };
3592
+ var REMOTE_PROVIDER_ENV_VARS = {
3593
+ openai: OPENAI_API_KEY_ENV,
3594
+ openrouter: OPENROUTER_API_KEY_ENV
3595
+ };
3596
+ function getRemoteProviderApiKey(provider) {
3597
+ const preferredKey = process.env[REMOTE_PROVIDER_ENV_VARS[provider]];
3598
+ if (preferredKey) return preferredKey;
3599
+ if (provider === "openai") {
3600
+ return process.env.OPENAI_API_KEY;
3601
+ }
3602
+ return void 0;
3603
+ }
3604
+ async function fetchRemoteProviderModels(provider, _baseUrl, timeoutMs = REMOTE_PROVIDER_TIMEOUT_MS) {
3605
+ const apiKey = getRemoteProviderApiKey(provider);
3606
+ if (!apiKey) return [];
3607
+ const baseUrl = REMOTE_PROVIDER_DEFAULTS[provider];
3608
+ const response = await fetch(`${baseUrl}${REMOTE_MODELS_ENDPOINT}`, {
3609
+ headers: {
3610
+ Authorization: `Bearer ${apiKey}`
3611
+ },
3612
+ signal: AbortSignal.timeout(timeoutMs)
3613
+ });
3614
+ if (!response.ok) {
3615
+ throw new Error(`${provider} models request failed with ${response.status}`);
3616
+ }
3617
+ const data = await response.json();
3618
+ const modelIds = (data.data ?? []).map((entry) => entry.id).filter((id) => typeof id === "string" && id.length > 0);
3619
+ return filterLlmModels(modelIds);
3620
+ }
3479
3621
  async function handleGetModels(req) {
3480
3622
  const provider = req.query.provider;
3481
3623
  const type = req.query.type;
3624
+ const localBackend = req.query.local_backend;
3482
3625
  if (!provider) {
3483
3626
  return { status: 400, body: { error: "provider query parameter required" } };
3484
3627
  }
3485
3628
  let models = [];
3486
3629
  try {
3487
- if (provider === "ollama") {
3488
- const backend = new OllamaBackend({ base_url: req.query.base_url });
3489
- models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
3490
- } else if (provider === "lm-studio" || provider === "openai-compatible") {
3491
- const backend = new LmStudioBackend({ base_url: req.query.base_url });
3630
+ const localBackendKind = inferLocalOpenAIBackendKind({
3631
+ type: provider === "lm-studio" ? "lmstudio" : provider,
3632
+ localBackend,
3633
+ baseUrl: req.query.base_url
3634
+ });
3635
+ if (localBackendKind) {
3636
+ const backend = createLocalOpenAIBackend(localBackendKind, req.query.base_url);
3492
3637
  models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
3493
3638
  } else if (provider === "anthropic") {
3494
3639
  models = ANTHROPIC_MODELS;
3640
+ } else if (provider === "openai" || provider === "openrouter") {
3641
+ models = await fetchRemoteProviderModels(provider, void 0, MODEL_LIST_TIMEOUT_MS);
3495
3642
  }
3496
3643
  } catch {
3497
3644
  }
@@ -3709,138 +3856,6 @@ function getAttachmentByFilePath(filePath) {
3709
3856
  return row ? toAttachmentRow(row) : null;
3710
3857
  }
3711
3858
 
3712
- // src/db/queries/plans.ts
3713
- var DEFAULT_LIST_LIMIT2 = 100;
3714
- var DEFAULT_STATUS2 = "active";
3715
- var DEFAULT_PROCESSED2 = 0;
3716
- var PLAN_COLUMNS = [
3717
- "id",
3718
- "status",
3719
- "author",
3720
- "title",
3721
- "content",
3722
- "source_path",
3723
- "tags",
3724
- "session_id",
3725
- "prompt_batch_id",
3726
- "content_hash",
3727
- "processed",
3728
- "embedded",
3729
- "created_at",
3730
- "updated_at",
3731
- "machine_id",
3732
- "synced_at"
3733
- ];
3734
- var SELECT_COLUMNS4 = PLAN_COLUMNS.join(", ");
3735
- function toPlanRow(row) {
3736
- return {
3737
- id: row.id,
3738
- status: row.status,
3739
- author: row.author ?? null,
3740
- title: row.title ?? null,
3741
- content: row.content ?? null,
3742
- source_path: row.source_path ?? null,
3743
- tags: row.tags ?? null,
3744
- session_id: row.session_id ?? null,
3745
- prompt_batch_id: row.prompt_batch_id ?? null,
3746
- content_hash: row.content_hash ?? null,
3747
- processed: row.processed,
3748
- embedded: row.embedded ?? 0,
3749
- created_at: row.created_at,
3750
- updated_at: row.updated_at ?? null,
3751
- machine_id: row.machine_id ?? "local",
3752
- synced_at: row.synced_at ?? null
3753
- };
3754
- }
3755
- function upsertPlan(data) {
3756
- const db = getDatabase();
3757
- db.prepare(
3758
- `INSERT INTO plans (
3759
- id, status, author, title, content,
3760
- source_path, tags, session_id, prompt_batch_id, content_hash,
3761
- processed, created_at, updated_at, machine_id
3762
- ) VALUES (
3763
- ?, ?, ?, ?, ?,
3764
- ?, ?, ?, ?, ?,
3765
- ?, ?, ?, ?
3766
- )
3767
- ON CONFLICT (id) DO UPDATE SET
3768
- status = EXCLUDED.status,
3769
- author = EXCLUDED.author,
3770
- title = EXCLUDED.title,
3771
- content = EXCLUDED.content,
3772
- source_path = EXCLUDED.source_path,
3773
- tags = EXCLUDED.tags,
3774
- session_id = EXCLUDED.session_id,
3775
- prompt_batch_id = EXCLUDED.prompt_batch_id,
3776
- content_hash = EXCLUDED.content_hash,
3777
- processed = EXCLUDED.processed,
3778
- updated_at = EXCLUDED.updated_at,
3779
- embedded = CASE
3780
- WHEN EXCLUDED.content_hash != plans.content_hash THEN 0
3781
- ELSE plans.embedded
3782
- END`
3783
- ).run(
3784
- data.id,
3785
- data.status ?? DEFAULT_STATUS2,
3786
- data.author ?? null,
3787
- data.title ?? null,
3788
- data.content ?? null,
3789
- data.source_path ?? null,
3790
- data.tags ?? null,
3791
- data.session_id ?? null,
3792
- data.prompt_batch_id ?? null,
3793
- data.content_hash ?? null,
3794
- data.processed ?? DEFAULT_PROCESSED2,
3795
- data.created_at,
3796
- data.updated_at ?? null,
3797
- data.machine_id ?? getTeamMachineId()
3798
- );
3799
- const row = toPlanRow(
3800
- db.prepare(`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`).get(data.id)
3801
- );
3802
- syncRow("plans", row);
3803
- return row;
3804
- }
3805
- function getPlan(id) {
3806
- const db = getDatabase();
3807
- const row = db.prepare(
3808
- `SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`
3809
- ).get(id);
3810
- if (!row) return null;
3811
- return toPlanRow(row);
3812
- }
3813
- function listPlans(options = {}) {
3814
- const db = getDatabase();
3815
- const conditions = [];
3816
- const params = [];
3817
- if (options.status !== void 0) {
3818
- conditions.push(`status = ?`);
3819
- params.push(options.status);
3820
- }
3821
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3822
- const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
3823
- params.push(limit);
3824
- const rows = db.prepare(
3825
- `SELECT ${SELECT_COLUMNS4}
3826
- FROM plans
3827
- ${where}
3828
- ORDER BY created_at DESC
3829
- LIMIT ?`
3830
- ).all(...params);
3831
- return rows.map(toPlanRow);
3832
- }
3833
- function listPlansBySession(sessionId) {
3834
- const db = getDatabase();
3835
- const rows = db.prepare(
3836
- `SELECT ${SELECT_COLUMNS4}
3837
- FROM plans
3838
- WHERE session_id = ?
3839
- ORDER BY created_at DESC`
3840
- ).all(sessionId);
3841
- return rows.map(toPlanRow);
3842
- }
3843
-
3844
3859
  // src/daemon/jobs/session-cleanup.ts
3845
3860
  import { unlink, glob } from "fs/promises";
3846
3861
  async function cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir) {
@@ -3885,8 +3900,8 @@ async function triggerTitleSummary(sessionId, deps) {
3885
3900
  if (config.agent.summary_batch_interval <= 0) return;
3886
3901
  if (config.agent.event_tasks_enabled === false) return;
3887
3902
  try {
3888
- const { runAgent } = await import("./executor-HWW2QNZQ.js");
3889
- runAgent(vaultDir, {
3903
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
3904
+ runAgent2(vaultDir, {
3890
3905
  task: "title-summary",
3891
3906
  instruction: `Process session ${sessionId} only`,
3892
3907
  embeddingManager
@@ -3900,11 +3915,16 @@ async function triggerTitleSummary(sessionId, deps) {
3900
3915
  }
3901
3916
  }
3902
3917
 
3918
+ // src/daemon/api/error-envelope.ts
3919
+ function errorBody(code, message) {
3920
+ return { error: { code, message } };
3921
+ }
3922
+
3903
3923
  // src/daemon/api/sessions.ts
3904
- var DEFAULT_LIST_LIMIT3 = 50;
3924
+ var DEFAULT_LIST_LIMIT2 = 50;
3905
3925
  var DEFAULT_LIST_OFFSET2 = 0;
3906
3926
  async function handleListSessions(req) {
3907
- const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT3;
3927
+ const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT2;
3908
3928
  const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET2;
3909
3929
  const status = req.query.status || void 0;
3910
3930
  const agent = req.query.agent || void 0;
@@ -3947,7 +3967,7 @@ async function handleGetSessionAttachments(req) {
3947
3967
  }
3948
3968
  async function handleGetSessionPlans(req) {
3949
3969
  const plans = listPlansBySession(req.params.id);
3950
- return { body: plans };
3970
+ return { body: { plans } };
3951
3971
  }
3952
3972
  function createSessionMutationHandlers(deps) {
3953
3973
  const { embeddingManager, vaultDir, logger, liveConfig } = deps;
@@ -3988,7 +4008,125 @@ function createSessionMutationHandlers(deps) {
3988
4008
  const impact = getSessionImpact(sessionId);
3989
4009
  return { body: impact };
3990
4010
  }
3991
- return { handleDeleteSession, handleCompleteSession, handleGetSessionImpact };
4011
+ async function handleDeletePlan(req) {
4012
+ const existing = getPlan(req.params.id);
4013
+ if (!existing) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
4014
+ const localMachineId = getTeamMachineId();
4015
+ const body = req.body;
4016
+ const forceRemote = body?.force_remote === true;
4017
+ if (existing.machine_id !== localMachineId && !forceRemote) {
4018
+ logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete rejected", {
4019
+ plan_id: existing.id,
4020
+ plan_machine_id: existing.machine_id,
4021
+ local_machine_id: localMachineId
4022
+ });
4023
+ return {
4024
+ status: 403,
4025
+ body: errorBody(
4026
+ "cross-machine-delete",
4027
+ 'Plan belongs to another machine; pass {"force_remote": true} to delete.'
4028
+ )
4029
+ };
4030
+ }
4031
+ if (existing.machine_id !== localMachineId && forceRemote) {
4032
+ logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete allowed by force_remote", {
4033
+ plan_id: existing.id,
4034
+ plan_machine_id: existing.machine_id,
4035
+ local_machine_id: localMachineId
4036
+ });
4037
+ }
4038
+ const deleted = deletePlan(req.params.id);
4039
+ if (!deleted) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
4040
+ embeddingManager.onRemoved("plans", deleted.id);
4041
+ logger.info(LOG_KINDS.API_SESSION_DELETE, "Plan deleted", {
4042
+ plan_id: deleted.id,
4043
+ session_id: deleted.session_id,
4044
+ logical_key: deleted.logical_key
4045
+ });
4046
+ return {
4047
+ body: {
4048
+ ok: true,
4049
+ id: deleted.id,
4050
+ session_id: deleted.session_id
4051
+ }
4052
+ };
4053
+ }
4054
+ return { handleDeleteSession, handleCompleteSession, handleGetSessionImpact, handleDeletePlan };
4055
+ }
4056
+
4057
+ // src/db/queries/entities.ts
4058
+ var DEFAULT_LIST_LIMIT3 = 100;
4059
+ var ENTITY_COLUMNS = [
4060
+ "id",
4061
+ "agent_id",
4062
+ "type",
4063
+ "name",
4064
+ "properties",
4065
+ "first_seen",
4066
+ "last_seen",
4067
+ "status",
4068
+ "machine_id",
4069
+ "synced_at"
4070
+ ];
4071
+ var SELECT_COLUMNS4 = ENTITY_COLUMNS.join(", ");
4072
+ function toEntityRow(row) {
4073
+ return {
4074
+ id: row.id,
4075
+ agent_id: row.agent_id,
4076
+ type: row.type,
4077
+ name: row.name,
4078
+ properties: row.properties ?? null,
4079
+ first_seen: row.first_seen,
4080
+ last_seen: row.last_seen,
4081
+ status: row.status ?? "active",
4082
+ machine_id: row.machine_id ?? "local",
4083
+ synced_at: row.synced_at ?? null
4084
+ };
4085
+ }
4086
+ function listEntities(options = {}) {
4087
+ const db = getDatabase();
4088
+ const conditions = [];
4089
+ const params = [];
4090
+ if (options.agent_id !== void 0) {
4091
+ conditions.push(`agent_id = ?`);
4092
+ params.push(options.agent_id);
4093
+ }
4094
+ if (options.type !== void 0) {
4095
+ conditions.push(`type = ?`);
4096
+ params.push(options.type);
4097
+ }
4098
+ if (options.name !== void 0) {
4099
+ conditions.push(`name = ?`);
4100
+ params.push(options.name);
4101
+ }
4102
+ if (options.status !== void 0) {
4103
+ conditions.push(`status = ?`);
4104
+ params.push(options.status);
4105
+ } else {
4106
+ conditions.push(`status = ?`);
4107
+ params.push("active");
4108
+ }
4109
+ if (options.mentioned_in !== void 0 && options.note_type !== void 0) {
4110
+ conditions.push(
4111
+ `id IN (SELECT entity_id FROM entity_mentions WHERE note_id = ? AND note_type = ?)`
4112
+ );
4113
+ params.push(options.mentioned_in);
4114
+ params.push(options.note_type);
4115
+ }
4116
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4117
+ const limit = options.limit ?? DEFAULT_LIST_LIMIT3;
4118
+ const offset = options.offset ?? 0;
4119
+ params.push(limit);
4120
+ params.push(offset);
4121
+ const rows = db.prepare(
4122
+ `SELECT ${SELECT_COLUMNS4}
4123
+ FROM entities
4124
+ ${where}
4125
+ ORDER BY last_seen DESC
4126
+ LIMIT ?
4127
+ OFFSET ?`
4128
+ ).all(...params);
4129
+ return rows.map(toEntityRow);
3992
4130
  }
3993
4131
 
3994
4132
  // src/daemon/api/mycelium.ts
@@ -3997,7 +4135,6 @@ var DEFAULT_LIST_OFFSET3 = 0;
3997
4135
  var DEFAULT_GRAPH_DEPTH = 1;
3998
4136
  var MAX_GRAPH_DEPTH = 3;
3999
4137
  var SPORE_NAME_PREVIEW_CHARS = 60;
4000
- var GRAPH_SEED_ENTITY_LIMIT = 4;
4001
4138
  var GRAPH_SEED_SPORE_LIMIT = 4;
4002
4139
  var GRAPH_SEED_SESSION_LIMIT = 4;
4003
4140
  var EXCLUDED_GRAPH_EDGE_TYPES = /* @__PURE__ */ new Set(["HAS_BATCH", "EXTRACTED_FROM"]);
@@ -4056,15 +4193,6 @@ async function handleGetGraphSeeds(_req) {
4056
4193
  ORDER BY started_at DESC
4057
4194
  LIMIT ?`
4058
4195
  ).all(GRAPH_SEED_SESSION_LIMIT);
4059
- const entityRows = db.prepare(
4060
- `SELECT e.id, e.type, e.name, e.status, e.first_seen as created_at, COUNT(em.entity_id) as mention_count
4061
- FROM entities e
4062
- LEFT JOIN entity_mentions em ON em.entity_id = e.id
4063
- WHERE e.agent_id = ? AND e.status = 'active'
4064
- GROUP BY e.id
4065
- ORDER BY mention_count DESC, e.last_seen DESC
4066
- LIMIT ?`
4067
- ).all(DEFAULT_AGENT_ID, GRAPH_SEED_ENTITY_LIMIT);
4068
4196
  const sporeSeeds = sporeRows.map((row) => ({
4069
4197
  id: row.id,
4070
4198
  name: (row.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4082,20 +4210,66 @@ async function handleGetGraphSeeds(_req) {
4082
4210
  created_at: row.created_at,
4083
4211
  content: row.summary ?? void 0
4084
4212
  }));
4085
- const entitySeeds = entityRows.map((row) => ({
4086
- id: row.id,
4087
- name: row.name,
4088
- type: row.type,
4089
- status: row.status ?? void 0,
4090
- created_at: row.created_at,
4091
- mention_count: Number(row.mention_count) || 0
4092
- }));
4213
+ const topConnectedRow = db.prepare(
4214
+ `SELECT node_id FROM (
4215
+ SELECT source_id AS node_id, COUNT(*) AS cnt
4216
+ FROM graph_edges
4217
+ WHERE agent_id = ?
4218
+ AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
4219
+ AND source_type IN ('spore', 'session')
4220
+ GROUP BY source_id
4221
+ UNION ALL
4222
+ SELECT target_id, COUNT(*)
4223
+ FROM graph_edges
4224
+ WHERE agent_id = ?
4225
+ AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
4226
+ AND target_type IN ('spore', 'session')
4227
+ GROUP BY target_id
4228
+ )
4229
+ GROUP BY node_id
4230
+ ORDER BY SUM(cnt) DESC
4231
+ LIMIT 1`
4232
+ ).get(DEFAULT_AGENT_ID, DEFAULT_AGENT_ID);
4233
+ let topSeed = null;
4234
+ if (topConnectedRow?.node_id) {
4235
+ const topId = topConnectedRow.node_id;
4236
+ const sporeHit = db.prepare(
4237
+ `SELECT id, observation_type, status, content, created_at
4238
+ FROM spores WHERE id = ?`
4239
+ ).get(topId);
4240
+ if (sporeHit) {
4241
+ topSeed = {
4242
+ id: sporeHit.id,
4243
+ name: (sporeHit.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
4244
+ type: "spore",
4245
+ status: sporeHit.status ?? void 0,
4246
+ created_at: sporeHit.created_at,
4247
+ content: sporeHit.content,
4248
+ observation_type: sporeHit.observation_type
4249
+ };
4250
+ } else {
4251
+ const sessionHit = db.prepare(
4252
+ `SELECT id, title, summary, status, started_at as created_at
4253
+ FROM sessions WHERE id = ?`
4254
+ ).get(topId);
4255
+ if (sessionHit) {
4256
+ topSeed = {
4257
+ id: sessionHit.id,
4258
+ name: sessionHit.title ?? `Session ${sessionHit.id.slice(-6)}`,
4259
+ type: "session",
4260
+ status: sessionHit.status ?? void 0,
4261
+ created_at: sessionHit.created_at,
4262
+ content: sessionHit.summary ?? void 0
4263
+ };
4264
+ }
4265
+ }
4266
+ }
4093
4267
  const seeds = [
4094
- ...entitySeeds,
4095
- ...sessionSeeds,
4096
- ...sporeSeeds
4268
+ ...topSeed ? [topSeed] : [],
4269
+ ...sessionSeeds.filter((s) => s.id !== topSeed?.id),
4270
+ ...sporeSeeds.filter((s) => s.id !== topSeed?.id)
4097
4271
  ];
4098
- const recommendedId = entitySeeds[0]?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
4272
+ const recommendedId = topSeed?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
4099
4273
  return {
4100
4274
  body: {
4101
4275
  seeds,
@@ -4106,32 +4280,19 @@ async function handleGetGraphSeeds(_req) {
4106
4280
  async function handleGetGraph(req) {
4107
4281
  const depth = Math.min(Number(req.query.depth) || DEFAULT_GRAPH_DEPTH, MAX_GRAPH_DEPTH);
4108
4282
  const id = req.params.id;
4109
- let centerNode = null;
4110
- let centerType = "entity";
4111
- const entity = getEntity(id);
4112
- if (entity) {
4113
- centerNode = entity;
4114
- centerType = "entity";
4283
+ let centerType;
4284
+ if (getSpore(id)) {
4285
+ centerType = "spore";
4286
+ } else if (getSession(id)) {
4287
+ centerType = "session";
4115
4288
  } else {
4116
- const spore = getSpore(id);
4117
- if (spore) {
4118
- centerNode = spore;
4119
- centerType = "spore";
4120
- } else {
4121
- const session = getSession(id);
4122
- if (session) {
4123
- centerNode = session;
4124
- centerType = "session";
4125
- }
4126
- }
4289
+ return { status: 404, body: { error: "not_found" } };
4127
4290
  }
4128
- if (!centerNode) return { status: 404, body: { error: "not_found" } };
4129
4291
  const graph = getGraphForNode(id, centerType, { depth });
4130
4292
  const filteredEdges = graph.edges.filter(
4131
4293
  (e) => !EXCLUDED_GRAPH_EDGE_TYPES.has(e.type)
4132
4294
  );
4133
4295
  const graphDb = getDatabase();
4134
- const entityIds = /* @__PURE__ */ new Set();
4135
4296
  const sporeIds = /* @__PURE__ */ new Set();
4136
4297
  const sessionIds = /* @__PURE__ */ new Set();
4137
4298
  for (const edge of filteredEdges) {
@@ -4139,70 +4300,23 @@ async function handleGetGraph(req) {
4139
4300
  [edge.source_id, edge.source_type],
4140
4301
  [edge.target_id, edge.target_type]
4141
4302
  ]) {
4142
- switch (type) {
4143
- case "entity":
4144
- entityIds.add(nodeId);
4145
- break;
4146
- case "spore":
4147
- sporeIds.add(nodeId);
4148
- break;
4149
- case "session":
4150
- sessionIds.add(nodeId);
4151
- break;
4152
- }
4303
+ if (type === "spore") sporeIds.add(nodeId);
4304
+ else if (type === "session") sessionIds.add(nodeId);
4153
4305
  }
4154
4306
  }
4155
- if (centerType === "entity") entityIds.add(id);
4156
4307
  if (centerType === "spore") sporeIds.add(id);
4157
- if (centerType === "session") sessionIds.add(id);
4158
- const entityIdArray = Array.from(entityIds);
4159
- let entityNodes = [];
4160
- if (entityIdArray.length > 0) {
4161
- const placeholders = entityIdArray.map(() => "?").join(", ");
4162
- entityNodes = graphDb.prepare(
4163
- `SELECT id, type, name, properties, status, first_seen as created_at
4164
- FROM entities WHERE id IN (${placeholders})`
4165
- ).all(...entityIdArray);
4166
- }
4308
+ else sessionIds.add(id);
4167
4309
  const sporeIdArray = Array.from(sporeIds);
4168
- let sporeNodes = [];
4169
- if (sporeIdArray.length > 0) {
4170
- const placeholders = sporeIdArray.map(() => "?").join(", ");
4171
- sporeNodes = graphDb.prepare(
4172
- `SELECT id, observation_type, status, content, properties, created_at
4173
- FROM spores WHERE id IN (${placeholders})`
4174
- ).all(...sporeIdArray);
4175
- }
4310
+ const sporeNodes = sporeIdArray.length > 0 ? graphDb.prepare(
4311
+ `SELECT id, observation_type, status, content, properties, created_at
4312
+ FROM spores WHERE id IN (${sporeIdArray.map(() => "?").join(", ")})`
4313
+ ).all(...sporeIdArray) : [];
4176
4314
  const sessionIdArray = Array.from(sessionIds);
4177
- let sessionNodes = [];
4178
- if (sessionIdArray.length > 0) {
4179
- const placeholders = sessionIdArray.map(() => "?").join(", ");
4180
- sessionNodes = graphDb.prepare(
4181
- `SELECT id, title, summary, status, started_at as created_at
4182
- FROM sessions WHERE id IN (${placeholders})`
4183
- ).all(...sessionIdArray);
4184
- }
4185
- const mentionCounts = /* @__PURE__ */ new Map();
4186
- if (entityIdArray.length > 0) {
4187
- const placeholders = entityIdArray.map(() => "?").join(", ");
4188
- const mentionRows = graphDb.prepare(
4189
- `SELECT entity_id, COUNT(*) as count FROM entity_mentions
4190
- WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
4191
- ).all(...entityIdArray);
4192
- for (const row of mentionRows) {
4193
- mentionCounts.set(row.entity_id, Number(row.count));
4194
- }
4195
- }
4315
+ const sessionNodes = sessionIdArray.length > 0 ? graphDb.prepare(
4316
+ `SELECT id, title, summary, status, started_at as created_at
4317
+ FROM sessions WHERE id IN (${sessionIdArray.map(() => "?").join(", ")})`
4318
+ ).all(...sessionIdArray) : [];
4196
4319
  const allNodes = [
4197
- ...entityNodes.map((n) => ({
4198
- id: n.id,
4199
- name: n.name,
4200
- type: n.type,
4201
- status: n.status ?? void 0,
4202
- created_at: n.created_at,
4203
- properties: n.properties ?? void 0,
4204
- mention_count: mentionCounts.get(n.id) ?? 0
4205
- })),
4206
4320
  ...sporeNodes.map((n) => ({
4207
4321
  id: n.id,
4208
4322
  name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4241,10 +4355,6 @@ async function handleGetGraph(req) {
4241
4355
  var FULL_GRAPH_NODE_LIMIT = 500;
4242
4356
  async function handleGetFullGraph(_req) {
4243
4357
  const db = getDatabase();
4244
- const entityRows = db.prepare(
4245
- `SELECT id, type, name, properties, status, first_seen as created_at
4246
- FROM entities WHERE agent_id = ? LIMIT ?`
4247
- ).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT);
4248
4358
  const sporeRows = db.prepare(
4249
4359
  `SELECT id, observation_type, status, content, properties, created_at
4250
4360
  FROM spores WHERE agent_id = ? AND status = 'active' LIMIT ?`
@@ -4254,43 +4364,21 @@ async function handleGetFullGraph(_req) {
4254
4364
  FROM sessions ORDER BY created_at DESC LIMIT ?`
4255
4365
  ).all(FULL_GRAPH_NODE_LIMIT);
4256
4366
  const allIds = /* @__PURE__ */ new Set();
4257
- for (const r of [...entityRows, ...sporeRows, ...sessionRows]) {
4367
+ for (const r of [...sporeRows, ...sessionRows]) {
4258
4368
  allIds.add(r.id);
4259
4369
  }
4260
4370
  const excludedTypes = Array.from(EXCLUDED_GRAPH_EDGE_TYPES).map(() => "?").join(", ");
4261
4371
  const allIdsList = Array.from(allIds);
4262
4372
  const idPlaceholders = allIdsList.map(() => "?").join(", ");
4263
- const edgeRows = db.prepare(
4373
+ const edgeRows = allIdsList.length > 0 ? db.prepare(
4264
4374
  `SELECT source_id, source_type, target_id, target_type, type, confidence
4265
- FROM graph_edges
4266
- WHERE agent_id = ?
4267
- AND type NOT IN (${excludedTypes})
4268
- AND source_id IN (${idPlaceholders})
4269
- AND target_id IN (${idPlaceholders})`
4270
- ).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList);
4271
- const filteredEdges = edgeRows;
4272
- const mentionCounts = /* @__PURE__ */ new Map();
4273
- const entityIdArray = entityRows.map((r) => r.id);
4274
- if (entityIdArray.length > 0) {
4275
- const placeholders = entityIdArray.map(() => "?").join(", ");
4276
- const mentionRows = db.prepare(
4277
- `SELECT entity_id, COUNT(*) as count FROM entity_mentions
4278
- WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
4279
- ).all(...entityIdArray);
4280
- for (const row of mentionRows) {
4281
- mentionCounts.set(row.entity_id, Number(row.count));
4282
- }
4283
- }
4375
+ FROM graph_edges
4376
+ WHERE agent_id = ?
4377
+ AND type NOT IN (${excludedTypes})
4378
+ AND source_id IN (${idPlaceholders})
4379
+ AND target_id IN (${idPlaceholders})`
4380
+ ).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList) : [];
4284
4381
  const nodes = [
4285
- ...entityRows.map((n) => ({
4286
- id: n.id,
4287
- name: n.name,
4288
- type: n.type,
4289
- status: n.status ?? void 0,
4290
- created_at: n.created_at,
4291
- properties: n.properties ?? void 0,
4292
- mention_count: mentionCounts.get(n.id) ?? 0
4293
- })),
4294
4382
  ...sporeRows.map((n) => ({
4295
4383
  id: n.id,
4296
4384
  name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4310,7 +4398,7 @@ async function handleGetFullGraph(_req) {
4310
4398
  content: n.summary ?? void 0
4311
4399
  }))
4312
4400
  ];
4313
- const edges = filteredEdges.map((e) => ({
4401
+ const edges = edgeRows.map((e) => ({
4314
4402
  source_id: e.source_id,
4315
4403
  target_id: e.target_id,
4316
4404
  label: e.type,
@@ -4393,75 +4481,305 @@ function createSearchHandler(deps) {
4393
4481
  };
4394
4482
  }
4395
4483
 
4396
- // src/daemon/api/context.ts
4397
- var SessionContextBody = external_exports.object({
4398
- session_id: external_exports.string().optional(),
4399
- branch: external_exports.string().optional()
4400
- });
4401
- var ResumeContextBody = external_exports.object({
4402
- session_id: external_exports.string(),
4403
- parent_session_id: external_exports.string().optional(),
4404
- branch: external_exports.string().optional()
4405
- });
4406
- var PromptContextBody = external_exports.object({
4407
- prompt: external_exports.string(),
4408
- session_id: external_exports.string().optional()
4409
- });
4410
- function createSessionContextHandler(deps) {
4411
- return async function handleSessionContext(req) {
4412
- const { session_id, branch } = SessionContextBody.parse(req.body);
4413
- const { logger, liveConfig } = deps;
4414
- const config = liveConfig.current;
4415
- logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
4416
- try {
4417
- const parts = [];
4418
- const tier = config.context.digest_tier;
4419
- const extract = getDigestExtract(DEFAULT_AGENT_ID, tier);
4420
- if (extract) {
4421
- parts.push(extract.content);
4422
- logger.info(LOG_KINDS.CONTEXT_DIGEST, "Digest extract found", {
4423
- session_id,
4424
- tier,
4425
- content_length: extract.content.length,
4426
- generated_at: extract.generated_at
4427
- });
4428
- } else {
4429
- logger.debug(LOG_KINDS.CONTEXT_DIGEST, "No digest extract available", { session_id, tier });
4430
- }
4431
- if (branch) {
4432
- parts.push(`Branch:: \`${branch}\``);
4433
- }
4434
- parts.push(`Session:: \`${session_id}\``);
4435
- const source = extract ? "digest" : "basic";
4436
- const contextText = parts.join("\n\n");
4437
- const estimatedTokens = estimateTokens(contextText);
4438
- logger.info(
4439
- LOG_KINDS.CONTEXT_SESSION,
4440
- `Session context: ${estimatedTokens} est. tokens, source=${source}${extract ? `, tier=${tier}` : ""}`,
4441
- {
4442
- session_id,
4443
- source,
4444
- tier: extract ? tier : void 0,
4445
- text_length: contextText.length,
4446
- estimated_tokens: estimatedTokens,
4447
- generated_at: extract?.generated_at,
4448
- injected_text: contextText
4449
- }
4450
- );
4451
- return {
4452
- body: {
4453
- text: contextText,
4454
- source,
4455
- ...extract ? { tier } : {}
4456
- }
4457
- };
4458
- } catch (error) {
4459
- logger.error(LOG_KINDS.CONTEXT_SESSION, "Session context failed", { error: error.message });
4460
- return { body: { text: "" } };
4484
+ // src/symbionts/injection-support.ts
4485
+ import fs16 from "fs";
4486
+ import path17 from "path";
4487
+ var SESSION_START_SIGNALS = ["hook session-start", '"/context"', '"/context/resume"'];
4488
+ var PROMPT_SUBMIT_SIGNALS = ["hook user-prompt-submit", '"/context/prompt"'];
4489
+ function resolveTemplateCandidates(manifest) {
4490
+ const packageRoot = resolvePackageRoot();
4491
+ const templateFile = manifest.registration?.hooksFormat === "plugin-file" ? "plugin.ts" : "hooks.json";
4492
+ return [
4493
+ path17.join(packageRoot, "src", "symbionts", "templates", manifest.name, templateFile),
4494
+ path17.join(packageRoot, "dist", "src", "symbionts", "templates", manifest.name, templateFile)
4495
+ ];
4496
+ }
4497
+ function readHooksTemplate(manifest) {
4498
+ for (const candidate of resolveTemplateCandidates(manifest)) {
4499
+ if (fs16.existsSync(candidate)) {
4500
+ return fs16.readFileSync(candidate, "utf-8");
4461
4501
  }
4502
+ }
4503
+ return "";
4504
+ }
4505
+ function hasAnySignal(template, signals) {
4506
+ return signals.some((signal) => template.includes(signal));
4507
+ }
4508
+ function detectSymbiontInjectionSupport(manifest) {
4509
+ const template = readHooksTemplate(manifest);
4510
+ return {
4511
+ supportsSessionStartInjection: hasAnySignal(template, SESSION_START_SIGNALS),
4512
+ supportsPromptSubmitInjection: hasAnySignal(template, PROMPT_SUBMIT_SIGNALS)
4462
4513
  };
4463
4514
  }
4464
- function createResumeContextHandler(deps) {
4515
+
4516
+ // src/daemon/api/symbionts.ts
4517
+ function listSymbiontInfos(vaultDir) {
4518
+ const manifests = loadManifests();
4519
+ let enabledNames = null;
4520
+ try {
4521
+ enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
4522
+ } catch {
4523
+ }
4524
+ return manifests.map((manifest) => ({
4525
+ name: manifest.name,
4526
+ displayName: manifest.displayName,
4527
+ binary: manifest.binary,
4528
+ enabled: enabledNames ? enabledNames.has(manifest.name) : true,
4529
+ ...manifest.resumeCommand ? { resumeCommand: manifest.resumeCommand } : {},
4530
+ ...detectSymbiontInjectionSupport(manifest)
4531
+ }));
4532
+ }
4533
+ async function handleListSymbionts(vaultDir) {
4534
+ return { body: { symbionts: listSymbiontInfos(vaultDir) } };
4535
+ }
4536
+
4537
+ // src/daemon/cortex.ts
4538
+ var CORTEX_PROMPT_BUILDER_TASK = "cortex-prompt-builder";
4539
+ var CORTEX_INSTRUCTIONS_TASK = "cortex-instructions";
4540
+ var JSON_INDENT = 2;
4541
+ function isCortexPromptBuilderDetails(value) {
4542
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4543
+ }
4544
+ function getLatestReportForAction(runId, action) {
4545
+ const reports = listReports(runId);
4546
+ for (let i = reports.length - 1; i >= 0; i -= 1) {
4547
+ if (reports[i]?.action === action) return reports[i];
4548
+ }
4549
+ return void 0;
4550
+ }
4551
+ function getCortexInstructionsSnapshot(config) {
4552
+ const row = getCortexInstructions(DEFAULT_AGENT_ID);
4553
+ return {
4554
+ content: row?.content ?? "",
4555
+ generatedAt: row?.generated_at ?? null,
4556
+ sourceRunId: row?.source_run_id ?? null,
4557
+ enabled: config.context.cortex_enabled,
4558
+ stored: Boolean(row)
4559
+ };
4560
+ }
4561
+ function resolvePromptBuilderSymbiont(vaultDir, requestedName) {
4562
+ const enabledSymbionts = listSymbiontInfos(vaultDir).filter((symbiont) => symbiont.enabled);
4563
+ if (enabledSymbionts.length === 0) return null;
4564
+ if (!requestedName) return enabledSymbionts[0] ?? null;
4565
+ return enabledSymbionts.find((symbiont) => symbiont.name === requestedName) ?? null;
4566
+ }
4567
+ async function buildCortexPrompt(vaultDir, deps, goal, requestedSymbiont) {
4568
+ const targetSymbiont = resolvePromptBuilderSymbiont(vaultDir, requestedSymbiont);
4569
+ const delivery = resolveInstructionDelivery(deps.config.context, targetSymbiont);
4570
+ const instructions = delivery.inlineInstructions ? getCortexInstructions(DEFAULT_AGENT_ID) : null;
4571
+ const builderInstruction = [
4572
+ `Goal:
4573
+ ${goal.trim()}`,
4574
+ "",
4575
+ "## Target symbiont",
4576
+ JSON.stringify(
4577
+ targetSymbiont ? {
4578
+ name: targetSymbiont.name,
4579
+ displayName: targetSymbiont.displayName,
4580
+ supportsSessionStartInjection: targetSymbiont.supportsSessionStartInjection,
4581
+ supportsPromptSubmitInjection: targetSymbiont.supportsPromptSubmitInjection
4582
+ } : null,
4583
+ null,
4584
+ JSON_INDENT
4585
+ ),
4586
+ "",
4587
+ "## Delivery contract",
4588
+ JSON.stringify(
4589
+ {
4590
+ inline_instructions: delivery.inlineInstructions,
4591
+ reason: delivery.reason
4592
+ },
4593
+ null,
4594
+ JSON_INDENT
4595
+ ),
4596
+ "",
4597
+ delivery.inlineInstructions ? [
4598
+ "## Current Cortex session-start instructions",
4599
+ instructions?.content || "No current Cortex instructions are available.",
4600
+ ""
4601
+ ].join("\n") : "## Current Cortex session-start instructions\nOmit them from the prompt because this symbiont receives session-start injection.\n"
4602
+ ].join("\n");
4603
+ const resultPromise = runAgent(vaultDir, {
4604
+ task: CORTEX_PROMPT_BUILDER_TASK,
4605
+ agentId: DEFAULT_AGENT_ID,
4606
+ instruction: builderInstruction,
4607
+ embeddingManager: deps.embeddingManager
4608
+ });
4609
+ const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_PROMPT_BUILDER_TASK);
4610
+ const tracked = resultPromise.catch((err) => {
4611
+ deps.logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-prompt-builder task failed", {
4612
+ run_id: runId ?? void 0,
4613
+ error: String(err)
4614
+ });
4615
+ });
4616
+ deps.registerInflightRun?.(tracked);
4617
+ return {
4618
+ started: true,
4619
+ runId,
4620
+ inlineInstructions: delivery.inlineInstructions,
4621
+ targetSymbiont
4622
+ };
4623
+ }
4624
+ function getCortexPromptResult(runId) {
4625
+ const run = getRun(runId);
4626
+ if (!run) return null;
4627
+ const reports = listReports(runId);
4628
+ const promptReport = getLatestReportForAction(runId, "cortex_prompt_builder");
4629
+ const details = tryParseJson(promptReport?.details, isCortexPromptBuilderDetails);
4630
+ return {
4631
+ runId,
4632
+ status: run.status,
4633
+ prompt: details?.prompt ?? "",
4634
+ reports: reports.map((report) => ({
4635
+ id: report.id,
4636
+ action: report.action,
4637
+ summary: report.summary,
4638
+ created_at: report.created_at
4639
+ })),
4640
+ error: run.error
4641
+ };
4642
+ }
4643
+ async function triggerCortexInstructions(deps) {
4644
+ const { vaultDir, embeddingManager, liveConfig, logger, getTeamClient } = deps;
4645
+ const loadExecutor = deps.loadExecutor ?? (() => import("./executor-F2YU7HXJ.js"));
4646
+ const config = liveConfig.current;
4647
+ if (config.agent.event_tasks_enabled === false) {
4648
+ return { started: false, reason: "event-tasks-disabled" };
4649
+ }
4650
+ if (!hasConfiguredProvider(config, CORTEX_INSTRUCTIONS_TASK)) {
4651
+ return { started: false, reason: "provider-not-configured" };
4652
+ }
4653
+ let runAgentFn;
4654
+ try {
4655
+ ({ runAgent: runAgentFn } = await loadExecutor());
4656
+ } catch (err) {
4657
+ logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-instructions: agent module unavailable", {
4658
+ error: String(err)
4659
+ });
4660
+ return {
4661
+ started: false,
4662
+ reason: "agent-module-unavailable",
4663
+ error: String(err)
4664
+ };
4665
+ }
4666
+ try {
4667
+ const built = await buildCortexInstructionsInput(config, getTeamClient);
4668
+ const resultPromise = runAgentFn(vaultDir, {
4669
+ task: CORTEX_INSTRUCTIONS_TASK,
4670
+ agentId: DEFAULT_AGENT_ID,
4671
+ instruction: built.instruction,
4672
+ runContext: { cortex_instruction_input_hash: built.inputHash },
4673
+ embeddingManager
4674
+ });
4675
+ const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_INSTRUCTIONS_TASK);
4676
+ const tracked = resultPromise.catch((err) => {
4677
+ logger.warn(LOG_KINDS.AGENT_ERROR, "Cortex instructions task failed", {
4678
+ error: String(err),
4679
+ run_id: runId ?? void 0
4680
+ });
4681
+ });
4682
+ deps.registerInflightRun?.(tracked);
4683
+ return { started: true, runId };
4684
+ } catch (err) {
4685
+ logger.warn(LOG_KINDS.AGENT_ERROR, "Failed to start cortex-instructions task", {
4686
+ error: String(err)
4687
+ });
4688
+ return {
4689
+ started: false,
4690
+ reason: "startup-failed",
4691
+ error: String(err)
4692
+ };
4693
+ }
4694
+ }
4695
+
4696
+ // src/daemon/api/context.ts
4697
+ var SessionContextBody = external_exports.object({
4698
+ session_id: external_exports.string().optional(),
4699
+ branch: external_exports.string().optional()
4700
+ });
4701
+ var ResumeContextBody = external_exports.object({
4702
+ session_id: external_exports.string(),
4703
+ parent_session_id: external_exports.string().optional(),
4704
+ branch: external_exports.string().optional()
4705
+ });
4706
+ var PromptContextBody = external_exports.object({
4707
+ prompt: external_exports.string(),
4708
+ session_id: external_exports.string().optional()
4709
+ });
4710
+ function createSessionContextHandler(deps) {
4711
+ return async function handleSessionContext(req) {
4712
+ const { session_id, branch } = SessionContextBody.parse(req.body);
4713
+ const { logger, liveConfig } = deps;
4714
+ const config = liveConfig.current;
4715
+ logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
4716
+ try {
4717
+ const cortexEnabled = shouldInjectCortex(config.context);
4718
+ const digestEnabled = shouldInjectSessionStartDigest(config.context);
4719
+ if (!cortexEnabled && !digestEnabled) {
4720
+ logger.debug(LOG_KINDS.CONTEXT_SESSION, "Session-start context disabled", { session_id });
4721
+ return { body: { text: "" } };
4722
+ }
4723
+ let sourceRunId = null;
4724
+ let cortexContent = "";
4725
+ if (cortexEnabled) {
4726
+ const snapshot = getCortexInstructionsSnapshot(config);
4727
+ if (snapshot.content) {
4728
+ cortexContent = snapshot.content;
4729
+ sourceRunId = snapshot.sourceRunId;
4730
+ } else {
4731
+ logger.debug(LOG_KINDS.CONTEXT_SESSION, "No stored Cortex instructions available for session start", {
4732
+ session_id
4733
+ });
4734
+ }
4735
+ }
4736
+ const composed = composeSessionStartContext(config, cortexContent);
4737
+ const textParts = composed.parts.map((p) => p.text);
4738
+ const sourceParts = composed.parts.map(
4739
+ (p) => p.kind === "cortex" ? "cortex" : `digest:${p.tier ?? config.context.digest_tier}`
4740
+ );
4741
+ if (digestEnabled && !composed.parts.some((p) => p.kind === "digest")) {
4742
+ logger.debug(LOG_KINDS.CONTEXT_SESSION, "No preferred digest extract available for session start", {
4743
+ session_id,
4744
+ preferred_tier: config.context.digest_tier
4745
+ });
4746
+ }
4747
+ if (textParts.length === 0) {
4748
+ return { body: { text: "" } };
4749
+ }
4750
+ if (branch) {
4751
+ textParts.push(`Branch:: \`${branch}\``);
4752
+ }
4753
+ textParts.push(`Session:: \`${session_id}\``);
4754
+ const source = sourceParts.join("+");
4755
+ const contextText = textParts.join("\n\n");
4756
+ const estimatedTokens = estimateTokens(contextText);
4757
+ logger.info(
4758
+ LOG_KINDS.CONTEXT_SESSION,
4759
+ `Session context: ${estimatedTokens} est. tokens, source=${source}`,
4760
+ {
4761
+ session_id,
4762
+ source,
4763
+ branch,
4764
+ source_run_id: sourceRunId,
4765
+ text_length: contextText.length,
4766
+ estimated_tokens: estimatedTokens,
4767
+ injected_text: contextText
4768
+ }
4769
+ );
4770
+ return {
4771
+ body: {
4772
+ text: contextText,
4773
+ source
4774
+ }
4775
+ };
4776
+ } catch (error) {
4777
+ logger.error(LOG_KINDS.CONTEXT_SESSION, "Session context failed", { error: error.message });
4778
+ return { body: { text: "" } };
4779
+ }
4780
+ };
4781
+ }
4782
+ function createResumeContextHandler(deps) {
4465
4783
  return async function handleResumeContext(req) {
4466
4784
  const { session_id, parent_session_id, branch } = ResumeContextBody.parse(req.body);
4467
4785
  const { logger } = deps;
@@ -4557,9 +4875,7 @@ function createPromptContextHandler(deps) {
4557
4875
  raw_results: vectorResults.length,
4558
4876
  top_similarity: vectorResults[0]?.similarity
4559
4877
  });
4560
- if (vectorResults.length === 0) {
4561
- return { body: { text: "" } };
4562
- }
4878
+ if (vectorResults.length === 0) return { body: { text: "" } };
4563
4879
  const eligible = vectorResults.filter(
4564
4880
  (r) => !EXCLUDED_SPORE_STATUSES.has(r.metadata.status)
4565
4881
  );
@@ -4570,20 +4886,22 @@ function createPromptContextHandler(deps) {
4570
4886
  const topResults = eligible.slice(0, maxSpores);
4571
4887
  const hydrated = hydrateSearchResults(topResults);
4572
4888
  const spores = hydrated.filter((r) => r.type === "spore");
4573
- if (spores.length === 0) {
4574
- return { body: { text: "" } };
4575
- }
4889
+ if (spores.length === 0) return { body: { text: "" } };
4576
4890
  const text = formatSporeContext(spores);
4577
4891
  const promptTokens = estimateTokens(text);
4578
4892
  const titles = spores.map((s) => s.title);
4579
- logger.info(LOG_KINDS.CONTEXT_PROMPT, `Prompt context: ${spores.length} spores [${titles.join(", ")}] (~${promptTokens} tokens)`, {
4580
- session_id,
4581
- spore_count: spores.length,
4582
- spore_titles: titles,
4583
- scores: spores.map((s) => s.score.toFixed(3)),
4584
- estimated_tokens: promptTokens,
4585
- injected_text: text
4586
- });
4893
+ logger.info(
4894
+ LOG_KINDS.CONTEXT_PROMPT,
4895
+ `Prompt context: ${spores.length} spores [${titles.join(", ")}] (~${promptTokens} tokens)`,
4896
+ {
4897
+ session_id,
4898
+ spore_count: spores.length,
4899
+ spore_titles: titles,
4900
+ scores: spores.map((s) => s.score.toFixed(3)),
4901
+ estimated_tokens: promptTokens,
4902
+ injected_text: text
4903
+ }
4904
+ );
4587
4905
  return { body: { text } };
4588
4906
  };
4589
4907
  }
@@ -4602,6 +4920,75 @@ function formatSporeContext(spores) {
4602
4920
  return text === header ? "" : text;
4603
4921
  }
4604
4922
 
4923
+ // src/daemon/api/cortex.ts
4924
+ var PromptBuilderBody = external_exports.object({
4925
+ goal: external_exports.string().trim().min(1),
4926
+ symbiont: external_exports.string().trim().optional()
4927
+ });
4928
+ var PromptBuilderStatusParams = external_exports.object({
4929
+ runId: external_exports.string().trim().min(1)
4930
+ });
4931
+ function createCortexHandlers(vaultDir, deps) {
4932
+ async function handleGetInstructions() {
4933
+ const snapshot = getCortexInstructionsSnapshot(deps.liveConfig.current);
4934
+ return { body: snapshot };
4935
+ }
4936
+ async function handleRefreshInstructions() {
4937
+ const result = await triggerCortexInstructions({
4938
+ vaultDir,
4939
+ embeddingManager: deps.embeddingManager,
4940
+ liveConfig: deps.liveConfig,
4941
+ logger: deps.logger,
4942
+ getTeamClient: deps.getTeamClient,
4943
+ registerInflightRun: deps.registerInflightRun
4944
+ });
4945
+ if (!result.started && (result.reason === "provider-not-configured" || result.reason === "event-tasks-disabled")) {
4946
+ return {
4947
+ status: 400,
4948
+ body: {
4949
+ ...errorBody(
4950
+ result.reason,
4951
+ result.reason === "provider-not-configured" ? "No agent provider configured. Configure one in Settings." : "Event-driven tasks are disabled. Enable them in Settings."
4952
+ ),
4953
+ started: false,
4954
+ reason: result.reason
4955
+ }
4956
+ };
4957
+ }
4958
+ return { body: result };
4959
+ }
4960
+ async function handleBuildPrompt(req) {
4961
+ const { goal, symbiont } = PromptBuilderBody.parse(req.body);
4962
+ const result = await buildCortexPrompt(
4963
+ vaultDir,
4964
+ {
4965
+ config: deps.liveConfig.current,
4966
+ embeddingManager: deps.embeddingManager,
4967
+ getTeamClient: deps.getTeamClient,
4968
+ logger: deps.logger,
4969
+ registerInflightRun: deps.registerInflightRun
4970
+ },
4971
+ goal,
4972
+ symbiont
4973
+ );
4974
+ return { body: result };
4975
+ }
4976
+ async function handleGetPromptResult(req) {
4977
+ const { runId } = PromptBuilderStatusParams.parse(req.params);
4978
+ const result = getCortexPromptResult(runId);
4979
+ if (!result) {
4980
+ return { status: 404, body: errorBody("run-not-found", "Run not found") };
4981
+ }
4982
+ return { body: result };
4983
+ }
4984
+ return {
4985
+ handleGetInstructions,
4986
+ handleRefreshInstructions,
4987
+ handleBuildPrompt,
4988
+ handleGetPromptResult
4989
+ };
4990
+ }
4991
+
4605
4992
  // src/db/queries/feed.ts
4606
4993
  function getActivityFeed(limit = FEED_DEFAULT_LIMIT) {
4607
4994
  const db = getDatabase();
@@ -4640,24 +5027,6 @@ async function handleGetFeed(req) {
4640
5027
  return { body: feed };
4641
5028
  }
4642
5029
 
4643
- // src/daemon/api/symbionts.ts
4644
- async function handleListSymbionts(vaultDir) {
4645
- const manifests = loadManifests();
4646
- let enabledNames = null;
4647
- try {
4648
- enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
4649
- } catch {
4650
- }
4651
- const symbionts = manifests.map((m) => ({
4652
- name: m.name,
4653
- displayName: m.displayName,
4654
- binary: m.binary,
4655
- enabled: enabledNames ? enabledNames.has(m.name) : true,
4656
- ...m.resumeCommand ? { resumeCommand: m.resumeCommand } : {}
4657
- }));
4658
- return { body: { symbionts } };
4659
- }
4660
-
4661
5030
  // src/daemon/api/embedding.ts
4662
5031
  var EMBEDDING_STATUS_IDLE = "idle";
4663
5032
  var EMBEDDING_STATUS_PENDING = "pending";
@@ -5661,17 +6030,17 @@ var SqliteRecordSource = class {
5661
6030
  };
5662
6031
 
5663
6032
  // src/daemon/database/manager.ts
5664
- import fs17 from "fs";
6033
+ import fs18 from "fs";
5665
6034
 
5666
6035
  // src/db/queries/database.ts
5667
- import fs16 from "fs";
6036
+ import fs17 from "fs";
5668
6037
  function pragmaScalar(name) {
5669
6038
  const db = getDatabase();
5670
6039
  return db.pragma(name, { simple: true });
5671
6040
  }
5672
6041
  function safeFileSize(filePath) {
5673
6042
  try {
5674
- return fs16.statSync(filePath).size;
6043
+ return fs17.statSync(filePath).size;
5675
6044
  } catch (err) {
5676
6045
  if (err.code === "ENOENT") return 0;
5677
6046
  throw err;
@@ -5943,7 +6312,7 @@ var DatabaseMaintenanceManager = class {
5943
6312
  }
5944
6313
  async vacuum() {
5945
6314
  const size_before = this.fileSize();
5946
- const stats = await fs17.promises.statfs(this.vaultDir);
6315
+ const stats = await fs18.promises.statfs(this.vaultDir);
5947
6316
  const free_bytes = Number(stats.bavail) * Number(stats.bsize);
5948
6317
  const required_bytes = size_before * VACUUM_FREE_SPACE_MULTIPLIER;
5949
6318
  if (free_bytes < required_bytes) {
@@ -5991,7 +6360,7 @@ var DatabaseMaintenanceManager = class {
5991
6360
  }
5992
6361
  fileSize() {
5993
6362
  try {
5994
- return fs17.statSync(this.dbPath).size;
6363
+ return fs18.statSync(this.dbPath).size;
5995
6364
  } catch {
5996
6365
  return 0;
5997
6366
  }
@@ -6308,6 +6677,16 @@ async function handleUpdateTaskConfig(req, vaultDir) {
6308
6677
  };
6309
6678
  }
6310
6679
 
6680
+ // src/agent/types.ts
6681
+ var PROVIDER_TYPES = [
6682
+ "anthropic",
6683
+ "ollama",
6684
+ "lmstudio",
6685
+ "openai",
6686
+ "openrouter",
6687
+ "openai-compatible"
6688
+ ];
6689
+
6311
6690
  // src/daemon/api/providers.ts
6312
6691
  var ANTHROPIC_MODELS_TIMEOUT_MS = 5e3;
6313
6692
  var ANTHROPIC_MODELS_CACHE_TTL_MS = 10 * 60 * 1e3;
@@ -6315,33 +6694,79 @@ var anthropicModelsCache = null;
6315
6694
  var HTTP_OK2 = 200;
6316
6695
  var HTTP_BAD_REQUEST2 = 400;
6317
6696
  async function handleGetProviders() {
6318
- const results = await Promise.allSettled([
6319
- detectAnthropic(),
6320
- detectLocalProviderInfo("ollama", OllamaBackend.DEFAULT_BASE_URL),
6321
- detectLocalProviderInfo("lmstudio", LmStudioBackend.DEFAULT_BASE_URL)
6322
- ]);
6697
+ const detectionPlan = [
6698
+ {
6699
+ detect: () => detectAnthropic(),
6700
+ fallback: { type: "anthropic", runtime: "claude-sdk", available: false, models: [] }
6701
+ },
6702
+ {
6703
+ detect: () => detectLocalProviderInfo("ollama", OllamaBackend.DEFAULT_BASE_URL),
6704
+ fallback: { type: "ollama", runtime: "claude-sdk", available: false, baseUrl: OllamaBackend.DEFAULT_BASE_URL, models: [] }
6705
+ },
6706
+ {
6707
+ detect: () => detectLocalProviderInfo("lmstudio", LmStudioBackend.DEFAULT_BASE_URL),
6708
+ fallback: { type: "lmstudio", runtime: "claude-sdk", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
6709
+ },
6710
+ {
6711
+ detect: () => detectRemoteProviderInfo("openai", DEFAULT_OPENAI_URL),
6712
+ fallback: { type: "openai", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENAI_URL, models: [] }
6713
+ },
6714
+ {
6715
+ detect: () => detectRemoteProviderInfo("openrouter", DEFAULT_OPENROUTER_URL),
6716
+ fallback: { type: "openrouter", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENROUTER_URL, models: [] }
6717
+ },
6718
+ {
6719
+ detect: () => detectLocalProviderInfo("openai-compatible", LmStudioBackend.DEFAULT_BASE_URL),
6720
+ fallback: { type: "openai-compatible", runtime: "openai-agents", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
6721
+ }
6722
+ ];
6723
+ const results = await Promise.allSettled(detectionPlan.map((entry) => entry.detect()));
6323
6724
  const providers = results.map(
6324
- (r) => r.status === "fulfilled" ? r.value : { type: "unknown", available: false, models: [] }
6725
+ (result, index) => result.status === "fulfilled" ? result.value : detectionPlan[index].fallback
6325
6726
  );
6326
6727
  return { status: HTTP_OK2, body: { providers } };
6327
6728
  }
6729
+ var ProviderTestBody = external_exports.object({
6730
+ type: external_exports.enum(PROVIDER_TYPES),
6731
+ baseUrl: external_exports.string().optional(),
6732
+ /**
6733
+ * Deprecated: `base_url` is the legacy snake_case key. Accepted for one
6734
+ * release and then removed. Use `baseUrl` going forward.
6735
+ */
6736
+ base_url: external_exports.string().optional(),
6737
+ local_backend: external_exports.enum(["ollama", "lmstudio"]).optional(),
6738
+ model: external_exports.string().optional()
6739
+ });
6328
6740
  async function handleTestProvider(req) {
6329
- const body = req.body;
6330
- const type = body?.type;
6331
- if (!type || !["anthropic", "ollama", "lmstudio"].includes(type)) {
6741
+ const parse = ProviderTestBody.safeParse(req.body);
6742
+ if (!parse.success) {
6332
6743
  return {
6333
6744
  status: HTTP_BAD_REQUEST2,
6334
- body: { error: "type is required and must be one of: anthropic, ollama, lmstudio" }
6745
+ body: { error: `type is required and must be one of: ${PROVIDER_TYPES.join(", ")}` }
6335
6746
  };
6336
6747
  }
6337
- const baseUrl = body?.baseUrl;
6748
+ const parsed = parse.data;
6749
+ if (parsed.base_url !== void 0 && parsed.baseUrl === void 0) {
6750
+ process.stderr.write(
6751
+ "[myco providers] POST /api/providers/test: base_url is deprecated; use baseUrl\n"
6752
+ );
6753
+ }
6754
+ const type = parsed.type;
6755
+ const baseUrl = parsed.baseUrl ?? parsed.base_url;
6756
+ const localBackend = parsed.local_backend;
6338
6757
  const start = performance.now();
6339
6758
  let result;
6340
6759
  try {
6341
6760
  if (type === "ollama") {
6342
- result = await testLocalProvider(new OllamaBackend({ base_url: baseUrl }), "Ollama", OllamaBackend.DEFAULT_BASE_URL, baseUrl);
6761
+ result = await testResolvedLocalProvider("ollama", baseUrl);
6343
6762
  } else if (type === "lmstudio") {
6344
- result = await testLocalProvider(new LmStudioBackend({ base_url: baseUrl }), "LM Studio", LmStudioBackend.DEFAULT_BASE_URL, baseUrl);
6763
+ result = await testResolvedLocalProvider("lmstudio", baseUrl);
6764
+ } else if (type === "openai-compatible") {
6765
+ result = await testResolvedLocalProvider("openai-compatible", baseUrl, localBackend);
6766
+ } else if (type === "openai") {
6767
+ result = await testRemoteProvider("openai", "OpenAI");
6768
+ } else if (type === "openrouter") {
6769
+ result = await testRemoteProvider("openrouter", "OpenRouter");
6345
6770
  } else {
6346
6771
  result = testAnthropic();
6347
6772
  }
@@ -6354,15 +6779,21 @@ async function handleTestProvider(req) {
6354
6779
  return { status: HTTP_OK2, body: result };
6355
6780
  }
6356
6781
  async function detectLocalProviderInfo(type, defaultBaseUrl) {
6357
- const status = await checkLocalProvider(type);
6782
+ const status = await checkLocalProvider(type === "openai-compatible" ? "lmstudio" : type);
6358
6783
  const variantFiltered = status.models.filter((m) => !/-ctx\d+/.test(m));
6359
6784
  const models = filterLlmModels(variantFiltered);
6360
- return { type, available: status.available, baseUrl: defaultBaseUrl, models };
6785
+ return {
6786
+ type,
6787
+ runtime: type === "openai-compatible" ? "openai-agents" : "claude-sdk",
6788
+ available: status.available,
6789
+ baseUrl: defaultBaseUrl,
6790
+ models
6791
+ };
6361
6792
  }
6362
6793
  async function detectAnthropic() {
6363
6794
  const now = Date.now();
6364
6795
  if (anthropicModelsCache && now - anthropicModelsCache.ts < ANTHROPIC_MODELS_CACHE_TTL_MS) {
6365
- return { type: "anthropic", available: true, models: anthropicModelsCache.models };
6796
+ return { type: "anthropic", runtime: "claude-sdk", available: true, models: anthropicModelsCache.models };
6366
6797
  }
6367
6798
  let models = ANTHROPIC_MODELS;
6368
6799
  try {
@@ -6378,7 +6809,28 @@ async function detectAnthropic() {
6378
6809
  } catch {
6379
6810
  }
6380
6811
  anthropicModelsCache = { ts: now, models };
6381
- return { type: "anthropic", available: true, models };
6812
+ return { type: "anthropic", runtime: "claude-sdk", available: true, models };
6813
+ }
6814
+ async function detectRemoteProviderInfo(type, baseUrl) {
6815
+ const authConfigured = Boolean(getRemoteProviderApiKey(type));
6816
+ let models = [];
6817
+ let available = false;
6818
+ if (authConfigured) {
6819
+ try {
6820
+ models = await fetchRemoteProviderModels(type, void 0, ANTHROPIC_MODELS_TIMEOUT_MS);
6821
+ available = true;
6822
+ } catch {
6823
+ available = false;
6824
+ }
6825
+ }
6826
+ return {
6827
+ type,
6828
+ runtime: "openai-agents",
6829
+ available,
6830
+ authConfigured,
6831
+ baseUrl,
6832
+ models
6833
+ };
6382
6834
  }
6383
6835
  async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
6384
6836
  const available = await backend.isAvailable();
@@ -6387,9 +6839,114 @@ async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
6387
6839
  }
6388
6840
  return { ok: true };
6389
6841
  }
6842
+ async function testResolvedLocalProvider(type, baseUrl, localBackend) {
6843
+ const kind = inferLocalOpenAIBackendKind({ type, localBackend, baseUrl }) ?? "lmstudio";
6844
+ const label = type === "openai-compatible" ? `OpenAI-compatible ${getLocalOpenAIBackendLabel(kind)} provider` : getLocalOpenAIBackendLabel(kind);
6845
+ return testLocalProvider(
6846
+ createLocalOpenAIBackend(kind, baseUrl),
6847
+ label,
6848
+ getLocalOpenAIBackendDefaultBaseUrl(kind),
6849
+ baseUrl
6850
+ );
6851
+ }
6390
6852
  function testAnthropic() {
6391
6853
  return { ok: true };
6392
6854
  }
6855
+ async function testRemoteProvider(provider, label) {
6856
+ if (!getRemoteProviderApiKey(provider)) {
6857
+ return { ok: false, error: `${label} API key not configured in daemon secrets or environment` };
6858
+ }
6859
+ try {
6860
+ await fetchRemoteProviderModels(provider);
6861
+ return { ok: true };
6862
+ } catch (error) {
6863
+ return { ok: false, error: error instanceof Error ? error.message : `${label} connection failed` };
6864
+ }
6865
+ }
6866
+
6867
+ // src/daemon/api/provider-secrets.ts
6868
+ var SECRET_PREVIEW_PREFIX_CHARS = 8;
6869
+ var SECRET_PREVIEW_SUFFIX_CHARS = 4;
6870
+ var SECRET_ENV_BY_PROVIDER = {
6871
+ openai: OPENAI_API_KEY_ENV,
6872
+ openrouter: OPENROUTER_API_KEY_ENV
6873
+ };
6874
+ function isSecretProvider(value) {
6875
+ return value === "openai" || value === "openrouter";
6876
+ }
6877
+ function maskSecret(secret) {
6878
+ if (secret.length <= SECRET_PREVIEW_PREFIX_CHARS + SECRET_PREVIEW_SUFFIX_CHARS) {
6879
+ return "*".repeat(secret.length);
6880
+ }
6881
+ return `${secret.slice(0, SECRET_PREVIEW_PREFIX_CHARS)}${"*".repeat(secret.length - SECRET_PREVIEW_PREFIX_CHARS - SECRET_PREVIEW_SUFFIX_CHARS)}${secret.slice(-SECRET_PREVIEW_SUFFIX_CHARS)}`;
6882
+ }
6883
+ function buildSecretInfo(provider, storedSecrets) {
6884
+ const envKey = SECRET_ENV_BY_PROVIDER[provider];
6885
+ const storedValue = storedSecrets[envKey];
6886
+ const envValue = process.env[envKey];
6887
+ const effectiveValue = storedValue ?? envValue;
6888
+ return {
6889
+ configured: Boolean(effectiveValue),
6890
+ envKey,
6891
+ maskedValue: effectiveValue ? maskSecret(effectiveValue) : null,
6892
+ source: storedValue ? "vault" : envValue ? "env" : "none"
6893
+ };
6894
+ }
6895
+ function getSecretInfo(vaultDir, provider) {
6896
+ return buildSecretInfo(provider, readSecrets(vaultDir));
6897
+ }
6898
+ async function handleGetProviderSecrets(vaultDir) {
6899
+ const storedSecrets = readSecrets(vaultDir);
6900
+ return {
6901
+ body: {
6902
+ secrets: {
6903
+ openai: buildSecretInfo("openai", storedSecrets),
6904
+ openrouter: buildSecretInfo("openrouter", storedSecrets)
6905
+ }
6906
+ }
6907
+ };
6908
+ }
6909
+ async function handlePutProviderSecret(vaultDir, req) {
6910
+ const provider = req.params.provider;
6911
+ const body = req.body;
6912
+ if (!provider || !isSecretProvider(provider)) {
6913
+ return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
6914
+ }
6915
+ if (!body?.api_key?.trim()) {
6916
+ return { status: 400, body: { error: "api_key is required" } };
6917
+ }
6918
+ const envKey = SECRET_ENV_BY_PROVIDER[provider];
6919
+ const apiKey = body.api_key.trim();
6920
+ writeSecret(vaultDir, envKey, apiKey);
6921
+ process.env[envKey] = apiKey;
6922
+ if (provider === "openai") {
6923
+ process.env.OPENAI_API_KEY = apiKey;
6924
+ }
6925
+ return {
6926
+ body: {
6927
+ provider,
6928
+ secret: getSecretInfo(vaultDir, provider)
6929
+ }
6930
+ };
6931
+ }
6932
+ async function handleDeleteProviderSecret(vaultDir, req) {
6933
+ const provider = req.params.provider;
6934
+ if (!provider || !isSecretProvider(provider)) {
6935
+ return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
6936
+ }
6937
+ const envKey = SECRET_ENV_BY_PROVIDER[provider];
6938
+ deleteSecrets(vaultDir, [envKey]);
6939
+ delete process.env[envKey];
6940
+ if (provider === "openai") {
6941
+ delete process.env.OPENAI_API_KEY;
6942
+ }
6943
+ return {
6944
+ body: {
6945
+ provider,
6946
+ secret: getSecretInfo(vaultDir, provider)
6947
+ }
6948
+ };
6949
+ }
6393
6950
 
6394
6951
  // src/daemon/task-scheduling.ts
6395
6952
  import { resolve } from "path";
@@ -6442,7 +6999,7 @@ function buildScheduledJobs(tasks, configOverrides, context, initialLastRuns) {
6442
6999
  // src/daemon/task-scheduling.ts
6443
7000
  var SCHEDULED_JOB_PREFIX = "scheduled:";
6444
7001
  async function registerScheduledTasks(powerManager, deps) {
6445
- const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig } = deps;
7002
+ const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig, getTeamClient } = deps;
6446
7003
  const runningTasks = /* @__PURE__ */ new Set();
6447
7004
  if (!definitionsDir) {
6448
7005
  logger.warn(LOG_KINDS.AGENT_ERROR, "Skipping dynamic task scheduling \u2014 definitions directory unavailable");
@@ -6452,7 +7009,7 @@ async function registerScheduledTasks(powerManager, deps) {
6452
7009
  if (!lastEnabled) {
6453
7010
  logger.info(LOG_KINDS.AGENT_RUN, "Scheduled agent tasks disabled (agent.scheduled_tasks_enabled: false) \u2014 jobs registered but will no-op until enabled");
6454
7011
  }
6455
- const { loadAllTasks: loadAllTasks2 } = await import("./registry-U4CHXK6R.js");
7012
+ const { loadAllTasks: loadAllTasks2 } = await import("./registry-M2Z5QBWH.js");
6456
7013
  const allTasks = Array.from(loadAllTasks2(definitionsDir, vaultDir).values());
6457
7014
  const taskAgentMap = /* @__PURE__ */ new Map();
6458
7015
  for (const task of allTasks) {
@@ -6488,10 +7045,33 @@ async function registerScheduledTasks(powerManager, deps) {
6488
7045
  lastEnabled = enabled;
6489
7046
  }
6490
7047
  if (!enabled) return;
6491
- const { runAgent } = await import("./executor-HWW2QNZQ.js");
7048
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
7049
+ const resumableRun = getLatestResumableRunForTask(DEFAULT_AGENT_ID, taskName);
7050
+ if (resumableRun) {
7051
+ const resumed = await runAgent2(vaultDir, {
7052
+ agentId: DEFAULT_AGENT_ID,
7053
+ task: taskName,
7054
+ resumeRunId: resumableRun.id,
7055
+ resumeMode: "scheduled",
7056
+ embeddingManager
7057
+ });
7058
+ logger.info(LOG_KINDS.AGENT_RUN, `Scheduled task ${taskName} resumed`, {
7059
+ status: resumed.status,
7060
+ runId: resumed.runId
7061
+ });
7062
+ return;
7063
+ }
6492
7064
  const taskConfig = config.agent.tasks?.[taskName];
6493
7065
  const projectRoot = resolve(vaultDir, "..");
6494
- const built = buildTaskInstruction(taskName, taskConfig?.params, taskAgentMap.get(taskName), projectRoot, embeddingManager);
7066
+ const built = await buildTaskInstruction(
7067
+ taskName,
7068
+ taskConfig?.params,
7069
+ taskAgentMap.get(taskName),
7070
+ projectRoot,
7071
+ embeddingManager,
7072
+ config,
7073
+ getTeamClient
7074
+ );
6495
7075
  if (isInstructionRequiredTask(taskName) && !built) {
6496
7076
  logger.info(
6497
7077
  LOG_KINDS.AGENT_RUN,
@@ -6500,7 +7080,7 @@ async function registerScheduledTasks(powerManager, deps) {
6500
7080
  );
6501
7081
  return;
6502
7082
  }
6503
- const result = await runAgent(vaultDir, {
7083
+ const result = await runAgent2(vaultDir, {
6504
7084
  task: taskName,
6505
7085
  instruction: built?.instruction,
6506
7086
  runContext: built?.context,
@@ -6527,7 +7107,7 @@ async function registerScheduledTasks(powerManager, deps) {
6527
7107
  link: `/agent?run=${result.runId}`,
6528
7108
  metadata: { taskName, runId: result.runId }
6529
7109
  }, config);
6530
- const { countToolCallsByRun } = await import("./turns-YFNI5CQC.js");
7110
+ const { countToolCallsByRun } = await import("./turns-HU2CTZAP.js");
6531
7111
  const counts = countToolCallsByRun(result.runId, ["vault_create_spore", "vault_write_digest"]);
6532
7112
  const sporeCount = counts["vault_create_spore"] ?? 0;
6533
7113
  const digestCount = counts["vault_write_digest"] ?? 0;
@@ -6602,32 +7182,171 @@ function listTeamMembers() {
6602
7182
  ).all();
6603
7183
  }
6604
7184
 
6605
- // src/daemon/api/mcp-proxy.ts
6606
- var SPORE_ID_RANDOM_BYTES = 4;
6607
- var RESOLUTION_ID_RANDOM_BYTES = 8;
6608
- var MIN_CONSOLIDATE_SOURCES = 2;
6609
- var RememberBody = external_exports.object({
6610
- content: external_exports.string(),
6611
- type: external_exports.string().optional(),
6612
- tags: external_exports.array(external_exports.string()).optional()
6613
- });
6614
- var SupersedeBody = external_exports.object({
6615
- old_spore_id: external_exports.string(),
6616
- new_spore_id: external_exports.string(),
6617
- reason: external_exports.string().optional()
6618
- });
6619
- function isoToEpochSeconds(iso) {
6620
- const ms = Date.parse(iso);
6621
- return Number.isNaN(ms) ? void 0 : Math.floor(ms / 1e3);
6622
- }
6623
- function registerMcpUserAgent(createdAt) {
6624
- registerAgent({
6625
- id: USER_AGENT_ID,
6626
- name: USER_AGENT_NAME,
6627
- created_at: createdAt
6628
- });
6629
- }
6630
- function toPlanProgress(content) {
7185
+ // src/daemon/plan-capture.ts
7186
+ import { createHash as createHash4 } from "crypto";
7187
+ import os8 from "os";
7188
+ import path18 from "path";
7189
+ function extractTaggedPlans(text, tags) {
7190
+ const results = [];
7191
+ for (const tag of tags) {
7192
+ const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
7193
+ let match;
7194
+ while ((match = regex.exec(text)) !== null) {
7195
+ const content = match[1].trim();
7196
+ if (content) results.push({ tag, content });
7197
+ }
7198
+ }
7199
+ return results;
7200
+ }
7201
+ var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
7202
+ "Write",
7203
+ "Edit",
7204
+ "Create",
7205
+ "write",
7206
+ "edit",
7207
+ "patch",
7208
+ "create"
7209
+ ]);
7210
+ var HEADING_REGEX = /^#\s+(.+)$/m;
7211
+ var TRANSCRIPT_SOURCE_TITLE_PREFIX = `${TRANSCRIPT_SOURCE_PREFIX}`;
7212
+ function resolvePlanWatchDir(watchDir, projectRoot) {
7213
+ const expanded = watchDir.startsWith("~/") ? path18.join(os8.homedir(), watchDir.slice(2)) : watchDir;
7214
+ return path18.isAbsolute(expanded) ? expanded : path18.resolve(projectRoot, expanded);
7215
+ }
7216
+ function isInPlanDirectory(filePath, watchDirs, projectRoot) {
7217
+ const abs = path18.isAbsolute(filePath) ? filePath : path18.resolve(projectRoot, filePath);
7218
+ return watchDirs.some((dir) => {
7219
+ const absDir = resolvePlanWatchDir(dir, projectRoot);
7220
+ const prefix = absDir.endsWith(path18.sep) ? absDir : absDir + path18.sep;
7221
+ return abs === absDir || abs.startsWith(prefix);
7222
+ });
7223
+ }
7224
+ function isPlanWriteEvent(toolName, toolInput, config) {
7225
+ if (!FILE_WRITE_TOOLS.has(toolName)) return null;
7226
+ const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
7227
+ if (typeof filePath !== "string") return null;
7228
+ if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
7229
+ if (config.extensions?.length) {
7230
+ const ext = path18.extname(filePath).toLowerCase();
7231
+ if (!config.extensions.includes(ext)) return null;
7232
+ }
7233
+ return filePath;
7234
+ }
7235
+ function parsePlanTitle(content, filename) {
7236
+ const match = HEADING_REGEX.exec(content);
7237
+ if (match) return match[1].trim();
7238
+ return filename ?? null;
7239
+ }
7240
+ function normalizePlanTags(tags) {
7241
+ if (tags === void 0 || tags === null) return null;
7242
+ return Array.isArray(tags) ? tags.join(", ") : tags;
7243
+ }
7244
+ function fileTitleFromSourcePath(sourcePath) {
7245
+ if (!sourcePath || sourcePath.startsWith(TRANSCRIPT_SOURCE_TITLE_PREFIX)) return null;
7246
+ return path18.basename(sourcePath);
7247
+ }
7248
+ function resolvePlanTitle(input) {
7249
+ const explicitTitle = input.title?.trim();
7250
+ if (explicitTitle) return explicitTitle;
7251
+ const headingTitle = parsePlanTitle(input.content);
7252
+ if (headingTitle) return headingTitle;
7253
+ const sourcePathTitle = fileTitleFromSourcePath(input.sourcePath);
7254
+ if (sourcePathTitle) return sourcePathTitle;
7255
+ return input.planKey ? humanizePlanToken(input.planKey) : null;
7256
+ }
7257
+ function persistPlan(input) {
7258
+ const createdAt = input.createdAt ?? Math.floor(Date.now() / 1e3);
7259
+ const updatedAt = input.updatedAt ?? createdAt;
7260
+ const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
7261
+ const existingPlan = getPlanByLogicalKey(input.logicalKey);
7262
+ const status = input.status ?? existingPlan?.status ?? "active";
7263
+ const promptBatchId = input.promptBatchId === void 0 ? existingPlan?.prompt_batch_id ?? null : input.promptBatchId;
7264
+ const resolvedTitle = resolvePlanTitle({
7265
+ content: input.content,
7266
+ title: input.title,
7267
+ sourcePath: input.sourcePath,
7268
+ planKey: input.planKey
7269
+ });
7270
+ if (existingPlan && existingPlan.content_hash === contentHash && existingPlan.title === resolvedTitle && existingPlan.status === status) {
7271
+ return existingPlan;
7272
+ }
7273
+ if (existingPlan && existingPlan.content_hash !== contentHash) {
7274
+ const priorSource = existingPlan.source_path ?? null;
7275
+ const newSource = input.sourcePath ?? null;
7276
+ if (priorSource !== newSource) {
7277
+ input.logger?.warn(LOG_KINDS.CAPTURE_PLAN, "Plan overwritten mid-session", {
7278
+ logical_key: input.logicalKey,
7279
+ session_id: input.sessionId,
7280
+ prior_source: priorSource,
7281
+ new_source: newSource,
7282
+ prior_updated_at: existingPlan.updated_at
7283
+ });
7284
+ }
7285
+ }
7286
+ return upsertPlan({
7287
+ id: buildPlanId(input.logicalKey),
7288
+ logical_key: input.logicalKey,
7289
+ title: resolvedTitle,
7290
+ content: input.content,
7291
+ source_path: input.sourcePath ?? null,
7292
+ tags: normalizePlanTags(input.tags),
7293
+ session_id: input.sessionId,
7294
+ prompt_batch_id: promptBatchId,
7295
+ content_hash: contentHash,
7296
+ status,
7297
+ created_at: createdAt,
7298
+ updated_at: updatedAt
7299
+ });
7300
+ }
7301
+ function capturePlan(input) {
7302
+ const normalizedSourcePath = normalizePlanSourcePath(input.sourcePath, input.projectRoot);
7303
+ return persistPlan({
7304
+ sessionId: input.sessionId,
7305
+ content: input.content,
7306
+ logicalKey: buildPathPlanLogicalKey(normalizedSourcePath),
7307
+ sourcePath: normalizedSourcePath,
7308
+ promptBatchId: input.promptBatchId,
7309
+ logger: input.logger
7310
+ });
7311
+ }
7312
+ function captureTaggedPlan(input) {
7313
+ return persistPlan({
7314
+ sessionId: input.sessionId,
7315
+ content: input.content,
7316
+ logicalKey: buildSessionTagPlanLogicalKey(input.sessionId, input.tag),
7317
+ sourcePath: `${TRANSCRIPT_SOURCE_PREFIX}${input.tag}`,
7318
+ promptBatchId: input.promptBatchId,
7319
+ planKey: input.tag,
7320
+ logger: input.logger
7321
+ });
7322
+ }
7323
+
7324
+ // src/daemon/api/mcp-proxy.ts
7325
+ var SPORE_ID_RANDOM_BYTES = 4;
7326
+ var RESOLUTION_ID_RANDOM_BYTES = 8;
7327
+ var MIN_CONSOLIDATE_SOURCES = 2;
7328
+ var RememberBody = external_exports.object({
7329
+ content: external_exports.string(),
7330
+ type: external_exports.string().optional(),
7331
+ tags: external_exports.array(external_exports.string()).optional()
7332
+ });
7333
+ var SupersedeBody = external_exports.object({
7334
+ old_spore_id: external_exports.string(),
7335
+ new_spore_id: external_exports.string(),
7336
+ reason: external_exports.string().optional()
7337
+ });
7338
+ function isoToEpochSeconds(iso) {
7339
+ const ms = Date.parse(iso);
7340
+ return Number.isNaN(ms) ? void 0 : Math.floor(ms / 1e3);
7341
+ }
7342
+ function registerMcpUserAgent(createdAt) {
7343
+ registerAgent({
7344
+ id: USER_AGENT_ID,
7345
+ name: USER_AGENT_NAME,
7346
+ created_at: createdAt
7347
+ });
7348
+ }
7349
+ function toPlanProgress(content) {
6631
7350
  const planContent = content ?? "";
6632
7351
  const checked = (planContent.match(/- \[x\]/gi) ?? []).length;
6633
7352
  const unchecked = (planContent.match(/- \[ \]/g) ?? []).length;
@@ -6644,8 +7363,20 @@ var ConsolidateBody = external_exports.object({
6644
7363
  tags: external_exports.array(external_exports.string()).optional(),
6645
7364
  reason: external_exports.string().optional()
6646
7365
  });
7366
+ var SavePlanBody = external_exports.object({
7367
+ session_id: external_exports.string(),
7368
+ content: external_exports.string().min(1),
7369
+ source_path: external_exports.string().min(1).optional(),
7370
+ plan_key: external_exports.string().min(1).optional(),
7371
+ title: external_exports.string().min(1).optional(),
7372
+ status: external_exports.enum(PLAN_STATUSES).optional(),
7373
+ tags: external_exports.array(external_exports.string()).optional()
7374
+ }).refine(
7375
+ (value) => Boolean(value.source_path) !== Boolean(value.plan_key),
7376
+ { message: "Provide exactly one of source_path or plan_key" }
7377
+ );
6647
7378
  function createMcpProxyHandlers(deps) {
6648
- const { machineId, embeddingManager } = deps;
7379
+ const { machineId, embeddingManager, projectRoot, logger } = deps;
6649
7380
  function toPlanSummary(row) {
6650
7381
  return {
6651
7382
  id: row.id,
@@ -6769,8 +7500,46 @@ function createMcpProxyHandlers(deps) {
6769
7500
  }
6770
7501
  };
6771
7502
  }
7503
+ async function handleSavePlan(req) {
7504
+ const { session_id, content, source_path, plan_key, title, status, tags } = SavePlanBody.parse(req.body);
7505
+ const session = getSession(session_id);
7506
+ if (!session) return { status: 404, body: errorBody("session-not-found", "Session not found") };
7507
+ const openBatch = getLatestOpenBatch(session_id);
7508
+ const normalizedSourcePath = source_path ? normalizePlanSourcePath(source_path, projectRoot) : null;
7509
+ const logicalKey = normalizedSourcePath ? buildPathPlanLogicalKey(normalizedSourcePath) : buildSessionPlanLogicalKey(session_id, plan_key);
7510
+ const row = persistPlan({
7511
+ sessionId: session_id,
7512
+ content,
7513
+ logicalKey,
7514
+ sourcePath: normalizedSourcePath,
7515
+ promptBatchId: openBatch?.id,
7516
+ title,
7517
+ status,
7518
+ tags,
7519
+ planKey: plan_key ?? null,
7520
+ logger
7521
+ });
7522
+ return {
7523
+ body: {
7524
+ id: row.id,
7525
+ logical_key: row.logical_key,
7526
+ title: row.title,
7527
+ status: row.status,
7528
+ source_path: row.source_path,
7529
+ session_id: row.session_id,
7530
+ prompt_batch_id: row.prompt_batch_id,
7531
+ tags: toPlanTags(row.tags),
7532
+ created_at: row.created_at,
7533
+ updated_at: row.updated_at
7534
+ }
7535
+ };
7536
+ }
6772
7537
  async function handlePlans(req) {
6773
7538
  const id = typeof req.query.id === "string" ? req.query.id : void 0;
7539
+ const session = typeof req.query.session === "string" ? req.query.session : void 0;
7540
+ if (id && session) {
7541
+ return { status: 400, body: errorBody("mutually-exclusive-query", "Pass either id or session, not both") };
7542
+ }
6774
7543
  if (id) {
6775
7544
  const row = getPlan(id);
6776
7545
  if (!row) return { body: { plans: [] } };
@@ -6783,6 +7552,11 @@ function createMcpProxyHandlers(deps) {
6783
7552
  }
6784
7553
  };
6785
7554
  }
7555
+ if (session) {
7556
+ const rows2 = listPlansBySession(session);
7557
+ const plans2 = rows2.map(toPlanSummary);
7558
+ return { body: { plans: plans2 } };
7559
+ }
6786
7560
  const statusFilter = req.query.status === "all" ? void 0 : req.query.status;
6787
7561
  const limit = req.query.limit ? Number(req.query.limit) : void 0;
6788
7562
  const rows = listPlans({ status: statusFilter, limit });
@@ -6836,6 +7610,7 @@ function createMcpProxyHandlers(deps) {
6836
7610
  handleSupersede,
6837
7611
  handleConsolidate,
6838
7612
  handlePlans,
7613
+ handleSavePlan,
6839
7614
  handleSessions,
6840
7615
  handleTeam
6841
7616
  };
@@ -6843,16 +7618,386 @@ function createMcpProxyHandlers(deps) {
6843
7618
 
6844
7619
  // src/daemon/api/agent-runs.ts
6845
7620
  import { resolve as resolve2 } from "path";
7621
+
7622
+ // src/services/phase-audit.ts
7623
+ function isStoredUsageData(value) {
7624
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7625
+ }
7626
+ function parseUsageData(raw) {
7627
+ return tryParseJson(raw, isStoredUsageData) ?? {};
7628
+ }
7629
+ function normalizeStatus(raw) {
7630
+ if (raw === "completed" || raw === "failed" || raw === "skipped") return raw;
7631
+ return "pending";
7632
+ }
7633
+ function aggregateTurnCounts(turns) {
7634
+ const toolCalls = {};
7635
+ for (const turn of turns) {
7636
+ toolCalls[turn.tool_name] = (toolCalls[turn.tool_name] ?? 0) + 1;
7637
+ }
7638
+ const toolErrors = {};
7639
+ return [toolCalls, toolErrors];
7640
+ }
7641
+ function aggregateWriteIntents(intents) {
7642
+ const byPhase = {};
7643
+ const unattributedByTool = {};
7644
+ const totalByTool = {};
7645
+ for (const intent of intents) {
7646
+ const tool = intent.tool_name;
7647
+ totalByTool[tool] = (totalByTool[tool] ?? 0) + 1;
7648
+ if (intent.phase_id) {
7649
+ if (!byPhase[intent.phase_id]) byPhase[intent.phase_id] = {};
7650
+ byPhase[intent.phase_id][tool] = (byPhase[intent.phase_id][tool] ?? 0) + 1;
7651
+ } else {
7652
+ unattributedByTool[tool] = (unattributedByTool[tool] ?? 0) + 1;
7653
+ }
7654
+ }
7655
+ return {
7656
+ total: intents.length,
7657
+ byPhase,
7658
+ unattributedByTool,
7659
+ totalByTool
7660
+ };
7661
+ }
7662
+ function serializeReports(reports) {
7663
+ return reports.map((r) => ({
7664
+ action: r.action,
7665
+ summary: r.summary ?? null,
7666
+ details: r.details ?? null,
7667
+ createdAt: r.created_at
7668
+ }));
7669
+ }
7670
+ function buildPhaseAudit(runId) {
7671
+ const run = getRun(runId);
7672
+ if (!run) return null;
7673
+ const dryRun = run.dry_run;
7674
+ const usageData = parseUsageData(run.usage_data);
7675
+ const checkpointState = parseCheckpointState(run.checkpoints);
7676
+ const usageByName = new Map(
7677
+ (usageData.phases ?? []).map((p) => [p.name, p])
7678
+ );
7679
+ const checkpointByName = new Map(
7680
+ Object.entries(checkpointState.phases).map(([key, cp]) => [
7681
+ cp.name ?? key,
7682
+ cp
7683
+ ])
7684
+ );
7685
+ const phaseNames = [];
7686
+ const seen = /* @__PURE__ */ new Set();
7687
+ for (const p of usageData.phases ?? []) {
7688
+ if (!seen.has(p.name)) {
7689
+ phaseNames.push(p.name);
7690
+ seen.add(p.name);
7691
+ }
7692
+ }
7693
+ for (const name of checkpointByName.keys()) {
7694
+ if (!seen.has(name)) {
7695
+ phaseNames.push(name);
7696
+ seen.add(name);
7697
+ }
7698
+ }
7699
+ const reports = listReports(runId);
7700
+ const turns = listTurnsByRun(runId);
7701
+ const [toolCalls, toolErrors] = aggregateTurnCounts(turns);
7702
+ const serializedReports = serializeReports(reports);
7703
+ let intentSummary = null;
7704
+ if (dryRun) {
7705
+ const intents = listWriteIntentTools(runId);
7706
+ intentSummary = aggregateWriteIntents(intents);
7707
+ }
7708
+ if (phaseNames.length === 0 && turns.length > 0) {
7709
+ const runStatus = normalizeStatus(run.status);
7710
+ const durationMs = runDurationMs(run);
7711
+ let writeIntents = null;
7712
+ if (intentSummary) {
7713
+ writeIntents = {
7714
+ total: intentSummary.total,
7715
+ byTool: intentSummary.totalByTool
7716
+ };
7717
+ }
7718
+ const summary = run.status === "failed" ? run.error ?? null : null;
7719
+ return {
7720
+ runId,
7721
+ taskName: run.task ?? null,
7722
+ dryRun,
7723
+ phases: [
7724
+ {
7725
+ phaseName: "run",
7726
+ status: runStatus,
7727
+ summary,
7728
+ turnsUsed: turns.length,
7729
+ maxTurns: null,
7730
+ tokensUsed: run.tokens_used ?? 0,
7731
+ costUsd: run.cost_usd ?? null,
7732
+ costSource: run.cost_source ?? null,
7733
+ durationMs,
7734
+ startedAt: run.started_at,
7735
+ completedAt: run.completed_at,
7736
+ skipReason: null,
7737
+ toolCalls,
7738
+ toolErrors,
7739
+ writeIntents,
7740
+ reports: serializedReports
7741
+ }
7742
+ ]
7743
+ };
7744
+ }
7745
+ const phases = phaseNames.map((phaseName) => {
7746
+ const usage = usageByName.get(phaseName);
7747
+ const cp = checkpointByName.get(phaseName);
7748
+ const status = normalizeStatus(cp?.status ?? (usage ? "pending" : "pending"));
7749
+ const tokensUsed = cp?.tokensUsed ?? usage?.tokensUsed ?? usage?.usage?.totalTokens ?? 0;
7750
+ const costUsd = cp?.costUsd !== void 0 ? cp.costUsd : usage?.costUsd !== void 0 ? usage.costUsd : null;
7751
+ const costSource = cp?.costSource ?? usage?.costSource ?? null;
7752
+ const durationMs = usage?.usage?.durationMs ?? null;
7753
+ const completedAt = status === "completed" || status === "failed" ? cp?.updatedAt ?? null : null;
7754
+ const startedAt = null;
7755
+ const skipReason = null;
7756
+ let writeIntents = null;
7757
+ if (intentSummary) {
7758
+ const phaseByTool = intentSummary.byPhase[phaseName] ?? {};
7759
+ writeIntents = {
7760
+ // Phase total = sum of tool calls attributed to this phase
7761
+ total: Object.values(phaseByTool).reduce((sum, n) => sum + n, 0),
7762
+ byTool: phaseByTool
7763
+ };
7764
+ }
7765
+ return {
7766
+ phaseName,
7767
+ status,
7768
+ summary: cp?.summary ?? null,
7769
+ turnsUsed: cp?.turnsUsed ?? 0,
7770
+ maxTurns: null,
7771
+ // Not persisted — see module JSDoc
7772
+ tokensUsed,
7773
+ costUsd,
7774
+ costSource,
7775
+ durationMs,
7776
+ startedAt,
7777
+ completedAt,
7778
+ skipReason,
7779
+ toolCalls,
7780
+ // run-level aggregate (no per-phase turn attribution)
7781
+ toolErrors,
7782
+ // always empty — no error flag on turn rows
7783
+ writeIntents,
7784
+ reports: serializedReports
7785
+ // all reports; no phase column exists
7786
+ };
7787
+ });
7788
+ return {
7789
+ runId,
7790
+ taskName: run.task ?? null,
7791
+ dryRun,
7792
+ phases
7793
+ };
7794
+ }
7795
+
7796
+ // src/daemon/api/schemas/execution-overrides.ts
7797
+ var ReasoningLevelEnum = external_exports.enum(["low", "default", "high"]);
7798
+ var RuntimeIdEnum = external_exports.enum(["claude-sdk", "openai-agents"]);
7799
+ var ProviderTypeEnum = external_exports.enum([
7800
+ "anthropic",
7801
+ "ollama",
7802
+ "lmstudio",
7803
+ "openai",
7804
+ "openrouter",
7805
+ "openai-compatible"
7806
+ ]);
7807
+ var ProviderOverrideWireSchema = external_exports.object({
7808
+ runtime: RuntimeIdEnum.optional(),
7809
+ type: ProviderTypeEnum,
7810
+ localBackend: external_exports.enum(["ollama", "lmstudio"]).optional(),
7811
+ baseUrl: external_exports.string().optional(),
7812
+ model: external_exports.string().optional(),
7813
+ reasoningMap: external_exports.object({
7814
+ low: external_exports.string().optional(),
7815
+ default: external_exports.string().optional(),
7816
+ high: external_exports.string().optional()
7817
+ }).optional(),
7818
+ contextLength: external_exports.number().int().positive().optional()
7819
+ });
7820
+ var PhaseExecutionOverrideBody = external_exports.object({
7821
+ reasoningLevel: ReasoningLevelEnum.optional(),
7822
+ model: external_exports.string().optional(),
7823
+ provider: ProviderOverrideWireSchema.optional(),
7824
+ maxTurns: external_exports.number().int().positive().optional()
7825
+ });
7826
+ var ExecutionOverrideBody = external_exports.object({
7827
+ runtime: RuntimeIdEnum.optional(),
7828
+ reasoningLevel: ReasoningLevelEnum.optional(),
7829
+ model: external_exports.string().optional(),
7830
+ provider: ProviderOverrideWireSchema.optional(),
7831
+ phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
7832
+ }).optional();
7833
+
7834
+ // src/daemon/api/schemas/execution-overrides-traversal.ts
7835
+ function transformProviderOverrides(overrides, transform) {
7836
+ if (overrides === null || overrides === void 0) {
7837
+ return overrides ?? null;
7838
+ }
7839
+ if (typeof overrides !== "object") return overrides;
7840
+ const cloned = { ...overrides };
7841
+ if (isPlainObject(cloned.provider)) {
7842
+ const next = transform({ ...cloned.provider });
7843
+ if (next === null) {
7844
+ delete cloned.provider;
7845
+ } else {
7846
+ cloned.provider = next;
7847
+ }
7848
+ }
7849
+ if (isPlainObject(cloned.phases)) {
7850
+ const nextPhases = {};
7851
+ for (const [name, phase] of Object.entries(cloned.phases)) {
7852
+ if (!isPlainObject(phase)) {
7853
+ nextPhases[name] = phase;
7854
+ continue;
7855
+ }
7856
+ const clonedPhase = { ...phase };
7857
+ if (isPlainObject(clonedPhase.provider)) {
7858
+ const nextProvider = transform({ ...clonedPhase.provider });
7859
+ if (nextProvider === null) {
7860
+ delete clonedPhase.provider;
7861
+ } else {
7862
+ clonedPhase.provider = nextProvider;
7863
+ }
7864
+ }
7865
+ nextPhases[name] = clonedPhase;
7866
+ }
7867
+ cloned.phases = nextPhases;
7868
+ }
7869
+ return cloned;
7870
+ }
7871
+ function isPlainObject(value) {
7872
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7873
+ }
7874
+
7875
+ // src/daemon/api/run-serializer.ts
7876
+ function buildPhaseCheckpointSummary(checkpointsRaw) {
7877
+ if (!checkpointsRaw) return [];
7878
+ try {
7879
+ const parsed = JSON.parse(checkpointsRaw);
7880
+ return Object.entries(parsed.phases ?? {}).map(([name, phase]) => ({
7881
+ name: phase.name ?? name,
7882
+ status: phase.status ?? "pending",
7883
+ updatedAt: phase.updatedAt ?? 0,
7884
+ ...phase.tokensUsed !== void 0 ? { tokensUsed: phase.tokensUsed } : {},
7885
+ ...phase.costUsd !== void 0 ? { costUsd: phase.costUsd } : {},
7886
+ ...phase.costSource !== void 0 ? { costSource: phase.costSource } : {}
7887
+ }));
7888
+ } catch {
7889
+ return [];
7890
+ }
7891
+ }
7892
+ function scrubExecutionOverrides(overrides) {
7893
+ if (!overrides || typeof overrides !== "object") return overrides;
7894
+ const topLevelStripped = { ...overrides };
7895
+ if ("apiKey" in topLevelStripped) delete topLevelStripped.apiKey;
7896
+ return transformProviderOverrides(topLevelStripped, stripApiKey);
7897
+ }
7898
+ function stripApiKey(provider) {
7899
+ const cloned = { ...provider };
7900
+ if ("apiKey" in cloned) delete cloned.apiKey;
7901
+ return cloned;
7902
+ }
7903
+ function serializeRun(run, opts = {}) {
7904
+ const {
7905
+ includeResumeFields = true,
7906
+ includePhaseCheckpoints = true,
7907
+ writeIntents,
7908
+ duration_ms
7909
+ } = opts;
7910
+ const base = {
7911
+ id: run.id,
7912
+ agent_id: run.agent_id,
7913
+ task: run.task,
7914
+ instruction: run.instruction,
7915
+ status: run.status,
7916
+ runtime: run.runtime,
7917
+ provider: run.provider,
7918
+ model: run.model,
7919
+ session_ref: run.session_ref,
7920
+ started_at: run.started_at,
7921
+ completed_at: run.completed_at,
7922
+ tokens_used: run.tokens_used,
7923
+ cost_usd: run.cost_usd,
7924
+ actual_cost_usd: run.actual_cost_usd,
7925
+ estimated_cost_usd: run.estimated_cost_usd,
7926
+ cost_source: run.cost_source,
7927
+ cost_data: run.cost_data,
7928
+ actions_taken: run.actions_taken,
7929
+ usage_data: run.usage_data,
7930
+ error: run.error,
7931
+ dry_run: run.dry_run,
7932
+ evaluation_id: run.evaluation_id,
7933
+ reasoning_level: run.reasoning_level,
7934
+ // Strip `apiKey` from historical rows defensively — before this PR the
7935
+ // API accepted apiKey in executionOverrides and stored it unmasked.
7936
+ execution_overrides: scrubExecutionOverrides(run.execution_overrides)
7937
+ };
7938
+ return {
7939
+ ...base,
7940
+ ...includeResumeFields ? {
7941
+ resumable: run.resumable === 1,
7942
+ resume_status: run.resume_status,
7943
+ resume_mode: run.resume_mode,
7944
+ resumed_at: run.resumed_at,
7945
+ checkpoints: run.checkpoints
7946
+ } : {},
7947
+ ...includePhaseCheckpoints ? { phase_checkpoints: buildPhaseCheckpointSummary(run.checkpoints) } : {},
7948
+ ...writeIntents !== void 0 && writeIntents !== null ? { write_intents: writeIntents } : {},
7949
+ ...duration_ms !== void 0 ? { duration_ms } : {}
7950
+ };
7951
+ }
7952
+
7953
+ // src/daemon/api/agent-runs.ts
6846
7954
  var AGENT_RUNS_DEFAULT_LIMIT = 50;
7955
+ var REMOTE_PROVIDER_TYPES = /* @__PURE__ */ new Set(["openai", "openrouter"]);
7956
+ function stripBaseUrlForRemoteProviders(provider) {
7957
+ const type = provider.type;
7958
+ if (typeof type === "string" && REMOTE_PROVIDER_TYPES.has(type)) {
7959
+ const { baseUrl: _dropped, ...rest } = provider;
7960
+ return rest;
7961
+ }
7962
+ return provider;
7963
+ }
7964
+ function sanitizeExecutionOverrides(overrides) {
7965
+ if (!overrides) return overrides;
7966
+ const result = transformProviderOverrides(
7967
+ overrides,
7968
+ stripBaseUrlForRemoteProviders
7969
+ );
7970
+ return result;
7971
+ }
6847
7972
  var AgentRunBody = external_exports.object({
6848
7973
  task: external_exports.string().optional(),
6849
7974
  instruction: external_exports.string().optional(),
6850
- agentId: external_exports.string().optional()
7975
+ agentId: external_exports.string().optional(),
7976
+ /**
7977
+ * Run in dry-run mode — writes intercepted by the tool surface and
7978
+ * recorded to `agent_run_write_intents` instead of mutating the vault.
7979
+ */
7980
+ dryRun: external_exports.boolean().optional(),
7981
+ /** Evaluation matrix this run belongs to, if any. */
7982
+ evaluationId: external_exports.string().nullable().optional(),
7983
+ /** Per-run runtime/reasoning/model overrides; also per-phase overrides. */
7984
+ executionOverrides: ExecutionOverrideBody
7985
+ });
7986
+ var ResumeRunBody = external_exports.object({
7987
+ mode: external_exports.enum(["manual", "scheduled"]).optional()
6851
7988
  });
6852
7989
  function createAgentRunHandlers(deps) {
6853
- const { vaultDir, embeddingManager, logger } = deps;
7990
+ const { vaultDir, embeddingManager, logger, getTeamClient } = deps;
6854
7991
  async function handleRun(req) {
6855
- const { task, instruction: rawInstruction, agentId } = AgentRunBody.parse(req.body);
7992
+ const {
7993
+ task,
7994
+ instruction: rawInstruction,
7995
+ agentId,
7996
+ dryRun,
7997
+ evaluationId,
7998
+ executionOverrides: rawExecutionOverrides
7999
+ } = AgentRunBody.parse(req.body);
8000
+ const executionOverrides = sanitizeExecutionOverrides(rawExecutionOverrides);
6856
8001
  const mycoConfig = loadMergedConfig(vaultDir);
6857
8002
  if (!hasConfiguredProvider(mycoConfig, task)) {
6858
8003
  return {
@@ -6870,10 +8015,10 @@ function createAgentRunHandlers(deps) {
6870
8015
  try {
6871
8016
  const taskParams = mycoConfig.agent.tasks?.[task]?.params;
6872
8017
  const projectRoot = resolve2(vaultDir, "..");
6873
- built = buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager);
8018
+ built = await buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
6874
8019
  } catch {
6875
8020
  const projectRoot = resolve2(vaultDir, "..");
6876
- built = buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager);
8021
+ built = await buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
6877
8022
  }
6878
8023
  instruction = built?.instruction;
6879
8024
  runContext = built?.context;
@@ -6888,13 +8033,16 @@ function createAgentRunHandlers(deps) {
6888
8033
  };
6889
8034
  }
6890
8035
  }
6891
- const { runAgent } = await import("./executor-HWW2QNZQ.js");
6892
- const resultPromise = runAgent(vaultDir, {
8036
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
8037
+ const resultPromise = runAgent2(vaultDir, {
6893
8038
  task,
6894
8039
  instruction,
6895
8040
  agentId,
6896
8041
  embeddingManager,
6897
- runContext
8042
+ runContext,
8043
+ dryRun,
8044
+ evaluationId,
8045
+ executionOverrides
6898
8046
  });
6899
8047
  const effectiveAgentId = agentId ?? "myco-agent";
6900
8048
  const runId = getLatestRunId(effectiveAgentId, task);
@@ -6946,14 +8094,57 @@ function createAgentRunHandlers(deps) {
6946
8094
  const filterOpts = { agent_id: agentId, status, task, search };
6947
8095
  const runs = listRuns({ ...filterOpts, limit, offset });
6948
8096
  const total = countRuns(filterOpts);
6949
- return { body: { runs, total, offset, limit } };
8097
+ return { body: { runs: runs.map((run) => serializeRun(run)), total, offset, limit } };
6950
8098
  }
6951
8099
  async function handleGetRun(req) {
6952
8100
  const run = getRun(req.params.id);
6953
8101
  if (!run) {
6954
8102
  return { status: 404, body: { error: "Run not found" } };
6955
8103
  }
6956
- return { body: { run } };
8104
+ const byTool = countWriteIntentsByTool(run.id);
8105
+ const total = Object.values(byTool).reduce((acc, n) => acc + n, 0);
8106
+ return {
8107
+ body: {
8108
+ run: serializeRun(run, {
8109
+ writeIntents: { total, by_tool: byTool },
8110
+ duration_ms: runDurationMs(run)
8111
+ })
8112
+ }
8113
+ };
8114
+ }
8115
+ async function handleResumeRun(req) {
8116
+ const run = getRun(req.params.id);
8117
+ if (!run) {
8118
+ return { status: 404, body: { error: "Run not found" } };
8119
+ }
8120
+ if (run.resumable !== 1 || run.status !== "failed") {
8121
+ return { status: 400, body: { error: "Run is not resumable" } };
8122
+ }
8123
+ const { mode } = ResumeRunBody.parse(req.body ?? {});
8124
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
8125
+ const resultPromise = runAgent2(vaultDir, {
8126
+ agentId: run.agent_id,
8127
+ task: run.task ?? void 0,
8128
+ instruction: run.instruction ?? void 0,
8129
+ resumeRunId: run.id,
8130
+ resumeMode: mode ?? "manual",
8131
+ embeddingManager
8132
+ });
8133
+ resultPromise.then((result) => {
8134
+ logger.info(LOG_KINDS.AGENT_RUN, "Agent run resumed", {
8135
+ runId: result.runId,
8136
+ status: result.status,
8137
+ runtime: result.runtime,
8138
+ provider: result.provider,
8139
+ model: result.model
8140
+ });
8141
+ }).catch((err) => {
8142
+ logger.error(LOG_KINDS.AGENT_ERROR, "Agent run resume threw unhandled error", {
8143
+ runId: run.id,
8144
+ error: err instanceof Error ? err.message : String(err)
8145
+ });
8146
+ });
8147
+ return { body: { ok: true, message: "Agent resume started", runId: run.id } };
6957
8148
  }
6958
8149
  async function handleGetRunReports(req) {
6959
8150
  const reports = listReports(req.params.id);
@@ -6963,18 +8154,376 @@ function createAgentRunHandlers(deps) {
6963
8154
  const turns = listTurnsByRun(req.params.id);
6964
8155
  return { body: turns };
6965
8156
  }
8157
+ async function handleGetRunWriteIntents(req) {
8158
+ const rawLimit = req.query.limit ? Number(req.query.limit) : void 0;
8159
+ const rawOffset = req.query.offset ? Number(req.query.offset) : void 0;
8160
+ const limit = Number.isFinite(rawLimit) && rawLimit !== void 0 && rawLimit > 0 ? Math.min(rawLimit, 5e3) : 500;
8161
+ const offset = Number.isFinite(rawOffset) && rawOffset !== void 0 && rawOffset >= 0 ? rawOffset : 0;
8162
+ const intents = listWriteIntents(req.params.id, { limit, offset });
8163
+ const total = countWriteIntents(req.params.id);
8164
+ return { body: { intents, count: intents.length, total } };
8165
+ }
8166
+ async function handleGetRunAudit(req) {
8167
+ const audit = buildPhaseAudit(req.params.id);
8168
+ if (!audit) {
8169
+ return { status: 404, body: { error: "Run not found" } };
8170
+ }
8171
+ return { body: { audit } };
8172
+ }
6966
8173
  return {
6967
8174
  handleRun,
6968
8175
  handleListRuns,
6969
8176
  handleGetRun,
8177
+ handleResumeRun,
6970
8178
  handleGetRunReports,
6971
- handleGetRunTurns
8179
+ handleGetRunTurns,
8180
+ handleGetRunWriteIntents,
8181
+ handleGetRunAudit
8182
+ };
8183
+ }
8184
+
8185
+ // src/daemon/api/agent-evaluations.ts
8186
+ import crypto from "crypto";
8187
+
8188
+ // src/db/queries/evaluations.ts
8189
+ var DEFAULT_LIMIT = 50;
8190
+ var EVAL_STATUS_PENDING = "pending";
8191
+ var EVAL_STATUS_RUNNING = "running";
8192
+ var EVAL_STATUS_COMPLETED = "completed";
8193
+ var EVAL_STATUS_FAILED = "failed";
8194
+ var EVAL_COLUMNS = [
8195
+ "id",
8196
+ "task_id",
8197
+ "matrix_json",
8198
+ "notes",
8199
+ "status",
8200
+ "created_at",
8201
+ "completed_at"
8202
+ ];
8203
+ var SELECT_COLUMNS5 = EVAL_COLUMNS.join(", ");
8204
+ function toEvaluationRow(row) {
8205
+ const matrixJson = row.matrix_json;
8206
+ let matrix = null;
8207
+ if (matrixJson) {
8208
+ try {
8209
+ matrix = JSON.parse(matrixJson);
8210
+ } catch {
8211
+ matrix = null;
8212
+ }
8213
+ }
8214
+ return {
8215
+ id: row.id,
8216
+ task_id: row.task_id,
8217
+ matrix,
8218
+ notes: row.notes ?? null,
8219
+ status: row.status,
8220
+ created_at: row.created_at,
8221
+ completed_at: row.completed_at ?? null
8222
+ };
8223
+ }
8224
+ function insertEvaluation(data) {
8225
+ const db = getDatabase();
8226
+ const createdAt = data.createdAt ?? epochSeconds();
8227
+ db.prepare(
8228
+ `INSERT INTO agent_run_evaluations
8229
+ (id, task_id, matrix_json, notes, status, created_at, completed_at)
8230
+ VALUES (?, ?, ?, ?, ?, ?, NULL)`
8231
+ ).run(
8232
+ data.id,
8233
+ data.taskId,
8234
+ JSON.stringify(data.matrix ?? null),
8235
+ data.notes ?? null,
8236
+ EVAL_STATUS_PENDING,
8237
+ createdAt
8238
+ );
8239
+ return getEvaluation(data.id);
8240
+ }
8241
+ function getEvaluation(id) {
8242
+ const db = getDatabase();
8243
+ const row = db.prepare(
8244
+ `SELECT ${SELECT_COLUMNS5} FROM agent_run_evaluations WHERE id = ?`
8245
+ ).get(id);
8246
+ return row ? toEvaluationRow(row) : null;
8247
+ }
8248
+ function listEvaluations(options = {}) {
8249
+ const db = getDatabase();
8250
+ const limit = options.limit ?? DEFAULT_LIMIT;
8251
+ const offset = options.offset ?? 0;
8252
+ const rows = db.prepare(
8253
+ `SELECT ${SELECT_COLUMNS5}
8254
+ FROM agent_run_evaluations
8255
+ ORDER BY created_at DESC, id DESC
8256
+ LIMIT ? OFFSET ?`
8257
+ ).all(limit, offset);
8258
+ return rows.map(toEvaluationRow);
8259
+ }
8260
+ function updateEvaluationStatus(id, status, completedAt) {
8261
+ const db = getDatabase();
8262
+ if (completedAt === void 0) {
8263
+ db.prepare(
8264
+ `UPDATE agent_run_evaluations SET status = ? WHERE id = ?`
8265
+ ).run(status, id);
8266
+ } else {
8267
+ db.prepare(
8268
+ `UPDATE agent_run_evaluations SET status = ?, completed_at = ? WHERE id = ?`
8269
+ ).run(status, completedAt, id);
8270
+ }
8271
+ return getEvaluation(id);
8272
+ }
8273
+
8274
+ // src/daemon/api/agent-evaluations.ts
8275
+ var DEFAULT_LIMIT2 = 50;
8276
+ var CreateEvaluationBody = external_exports.object({
8277
+ taskId: external_exports.string(),
8278
+ matrix: external_exports.object({
8279
+ runtimes: external_exports.array(RuntimeIdEnum).optional(),
8280
+ reasoningLevels: external_exports.array(ReasoningLevelEnum).optional(),
8281
+ models: external_exports.array(external_exports.string()).optional(),
8282
+ dryRun: external_exports.boolean().optional(),
8283
+ notes: external_exports.string().optional(),
8284
+ /**
8285
+ * Phase-level overrides applied to EVERY cell in the matrix. The matrix
8286
+ * dimensions (runtimes / reasoningLevels / models) vary the top-level
8287
+ * execution per cell; phase overrides here let you pin specific phases
8288
+ * to fixed reasoning/model/provider/maxTurns regardless of what the cell
8289
+ * is varying at the top level.
8290
+ *
8291
+ * Merged into each cell's `executionOverrides.phases` before runAgent
8292
+ * is invoked. Unknown phase names are ignored by the executor (a
8293
+ * one-shot warning is logged at run startup).
8294
+ */
8295
+ phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
8296
+ }),
8297
+ notes: external_exports.string().optional()
8298
+ });
8299
+ function aggregateRuns(runs) {
8300
+ let completed = 0;
8301
+ let failed = 0;
8302
+ let skipped = 0;
8303
+ let totalTokens = 0;
8304
+ let totalCostUsd = 0;
8305
+ for (const run of runs) {
8306
+ if (run.status === "completed") completed++;
8307
+ else if (run.status === "failed") failed++;
8308
+ else if (run.status === "skipped") skipped++;
8309
+ totalTokens += run.tokens_used ?? 0;
8310
+ totalCostUsd += run.cost_usd ?? 0;
8311
+ }
8312
+ return {
8313
+ total: runs.length,
8314
+ completed,
8315
+ failed,
8316
+ skipped,
8317
+ totalTokens,
8318
+ totalCostUsd
6972
8319
  };
6973
8320
  }
8321
+ function summarizeFromByTool(byTool) {
8322
+ const tools = byTool ?? {};
8323
+ let total = 0;
8324
+ for (const count of Object.values(tools)) total += count;
8325
+ return { total, by_tool: tools };
8326
+ }
8327
+ function createAgentEvaluationHandlers(deps) {
8328
+ const { vaultDir, embeddingManager, logger } = deps;
8329
+ async function handleCreate(req) {
8330
+ const parsed = CreateEvaluationBody.safeParse(req.body);
8331
+ if (!parsed.success) {
8332
+ return {
8333
+ status: 400,
8334
+ body: { error: "Invalid request body", details: parsed.error.flatten() }
8335
+ };
8336
+ }
8337
+ const body = parsed.data;
8338
+ const cells = enumerateMatrixCells(body.matrix);
8339
+ if (cells.length === 0) {
8340
+ return {
8341
+ status: 400,
8342
+ body: { error: "Matrix produced zero cells" }
8343
+ };
8344
+ }
8345
+ const evalId = crypto.randomUUID();
8346
+ insertEvaluation({
8347
+ id: evalId,
8348
+ taskId: body.taskId,
8349
+ matrix: body.matrix,
8350
+ notes: body.notes ?? null
8351
+ });
8352
+ const dryRun = body.matrix.dryRun ?? false;
8353
+ void (async () => {
8354
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
8355
+ let anyCompleted = false;
8356
+ let transitionedToRunning = false;
8357
+ const sharedPhases = body.matrix.phases;
8358
+ for (const cell of cells) {
8359
+ const cellOptions = {
8360
+ task: body.taskId,
8361
+ evaluationId: evalId,
8362
+ dryRun,
8363
+ embeddingManager,
8364
+ executionOverrides: {
8365
+ ...cell.runtime ? { runtime: cell.runtime } : {},
8366
+ ...cell.reasoningLevel ? { reasoningLevel: cell.reasoningLevel } : {},
8367
+ ...cell.model ? { model: cell.model } : {},
8368
+ ...sharedPhases && Object.keys(sharedPhases).length > 0 ? { phases: sharedPhases } : {}
8369
+ }
8370
+ };
8371
+ if (!transitionedToRunning) {
8372
+ updateEvaluationStatus(evalId, EVAL_STATUS_RUNNING);
8373
+ transitionedToRunning = true;
8374
+ }
8375
+ try {
8376
+ const result = await runAgent2(vaultDir, cellOptions);
8377
+ if (result.status === "completed") {
8378
+ anyCompleted = true;
8379
+ }
8380
+ logger.info(LOG_KINDS.AGENT_RUN, "Evaluation cell finished", {
8381
+ evaluationId: evalId,
8382
+ runId: result.runId,
8383
+ status: result.status,
8384
+ runtime: cell.runtime,
8385
+ reasoningLevel: cell.reasoningLevel,
8386
+ model: cell.model
8387
+ });
8388
+ } catch (err) {
8389
+ logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation cell threw", {
8390
+ evaluationId: evalId,
8391
+ runtime: cell.runtime,
8392
+ reasoningLevel: cell.reasoningLevel,
8393
+ model: cell.model,
8394
+ error: err instanceof Error ? err.message : String(err)
8395
+ });
8396
+ }
8397
+ }
8398
+ const status = anyCompleted ? EVAL_STATUS_COMPLETED : EVAL_STATUS_FAILED;
8399
+ updateEvaluationStatus(evalId, status, epochSeconds());
8400
+ logger.info(LOG_KINDS.AGENT_RUN, "Evaluation finished", {
8401
+ evaluationId: evalId,
8402
+ status,
8403
+ cellCount: cells.length
8404
+ });
8405
+ })().catch((err) => {
8406
+ logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation fan-out failed", {
8407
+ evaluationId: evalId,
8408
+ error: err instanceof Error ? err.message : String(err)
8409
+ });
8410
+ });
8411
+ return { body: { evaluationId: evalId, cellCount: cells.length } };
8412
+ }
8413
+ async function handleGet(req) {
8414
+ const evaluation = getEvaluation(req.params.id);
8415
+ if (!evaluation) {
8416
+ return { status: 404, body: { error: "Evaluation not found" } };
8417
+ }
8418
+ const runs = listRunsForEvaluation(evaluation.id);
8419
+ const writeIntentsByRun = countWriteIntentsByToolForEvaluation(evaluation.id);
8420
+ return {
8421
+ body: {
8422
+ evaluation: {
8423
+ id: evaluation.id,
8424
+ taskId: evaluation.task_id,
8425
+ matrix: evaluation.matrix,
8426
+ notes: evaluation.notes,
8427
+ status: evaluation.status,
8428
+ createdAt: evaluation.created_at,
8429
+ completedAt: evaluation.completed_at
8430
+ },
8431
+ runs: runs.map(
8432
+ (run) => serializeRun(run, {
8433
+ includeResumeFields: false,
8434
+ includePhaseCheckpoints: false,
8435
+ writeIntents: summarizeFromByTool(writeIntentsByRun[run.id]),
8436
+ duration_ms: runDurationMs(run)
8437
+ })
8438
+ ),
8439
+ aggregate: aggregateRuns(runs)
8440
+ }
8441
+ };
8442
+ }
8443
+ async function handleList(req) {
8444
+ const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT2;
8445
+ const offset = req.query.offset ? Number(req.query.offset) : 0;
8446
+ const evaluations = listEvaluations({ limit, offset });
8447
+ return {
8448
+ body: {
8449
+ evaluations: evaluations.map((e) => ({
8450
+ id: e.id,
8451
+ taskId: e.task_id,
8452
+ matrix: e.matrix,
8453
+ notes: e.notes,
8454
+ status: e.status,
8455
+ createdAt: e.created_at,
8456
+ completedAt: e.completed_at
8457
+ })),
8458
+ total: evaluations.length
8459
+ }
8460
+ };
8461
+ }
8462
+ return { handleCreate, handleGet, handleList };
8463
+ }
8464
+
8465
+ // src/daemon/api/digest-revisions.ts
8466
+ var DEFAULT_LIMIT3 = 50;
8467
+ var RestoreBody = external_exports.object({
8468
+ /** If supplied, recorded on the new revision row as the run that triggered the restore. */
8469
+ runId: external_exports.string().optional()
8470
+ });
8471
+ function createDigestRevisionHandlers(deps) {
8472
+ const { logger } = deps;
8473
+ async function handleList(req) {
8474
+ const agentId = req.query.agentId || DEFAULT_AGENT_ID;
8475
+ const tierRaw = req.query.tier;
8476
+ if (!tierRaw) {
8477
+ return { status: 400, body: { error: "tier is required" } };
8478
+ }
8479
+ const tier = Number(tierRaw);
8480
+ if (!Number.isFinite(tier)) {
8481
+ return { status: 400, body: { error: `tier must be numeric, got ${tierRaw}` } };
8482
+ }
8483
+ const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT3;
8484
+ const revisions = listDigestRevisions({ agentId, tier, limit });
8485
+ return { body: { revisions, count: revisions.length } };
8486
+ }
8487
+ async function handleRestore(req) {
8488
+ const revisionId = Number(req.params.id);
8489
+ if (!Number.isFinite(revisionId)) {
8490
+ return { status: 400, body: { error: "Invalid revision id" } };
8491
+ }
8492
+ const parsed = RestoreBody.safeParse(req.body ?? {});
8493
+ if (!parsed.success) {
8494
+ return {
8495
+ status: 400,
8496
+ body: { error: "Invalid request body", details: parsed.error.flatten() }
8497
+ };
8498
+ }
8499
+ const result = rollbackDigestExtract({
8500
+ revisionId,
8501
+ runId: parsed.data.runId ?? null
8502
+ });
8503
+ if (!result) {
8504
+ return { status: 404, body: { error: "Revision not found" } };
8505
+ }
8506
+ logger.info(LOG_KINDS.AGENT_RUN, "Digest revision restored", {
8507
+ revisionId,
8508
+ newRevisionId: result.newRevisionId,
8509
+ agentId: result.row.agent_id,
8510
+ tier: result.row.tier,
8511
+ triggeredBy: parsed.data.runId ?? "operator"
8512
+ });
8513
+ return {
8514
+ body: {
8515
+ ok: true,
8516
+ restored: revisionId,
8517
+ newRevisionId: result.newRevisionId
8518
+ }
8519
+ };
8520
+ }
8521
+ return { handleList, handleRestore };
8522
+ }
6974
8523
 
6975
8524
  // src/daemon/api/attachments.ts
6976
- import fs18 from "fs";
6977
- import path17 from "path";
8525
+ import fs19 from "fs";
8526
+ import path19 from "path";
6978
8527
  var ATTACHMENT_MEDIA_TYPES = {
6979
8528
  png: "image/png",
6980
8529
  jpg: "image/jpeg",
@@ -6994,14 +8543,14 @@ function createAttachmentHandler(deps) {
6994
8543
  const contentType2 = att.media_type ?? "application/octet-stream";
6995
8544
  return { status: 200, headers: { "Content-Type": contentType2 }, body: att.data };
6996
8545
  }
6997
- const filePath = path17.join(vaultDir, "attachments", filename);
8546
+ const filePath = path19.join(vaultDir, "attachments", filename);
6998
8547
  let diskData;
6999
8548
  try {
7000
- diskData = fs18.readFileSync(filePath);
8549
+ diskData = fs19.readFileSync(filePath);
7001
8550
  } catch {
7002
8551
  return { status: 404, body: { error: "not_found" } };
7003
8552
  }
7004
- const ext = path17.extname(filename).slice(1).toLowerCase();
8553
+ const ext = path19.extname(filename).slice(1).toLowerCase();
7005
8554
  const contentType = ATTACHMENT_MEDIA_TYPES[ext] ?? "application/octet-stream";
7006
8555
  return { status: 200, headers: { "Content-Type": contentType }, body: diskData };
7007
8556
  }
@@ -7009,19 +8558,19 @@ function createAttachmentHandler(deps) {
7009
8558
  }
7010
8559
 
7011
8560
  // src/daemon/log-reconcile.ts
7012
- import fs19 from "fs";
7013
- import path18 from "path";
8561
+ import fs20 from "fs";
8562
+ import path20 from "path";
7014
8563
  function reconcileLogBuffer(logDir, sinceTimestamp) {
7015
8564
  let replayed = 0;
7016
8565
  const files = [];
7017
8566
  for (let i = 3; i >= 1; i--) {
7018
- const rotated = path18.join(logDir, `daemon.${i}.log`);
7019
- if (fs19.existsSync(rotated)) files.push(rotated);
8567
+ const rotated = path20.join(logDir, `daemon.${i}.log`);
8568
+ if (fs20.existsSync(rotated)) files.push(rotated);
7020
8569
  }
7021
- const current = path18.join(logDir, "daemon.log");
7022
- if (fs19.existsSync(current)) files.push(current);
8570
+ const current = path20.join(logDir, "daemon.log");
8571
+ if (fs20.existsSync(current)) files.push(current);
7023
8572
  for (const file of files) {
7024
- const content = fs19.readFileSync(file, "utf-8");
8573
+ const content = fs20.readFileSync(file, "utf-8");
7025
8574
  for (const line of content.split("\n")) {
7026
8575
  if (!line.trim()) continue;
7027
8576
  try {
@@ -7049,11 +8598,14 @@ function reconcileLogBuffer(logDir, sinceTimestamp) {
7049
8598
  // src/config/focus.ts
7050
8599
  var CONFIG_FOCUS_SECTION_PARAM = "configSection";
7051
8600
  var CONFIG_FOCUS_FIELD_PARAM = "configField";
8601
+ var CONFIG_FOCUS_TAB_PARAM = "tab";
7052
8602
  var CONFIG_SECTION_IDS = {
7053
8603
  appearance: "config-section-appearance",
8604
+ cortexInstructions: "config-section-cortex-instructions",
8605
+ cortexBuilder: "config-section-cortex-builder",
8606
+ cortexDigest: "config-section-cortex-digest",
7054
8607
  settingsAgent: "config-section-settings-agent",
7055
8608
  settingsEmbedding: "config-section-settings-embedding",
7056
- settingsContextInjection: "config-section-settings-context-injection",
7057
8609
  settingsNotifications: "config-section-settings-notifications",
7058
8610
  settingsPlanCapture: "config-section-settings-plan-capture",
7059
8611
  settingsProject: "config-section-settings-project",
@@ -7080,11 +8632,30 @@ var SECTION_RULES = [
7080
8632
  sectionId: CONFIG_SECTION_IDS.settingsEmbedding,
7081
8633
  sectionLabel: "Embedding"
7082
8634
  },
8635
+ {
8636
+ prefix: "context.digest_tier",
8637
+ page: "/cortex",
8638
+ sectionId: CONFIG_SECTION_IDS.cortexDigest,
8639
+ sectionLabel: "Digest",
8640
+ searchParams: { [CONFIG_FOCUS_TAB_PARAM]: "digest" }
8641
+ },
8642
+ {
8643
+ prefix: "context.cortex",
8644
+ page: "/cortex",
8645
+ sectionId: CONFIG_SECTION_IDS.cortexInstructions,
8646
+ sectionLabel: "Instructions"
8647
+ },
8648
+ {
8649
+ prefix: "context.prompt",
8650
+ page: "/cortex",
8651
+ sectionId: CONFIG_SECTION_IDS.cortexInstructions,
8652
+ sectionLabel: "Instructions"
8653
+ },
7083
8654
  {
7084
8655
  prefix: "context",
7085
- page: "/settings",
7086
- sectionId: CONFIG_SECTION_IDS.settingsContextInjection,
7087
- sectionLabel: "Context Injection"
8656
+ page: "/cortex",
8657
+ sectionId: CONFIG_SECTION_IDS.cortexInstructions,
8658
+ sectionLabel: "Instructions"
7088
8659
  },
7089
8660
  {
7090
8661
  prefix: "notifications",
@@ -7151,7 +8722,9 @@ var EXACT_FIELD_LABELS = {
7151
8722
  "embedding.provider": "Provider",
7152
8723
  "embedding.model": "Model",
7153
8724
  "embedding.base_url": "Base URL",
7154
- "context.digest_tier": "Digest Tier",
8725
+ "context.digest_tier": "Preferred Digest Tier",
8726
+ "context.session_start_digest_enabled": "Session-Start Digest",
8727
+ "context.cortex_enabled": "Session-Start Instructions",
7155
8728
  "context.prompt_search": "Prompt Search",
7156
8729
  "context.prompt_max_spores": "Max Spores per Prompt",
7157
8730
  "notifications.enabled": "Notifications",
@@ -7172,8 +8745,8 @@ var EXACT_FIELD_LABELS = {
7172
8745
  var DYNAMIC_FIELD_LABEL_RULES = [
7173
8746
  {
7174
8747
  prefix: "notifications.domains.",
7175
- format: (path23) => {
7176
- const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path23);
8748
+ format: (path25) => {
8749
+ const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path25);
7177
8750
  if (!match) return null;
7178
8751
  const [, domain, leaf] = match;
7179
8752
  const domainLabel = humanizeToken(domain);
@@ -7182,13 +8755,13 @@ var DYNAMIC_FIELD_LABEL_RULES = [
7182
8755
  }
7183
8756
  ];
7184
8757
  var SAVE_MESSAGE_LABEL_LIMIT = 3;
7185
- function resolveConfigFocusTarget(path23) {
7186
- const section = findSectionRule(path23);
8758
+ function resolveConfigFocusTarget(path25) {
8759
+ const section = findSectionRule(path25);
7187
8760
  if (!section) return null;
7188
8761
  return {
7189
8762
  ...section,
7190
- fieldPath: path23,
7191
- fieldLabel: resolveFieldLabel(path23)
8763
+ fieldPath: path25,
8764
+ fieldLabel: resolveFieldLabel(path25)
7192
8765
  };
7193
8766
  }
7194
8767
  function buildConfigFocusLink(target) {
@@ -7223,25 +8796,25 @@ function buildScopedConfigSaveNotification(scope, touchedPaths) {
7223
8796
  }
7224
8797
  };
7225
8798
  }
7226
- function findSectionRule(path23) {
8799
+ function findSectionRule(path25) {
7227
8800
  for (const rule of SECTION_RULES) {
7228
- if (path23 === rule.prefix || path23.startsWith(`${rule.prefix}.`)) {
8801
+ if (path25 === rule.prefix || path25.startsWith(`${rule.prefix}.`)) {
7229
8802
  const { page, sectionId, sectionLabel, searchParams } = rule;
7230
8803
  return { page, sectionId, sectionLabel, searchParams };
7231
8804
  }
7232
8805
  }
7233
8806
  return null;
7234
8807
  }
7235
- function resolveFieldLabel(path23) {
7236
- const exact = EXACT_FIELD_LABELS[path23];
8808
+ function resolveFieldLabel(path25) {
8809
+ const exact = EXACT_FIELD_LABELS[path25];
7237
8810
  if (exact) return exact;
7238
8811
  for (const rule of DYNAMIC_FIELD_LABEL_RULES) {
7239
- if (path23 === rule.prefix || path23.startsWith(rule.prefix)) {
7240
- const label = rule.format(path23);
8812
+ if (path25 === rule.prefix || path25.startsWith(rule.prefix)) {
8813
+ const label = rule.format(path25);
7241
8814
  if (label) return label;
7242
8815
  }
7243
8816
  }
7244
- return humanizeToken(path23.split(".").pop() ?? "setting");
8817
+ return humanizeToken(path25.split(".").pop() ?? "setting");
7245
8818
  }
7246
8819
  function humanizeToken(value) {
7247
8820
  return value.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
@@ -7361,6 +8934,48 @@ var PowerManager = class {
7361
8934
  }
7362
8935
  };
7363
8936
 
8937
+ // src/daemon/inflight-runs.ts
8938
+ var DEFAULT_DRAIN_TIMEOUT_MS = 3e4;
8939
+ var InflightRunRegistry = class {
8940
+ runs = /* @__PURE__ */ new Set();
8941
+ /**
8942
+ * Track a fire-and-forget agent run. The promise is removed from the
8943
+ * registry in a finally handler so a resolved/rejected run does not hold
8944
+ * memory. Resolves immediately — callers must not await this directly.
8945
+ */
8946
+ register(promise) {
8947
+ const tracked = Promise.resolve(promise).finally(() => {
8948
+ this.runs.delete(tracked);
8949
+ });
8950
+ this.runs.add(tracked);
8951
+ }
8952
+ /** Number of runs currently being tracked. */
8953
+ get size() {
8954
+ return this.runs.size;
8955
+ }
8956
+ /**
8957
+ * Wait for every tracked run to settle, up to `timeoutMs`. Returns when
8958
+ * either every run completes or the deadline elapses — whichever happens
8959
+ * first. The 30-second default mirrors a typical daemon shutdown budget;
8960
+ * callers are free to override for tests or tighter environments.
8961
+ */
8962
+ async drain(timeoutMs = DEFAULT_DRAIN_TIMEOUT_MS) {
8963
+ if (this.runs.size === 0) return { settled: true, remaining: 0 };
8964
+ const snapshot = Array.from(this.runs);
8965
+ let timer;
8966
+ const timeoutPromise = new Promise((resolve3) => {
8967
+ timer = setTimeout(() => resolve3("timeout"), timeoutMs);
8968
+ });
8969
+ const allSettled = Promise.allSettled(snapshot).then(() => "settled");
8970
+ try {
8971
+ const outcome = await Promise.race([allSettled, timeoutPromise]);
8972
+ return { settled: outcome === "settled", remaining: this.runs.size };
8973
+ } finally {
8974
+ if (timer !== void 0) clearTimeout(timer);
8975
+ }
8976
+ }
8977
+ };
8978
+
7364
8979
  // src/daemon/jobs/session-maintenance.ts
7365
8980
  function completeStaleActiveSessions(thresholdSeconds = STALE_SESSION_THRESHOLD_MS / MS_PER_SECOND) {
7366
8981
  const db = getDatabase();
@@ -7505,8 +9120,8 @@ function registerPowerJobs(powerManager, deps) {
7505
9120
  }
7506
9121
 
7507
9122
  // src/daemon/reconciliation.ts
7508
- import fs20 from "fs";
7509
- import path19 from "path";
9123
+ import fs21 from "fs";
9124
+ import path21 from "path";
7510
9125
 
7511
9126
  // src/daemon/event-handlers.ts
7512
9127
  var TOOL_INPUT_STORE_LIMIT = 4e3;
@@ -7675,10 +9290,10 @@ function createReconciler({ bufferDir, logger }) {
7675
9290
  function reconcileSession(sessionId) {
7676
9291
  if (reconciledSessions.has(sessionId)) return;
7677
9292
  reconciledSessions.add(sessionId);
7678
- const bufferPath = path19.join(bufferDir, `${sessionId}.jsonl`);
9293
+ const bufferPath = path21.join(bufferDir, `${sessionId}.jsonl`);
7679
9294
  let content;
7680
9295
  try {
7681
- content = fs20.readFileSync(bufferPath, "utf-8").trim();
9296
+ content = fs21.readFileSync(bufferPath, "utf-8").trim();
7682
9297
  } catch {
7683
9298
  return;
7684
9299
  }
@@ -7747,7 +9362,19 @@ function createReconciler({ bufferDir, logger }) {
7747
9362
  }
7748
9363
 
7749
9364
  // src/daemon/stop-processing.ts
7750
- import fs21 from "fs";
9365
+ import fs22 from "fs";
9366
+ import path22 from "path";
9367
+
9368
+ // src/daemon/capture-gating.ts
9369
+ function gateEventByCaptureRules(event, options) {
9370
+ const transcriptMeta = event.transcriptPath ? readTranscriptMeta(event.transcriptPath) ?? void 0 : void 0;
9371
+ const manifests = options?.manifests ?? loadManifests();
9372
+ const decision = evaluateSessionCaptureRules(manifests, event.agent, {
9373
+ transcriptPath: event.transcriptPath,
9374
+ transcriptMeta
9375
+ });
9376
+ return { decision, hadTranscriptMeta: transcriptMeta !== void 0 };
9377
+ }
7751
9378
 
7752
9379
  // src/daemon/capture-images.ts
7753
9380
  var SESSION_SHORT_LEN = 6;
@@ -7784,80 +9411,8 @@ function captureBatchImages(input) {
7784
9411
  }
7785
9412
  }
7786
9413
 
7787
- // src/daemon/plan-capture.ts
7788
- import { createHash as createHash4 } from "crypto";
7789
- import os8 from "os";
7790
- import path20 from "path";
7791
- function extractTaggedPlans(text, tags) {
7792
- const results = [];
7793
- for (const tag of tags) {
7794
- const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
7795
- let match;
7796
- while ((match = regex.exec(text)) !== null) {
7797
- const content = match[1].trim();
7798
- if (content) results.push({ tag, content });
7799
- }
7800
- }
7801
- return results;
7802
- }
7803
- var TRANSCRIPT_SOURCE_PREFIX = "transcript:";
7804
- var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
7805
- "Write",
7806
- "Edit",
7807
- "Create",
7808
- "write",
7809
- "edit",
7810
- "patch",
7811
- "create"
7812
- ]);
7813
- var HEADING_REGEX = /^#\s+(.+)$/m;
7814
- var PLAN_ID_HASH_LENGTH = 16;
7815
- function isInPlanDirectory(filePath, watchDirs, projectRoot) {
7816
- const abs = path20.isAbsolute(filePath) ? filePath : path20.resolve(projectRoot, filePath);
7817
- return watchDirs.some((dir) => {
7818
- const expanded = dir.startsWith("~/") ? path20.join(os8.homedir(), dir.slice(2)) : dir;
7819
- const absDir = path20.isAbsolute(expanded) ? expanded : path20.resolve(projectRoot, expanded);
7820
- const prefix = absDir.endsWith(path20.sep) ? absDir : absDir + path20.sep;
7821
- return abs === absDir || abs.startsWith(prefix);
7822
- });
7823
- }
7824
- function isPlanWriteEvent(toolName, toolInput, config) {
7825
- if (!FILE_WRITE_TOOLS.has(toolName)) return null;
7826
- const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
7827
- if (typeof filePath !== "string") return null;
7828
- if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
7829
- if (config.extensions?.length) {
7830
- const ext = path20.extname(filePath).toLowerCase();
7831
- if (!config.extensions.includes(ext)) return null;
7832
- }
7833
- return filePath;
7834
- }
7835
- function parsePlanTitle(content, filename) {
7836
- const match = HEADING_REGEX.exec(content);
7837
- if (match) return match[1].trim();
7838
- return filename ?? null;
7839
- }
7840
- function capturePlan(input) {
7841
- const now = Math.floor(Date.now() / 1e3);
7842
- const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
7843
- const id = createHash4("md5").update(input.sourcePath).digest("hex").slice(0, PLAN_ID_HASH_LENGTH);
7844
- const title = parsePlanTitle(input.content, path20.basename(input.sourcePath));
7845
- return upsertPlan({
7846
- id,
7847
- title,
7848
- content: input.content,
7849
- source_path: input.sourcePath,
7850
- session_id: input.sessionId,
7851
- prompt_batch_id: input.promptBatchId ?? null,
7852
- content_hash: contentHash,
7853
- status: "active",
7854
- created_at: now,
7855
- updated_at: now
7856
- });
7857
- }
7858
-
7859
9414
  // src/daemon/skill-usage.ts
7860
- import crypto from "crypto";
9415
+ import crypto2 from "crypto";
7861
9416
  var SKILL_USAGE_DETECTION_ENABLED = false;
7862
9417
  var MAX_ACTIVE_SKILLS_CHECK = 1e3;
7863
9418
  function detectSkillUsage(sessionId, transcriptContent) {
@@ -7877,7 +9432,7 @@ function detectSkillUsage(sessionId, transcriptContent) {
7877
9432
  if (!pattern.test(transcriptContent)) continue;
7878
9433
  if (hasUsageForSkillAndSession(skill.id, sessionId)) continue;
7879
9434
  insertSkillUsage({
7880
- id: crypto.randomUUID(),
9435
+ id: crypto2.randomUUID(),
7881
9436
  skill_id: skill.id,
7882
9437
  session_id: sessionId,
7883
9438
  detected_at: now
@@ -7892,6 +9447,46 @@ function escapeRegex(s) {
7892
9447
  }
7893
9448
 
7894
9449
  // src/daemon/stop-processing.ts
9450
+ var MILLISECONDS_PER_SECOND = 1e3;
9451
+ function isEligiblePlanExtension(filePath, extensions) {
9452
+ if (!extensions || extensions.length === 0) return true;
9453
+ const extension = path22.extname(filePath).toLowerCase();
9454
+ return extensions.includes(extension);
9455
+ }
9456
+ function collectPlanFiles(rootDir, extensions) {
9457
+ const files = [];
9458
+ const stack = [rootDir];
9459
+ while (stack.length > 0) {
9460
+ const currentDir = stack.pop();
9461
+ if (!currentDir) continue;
9462
+ let entries;
9463
+ try {
9464
+ entries = fs22.readdirSync(currentDir, { withFileTypes: true });
9465
+ } catch {
9466
+ continue;
9467
+ }
9468
+ for (const entry of entries) {
9469
+ const absolutePath = path22.join(currentDir, entry.name);
9470
+ if (entry.isDirectory()) {
9471
+ stack.push(absolutePath);
9472
+ continue;
9473
+ }
9474
+ if (entry.isFile() && isEligiblePlanExtension(absolutePath, extensions)) {
9475
+ files.push(absolutePath);
9476
+ }
9477
+ }
9478
+ }
9479
+ return files;
9480
+ }
9481
+ function resolvePromptBatchIdForPlanWrite(batches, modifiedAtSeconds) {
9482
+ for (let index = batches.length - 1; index >= 0; index--) {
9483
+ const startedAt = batches[index].started_at;
9484
+ if (startedAt !== null && startedAt <= modifiedAtSeconds) {
9485
+ return batches[index].id;
9486
+ }
9487
+ }
9488
+ return void 0;
9489
+ }
7895
9490
  function enrichTurnsWithToolMetadata(turns, events) {
7896
9491
  if (events.length === 0 || turns.length === 0) return;
7897
9492
  const toolEvents = events.filter((e) => e.type === "tool_use");
@@ -7919,7 +9514,7 @@ function enrichTurnsWithToolMetadata(turns, events) {
7919
9514
  }
7920
9515
  }
7921
9516
  function createStopProcessor(deps) {
7922
- const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir } = deps;
9517
+ const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir, planWatchConfig } = deps;
7923
9518
  let activeStopProcessing = null;
7924
9519
  const sessionTitleCache = /* @__PURE__ */ new Map();
7925
9520
  const StopBody = external_exports.object({
@@ -8021,7 +9616,7 @@ function createStopProcessor(deps) {
8021
9616
  let transcriptText = null;
8022
9617
  if (hookTranscriptPath) {
8023
9618
  try {
8024
- transcriptText = fs21.readFileSync(hookTranscriptPath, "utf-8");
9619
+ transcriptText = fs22.readFileSync(hookTranscriptPath, "utf-8");
8025
9620
  } catch {
8026
9621
  }
8027
9622
  }
@@ -8053,11 +9648,12 @@ function createStopProcessor(deps) {
8053
9648
  const taggedPlans = extractTaggedPlans(turn.aiResponse, deps.planTags);
8054
9649
  for (const { tag, content } of taggedPlans) {
8055
9650
  try {
8056
- capturePlan({
8057
- sourcePath: `${TRANSCRIPT_SOURCE_PREFIX}${tag}`,
9651
+ captureTaggedPlan({
9652
+ tag,
8058
9653
  content,
8059
9654
  sessionId,
8060
- promptBatchId: latestBatch?.id ?? null
9655
+ promptBatchId: latestBatch?.id ?? null,
9656
+ logger
8061
9657
  });
8062
9658
  logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from transcript tag", {
8063
9659
  session_id: sessionId,
@@ -8074,6 +9670,50 @@ function createStopProcessor(deps) {
8074
9670
  }
8075
9671
  }
8076
9672
  }
9673
+ const sessionStartedAt = currentSession?.started_at ?? (sessionMeta?.started_at ? Math.floor(new Date(sessionMeta.started_at).getTime() / MILLISECONDS_PER_SECOND) : epochSeconds());
9674
+ const sessionStopMs = Date.now();
9675
+ const sessionBatches = listBatchesBySession(sessionId);
9676
+ for (const watchDir of planWatchConfig.watchDirs) {
9677
+ const absoluteWatchDir = resolvePlanWatchDir(watchDir, planWatchConfig.projectRoot);
9678
+ if (!fs22.existsSync(absoluteWatchDir)) continue;
9679
+ const planFiles = collectPlanFiles(absoluteWatchDir, planWatchConfig.extensions);
9680
+ for (const planFile of planFiles) {
9681
+ let stats;
9682
+ try {
9683
+ stats = fs22.statSync(planFile);
9684
+ } catch {
9685
+ continue;
9686
+ }
9687
+ if (stats.mtimeMs < sessionStartedAt * MILLISECONDS_PER_SECOND || stats.mtimeMs > sessionStopMs) {
9688
+ continue;
9689
+ }
9690
+ try {
9691
+ const content = fs22.readFileSync(planFile, "utf-8");
9692
+ const promptBatchId = resolvePromptBatchIdForPlanWrite(
9693
+ sessionBatches,
9694
+ Math.floor(stats.mtimeMs / MILLISECONDS_PER_SECOND)
9695
+ );
9696
+ capturePlan({
9697
+ sourcePath: planFile,
9698
+ projectRoot: planWatchConfig.projectRoot,
9699
+ content,
9700
+ sessionId,
9701
+ promptBatchId,
9702
+ logger
9703
+ });
9704
+ logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan reconciled from configured plan dir", {
9705
+ session_id: sessionId,
9706
+ source_path: planFile
9707
+ });
9708
+ } catch (err) {
9709
+ logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to reconcile plan from configured plan dir", {
9710
+ session_id: sessionId,
9711
+ source_path: planFile,
9712
+ error: err.message
9713
+ });
9714
+ }
9715
+ }
9716
+ }
8077
9717
  if (!hasTitle) {
8078
9718
  triggerTitleSummary2(sessionId);
8079
9719
  }
@@ -8120,12 +9760,11 @@ function createStopProcessor(deps) {
8120
9760
  last_assistant_message: lastAssistantMessage
8121
9761
  } = StopBody.parse(req.body);
8122
9762
  if (hookTranscriptPath) {
8123
- const transcriptMeta = readTranscriptMeta(hookTranscriptPath) ?? void 0;
8124
9763
  const detectedAgent = agent ?? getSession(sessionId)?.agent ?? "claude-code";
8125
- const decision = evaluateSessionCaptureRules(loadManifests(), detectedAgent, {
8126
- transcriptPath: hookTranscriptPath,
8127
- transcriptMeta
8128
- });
9764
+ const { decision } = gateEventByCaptureRules(
9765
+ { agent: detectedAgent, transcriptPath: hookTranscriptPath },
9766
+ { manifests: loadManifests() }
9767
+ );
8129
9768
  if (decision.action === "drop") {
8130
9769
  const deleted = cleanupInvalidCapturedSession(sessionId);
8131
9770
  logger.info(LOG_KINDS.HOOKS_STOP, "Stop ignored \u2014 invalid captured session", {
@@ -8180,9 +9819,10 @@ function createStopProcessor(deps) {
8180
9819
  }
8181
9820
 
8182
9821
  // src/daemon/event-dispatch.ts
8183
- import fs22 from "fs";
8184
- import path21 from "path";
9822
+ import fs23 from "fs";
9823
+ import path23 from "path";
8185
9824
  var EventBody = external_exports.object({ type: external_exports.string(), session_id: external_exports.string() }).passthrough();
9825
+ var DROP_DECISION_CACHE_MAX = 1024;
8186
9826
  function createEventDispatcher(deps) {
8187
9827
  const {
8188
9828
  registry,
@@ -8197,6 +9837,39 @@ function createEventDispatcher(deps) {
8197
9837
  triggerTitleSummary: triggerTitleSummary2
8198
9838
  } = deps;
8199
9839
  const projectRoot = process.cwd();
9840
+ const manifests = loadManifests();
9841
+ const planTagsByAgent = new Map(
9842
+ manifests.map((manifest) => [manifest.name, manifest.capture?.planTags ?? []])
9843
+ );
9844
+ const droppedSessions = /* @__PURE__ */ new Map();
9845
+ function rememberDropped(sessionId, reason, hadTranscriptMeta) {
9846
+ if (droppedSessions.size >= DROP_DECISION_CACHE_MAX) {
9847
+ const oldest = droppedSessions.keys().next().value;
9848
+ if (oldest !== void 0) droppedSessions.delete(oldest);
9849
+ }
9850
+ droppedSessions.set(sessionId, { reason, hadTranscriptMeta });
9851
+ }
9852
+ function evaluateAutoRegistration(event) {
9853
+ const transcriptPath = typeof event.transcript_path === "string" && event.transcript_path.length > 0 ? event.transcript_path : void 0;
9854
+ const detectedAgent = typeof event.agent === "string" ? event.agent : DEFAULT_SYMBIONT_NAME;
9855
+ try {
9856
+ return gateEventByCaptureRules(
9857
+ { agent: detectedAgent, transcriptPath },
9858
+ { manifests }
9859
+ );
9860
+ } catch (err) {
9861
+ logger.error(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Capture-rules evaluator threw", {
9862
+ error: String(err),
9863
+ session_id: typeof event.session_id === "string" ? event.session_id : void 0,
9864
+ agent: detectedAgent
9865
+ });
9866
+ return { decision: { action: "pass" }, hadTranscriptMeta: false };
9867
+ }
9868
+ }
9869
+ function getPlanTagsForAgent(agent) {
9870
+ const agentName = typeof agent === "string" && agent.length > 0 ? agent : DEFAULT_SYMBIONT_NAME;
9871
+ return planTagsByAgent.get(agentName) ?? [];
9872
+ }
8200
9873
  return async (req) => {
8201
9874
  const validated = EventBody.parse(req.body);
8202
9875
  const event = {
@@ -8205,13 +9878,38 @@ function createEventDispatcher(deps) {
8205
9878
  };
8206
9879
  logger.debug(LOG_KINDS.HOOKS_EVENT, "Event received", { type: event.type, session_id: event.session_id });
8207
9880
  if (!registry.getSession(event.session_id)) {
9881
+ const cached = droppedSessions.get(event.session_id);
9882
+ const hasTranscriptNow = typeof event.transcript_path === "string" && event.transcript_path.length > 0;
9883
+ const shouldReevaluate = cached && !cached.hadTranscriptMeta && hasTranscriptNow;
9884
+ if (cached && !shouldReevaluate) {
9885
+ const reason = cached.reason ?? "rule";
9886
+ logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event for previously-dropped session", {
9887
+ session_id: event.session_id,
9888
+ type: event.type,
9889
+ reason
9890
+ });
9891
+ return { body: { ok: true, ignored: reason } };
9892
+ }
9893
+ if (shouldReevaluate) {
9894
+ droppedSessions.delete(event.session_id);
9895
+ }
9896
+ const { decision, hadTranscriptMeta } = evaluateAutoRegistration(event);
9897
+ if (decision.action === "drop") {
9898
+ rememberDropped(event.session_id, decision.reason, hadTranscriptMeta);
9899
+ logger.info(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event that failed session capture rules", {
9900
+ session_id: event.session_id,
9901
+ type: event.type,
9902
+ reason: decision.reason ?? "rule"
9903
+ });
9904
+ return { body: { ok: true, ignored: decision.reason ?? "rule" } };
9905
+ }
8208
9906
  registry.register(event.session_id, { started_at: event.timestamp });
8209
9907
  logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Auto-registered session from event", { session_id: event.session_id });
8210
9908
  const now = epochSeconds();
8211
9909
  const startedEpoch = Math.floor(new Date(event.timestamp).getTime() / 1e3);
8212
9910
  upsertSession({
8213
9911
  id: event.session_id,
8214
- agent: event.agent ?? "claude-code",
9912
+ agent: event.agent ?? DEFAULT_SYMBIONT_NAME,
8215
9913
  status: "active",
8216
9914
  started_at: startedEpoch,
8217
9915
  created_at: now,
@@ -8220,7 +9918,7 @@ function createEventDispatcher(deps) {
8220
9918
  reconcileSession(event.session_id);
8221
9919
  }
8222
9920
  if (!sessionBuffers.has(event.session_id)) {
8223
- const bufferDir = path21.join(vaultDir, "buffer");
9921
+ const bufferDir = path23.join(vaultDir, "buffer");
8224
9922
  sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));
8225
9923
  }
8226
9924
  sessionBuffers.get(event.session_id).append(event);
@@ -8246,6 +9944,29 @@ function createEventDispatcher(deps) {
8246
9944
  try {
8247
9945
  const { batchId, promptNumber } = handleUserPrompt(event.session_id, promptText || void 0);
8248
9946
  logger.debug(LOG_KINDS.CAPTURE_BATCH, "Batch opened", { session_id: event.session_id, batch_id: batchId, prompt_number: promptNumber });
9947
+ const taggedPlans = extractTaggedPlans(promptText, getPlanTagsForAgent(event.agent));
9948
+ for (const { tag, content } of taggedPlans) {
9949
+ try {
9950
+ captureTaggedPlan({
9951
+ tag,
9952
+ content,
9953
+ sessionId: event.session_id,
9954
+ promptBatchId: batchId,
9955
+ logger
9956
+ });
9957
+ logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from prompt tag", {
9958
+ session_id: event.session_id,
9959
+ tag,
9960
+ content_length: content.length
9961
+ });
9962
+ } catch (err) {
9963
+ logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to capture plan from prompt tag", {
9964
+ session_id: event.session_id,
9965
+ tag,
9966
+ error: err.message
9967
+ });
9968
+ }
9969
+ }
8249
9970
  const eventImages = event.images;
8250
9971
  if (Array.isArray(eventImages) && eventImages.length > 0) {
8251
9972
  captureBatchImages({
@@ -8279,13 +10000,15 @@ function createEventDispatcher(deps) {
8279
10000
  );
8280
10001
  if (planFilePath) {
8281
10002
  const captureSessionId = event.session_id;
8282
- fs22.promises.readFile(planFilePath, "utf-8").then((planContent) => {
10003
+ fs23.promises.readFile(planFilePath, "utf-8").then((planContent) => {
8283
10004
  const latestBatch = getLatestBatch(captureSessionId);
8284
10005
  capturePlan({
8285
- sourcePath: path21.relative(projectRoot, planFilePath),
10006
+ sourcePath: planFilePath,
10007
+ projectRoot,
8286
10008
  content: planContent,
8287
10009
  sessionId: captureSessionId,
8288
- promptBatchId: latestBatch?.id ?? null
10010
+ promptBatchId: latestBatch?.id ?? null,
10011
+ logger
8289
10012
  });
8290
10013
  logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured", {
8291
10014
  session_id: captureSessionId,
@@ -8445,8 +10168,8 @@ function createConfigReactionRegistry(logger) {
8445
10168
  function shouldFire(registeredPaths, touched) {
8446
10169
  if (registeredPaths.length === 0) return true;
8447
10170
  for (const prefix of registeredPaths) {
8448
- for (const path23 of touched) {
8449
- if (path23 === prefix || path23.startsWith(`${prefix}.`)) return true;
10171
+ for (const path25 of touched) {
10172
+ if (path25 === prefix || path25.startsWith(`${prefix}.`)) return true;
8450
10173
  }
8451
10174
  }
8452
10175
  return false;
@@ -8480,13 +10203,13 @@ function createPlanWatchReaction(deps) {
8480
10203
  }
8481
10204
 
8482
10205
  // src/daemon/main.ts
8483
- import fs23 from "fs";
8484
- import path22 from "path";
10206
+ import fs24 from "fs";
10207
+ import path24 from "path";
8485
10208
  function killStaleDaemon(vaultDir, logger) {
8486
- const daemonJsonPath = path22.join(vaultDir, "daemon.json");
10209
+ const daemonJsonPath = path24.join(vaultDir, "daemon.json");
8487
10210
  try {
8488
- if (!fs23.existsSync(daemonJsonPath)) return;
8489
- const info = JSON.parse(fs23.readFileSync(daemonJsonPath, "utf-8"));
10211
+ if (!fs24.existsSync(daemonJsonPath)) return;
10212
+ const info = JSON.parse(fs24.readFileSync(daemonJsonPath, "utf-8"));
8490
10213
  if (!info.pid) return;
8491
10214
  if (info.pid === process.pid) return;
8492
10215
  try {
@@ -8495,7 +10218,7 @@ function killStaleDaemon(vaultDir, logger) {
8495
10218
  logger.info(LOG_KINDS.DAEMON_START, "Killed stale daemon", { pid: info.pid });
8496
10219
  } catch {
8497
10220
  }
8498
- fs23.unlinkSync(daemonJsonPath);
10221
+ fs24.unlinkSync(daemonJsonPath);
8499
10222
  } catch {
8500
10223
  }
8501
10224
  }
@@ -8505,7 +10228,7 @@ async function main() {
8505
10228
  process.stderr.write("Usage: mycod --vault <path>\n");
8506
10229
  process.exit(1);
8507
10230
  }
8508
- const vaultDir = path22.resolve(vaultArg);
10231
+ const vaultDir = path24.resolve(vaultArg);
8509
10232
  loadSecrets(vaultDir);
8510
10233
  const config = loadMergedConfig(vaultDir);
8511
10234
  const liveConfig = { current: config };
@@ -8518,7 +10241,7 @@ async function main() {
8518
10241
  projectRoot,
8519
10242
  extensions: config.capture.artifact_extensions
8520
10243
  };
8521
- const logger = new DaemonLogger(path22.join(vaultDir, "logs"), {
10244
+ const logger = new DaemonLogger(path24.join(vaultDir, "logs"), {
8522
10245
  level: config.daemon.log_level
8523
10246
  });
8524
10247
  if (config.daemon.log_level === "debug") {
@@ -8547,7 +10270,7 @@ async function main() {
8547
10270
  const devCliEntry = detectDevBuild(
8548
10271
  globalPrefix,
8549
10272
  process.argv[1],
8550
- fs23.realpathSync
10273
+ fs24.realpathSync
8551
10274
  );
8552
10275
  if (devCliEntry) {
8553
10276
  setDevBuildCliEntry(devCliEntry);
@@ -8559,13 +10282,19 @@ async function main() {
8559
10282
  const db = initDatabase(vaultDbPath(vaultDir));
8560
10283
  createSchema(db, machineId);
8561
10284
  registerBuiltinDomains();
10285
+ const interruptedRuns = markRunningRunsInterrupted("Daemon restarted before the run completed");
10286
+ if (interruptedRuns > 0) {
10287
+ logger.warn(LOG_KINDS.AGENT_RUN, "Marked stale running runs as resumable after daemon restart", {
10288
+ count: interruptedRuns
10289
+ });
10290
+ }
8562
10291
  logger.info(LOG_KINDS.DAEMON_START, "SQLite initialized", { vault: vaultDir });
8563
10292
  {
8564
- const reasonPath = path22.join(vaultDir, RESTART_REASON_FILENAME);
10293
+ const reasonPath = path24.join(vaultDir, RESTART_REASON_FILENAME);
8565
10294
  try {
8566
- if (fs23.existsSync(reasonPath)) {
8567
- const raw = JSON.parse(fs23.readFileSync(reasonPath, "utf-8"));
8568
- fs23.unlinkSync(reasonPath);
10295
+ if (fs24.existsSync(reasonPath)) {
10296
+ const raw = JSON.parse(fs24.readFileSync(reasonPath, "utf-8"));
10297
+ fs24.unlinkSync(reasonPath);
8569
10298
  if (raw.reason === "version_sync" && raw.to_version) {
8570
10299
  const message = raw.local_update_ran ? "Restarted and updated local project hooks." : "Restarted to pick up the latest version.";
8571
10300
  notify(vaultDir, {
@@ -8607,13 +10336,13 @@ async function main() {
8607
10336
  });
8608
10337
  const lastLogTimestamp = getMaxTimestamp();
8609
10338
  if (lastLogTimestamp) {
8610
- const logDir = path22.join(vaultDir, "logs");
10339
+ const logDir = path24.join(vaultDir, "logs");
8611
10340
  const replayedCount = reconcileLogBuffer(logDir, lastLogTimestamp);
8612
10341
  if (replayedCount > 0) {
8613
10342
  logger.info(LOG_KINDS.DAEMON_RECONCILE, `Replayed ${replayedCount} log entries from buffer`, { replayed: replayedCount });
8614
10343
  }
8615
10344
  }
8616
- const vectorsDbPath = path22.join(vaultDir, "vectors.db");
10345
+ const vectorsDbPath = path24.join(vaultDir, "vectors.db");
8617
10346
  const vectorStore = new SqliteVecVectorStore(vectorsDbPath);
8618
10347
  const llmProvider = createEmbeddingProvider(config.embedding);
8619
10348
  const embeddingProvider = new EmbeddingProviderAdapter(llmProvider, config.embedding);
@@ -8623,7 +10352,7 @@ async function main() {
8623
10352
  const databaseManager = new DatabaseMaintenanceManager(vaultDbPath(vaultDir), vaultDir, logger);
8624
10353
  let definitionsDir;
8625
10354
  try {
8626
- const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-NOMBJUPW.js");
10355
+ const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-NAVVZK63.js");
8627
10356
  definitionsDir = resolveDefinitionsDir2();
8628
10357
  await registerBuiltInAgentsAndTasks(definitionsDir, vaultDir);
8629
10358
  logger.info(LOG_KINDS.AGENT_TASK, "Built-in agents and tasks registered");
@@ -8660,10 +10389,10 @@ async function main() {
8660
10389
  }
8661
10390
  let uiDir = null;
8662
10391
  {
8663
- const root = findPackageRoot(path22.dirname(new URL(import.meta.url).pathname));
10392
+ const root = findPackageRoot(path24.dirname(new URL(import.meta.url).pathname));
8664
10393
  if (root) {
8665
- const candidate = path22.join(root, "dist", "ui");
8666
- if (fs23.existsSync(candidate)) uiDir = candidate;
10394
+ const candidate = path24.join(root, "dist", "ui");
10395
+ if (fs24.existsSync(candidate)) uiDir = candidate;
8667
10396
  }
8668
10397
  }
8669
10398
  if (uiDir) {
@@ -8677,6 +10406,7 @@ async function main() {
8677
10406
  sleepIntervalMs: POWER_SLEEP_INTERVAL_MS,
8678
10407
  logger
8679
10408
  });
10409
+ const inflightRuns = new InflightRunRegistry();
8680
10410
  const server = new DaemonServer({
8681
10411
  vaultDir,
8682
10412
  logger,
@@ -8696,7 +10426,7 @@ async function main() {
8696
10426
  (p) => createPerProjectAdapter(p, claudeCodeAdapter.parseTurns)
8697
10427
  )
8698
10428
  });
8699
- const bufferDir = path22.join(vaultDir, "buffer");
10429
+ const bufferDir = path24.join(vaultDir, "buffer");
8700
10430
  const sessionBuffers = /* @__PURE__ */ new Map();
8701
10431
  const reconciler = createReconciler({ bufferDir, logger });
8702
10432
  reconciler.runStartupReconciliation();
@@ -8708,7 +10438,8 @@ async function main() {
8708
10438
  logger,
8709
10439
  liveConfig,
8710
10440
  vaultDir,
8711
- planTags: symbiontPlanTags
10441
+ planTags: symbiontPlanTags,
10442
+ planWatchConfig
8712
10443
  });
8713
10444
  const sessionLifecycle = createSessionLifecycleHandlers({
8714
10445
  registry,
@@ -8738,14 +10469,32 @@ async function main() {
8738
10469
  });
8739
10470
  server.registerRoute("POST", "/events", eventDispatcher);
8740
10471
  server.registerRoute("POST", "/events/stop", stopProcessor.handleStopRoute);
8741
- const contextDeps = { embeddingManager, liveConfig, logger };
10472
+ let teamSync;
10473
+ const contextDeps = {
10474
+ vaultDir,
10475
+ embeddingManager,
10476
+ liveConfig,
10477
+ logger,
10478
+ getTeamClient: () => teamSync.getTeamClient()
10479
+ };
8742
10480
  server.registerRoute("POST", "/context", createSessionContextHandler(contextDeps));
8743
10481
  server.registerRoute("POST", "/context/resume", createResumeContextHandler(contextDeps));
8744
10482
  server.registerRoute("POST", "/context/prompt", createPromptContextHandler(contextDeps));
8745
10483
  const progressTracker = new ProgressTracker();
8746
10484
  let configHash = computeConfigHash(vaultDir);
10485
+ const cortexHandlers = createCortexHandlers(vaultDir, {
10486
+ liveConfig,
10487
+ embeddingManager,
10488
+ logger,
10489
+ getTeamClient: () => teamSync.getTeamClient(),
10490
+ registerInflightRun: (p) => inflightRuns.register(p)
10491
+ });
8747
10492
  server.registerRoute("GET", "/api/config", async () => handleGetConfig(vaultDir));
8748
10493
  server.registerRoute("GET", "/api/symbionts", async () => handleListSymbionts(vaultDir));
10494
+ server.registerRoute("GET", "/api/cortex/instructions", cortexHandlers.handleGetInstructions);
10495
+ server.registerRoute("POST", "/api/cortex/instructions/refresh", cortexHandlers.handleRefreshInstructions);
10496
+ server.registerRoute("POST", "/api/cortex/prompt-builder", cortexHandlers.handleBuildPrompt);
10497
+ server.registerRoute("GET", "/api/cortex/prompt-builder/:runId", cortexHandlers.handleGetPromptResult);
8749
10498
  server.registerRoute("GET", "/api/config/merged", async () => handleGetMergedConfig(vaultDir));
8750
10499
  server.registerRoute("GET", "/api/config/local", async () => handleGetLocalConfig(vaultDir));
8751
10500
  const symbiontPlanDirsByAgent = {};
@@ -8761,7 +10510,7 @@ async function main() {
8761
10510
  liveConfig.current = ctx;
8762
10511
  });
8763
10512
  reactions.on(["capture", "symbionts"], (ctx) => {
8764
- reconcileConfiguredSymbionts(path22.dirname(vaultDir), vaultDir, ctx);
10513
+ reconcileConfiguredSymbionts(path24.dirname(vaultDir), vaultDir, ctx);
8765
10514
  });
8766
10515
  reactions.on(["capture"], createPlanWatchReaction({
8767
10516
  symbiontPlanDirs,
@@ -8776,7 +10525,14 @@ async function main() {
8776
10525
  }
8777
10526
  });
8778
10527
  async function syncScheduledTasks() {
8779
- await registerScheduledTasks(powerManager, { definitionsDir, vaultDir, embeddingManager, logger, liveConfig });
10528
+ await registerScheduledTasks(powerManager, {
10529
+ definitionsDir,
10530
+ vaultDir,
10531
+ embeddingManager,
10532
+ logger,
10533
+ liveConfig,
10534
+ getTeamClient: () => teamSync.getTeamClient()
10535
+ });
8780
10536
  }
8781
10537
  reactions.on(["agent.tasks"], async () => {
8782
10538
  await syncScheduledTasks();
@@ -8830,7 +10586,7 @@ async function main() {
8830
10586
  server.registerRoute("POST", "/api/log", createLogIngestionHandler(logger));
8831
10587
  server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req));
8832
10588
  server.registerRoute("POST", "/api/restart", async (req) => handleRestart({ vaultDir, progressTracker }, req.body));
8833
- const updateProjectRoot = path22.dirname(vaultDir);
10589
+ const updateProjectRoot = path24.dirname(vaultDir);
8834
10590
  const updateHandlers = createUpdateHandlers({
8835
10591
  vaultDir,
8836
10592
  projectRoot: updateProjectRoot,
@@ -8853,6 +10609,7 @@ async function main() {
8853
10609
  server.registerRoute("GET", "/api/sessions/:id/impact", sessionMutations.handleGetSessionImpact);
8854
10610
  server.registerRoute("POST", "/api/sessions/:id/complete", sessionMutations.handleCompleteSession);
8855
10611
  server.registerRoute("DELETE", "/api/sessions/:id", sessionMutations.handleDeleteSession);
10612
+ server.registerRoute("DELETE", "/api/plans/:id", sessionMutations.handleDeletePlan);
8856
10613
  server.registerRoute("GET", "/api/sessions/:id/batches", handleGetSessionBatches);
8857
10614
  server.registerRoute("GET", "/api/batches/:id/activities", handleGetBatchActivities);
8858
10615
  server.registerRoute("GET", "/api/sessions/:id/attachments", handleGetSessionAttachments);
@@ -8873,12 +10630,27 @@ async function main() {
8873
10630
  server.registerRoute("GET", "/api/digest", handleGetDigest);
8874
10631
  const attachments = createAttachmentHandler({ vaultDir });
8875
10632
  server.registerRoute("GET", "/api/attachments/:filename", attachments.handleGetAttachment);
8876
- const agentRunHandlers = createAgentRunHandlers({ vaultDir, embeddingManager, logger });
10633
+ const agentRunHandlers = createAgentRunHandlers({
10634
+ vaultDir,
10635
+ embeddingManager,
10636
+ logger,
10637
+ getTeamClient: () => teamSync.getTeamClient()
10638
+ });
8877
10639
  server.registerRoute("POST", "/api/agent/run", agentRunHandlers.handleRun);
8878
10640
  server.registerRoute("GET", "/api/agent/runs", agentRunHandlers.handleListRuns);
8879
10641
  server.registerRoute("GET", "/api/agent/runs/:id", agentRunHandlers.handleGetRun);
10642
+ server.registerRoute("POST", "/api/agent/runs/:id/resume", agentRunHandlers.handleResumeRun);
8880
10643
  server.registerRoute("GET", "/api/agent/runs/:id/reports", agentRunHandlers.handleGetRunReports);
8881
10644
  server.registerRoute("GET", "/api/agent/runs/:id/turns", agentRunHandlers.handleGetRunTurns);
10645
+ server.registerRoute("GET", "/api/agent/runs/:id/write-intents", agentRunHandlers.handleGetRunWriteIntents);
10646
+ server.registerRoute("GET", "/api/agent/runs/:id/audit", agentRunHandlers.handleGetRunAudit);
10647
+ const agentEvalHandlers = createAgentEvaluationHandlers({ vaultDir, embeddingManager, logger });
10648
+ server.registerRoute("POST", "/api/agent/evaluations", agentEvalHandlers.handleCreate);
10649
+ server.registerRoute("GET", "/api/agent/evaluations", agentEvalHandlers.handleList);
10650
+ server.registerRoute("GET", "/api/agent/evaluations/:id", agentEvalHandlers.handleGet);
10651
+ const digestRevisionHandlers = createDigestRevisionHandlers({ vaultDir, logger });
10652
+ server.registerRoute("GET", "/api/digest/revisions", digestRevisionHandlers.handleList);
10653
+ server.registerRoute("POST", "/api/digest/revisions/:id/restore", digestRevisionHandlers.handleRestore);
8882
10654
  server.registerRoute("GET", "/api/agent/tasks", async (req) => handleListTasks(req, vaultDir));
8883
10655
  server.registerRoute("GET", "/api/agent/tasks/:id", async (req) => handleGetTask(req, vaultDir));
8884
10656
  server.registerRoute("GET", "/api/agent/tasks/:id/yaml", async (req) => handleGetTaskYaml(req, vaultDir));
@@ -8920,11 +10692,15 @@ async function main() {
8920
10692
  });
8921
10693
  server.registerRoute("GET", "/api/providers", async () => handleGetProviders());
8922
10694
  server.registerRoute("POST", "/api/providers/test", async (req) => handleTestProvider(req));
8923
- const mcpProxy = createMcpProxyHandlers({ machineId, embeddingManager });
10695
+ server.registerRoute("GET", "/api/providers/secrets", async () => handleGetProviderSecrets(vaultDir));
10696
+ server.registerRoute("PUT", "/api/providers/secrets/:provider", async (req) => handlePutProviderSecret(vaultDir, req));
10697
+ server.registerRoute("DELETE", "/api/providers/secrets/:provider", async (req) => handleDeleteProviderSecret(vaultDir, req));
10698
+ const mcpProxy = createMcpProxyHandlers({ machineId, embeddingManager, projectRoot, logger });
8924
10699
  server.registerRoute("POST", "/api/mcp/remember", mcpProxy.handleRemember);
8925
10700
  server.registerRoute("POST", "/api/mcp/supersede", mcpProxy.handleSupersede);
8926
10701
  server.registerRoute("POST", "/api/mcp/consolidate", mcpProxy.handleConsolidate);
8927
10702
  server.registerRoute("GET", "/api/mcp/plans", mcpProxy.handlePlans);
10703
+ server.registerRoute("POST", "/api/mcp/plans", mcpProxy.handleSavePlan);
8928
10704
  server.registerRoute("GET", "/api/mcp/sessions", mcpProxy.handleSessions);
8929
10705
  server.registerRoute("GET", "/api/mcp/team", mcpProxy.handleTeam);
8930
10706
  const backupHandlers = createBackupHandlers({ db, machineId, vaultDir, liveConfig });
@@ -8941,7 +10717,7 @@ async function main() {
8941
10717
  }
8942
10718
  return result;
8943
10719
  });
8944
- const teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
10720
+ teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
8945
10721
  reactions.on(["team"], async () => {
8946
10722
  await teamSync.reconcileClient();
8947
10723
  });
@@ -8950,7 +10726,7 @@ async function main() {
8950
10726
  vaultDir,
8951
10727
  machineId,
8952
10728
  logger,
8953
- getTeamClient: teamSync.getTeamClient,
10729
+ getTeamClient: () => teamSync.getTeamClient(),
8954
10730
  setTeamClient: teamSync.setTeamClient
8955
10731
  });
8956
10732
  server.registerRoute("POST", "/api/team/connect", async (req) => {
@@ -8973,14 +10749,14 @@ async function main() {
8973
10749
  server.registerRoute("POST", "/api/team/upgrade-worker", teamHandlers.handleUpgradeWorker);
8974
10750
  server.registerRoute("POST", "/api/team/rotate-mcp-token", teamHandlers.handleRotateMcpToken);
8975
10751
  const collectiveHandlers = createCollectiveHandlers({
8976
- getTeamClient: teamSync.getTeamClient
10752
+ getTeamClient: () => teamSync.getTeamClient()
8977
10753
  });
8978
10754
  server.registerRoute("GET", "/api/collective/status", collectiveHandlers.handleStatus);
8979
10755
  server.registerRoute("GET", "/api/collective/search", collectiveHandlers.handleSearch);
8980
10756
  server.registerRoute("GET", "/api/collective/projects", collectiveHandlers.handleProjects);
8981
10757
  server.registerRoute("GET", "/api/collective/project", collectiveHandlers.handleProject);
8982
10758
  server.registerRoute("GET", "/api/collective/settings", collectiveHandlers.handleSettings);
8983
- server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: teamSync.getTeamClient, machineId }));
10759
+ server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: () => teamSync.getTeamClient(), machineId }));
8984
10760
  server.registerRoute("GET", "/api/activity", handleGetFeed);
8985
10761
  server.registerRoute("GET", "/api/embedding/status", async () => handleGetEmbeddingStatus(vaultDir));
8986
10762
  server.registerRoute("GET", "/api/embedding/details", async () => handleEmbeddingDetails(embeddingManager));
@@ -9018,9 +10794,25 @@ async function main() {
9018
10794
  logger.warn(LOG_KINDS.DAEMON_CONFIG, "Failed to persist auto-derived port", { error: err.message });
9019
10795
  }
9020
10796
  }
9021
- registerPowerJobs(powerManager, { embeddingManager, registry, logger, liveConfig, db, machineId, vaultDir, databaseManager });
10797
+ registerPowerJobs(powerManager, {
10798
+ embeddingManager,
10799
+ registry,
10800
+ logger,
10801
+ liveConfig,
10802
+ db,
10803
+ machineId,
10804
+ vaultDir,
10805
+ databaseManager
10806
+ });
9022
10807
  teamSync.registerFlushJob(powerManager);
9023
- await registerScheduledTasks(powerManager, { definitionsDir, vaultDir, embeddingManager, logger, liveConfig });
10808
+ await registerScheduledTasks(powerManager, {
10809
+ definitionsDir,
10810
+ vaultDir,
10811
+ embeddingManager,
10812
+ logger,
10813
+ liveConfig,
10814
+ getTeamClient: () => teamSync.getTeamClient()
10815
+ });
9024
10816
  powerManager.start();
9025
10817
  const shutdown = async (signal) => {
9026
10818
  logger.info(LOG_KINDS.DAEMON_START, `${signal} received`);
@@ -9030,6 +10822,17 @@ async function main() {
9030
10822
  logger.info(LOG_KINDS.DAEMON_START, "Waiting for active stop processing to complete...");
9031
10823
  await activeStopProcessing;
9032
10824
  }
10825
+ if (inflightRuns.size > 0) {
10826
+ logger.info(LOG_KINDS.DAEMON_START, "Draining in-flight agent runs before shutdown...", {
10827
+ inflight_count: inflightRuns.size
10828
+ });
10829
+ const outcome = await inflightRuns.drain();
10830
+ if (!outcome.settled) {
10831
+ logger.warn(LOG_KINDS.DAEMON_START, "Some in-flight runs did not settle before shutdown timeout", {
10832
+ remaining: outcome.remaining
10833
+ });
10834
+ }
10835
+ }
9033
10836
  registry.destroy();
9034
10837
  await server.stop();
9035
10838
  vectorStore.close();
@@ -9052,4 +10855,4 @@ export {
9052
10855
  handleUserPrompt,
9053
10856
  main
9054
10857
  };
9055
- //# sourceMappingURL=main-YTBVRTBI.js.map
10858
+ //# sourceMappingURL=main-5PRQNEEE.js.map