@goondocks/myco 0.20.1 → 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-PQXC246Y.js → agent-run-2JSYFOKU.js} +10 -8
  4. package/dist/{agent-run-PQXC246Y.js.map → agent-run-2JSYFOKU.js.map} +1 -1
  5. package/dist/{agent-tasks-323BZEBX.js → agent-tasks-APFJIM2T.js} +10 -8
  6. package/dist/{agent-tasks-323BZEBX.js.map → agent-tasks-APFJIM2T.js.map} +1 -1
  7. package/dist/{chunk-SRXTSI25.js → chunk-53RPGOEN.js} +57 -9
  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-22JALAXN.js → chunk-75Z7UKDY.js} +4 -4
  17. package/dist/{chunk-AIYFHQRX.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-63ZGP4Q2.js → chunk-G6QIBNZM.js} +9 -6
  29. package/dist/{chunk-63ZGP4Q2.js.map → chunk-G6QIBNZM.js.map} +1 -1
  30. package/dist/{chunk-57O67XVF.js → chunk-ILJPRYES.js} +6 -4
  31. package/dist/{chunk-57O67XVF.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-UEWMSIL3.js → chunk-JR54LTPP.js} +4 -4
  35. package/dist/{chunk-4M7EWPIA.js → chunk-JZS6GZ6T.js} +16 -93
  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-3WWJOTYG.js → chunk-P66DLD6G.js} +22 -10
  43. package/dist/chunk-P66DLD6G.js.map +1 -0
  44. package/dist/{chunk-QS5TWZBL.js → chunk-R2JIJBCL.js} +18 -4
  45. package/dist/{chunk-QS5TWZBL.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-LZP4IJB3.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-UWBAOHLL.js → cli-LNYSTDQM.js} +49 -42
  57. package/dist/cli-LNYSTDQM.js.map +1 -0
  58. package/dist/{client-SLDDMSKN.js → client-NWE4TCNO.js} +4 -4
  59. package/dist/{config-XPV5GDE4.js → config-VC4ACP42.js} +6 -4
  60. package/dist/{config-XPV5GDE4.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-UUUOVDZS.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-UDH3ZBDD.js → init-KG3TYVGE.js} +14 -12
  66. package/dist/{init-UDH3ZBDD.js.map → init-KG3TYVGE.js.map} +1 -1
  67. package/dist/{installer-I6KRGJOO.js → installer-UMH7OJ5A.js} +6 -4
  68. package/dist/{llm-TH4NLIRM.js → llm-AGVEF5XD.js} +5 -4
  69. package/dist/{loader-H7OFASVC.js → loader-LX7TFRM6.js} +5 -3
  70. package/dist/{loader-TSB5M7FD.js → loader-NAVVZK63.js} +4 -3
  71. package/dist/{main-E7HU4QYR.js → main-5PRQNEEE.js} +2457 -649
  72. package/dist/main-5PRQNEEE.js.map +1 -0
  73. package/dist/{open-4UGDPBKN.js → open-5A27BCSB.js} +10 -8
  74. package/dist/{open-4UGDPBKN.js.map → open-5A27BCSB.js.map} +1 -1
  75. package/dist/{post-compact-BFU3WZSV.js → post-compact-USAODKPQ.js} +6 -6
  76. package/dist/{post-tool-use-RYHXLCTC.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-DCVHK2P3.js → post-tool-use-failure-NZVSL2PO.js} +6 -6
  79. package/dist/{pre-compact-EZZCJ6AG.js → pre-compact-LZ57DLUS.js} +6 -6
  80. package/dist/{provider-check-43LAMSMH.js → provider-check-ZEV5P4KM.js} +4 -4
  81. package/dist/{registry-MGJSJBAS.js → registry-M2Z5QBWH.js} +5 -4
  82. package/dist/{remove-GBBQG3SJ.js → remove-T3KE6C5N.js} +10 -8
  83. package/dist/{remove-GBBQG3SJ.js.map → remove-T3KE6C5N.js.map} +1 -1
  84. package/dist/{restart-RMIGKMA6.js → restart-YWDEVZUJ.js} +11 -9
  85. package/dist/{restart-RMIGKMA6.js.map → restart-YWDEVZUJ.js.map} +1 -1
  86. package/dist/{search-2A5AJVNG.js → search-GKFDGELR.js} +11 -9
  87. package/dist/{search-2A5AJVNG.js.map → search-GKFDGELR.js.map} +1 -1
  88. package/dist/{server-TSYZMFFK.js → server-AHUR6CWF.js} +368 -269
  89. package/dist/server-AHUR6CWF.js.map +1 -0
  90. package/dist/{session-NKREOTEO.js → session-2ZEPLWW6.js} +11 -9
  91. package/dist/{session-NKREOTEO.js.map → session-2ZEPLWW6.js.map} +1 -1
  92. package/dist/{session-end-DQELLTGJ.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-SLX5764H.js → setup-llm-E7UU5IO7.js} +11 -9
  96. package/dist/{setup-llm-SLX5764H.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 +5 -4
  104. package/dist/src/agent/definitions/tasks/skill-generate.yaml +2 -1
  105. package/dist/src/agent/definitions/tasks/skill-survey.yaml +25 -13
  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} +18 -83
  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-ARBLNEE3.js → stats-DFG6S23S.js} +11 -9
  124. package/dist/{stats-ARBLNEE3.js.map → stats-DFG6S23S.js.map} +1 -1
  125. package/dist/{stop-5AJXBKEE.js → stop-WRBTXEVT.js} +5 -5
  126. package/dist/{stop-failure-XGKTZPLR.js → stop-failure-32MGIG2Q.js} +6 -6
  127. package/dist/{subagent-start-2JLIPGJN.js → subagent-start-VFGHQFVL.js} +6 -6
  128. package/dist/{subagent-stop-FV4EOKWZ.js → subagent-stop-663FXG3P.js} +6 -6
  129. package/dist/{task-completed-XJKT366I.js → task-completed-ZCQYEFMZ.js} +6 -6
  130. package/dist/{team-3JKF7VAD.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-XVSAMN4O.js → update-3NBQTG32.js} +10 -8
  136. package/dist/{update-XVSAMN4O.js.map → update-3NBQTG32.js.map} +1 -1
  137. package/dist/{user-prompt-submit-TTBTHBBH.js → user-prompt-submit-ME2TBKOS.js} +8 -7
  138. package/dist/{user-prompt-submit-TTBTHBBH.js.map → user-prompt-submit-ME2TBKOS.js.map} +1 -1
  139. package/dist/{verify-LMHNEYIP.js → verify-R76ZFJSZ.js} +8 -5
  140. package/dist/{verify-LMHNEYIP.js.map → verify-R76ZFJSZ.js.map} +1 -1
  141. package/dist/{version-HRVSEMUR.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-3WWJOTYG.js.map +0 -1
  148. package/dist/chunk-4M7EWPIA.js.map +0 -1
  149. package/dist/chunk-4YFKBL3F.js +0 -195
  150. package/dist/chunk-4YFKBL3F.js.map +0 -1
  151. package/dist/chunk-EVDQKYCG.js.map +0 -1
  152. package/dist/chunk-FGY7J6EZ.js +0 -2276
  153. package/dist/chunk-FGY7J6EZ.js.map +0 -1
  154. package/dist/chunk-FLLBJLHM.js.map +0 -1
  155. package/dist/chunk-MYOZLMB2.js.map +0 -1
  156. package/dist/chunk-SRXTSI25.js.map +0 -1
  157. package/dist/chunk-US4LNCAT.js.map +0 -1
  158. package/dist/cli-UWBAOHLL.js.map +0 -1
  159. package/dist/doctor-UUUOVDZS.js.map +0 -1
  160. package/dist/executor-J4IBXSBY.js +0 -2472
  161. package/dist/executor-J4IBXSBY.js.map +0 -1
  162. package/dist/main-E7HU4QYR.js.map +0 -1
  163. package/dist/post-tool-use-RYHXLCTC.js.map +0 -1
  164. package/dist/server-TSYZMFFK.js.map +0 -1
  165. package/dist/session-start-5SUTR5GP.js +0 -189
  166. package/dist/session-start-5SUTR5GP.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-22JALAXN.js.map → chunk-75Z7UKDY.js.map} +0 -0
  172. /package/dist/{chunk-AIYFHQRX.js.map → chunk-BUTL6IFS.js.map} +0 -0
  173. /package/dist/{chunk-UEWMSIL3.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-LZP4IJB3.js.map → chunk-VHNRMM4O.js.map} +0 -0
  177. /package/dist/{client-SLDDMSKN.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-I6KRGJOO.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-H7OFASVC.js.map → llm-AGVEF5XD.js.map} +0 -0
  182. /package/dist/{loader-TSB5M7FD.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-BFU3WZSV.js.map → post-compact-USAODKPQ.js.map} +0 -0
  185. /package/dist/{post-tool-use-failure-DCVHK2P3.js.map → post-tool-use-failure-NZVSL2PO.js.map} +0 -0
  186. /package/dist/{pre-compact-EZZCJ6AG.js.map → pre-compact-LZ57DLUS.js.map} +0 -0
  187. /package/dist/{registry-MGJSJBAS.js.map → provider-check-ZEV5P4KM.js.map} +0 -0
  188. /package/dist/{team-3JKF7VAD.js.map → registry-M2Z5QBWH.js.map} +0 -0
  189. /package/dist/{session-end-DQELLTGJ.js.map → session-end-LWJYQAXX.js.map} +0 -0
  190. /package/dist/{stop-5AJXBKEE.js.map → stop-WRBTXEVT.js.map} +0 -0
  191. /package/dist/{stop-failure-XGKTZPLR.js.map → stop-failure-32MGIG2Q.js.map} +0 -0
  192. /package/dist/{subagent-start-2JLIPGJN.js.map → subagent-start-VFGHQFVL.js.map} +0 -0
  193. /package/dist/{subagent-stop-FV4EOKWZ.js.map → subagent-stop-663FXG3P.js.map} +0 -0
  194. /package/dist/{task-completed-XJKT366I.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-HRVSEMUR.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,20 @@ import {
15
18
  getEmbeddingQueueDepth,
16
19
  getUnembedded,
17
20
  markEmbedded
18
- } from "./chunk-22JALAXN.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-QS5TWZBL.js";
30
+ } from "./chunk-R2JIJBCL.js";
27
31
  import {
32
+ DEFAULT_OPENAI_URL,
33
+ DEFAULT_OPENROUTER_URL,
34
+ SKILL_SURVEY_TASK,
28
35
  buildTaskInstruction,
29
36
  closeOpenBatches,
30
37
  countBatchesBySession,
@@ -32,67 +39,82 @@ import {
32
39
  countNotifications,
33
40
  countRuns,
34
41
  countSkillRecords,
42
+ countWriteIntents,
43
+ countWriteIntentsByTool,
44
+ countWriteIntentsByToolForEvaluation,
35
45
  createBatchLineage,
46
+ createLocalOpenAIBackend,
36
47
  deleteCandidate,
37
48
  deleteSkillRecordCascade,
38
49
  dismissAllNotifications,
39
- errorMessage,
40
50
  findBatchByPromptPrefix,
41
51
  getAllDomains,
42
52
  getCandidate,
43
- getDigestExtract,
44
- getEntity,
45
53
  getGraphForNode,
46
54
  getLatestBatch,
55
+ getLatestOpenBatch,
56
+ getLatestResumableRunForTask,
47
57
  getLatestRunId,
58
+ getLocalOpenAIBackendDefaultBaseUrl,
59
+ getLocalOpenAIBackendLabel,
48
60
  getNotification,
49
61
  getRun,
50
62
  getSkillRecord,
51
63
  getSkillRecordByName,
64
+ getSkillSurveyEligibility,
52
65
  hasConfiguredProvider,
53
66
  incrementActivityCount,
54
67
  incrementSkillUsageCount,
68
+ inferLocalOpenAIBackendKind,
55
69
  insertBatchStateless,
56
70
  insertResolutionEvent,
57
71
  isInstructionRequiredTask,
58
72
  listBatchesBySession,
59
73
  listCandidatesWithCount,
60
- listDigestExtracts,
61
- listEntities,
62
74
  listLineageForSkill,
63
75
  listNotifications,
64
76
  listReports,
65
77
  listRuns,
78
+ listRunsForEvaluation,
66
79
  listSkillRecords,
67
80
  listSkillRecordsWithCount,
81
+ listWriteIntentTools,
82
+ listWriteIntents,
68
83
  markAllRead,
84
+ markRunningRunsInterrupted,
69
85
  notify,
70
86
  populateBatchResponses,
71
87
  register,
88
+ runAgent,
72
89
  setResponseSummary,
90
+ tryParseJson,
73
91
  updateCandidate,
74
92
  updateNotificationStatus
75
- } from "./chunk-FGY7J6EZ.js";
93
+ } from "./chunk-NGH7U6A3.js";
94
+ import {
95
+ errorMessage,
96
+ parseCheckpointState,
97
+ runDurationMs
98
+ } from "./chunk-6LB7XELY.js";
76
99
  import {
77
100
  fullTextSearch,
78
101
  hydrateSearchResults
79
- } from "./chunk-5XIVBO25.js";
102
+ } from "./chunk-LVIY7P35.js";
80
103
  import {
81
104
  copyTaskToUser,
82
105
  deleteUserTask,
83
106
  loadAllTasks,
84
107
  validateTaskName,
85
108
  writeUserTask
86
- } from "./chunk-57O67XVF.js";
109
+ } from "./chunk-ILJPRYES.js";
87
110
  import {
88
- AgentTaskSchema,
89
111
  registerAgent,
90
112
  resolveDefinitionsDir,
91
113
  taskFromParsed
92
- } from "./chunk-4M7EWPIA.js";
114
+ } from "./chunk-JZS6GZ6T.js";
93
115
  import {
94
116
  listTurnsByRun
95
- } from "./chunk-6RFZWV4R.js";
117
+ } from "./chunk-FCJ5JV54.js";
96
118
  import {
97
119
  cleanupStagedSkill,
98
120
  listStaleStagingDirs
@@ -100,7 +122,11 @@ import {
100
122
  import {
101
123
  Anthropic,
102
124
  createEmbeddingProvider
103
- } 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";
104
130
  import {
105
131
  getMachineId
106
132
  } from "./chunk-ENWBFX7F.js";
@@ -109,26 +135,42 @@ import {
109
135
  cleanStaleBuffers,
110
136
  listBufferSessionIds
111
137
  } from "./chunk-V7XG6V6C.js";
112
- import "./chunk-POEPHBQK.js";
113
- import "./chunk-UEWMSIL3.js";
138
+ import "./chunk-JR54LTPP.js";
114
139
  import "./chunk-SAKJMNSR.js";
115
140
  import {
116
141
  SymbiontInstaller
117
- } from "./chunk-LZP4IJB3.js";
142
+ } from "./chunk-VHNRMM4O.js";
118
143
  import {
119
144
  checkLocalProvider
120
- } from "./chunk-BPRIYNLE.js";
145
+ } from "./chunk-TKAJ3JVF.js";
121
146
  import {
122
147
  LmStudioBackend,
123
148
  OllamaBackend
124
- } from "./chunk-FMRZ26U5.js";
149
+ } from "./chunk-X3IGT5RV.js";
150
+ import {
151
+ composeSessionStartContext,
152
+ shouldInjectSessionStartDigest
153
+ } from "./chunk-DJ3IHNYO.js";
125
154
  import {
155
+ buildCortexInstructionsInput,
126
156
  countSpores,
157
+ deletePlan,
158
+ getCortexInstructions,
159
+ getPlan,
160
+ getPlanByLogicalKey,
127
161
  getSpore,
128
162
  insertSpore,
163
+ listDigestExtracts,
164
+ listDigestRevisions,
165
+ listPlans,
166
+ listPlansBySession,
129
167
  listSpores,
130
- updateSporeStatus
131
- } from "./chunk-4YFKBL3F.js";
168
+ resolveInstructionDelivery,
169
+ rollbackDigestExtract,
170
+ shouldInjectCortex,
171
+ updateSporeStatus,
172
+ upsertPlan
173
+ } from "./chunk-F3OEQYLS.js";
132
174
  import {
133
175
  backfillUnsynced,
134
176
  closeSession,
@@ -151,20 +193,29 @@ import {
151
193
  pruneOld,
152
194
  reactivateSessionIfCompleted,
153
195
  retryDeadLettered,
154
- syncRow,
155
196
  updateSession,
156
197
  upsertSession
157
- } from "./chunk-EVDQKYCG.js";
198
+ } from "./chunk-RQSJLWP4.js";
158
199
  import {
159
200
  evaluateSessionCaptureRules,
160
201
  readTranscriptMeta
161
202
  } from "./chunk-XL75KZGI.js";
203
+ import {
204
+ PLAN_STATUSES
205
+ } from "./chunk-CESKJD44.js";
162
206
  import {
163
207
  EMBEDDING_DIMENSIONS,
164
208
  REST_SETTABLE_STATUSES,
165
209
  SCHEMA_VERSION,
166
- createSchema
167
- } 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";
168
219
  import {
169
220
  CONFIG_FILENAME,
170
221
  MycoConfigSchema,
@@ -177,7 +228,11 @@ import {
177
228
  updateConfig,
178
229
  updateLocalConfig,
179
230
  updateTeamConfig
180
- } from "./chunk-SRXTSI25.js";
231
+ } from "./chunk-53RPGOEN.js";
232
+ import {
233
+ AgentTaskSchema
234
+ } from "./chunk-OUJSQSKE.js";
235
+ import "./chunk-POEPHBQK.js";
181
236
  import {
182
237
  closeDatabase,
183
238
  getDatabase,
@@ -189,10 +244,10 @@ import {
189
244
  } from "./chunk-ZXZPJJN3.js";
190
245
  import {
191
246
  resolveCliEntryPath
192
- } from "./chunk-3WWJOTYG.js";
247
+ } from "./chunk-P66DLD6G.js";
193
248
  import {
194
249
  getPluginVersion
195
- } from "./chunk-AIYFHQRX.js";
250
+ } from "./chunk-BUTL6IFS.js";
196
251
  import {
197
252
  loadManifests,
198
253
  resolvePackageRoot
@@ -208,6 +263,7 @@ import {
208
263
  DEFAULT_AGENT_ID,
209
264
  DEFAULT_LIST_LIMIT,
210
265
  DEFAULT_RELEASE_CHANNEL,
266
+ DEFAULT_SYMBIONT_NAME,
211
267
  EMBEDDING_BATCH_SIZE,
212
268
  EXCLUDED_SPORE_STATUSES,
213
269
  FEED_DEFAULT_LIMIT,
@@ -241,8 +297,10 @@ import {
241
297
  SYNC_PROTOCOL_VERSION,
242
298
  TEAM_API_KEY_SECRET,
243
299
  TEAM_HEALTH_TIMEOUT_MS,
300
+ TEAM_REQUEST_TIMEOUT_MS,
244
301
  TEAM_SEARCH_TIMEOUT_MS,
245
302
  TEAM_SOURCE_PREFIX,
303
+ TEAM_SYNC_TIMEOUT_MS,
246
304
  UPDATE_CHECK_CACHE_PATH,
247
305
  UPDATE_CHECK_INTERVAL_HOURS,
248
306
  UPDATE_CONFIG_PATH,
@@ -255,7 +313,7 @@ import {
255
313
  USER_TASK_SOURCE,
256
314
  epochSeconds,
257
315
  estimateTokens
258
- } from "./chunk-FLLBJLHM.js";
316
+ } from "./chunk-6C6QZ4PM.js";
259
317
  import {
260
318
  LOG_KINDS,
261
319
  kindToComponent
@@ -426,13 +484,24 @@ var DaemonServer = class {
426
484
  uptime: process.uptime()
427
485
  }
428
486
  }));
487
+ this.registerRoute("GET", "/api/version", async () => ({
488
+ body: { version: this.version }
489
+ }));
429
490
  }
430
491
  async handleRequest(req, res) {
431
492
  const match = this.router.match(req.method, req.url);
432
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
+ }
433
500
  this.onRequest?.();
501
+ const versionHeader = { "X-Myco-Api-Version": this.version };
434
502
  try {
435
- 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;
436
505
  const result = await match.handler({
437
506
  body,
438
507
  query: match.query,
@@ -441,11 +510,11 @@ var DaemonServer = class {
441
510
  });
442
511
  const status = result.status ?? DEFAULT_STATUS;
443
512
  if (Buffer.isBuffer(result.body)) {
444
- res.writeHead(status, result.headers ?? {});
513
+ res.writeHead(status, { ...versionHeader, ...result.headers });
445
514
  res.end(result.body);
446
515
  return;
447
516
  }
448
- const headers = { "Content-Type": "application/json", ...result.headers };
517
+ const headers = { "Content-Type": "application/json", ...versionHeader, ...result.headers };
449
518
  res.writeHead(status, headers);
450
519
  res.end(JSON.stringify(result.body));
451
520
  } catch (error) {
@@ -453,7 +522,7 @@ var DaemonServer = class {
453
522
  path: req.url,
454
523
  error: error.message
455
524
  });
456
- res.writeHead(500, { "Content-Type": "application/json" });
525
+ res.writeHead(500, { "Content-Type": "application/json", ...versionHeader });
457
526
  res.end(JSON.stringify({ error: error.message }));
458
527
  }
459
528
  return;
@@ -576,6 +645,36 @@ function readBody(req) {
576
645
  req.on("error", reject);
577
646
  });
578
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
+ }
579
678
 
580
679
  // src/daemon/lifecycle.ts
581
680
  var SessionRegistry = class {
@@ -2597,7 +2696,7 @@ var TeamSyncClient = class _TeamSyncClient {
2597
2696
  content_hash: data.content_hash ?? null
2598
2697
  };
2599
2698
  })
2600
- });
2699
+ }, { timeoutMs: TEAM_SYNC_TIMEOUT_MS });
2601
2700
  return res;
2602
2701
  }
2603
2702
  /**
@@ -2697,17 +2796,25 @@ var TeamSyncClient = class _TeamSyncClient {
2697
2796
  "Content-Type": "application/json"
2698
2797
  };
2699
2798
  }
2700
- async request(method, path23, body) {
2701
- const res = await this.fetchFn(`${this.workerUrl}${path23}`, {
2702
- method,
2703
- headers: this.headers(),
2704
- body: body !== void 0 ? JSON.stringify(body) : void 0
2705
- });
2706
- if (!res.ok) {
2707
- const text = await res.text().catch(() => "");
2708
- 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);
2709
2817
  }
2710
- return res.json();
2711
2818
  }
2712
2819
  };
2713
2820
 
@@ -2838,7 +2945,7 @@ function createTeamHandlers(deps) {
2838
2945
  return { body: { retried: count } };
2839
2946
  }
2840
2947
  async function handleUpgradeWorker(_req) {
2841
- const { upgradeWorker } = await import("./team-3JKF7VAD.js");
2948
+ const { upgradeWorker } = await import("./team-JTI5CDUO.js");
2842
2949
  logger.info("team-sync.upgrade.start", "Starting worker upgrade");
2843
2950
  const result = upgradeWorker(vaultDir);
2844
2951
  if (!result.success) {
@@ -3230,7 +3337,7 @@ function createSkillRecordDeleteHandler(deps) {
3230
3337
  logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill directory", { name: record.name, error: String(err) });
3231
3338
  }
3232
3339
  try {
3233
- const { syncSkillSymlinks } = await import("./installer-I6KRGJOO.js");
3340
+ const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
3234
3341
  syncSkillSymlinks(projectRoot, record.name, { remove: true });
3235
3342
  } catch (err) {
3236
3343
  logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill symlinks", { name: record.name, error: String(err) });
@@ -3449,6 +3556,10 @@ async function handleGetProgress(tracker, token) {
3449
3556
 
3450
3557
  // src/daemon/api/models.ts
3451
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;
3452
3563
  var ANTHROPIC_MODELS = [
3453
3564
  "claude-sonnet-4-6",
3454
3565
  "claude-opus-4-6",
@@ -3474,22 +3585,60 @@ function filterLlmModels(models) {
3474
3585
  return !EMBEDDING_PATTERNS.some((p) => name.includes(p));
3475
3586
  });
3476
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
+ }
3477
3621
  async function handleGetModels(req) {
3478
3622
  const provider = req.query.provider;
3479
3623
  const type = req.query.type;
3624
+ const localBackend = req.query.local_backend;
3480
3625
  if (!provider) {
3481
3626
  return { status: 400, body: { error: "provider query parameter required" } };
3482
3627
  }
3483
3628
  let models = [];
3484
3629
  try {
3485
- if (provider === "ollama") {
3486
- const backend = new OllamaBackend({ base_url: req.query.base_url });
3487
- models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
3488
- } else if (provider === "lm-studio" || provider === "openai-compatible") {
3489
- 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);
3490
3637
  models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
3491
3638
  } else if (provider === "anthropic") {
3492
3639
  models = ANTHROPIC_MODELS;
3640
+ } else if (provider === "openai" || provider === "openrouter") {
3641
+ models = await fetchRemoteProviderModels(provider, void 0, MODEL_LIST_TIMEOUT_MS);
3493
3642
  }
3494
3643
  } catch {
3495
3644
  }
@@ -3707,138 +3856,6 @@ function getAttachmentByFilePath(filePath) {
3707
3856
  return row ? toAttachmentRow(row) : null;
3708
3857
  }
3709
3858
 
3710
- // src/db/queries/plans.ts
3711
- var DEFAULT_LIST_LIMIT2 = 100;
3712
- var DEFAULT_STATUS2 = "active";
3713
- var DEFAULT_PROCESSED2 = 0;
3714
- var PLAN_COLUMNS = [
3715
- "id",
3716
- "status",
3717
- "author",
3718
- "title",
3719
- "content",
3720
- "source_path",
3721
- "tags",
3722
- "session_id",
3723
- "prompt_batch_id",
3724
- "content_hash",
3725
- "processed",
3726
- "embedded",
3727
- "created_at",
3728
- "updated_at",
3729
- "machine_id",
3730
- "synced_at"
3731
- ];
3732
- var SELECT_COLUMNS4 = PLAN_COLUMNS.join(", ");
3733
- function toPlanRow(row) {
3734
- return {
3735
- id: row.id,
3736
- status: row.status,
3737
- author: row.author ?? null,
3738
- title: row.title ?? null,
3739
- content: row.content ?? null,
3740
- source_path: row.source_path ?? null,
3741
- tags: row.tags ?? null,
3742
- session_id: row.session_id ?? null,
3743
- prompt_batch_id: row.prompt_batch_id ?? null,
3744
- content_hash: row.content_hash ?? null,
3745
- processed: row.processed,
3746
- embedded: row.embedded ?? 0,
3747
- created_at: row.created_at,
3748
- updated_at: row.updated_at ?? null,
3749
- machine_id: row.machine_id ?? "local",
3750
- synced_at: row.synced_at ?? null
3751
- };
3752
- }
3753
- function upsertPlan(data) {
3754
- const db = getDatabase();
3755
- db.prepare(
3756
- `INSERT INTO plans (
3757
- id, status, author, title, content,
3758
- source_path, tags, session_id, prompt_batch_id, content_hash,
3759
- processed, created_at, updated_at, machine_id
3760
- ) VALUES (
3761
- ?, ?, ?, ?, ?,
3762
- ?, ?, ?, ?, ?,
3763
- ?, ?, ?, ?
3764
- )
3765
- ON CONFLICT (id) DO UPDATE SET
3766
- status = EXCLUDED.status,
3767
- author = EXCLUDED.author,
3768
- title = EXCLUDED.title,
3769
- content = EXCLUDED.content,
3770
- source_path = EXCLUDED.source_path,
3771
- tags = EXCLUDED.tags,
3772
- session_id = EXCLUDED.session_id,
3773
- prompt_batch_id = EXCLUDED.prompt_batch_id,
3774
- content_hash = EXCLUDED.content_hash,
3775
- processed = EXCLUDED.processed,
3776
- updated_at = EXCLUDED.updated_at,
3777
- embedded = CASE
3778
- WHEN EXCLUDED.content_hash != plans.content_hash THEN 0
3779
- ELSE plans.embedded
3780
- END`
3781
- ).run(
3782
- data.id,
3783
- data.status ?? DEFAULT_STATUS2,
3784
- data.author ?? null,
3785
- data.title ?? null,
3786
- data.content ?? null,
3787
- data.source_path ?? null,
3788
- data.tags ?? null,
3789
- data.session_id ?? null,
3790
- data.prompt_batch_id ?? null,
3791
- data.content_hash ?? null,
3792
- data.processed ?? DEFAULT_PROCESSED2,
3793
- data.created_at,
3794
- data.updated_at ?? null,
3795
- data.machine_id ?? getTeamMachineId()
3796
- );
3797
- const row = toPlanRow(
3798
- db.prepare(`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`).get(data.id)
3799
- );
3800
- syncRow("plans", row);
3801
- return row;
3802
- }
3803
- function getPlan(id) {
3804
- const db = getDatabase();
3805
- const row = db.prepare(
3806
- `SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`
3807
- ).get(id);
3808
- if (!row) return null;
3809
- return toPlanRow(row);
3810
- }
3811
- function listPlans(options = {}) {
3812
- const db = getDatabase();
3813
- const conditions = [];
3814
- const params = [];
3815
- if (options.status !== void 0) {
3816
- conditions.push(`status = ?`);
3817
- params.push(options.status);
3818
- }
3819
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3820
- const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
3821
- params.push(limit);
3822
- const rows = db.prepare(
3823
- `SELECT ${SELECT_COLUMNS4}
3824
- FROM plans
3825
- ${where}
3826
- ORDER BY created_at DESC
3827
- LIMIT ?`
3828
- ).all(...params);
3829
- return rows.map(toPlanRow);
3830
- }
3831
- function listPlansBySession(sessionId) {
3832
- const db = getDatabase();
3833
- const rows = db.prepare(
3834
- `SELECT ${SELECT_COLUMNS4}
3835
- FROM plans
3836
- WHERE session_id = ?
3837
- ORDER BY created_at DESC`
3838
- ).all(sessionId);
3839
- return rows.map(toPlanRow);
3840
- }
3841
-
3842
3859
  // src/daemon/jobs/session-cleanup.ts
3843
3860
  import { unlink, glob } from "fs/promises";
3844
3861
  async function cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir) {
@@ -3883,8 +3900,8 @@ async function triggerTitleSummary(sessionId, deps) {
3883
3900
  if (config.agent.summary_batch_interval <= 0) return;
3884
3901
  if (config.agent.event_tasks_enabled === false) return;
3885
3902
  try {
3886
- const { runAgent } = await import("./executor-J4IBXSBY.js");
3887
- runAgent(vaultDir, {
3903
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
3904
+ runAgent2(vaultDir, {
3888
3905
  task: "title-summary",
3889
3906
  instruction: `Process session ${sessionId} only`,
3890
3907
  embeddingManager
@@ -3898,11 +3915,16 @@ async function triggerTitleSummary(sessionId, deps) {
3898
3915
  }
3899
3916
  }
3900
3917
 
3918
+ // src/daemon/api/error-envelope.ts
3919
+ function errorBody(code, message) {
3920
+ return { error: { code, message } };
3921
+ }
3922
+
3901
3923
  // src/daemon/api/sessions.ts
3902
- var DEFAULT_LIST_LIMIT3 = 50;
3924
+ var DEFAULT_LIST_LIMIT2 = 50;
3903
3925
  var DEFAULT_LIST_OFFSET2 = 0;
3904
3926
  async function handleListSessions(req) {
3905
- 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;
3906
3928
  const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET2;
3907
3929
  const status = req.query.status || void 0;
3908
3930
  const agent = req.query.agent || void 0;
@@ -3945,7 +3967,7 @@ async function handleGetSessionAttachments(req) {
3945
3967
  }
3946
3968
  async function handleGetSessionPlans(req) {
3947
3969
  const plans = listPlansBySession(req.params.id);
3948
- return { body: plans };
3970
+ return { body: { plans } };
3949
3971
  }
3950
3972
  function createSessionMutationHandlers(deps) {
3951
3973
  const { embeddingManager, vaultDir, logger, liveConfig } = deps;
@@ -3986,7 +4008,125 @@ function createSessionMutationHandlers(deps) {
3986
4008
  const impact = getSessionImpact(sessionId);
3987
4009
  return { body: impact };
3988
4010
  }
3989
- 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);
3990
4130
  }
3991
4131
 
3992
4132
  // src/daemon/api/mycelium.ts
@@ -3995,7 +4135,6 @@ var DEFAULT_LIST_OFFSET3 = 0;
3995
4135
  var DEFAULT_GRAPH_DEPTH = 1;
3996
4136
  var MAX_GRAPH_DEPTH = 3;
3997
4137
  var SPORE_NAME_PREVIEW_CHARS = 60;
3998
- var GRAPH_SEED_ENTITY_LIMIT = 4;
3999
4138
  var GRAPH_SEED_SPORE_LIMIT = 4;
4000
4139
  var GRAPH_SEED_SESSION_LIMIT = 4;
4001
4140
  var EXCLUDED_GRAPH_EDGE_TYPES = /* @__PURE__ */ new Set(["HAS_BATCH", "EXTRACTED_FROM"]);
@@ -4054,15 +4193,6 @@ async function handleGetGraphSeeds(_req) {
4054
4193
  ORDER BY started_at DESC
4055
4194
  LIMIT ?`
4056
4195
  ).all(GRAPH_SEED_SESSION_LIMIT);
4057
- const entityRows = db.prepare(
4058
- `SELECT e.id, e.type, e.name, e.status, e.first_seen as created_at, COUNT(em.entity_id) as mention_count
4059
- FROM entities e
4060
- LEFT JOIN entity_mentions em ON em.entity_id = e.id
4061
- WHERE e.agent_id = ? AND e.status = 'active'
4062
- GROUP BY e.id
4063
- ORDER BY mention_count DESC, e.last_seen DESC
4064
- LIMIT ?`
4065
- ).all(DEFAULT_AGENT_ID, GRAPH_SEED_ENTITY_LIMIT);
4066
4196
  const sporeSeeds = sporeRows.map((row) => ({
4067
4197
  id: row.id,
4068
4198
  name: (row.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4080,20 +4210,66 @@ async function handleGetGraphSeeds(_req) {
4080
4210
  created_at: row.created_at,
4081
4211
  content: row.summary ?? void 0
4082
4212
  }));
4083
- const entitySeeds = entityRows.map((row) => ({
4084
- id: row.id,
4085
- name: row.name,
4086
- type: row.type,
4087
- status: row.status ?? void 0,
4088
- created_at: row.created_at,
4089
- mention_count: Number(row.mention_count) || 0
4090
- }));
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
+ }
4091
4267
  const seeds = [
4092
- ...entitySeeds,
4093
- ...sessionSeeds,
4094
- ...sporeSeeds
4268
+ ...topSeed ? [topSeed] : [],
4269
+ ...sessionSeeds.filter((s) => s.id !== topSeed?.id),
4270
+ ...sporeSeeds.filter((s) => s.id !== topSeed?.id)
4095
4271
  ];
4096
- const recommendedId = entitySeeds[0]?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
4272
+ const recommendedId = topSeed?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
4097
4273
  return {
4098
4274
  body: {
4099
4275
  seeds,
@@ -4104,32 +4280,19 @@ async function handleGetGraphSeeds(_req) {
4104
4280
  async function handleGetGraph(req) {
4105
4281
  const depth = Math.min(Number(req.query.depth) || DEFAULT_GRAPH_DEPTH, MAX_GRAPH_DEPTH);
4106
4282
  const id = req.params.id;
4107
- let centerNode = null;
4108
- let centerType = "entity";
4109
- const entity = getEntity(id);
4110
- if (entity) {
4111
- centerNode = entity;
4112
- centerType = "entity";
4283
+ let centerType;
4284
+ if (getSpore(id)) {
4285
+ centerType = "spore";
4286
+ } else if (getSession(id)) {
4287
+ centerType = "session";
4113
4288
  } else {
4114
- const spore = getSpore(id);
4115
- if (spore) {
4116
- centerNode = spore;
4117
- centerType = "spore";
4118
- } else {
4119
- const session = getSession(id);
4120
- if (session) {
4121
- centerNode = session;
4122
- centerType = "session";
4123
- }
4124
- }
4289
+ return { status: 404, body: { error: "not_found" } };
4125
4290
  }
4126
- if (!centerNode) return { status: 404, body: { error: "not_found" } };
4127
4291
  const graph = getGraphForNode(id, centerType, { depth });
4128
4292
  const filteredEdges = graph.edges.filter(
4129
4293
  (e) => !EXCLUDED_GRAPH_EDGE_TYPES.has(e.type)
4130
4294
  );
4131
4295
  const graphDb = getDatabase();
4132
- const entityIds = /* @__PURE__ */ new Set();
4133
4296
  const sporeIds = /* @__PURE__ */ new Set();
4134
4297
  const sessionIds = /* @__PURE__ */ new Set();
4135
4298
  for (const edge of filteredEdges) {
@@ -4137,70 +4300,23 @@ async function handleGetGraph(req) {
4137
4300
  [edge.source_id, edge.source_type],
4138
4301
  [edge.target_id, edge.target_type]
4139
4302
  ]) {
4140
- switch (type) {
4141
- case "entity":
4142
- entityIds.add(nodeId);
4143
- break;
4144
- case "spore":
4145
- sporeIds.add(nodeId);
4146
- break;
4147
- case "session":
4148
- sessionIds.add(nodeId);
4149
- break;
4150
- }
4303
+ if (type === "spore") sporeIds.add(nodeId);
4304
+ else if (type === "session") sessionIds.add(nodeId);
4151
4305
  }
4152
4306
  }
4153
- if (centerType === "entity") entityIds.add(id);
4154
4307
  if (centerType === "spore") sporeIds.add(id);
4155
- if (centerType === "session") sessionIds.add(id);
4156
- const entityIdArray = Array.from(entityIds);
4157
- let entityNodes = [];
4158
- if (entityIdArray.length > 0) {
4159
- const placeholders = entityIdArray.map(() => "?").join(", ");
4160
- entityNodes = graphDb.prepare(
4161
- `SELECT id, type, name, properties, status, first_seen as created_at
4162
- FROM entities WHERE id IN (${placeholders})`
4163
- ).all(...entityIdArray);
4164
- }
4308
+ else sessionIds.add(id);
4165
4309
  const sporeIdArray = Array.from(sporeIds);
4166
- let sporeNodes = [];
4167
- if (sporeIdArray.length > 0) {
4168
- const placeholders = sporeIdArray.map(() => "?").join(", ");
4169
- sporeNodes = graphDb.prepare(
4170
- `SELECT id, observation_type, status, content, properties, created_at
4171
- FROM spores WHERE id IN (${placeholders})`
4172
- ).all(...sporeIdArray);
4173
- }
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) : [];
4174
4314
  const sessionIdArray = Array.from(sessionIds);
4175
- let sessionNodes = [];
4176
- if (sessionIdArray.length > 0) {
4177
- const placeholders = sessionIdArray.map(() => "?").join(", ");
4178
- sessionNodes = graphDb.prepare(
4179
- `SELECT id, title, summary, status, started_at as created_at
4180
- FROM sessions WHERE id IN (${placeholders})`
4181
- ).all(...sessionIdArray);
4182
- }
4183
- const mentionCounts = /* @__PURE__ */ new Map();
4184
- if (entityIdArray.length > 0) {
4185
- const placeholders = entityIdArray.map(() => "?").join(", ");
4186
- const mentionRows = graphDb.prepare(
4187
- `SELECT entity_id, COUNT(*) as count FROM entity_mentions
4188
- WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
4189
- ).all(...entityIdArray);
4190
- for (const row of mentionRows) {
4191
- mentionCounts.set(row.entity_id, Number(row.count));
4192
- }
4193
- }
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) : [];
4194
4319
  const allNodes = [
4195
- ...entityNodes.map((n) => ({
4196
- id: n.id,
4197
- name: n.name,
4198
- type: n.type,
4199
- status: n.status ?? void 0,
4200
- created_at: n.created_at,
4201
- properties: n.properties ?? void 0,
4202
- mention_count: mentionCounts.get(n.id) ?? 0
4203
- })),
4204
4320
  ...sporeNodes.map((n) => ({
4205
4321
  id: n.id,
4206
4322
  name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4239,10 +4355,6 @@ async function handleGetGraph(req) {
4239
4355
  var FULL_GRAPH_NODE_LIMIT = 500;
4240
4356
  async function handleGetFullGraph(_req) {
4241
4357
  const db = getDatabase();
4242
- const entityRows = db.prepare(
4243
- `SELECT id, type, name, properties, status, first_seen as created_at
4244
- FROM entities WHERE agent_id = ? LIMIT ?`
4245
- ).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT);
4246
4358
  const sporeRows = db.prepare(
4247
4359
  `SELECT id, observation_type, status, content, properties, created_at
4248
4360
  FROM spores WHERE agent_id = ? AND status = 'active' LIMIT ?`
@@ -4252,43 +4364,21 @@ async function handleGetFullGraph(_req) {
4252
4364
  FROM sessions ORDER BY created_at DESC LIMIT ?`
4253
4365
  ).all(FULL_GRAPH_NODE_LIMIT);
4254
4366
  const allIds = /* @__PURE__ */ new Set();
4255
- for (const r of [...entityRows, ...sporeRows, ...sessionRows]) {
4367
+ for (const r of [...sporeRows, ...sessionRows]) {
4256
4368
  allIds.add(r.id);
4257
4369
  }
4258
4370
  const excludedTypes = Array.from(EXCLUDED_GRAPH_EDGE_TYPES).map(() => "?").join(", ");
4259
4371
  const allIdsList = Array.from(allIds);
4260
4372
  const idPlaceholders = allIdsList.map(() => "?").join(", ");
4261
- const edgeRows = db.prepare(
4373
+ const edgeRows = allIdsList.length > 0 ? db.prepare(
4262
4374
  `SELECT source_id, source_type, target_id, target_type, type, confidence
4263
- FROM graph_edges
4264
- WHERE agent_id = ?
4265
- AND type NOT IN (${excludedTypes})
4266
- AND source_id IN (${idPlaceholders})
4267
- AND target_id IN (${idPlaceholders})`
4268
- ).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList);
4269
- const filteredEdges = edgeRows;
4270
- const mentionCounts = /* @__PURE__ */ new Map();
4271
- const entityIdArray = entityRows.map((r) => r.id);
4272
- if (entityIdArray.length > 0) {
4273
- const placeholders = entityIdArray.map(() => "?").join(", ");
4274
- const mentionRows = db.prepare(
4275
- `SELECT entity_id, COUNT(*) as count FROM entity_mentions
4276
- WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
4277
- ).all(...entityIdArray);
4278
- for (const row of mentionRows) {
4279
- mentionCounts.set(row.entity_id, Number(row.count));
4280
- }
4281
- }
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) : [];
4282
4381
  const nodes = [
4283
- ...entityRows.map((n) => ({
4284
- id: n.id,
4285
- name: n.name,
4286
- type: n.type,
4287
- status: n.status ?? void 0,
4288
- created_at: n.created_at,
4289
- properties: n.properties ?? void 0,
4290
- mention_count: mentionCounts.get(n.id) ?? 0
4291
- })),
4292
4382
  ...sporeRows.map((n) => ({
4293
4383
  id: n.id,
4294
4384
  name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
@@ -4308,7 +4398,7 @@ async function handleGetFullGraph(_req) {
4308
4398
  content: n.summary ?? void 0
4309
4399
  }))
4310
4400
  ];
4311
- const edges = filteredEdges.map((e) => ({
4401
+ const edges = edgeRows.map((e) => ({
4312
4402
  source_id: e.source_id,
4313
4403
  target_id: e.target_id,
4314
4404
  label: e.type,
@@ -4391,75 +4481,305 @@ function createSearchHandler(deps) {
4391
4481
  };
4392
4482
  }
4393
4483
 
4394
- // src/daemon/api/context.ts
4395
- var SessionContextBody = external_exports.object({
4396
- session_id: external_exports.string().optional(),
4397
- branch: external_exports.string().optional()
4398
- });
4399
- var ResumeContextBody = external_exports.object({
4400
- session_id: external_exports.string(),
4401
- parent_session_id: external_exports.string().optional(),
4402
- branch: external_exports.string().optional()
4403
- });
4404
- var PromptContextBody = external_exports.object({
4405
- prompt: external_exports.string(),
4406
- session_id: external_exports.string().optional()
4407
- });
4408
- function createSessionContextHandler(deps) {
4409
- return async function handleSessionContext(req) {
4410
- const { session_id, branch } = SessionContextBody.parse(req.body);
4411
- const { logger, liveConfig } = deps;
4412
- const config = liveConfig.current;
4413
- logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
4414
- try {
4415
- const parts = [];
4416
- const tier = config.context.digest_tier;
4417
- const extract = getDigestExtract(DEFAULT_AGENT_ID, tier);
4418
- if (extract) {
4419
- parts.push(extract.content);
4420
- logger.info(LOG_KINDS.CONTEXT_DIGEST, "Digest extract found", {
4421
- session_id,
4422
- tier,
4423
- content_length: extract.content.length,
4424
- generated_at: extract.generated_at
4425
- });
4426
- } else {
4427
- logger.debug(LOG_KINDS.CONTEXT_DIGEST, "No digest extract available", { session_id, tier });
4428
- }
4429
- if (branch) {
4430
- parts.push(`Branch:: \`${branch}\``);
4431
- }
4432
- parts.push(`Session:: \`${session_id}\``);
4433
- const source = extract ? "digest" : "basic";
4434
- const contextText = parts.join("\n\n");
4435
- const estimatedTokens = estimateTokens(contextText);
4436
- logger.info(
4437
- LOG_KINDS.CONTEXT_SESSION,
4438
- `Session context: ${estimatedTokens} est. tokens, source=${source}${extract ? `, tier=${tier}` : ""}`,
4439
- {
4440
- session_id,
4441
- source,
4442
- tier: extract ? tier : void 0,
4443
- text_length: contextText.length,
4444
- estimated_tokens: estimatedTokens,
4445
- generated_at: extract?.generated_at,
4446
- injected_text: contextText
4447
- }
4448
- );
4449
- return {
4450
- body: {
4451
- text: contextText,
4452
- source,
4453
- ...extract ? { tier } : {}
4454
- }
4455
- };
4456
- } catch (error) {
4457
- logger.error(LOG_KINDS.CONTEXT_SESSION, "Session context failed", { error: error.message });
4458
- 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");
4459
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)
4460
4513
  };
4461
4514
  }
4462
- 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) {
4463
4783
  return async function handleResumeContext(req) {
4464
4784
  const { session_id, parent_session_id, branch } = ResumeContextBody.parse(req.body);
4465
4785
  const { logger } = deps;
@@ -4555,9 +4875,7 @@ function createPromptContextHandler(deps) {
4555
4875
  raw_results: vectorResults.length,
4556
4876
  top_similarity: vectorResults[0]?.similarity
4557
4877
  });
4558
- if (vectorResults.length === 0) {
4559
- return { body: { text: "" } };
4560
- }
4878
+ if (vectorResults.length === 0) return { body: { text: "" } };
4561
4879
  const eligible = vectorResults.filter(
4562
4880
  (r) => !EXCLUDED_SPORE_STATUSES.has(r.metadata.status)
4563
4881
  );
@@ -4568,20 +4886,22 @@ function createPromptContextHandler(deps) {
4568
4886
  const topResults = eligible.slice(0, maxSpores);
4569
4887
  const hydrated = hydrateSearchResults(topResults);
4570
4888
  const spores = hydrated.filter((r) => r.type === "spore");
4571
- if (spores.length === 0) {
4572
- return { body: { text: "" } };
4573
- }
4889
+ if (spores.length === 0) return { body: { text: "" } };
4574
4890
  const text = formatSporeContext(spores);
4575
4891
  const promptTokens = estimateTokens(text);
4576
4892
  const titles = spores.map((s) => s.title);
4577
- logger.info(LOG_KINDS.CONTEXT_PROMPT, `Prompt context: ${spores.length} spores [${titles.join(", ")}] (~${promptTokens} tokens)`, {
4578
- session_id,
4579
- spore_count: spores.length,
4580
- spore_titles: titles,
4581
- scores: spores.map((s) => s.score.toFixed(3)),
4582
- estimated_tokens: promptTokens,
4583
- injected_text: text
4584
- });
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
+ );
4585
4905
  return { body: { text } };
4586
4906
  };
4587
4907
  }
@@ -4600,6 +4920,75 @@ function formatSporeContext(spores) {
4600
4920
  return text === header ? "" : text;
4601
4921
  }
4602
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
+
4603
4992
  // src/db/queries/feed.ts
4604
4993
  function getActivityFeed(limit = FEED_DEFAULT_LIMIT) {
4605
4994
  const db = getDatabase();
@@ -4638,24 +5027,6 @@ async function handleGetFeed(req) {
4638
5027
  return { body: feed };
4639
5028
  }
4640
5029
 
4641
- // src/daemon/api/symbionts.ts
4642
- async function handleListSymbionts(vaultDir) {
4643
- const manifests = loadManifests();
4644
- let enabledNames = null;
4645
- try {
4646
- enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
4647
- } catch {
4648
- }
4649
- const symbionts = manifests.map((m) => ({
4650
- name: m.name,
4651
- displayName: m.displayName,
4652
- binary: m.binary,
4653
- enabled: enabledNames ? enabledNames.has(m.name) : true,
4654
- ...m.resumeCommand ? { resumeCommand: m.resumeCommand } : {}
4655
- }));
4656
- return { body: { symbionts } };
4657
- }
4658
-
4659
5030
  // src/daemon/api/embedding.ts
4660
5031
  var EMBEDDING_STATUS_IDLE = "idle";
4661
5032
  var EMBEDDING_STATUS_PENDING = "pending";
@@ -5659,17 +6030,17 @@ var SqliteRecordSource = class {
5659
6030
  };
5660
6031
 
5661
6032
  // src/daemon/database/manager.ts
5662
- import fs17 from "fs";
6033
+ import fs18 from "fs";
5663
6034
 
5664
6035
  // src/db/queries/database.ts
5665
- import fs16 from "fs";
6036
+ import fs17 from "fs";
5666
6037
  function pragmaScalar(name) {
5667
6038
  const db = getDatabase();
5668
6039
  return db.pragma(name, { simple: true });
5669
6040
  }
5670
6041
  function safeFileSize(filePath) {
5671
6042
  try {
5672
- return fs16.statSync(filePath).size;
6043
+ return fs17.statSync(filePath).size;
5673
6044
  } catch (err) {
5674
6045
  if (err.code === "ENOENT") return 0;
5675
6046
  throw err;
@@ -5941,7 +6312,7 @@ var DatabaseMaintenanceManager = class {
5941
6312
  }
5942
6313
  async vacuum() {
5943
6314
  const size_before = this.fileSize();
5944
- const stats = await fs17.promises.statfs(this.vaultDir);
6315
+ const stats = await fs18.promises.statfs(this.vaultDir);
5945
6316
  const free_bytes = Number(stats.bavail) * Number(stats.bsize);
5946
6317
  const required_bytes = size_before * VACUUM_FREE_SPACE_MULTIPLIER;
5947
6318
  if (free_bytes < required_bytes) {
@@ -5989,7 +6360,7 @@ var DatabaseMaintenanceManager = class {
5989
6360
  }
5990
6361
  fileSize() {
5991
6362
  try {
5992
- return fs17.statSync(this.dbPath).size;
6363
+ return fs18.statSync(this.dbPath).size;
5993
6364
  } catch {
5994
6365
  return 0;
5995
6366
  }
@@ -6306,6 +6677,16 @@ async function handleUpdateTaskConfig(req, vaultDir) {
6306
6677
  };
6307
6678
  }
6308
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
+
6309
6690
  // src/daemon/api/providers.ts
6310
6691
  var ANTHROPIC_MODELS_TIMEOUT_MS = 5e3;
6311
6692
  var ANTHROPIC_MODELS_CACHE_TTL_MS = 10 * 60 * 1e3;
@@ -6313,33 +6694,79 @@ var anthropicModelsCache = null;
6313
6694
  var HTTP_OK2 = 200;
6314
6695
  var HTTP_BAD_REQUEST2 = 400;
6315
6696
  async function handleGetProviders() {
6316
- const results = await Promise.allSettled([
6317
- detectAnthropic(),
6318
- detectLocalProviderInfo("ollama", OllamaBackend.DEFAULT_BASE_URL),
6319
- detectLocalProviderInfo("lmstudio", LmStudioBackend.DEFAULT_BASE_URL)
6320
- ]);
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()));
6321
6724
  const providers = results.map(
6322
- (r) => r.status === "fulfilled" ? r.value : { type: "unknown", available: false, models: [] }
6725
+ (result, index) => result.status === "fulfilled" ? result.value : detectionPlan[index].fallback
6323
6726
  );
6324
6727
  return { status: HTTP_OK2, body: { providers } };
6325
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
+ });
6326
6740
  async function handleTestProvider(req) {
6327
- const body = req.body;
6328
- const type = body?.type;
6329
- if (!type || !["anthropic", "ollama", "lmstudio"].includes(type)) {
6741
+ const parse = ProviderTestBody.safeParse(req.body);
6742
+ if (!parse.success) {
6330
6743
  return {
6331
6744
  status: HTTP_BAD_REQUEST2,
6332
- 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(", ")}` }
6333
6746
  };
6334
6747
  }
6335
- 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;
6336
6757
  const start = performance.now();
6337
6758
  let result;
6338
6759
  try {
6339
6760
  if (type === "ollama") {
6340
- result = await testLocalProvider(new OllamaBackend({ base_url: baseUrl }), "Ollama", OllamaBackend.DEFAULT_BASE_URL, baseUrl);
6761
+ result = await testResolvedLocalProvider("ollama", baseUrl);
6341
6762
  } else if (type === "lmstudio") {
6342
- 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");
6343
6770
  } else {
6344
6771
  result = testAnthropic();
6345
6772
  }
@@ -6352,15 +6779,21 @@ async function handleTestProvider(req) {
6352
6779
  return { status: HTTP_OK2, body: result };
6353
6780
  }
6354
6781
  async function detectLocalProviderInfo(type, defaultBaseUrl) {
6355
- const status = await checkLocalProvider(type);
6782
+ const status = await checkLocalProvider(type === "openai-compatible" ? "lmstudio" : type);
6356
6783
  const variantFiltered = status.models.filter((m) => !/-ctx\d+/.test(m));
6357
6784
  const models = filterLlmModels(variantFiltered);
6358
- 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
+ };
6359
6792
  }
6360
6793
  async function detectAnthropic() {
6361
6794
  const now = Date.now();
6362
6795
  if (anthropicModelsCache && now - anthropicModelsCache.ts < ANTHROPIC_MODELS_CACHE_TTL_MS) {
6363
- return { type: "anthropic", available: true, models: anthropicModelsCache.models };
6796
+ return { type: "anthropic", runtime: "claude-sdk", available: true, models: anthropicModelsCache.models };
6364
6797
  }
6365
6798
  let models = ANTHROPIC_MODELS;
6366
6799
  try {
@@ -6376,7 +6809,28 @@ async function detectAnthropic() {
6376
6809
  } catch {
6377
6810
  }
6378
6811
  anthropicModelsCache = { ts: now, models };
6379
- 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
+ };
6380
6834
  }
6381
6835
  async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
6382
6836
  const available = await backend.isAvailable();
@@ -6385,9 +6839,114 @@ async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
6385
6839
  }
6386
6840
  return { ok: true };
6387
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
+ }
6388
6852
  function testAnthropic() {
6389
6853
  return { ok: true };
6390
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
+ }
6391
6950
 
6392
6951
  // src/daemon/task-scheduling.ts
6393
6952
  import { resolve } from "path";
@@ -6440,7 +6999,7 @@ function buildScheduledJobs(tasks, configOverrides, context, initialLastRuns) {
6440
6999
  // src/daemon/task-scheduling.ts
6441
7000
  var SCHEDULED_JOB_PREFIX = "scheduled:";
6442
7001
  async function registerScheduledTasks(powerManager, deps) {
6443
- const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig } = deps;
7002
+ const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig, getTeamClient } = deps;
6444
7003
  const runningTasks = /* @__PURE__ */ new Set();
6445
7004
  if (!definitionsDir) {
6446
7005
  logger.warn(LOG_KINDS.AGENT_ERROR, "Skipping dynamic task scheduling \u2014 definitions directory unavailable");
@@ -6450,7 +7009,7 @@ async function registerScheduledTasks(powerManager, deps) {
6450
7009
  if (!lastEnabled) {
6451
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");
6452
7011
  }
6453
- const { loadAllTasks: loadAllTasks2 } = await import("./registry-MGJSJBAS.js");
7012
+ const { loadAllTasks: loadAllTasks2 } = await import("./registry-M2Z5QBWH.js");
6454
7013
  const allTasks = Array.from(loadAllTasks2(definitionsDir, vaultDir).values());
6455
7014
  const taskAgentMap = /* @__PURE__ */ new Map();
6456
7015
  for (const task of allTasks) {
@@ -6486,10 +7045,33 @@ async function registerScheduledTasks(powerManager, deps) {
6486
7045
  lastEnabled = enabled;
6487
7046
  }
6488
7047
  if (!enabled) return;
6489
- const { runAgent } = await import("./executor-J4IBXSBY.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
+ }
6490
7064
  const taskConfig = config.agent.tasks?.[taskName];
6491
7065
  const projectRoot = resolve(vaultDir, "..");
6492
- 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
+ );
6493
7075
  if (isInstructionRequiredTask(taskName) && !built) {
6494
7076
  logger.info(
6495
7077
  LOG_KINDS.AGENT_RUN,
@@ -6498,7 +7080,7 @@ async function registerScheduledTasks(powerManager, deps) {
6498
7080
  );
6499
7081
  return;
6500
7082
  }
6501
- const result = await runAgent(vaultDir, {
7083
+ const result = await runAgent2(vaultDir, {
6502
7084
  task: taskName,
6503
7085
  instruction: built?.instruction,
6504
7086
  runContext: built?.context,
@@ -6525,7 +7107,7 @@ async function registerScheduledTasks(powerManager, deps) {
6525
7107
  link: `/agent?run=${result.runId}`,
6526
7108
  metadata: { taskName, runId: result.runId }
6527
7109
  }, config);
6528
- const { countToolCallsByRun } = await import("./turns-YFNI5CQC.js");
7110
+ const { countToolCallsByRun } = await import("./turns-HU2CTZAP.js");
6529
7111
  const counts = countToolCallsByRun(result.runId, ["vault_create_spore", "vault_write_digest"]);
6530
7112
  const sporeCount = counts["vault_create_spore"] ?? 0;
6531
7113
  const digestCount = counts["vault_write_digest"] ?? 0;
@@ -6568,6 +7150,9 @@ async function registerScheduledTasks(powerManager, deps) {
6568
7150
  },
6569
7151
  "has-approved-candidates": () => {
6570
7152
  return countCandidates({ status: "approved" }) > 0;
7153
+ },
7154
+ "has-skill-survey-evidence": () => {
7155
+ return getSkillSurveyEligibility(taskAgentMap.get(SKILL_SURVEY_TASK)).eligible;
6571
7156
  }
6572
7157
  },
6573
7158
  onTaskError: (taskName, err) => {
@@ -6597,31 +7182,170 @@ function listTeamMembers() {
6597
7182
  ).all();
6598
7183
  }
6599
7184
 
6600
- // src/daemon/api/mcp-proxy.ts
6601
- var SPORE_ID_RANDOM_BYTES = 4;
6602
- var RESOLUTION_ID_RANDOM_BYTES = 8;
6603
- var MIN_CONSOLIDATE_SOURCES = 2;
6604
- var RememberBody = external_exports.object({
6605
- content: external_exports.string(),
6606
- type: external_exports.string().optional(),
6607
- tags: external_exports.array(external_exports.string()).optional()
6608
- });
6609
- var SupersedeBody = external_exports.object({
6610
- old_spore_id: external_exports.string(),
6611
- new_spore_id: external_exports.string(),
6612
- reason: external_exports.string().optional()
6613
- });
6614
- function isoToEpochSeconds(iso) {
6615
- const ms = Date.parse(iso);
6616
- return Number.isNaN(ms) ? void 0 : Math.floor(ms / 1e3);
6617
- }
6618
- function registerMcpUserAgent(createdAt) {
6619
- registerAgent({
6620
- id: USER_AGENT_ID,
6621
- name: USER_AGENT_NAME,
6622
- created_at: createdAt
6623
- });
6624
- }
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
+ }
6625
7349
  function toPlanProgress(content) {
6626
7350
  const planContent = content ?? "";
6627
7351
  const checked = (planContent.match(/- \[x\]/gi) ?? []).length;
@@ -6639,8 +7363,20 @@ var ConsolidateBody = external_exports.object({
6639
7363
  tags: external_exports.array(external_exports.string()).optional(),
6640
7364
  reason: external_exports.string().optional()
6641
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
+ );
6642
7378
  function createMcpProxyHandlers(deps) {
6643
- const { machineId, embeddingManager } = deps;
7379
+ const { machineId, embeddingManager, projectRoot, logger } = deps;
6644
7380
  function toPlanSummary(row) {
6645
7381
  return {
6646
7382
  id: row.id,
@@ -6764,8 +7500,46 @@ function createMcpProxyHandlers(deps) {
6764
7500
  }
6765
7501
  };
6766
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
+ }
6767
7537
  async function handlePlans(req) {
6768
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
+ }
6769
7543
  if (id) {
6770
7544
  const row = getPlan(id);
6771
7545
  if (!row) return { body: { plans: [] } };
@@ -6778,6 +7552,11 @@ function createMcpProxyHandlers(deps) {
6778
7552
  }
6779
7553
  };
6780
7554
  }
7555
+ if (session) {
7556
+ const rows2 = listPlansBySession(session);
7557
+ const plans2 = rows2.map(toPlanSummary);
7558
+ return { body: { plans: plans2 } };
7559
+ }
6781
7560
  const statusFilter = req.query.status === "all" ? void 0 : req.query.status;
6782
7561
  const limit = req.query.limit ? Number(req.query.limit) : void 0;
6783
7562
  const rows = listPlans({ status: statusFilter, limit });
@@ -6831,6 +7610,7 @@ function createMcpProxyHandlers(deps) {
6831
7610
  handleSupersede,
6832
7611
  handleConsolidate,
6833
7612
  handlePlans,
7613
+ handleSavePlan,
6834
7614
  handleSessions,
6835
7615
  handleTeam
6836
7616
  };
@@ -6838,16 +7618,386 @@ function createMcpProxyHandlers(deps) {
6838
7618
 
6839
7619
  // src/daemon/api/agent-runs.ts
6840
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
6841
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
+ }
6842
7972
  var AgentRunBody = external_exports.object({
6843
7973
  task: external_exports.string().optional(),
6844
7974
  instruction: external_exports.string().optional(),
6845
- 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()
6846
7988
  });
6847
7989
  function createAgentRunHandlers(deps) {
6848
- const { vaultDir, embeddingManager, logger } = deps;
7990
+ const { vaultDir, embeddingManager, logger, getTeamClient } = deps;
6849
7991
  async function handleRun(req) {
6850
- 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);
6851
8001
  const mycoConfig = loadMergedConfig(vaultDir);
6852
8002
  if (!hasConfiguredProvider(mycoConfig, task)) {
6853
8003
  return {
@@ -6865,10 +8015,10 @@ function createAgentRunHandlers(deps) {
6865
8015
  try {
6866
8016
  const taskParams = mycoConfig.agent.tasks?.[task]?.params;
6867
8017
  const projectRoot = resolve2(vaultDir, "..");
6868
- built = buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager);
8018
+ built = await buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
6869
8019
  } catch {
6870
8020
  const projectRoot = resolve2(vaultDir, "..");
6871
- built = buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager);
8021
+ built = await buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
6872
8022
  }
6873
8023
  instruction = built?.instruction;
6874
8024
  runContext = built?.context;
@@ -6883,13 +8033,16 @@ function createAgentRunHandlers(deps) {
6883
8033
  };
6884
8034
  }
6885
8035
  }
6886
- const { runAgent } = await import("./executor-J4IBXSBY.js");
6887
- const resultPromise = runAgent(vaultDir, {
8036
+ const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
8037
+ const resultPromise = runAgent2(vaultDir, {
6888
8038
  task,
6889
8039
  instruction,
6890
8040
  agentId,
6891
8041
  embeddingManager,
6892
- runContext
8042
+ runContext,
8043
+ dryRun,
8044
+ evaluationId,
8045
+ executionOverrides
6893
8046
  });
6894
8047
  const effectiveAgentId = agentId ?? "myco-agent";
6895
8048
  const runId = getLatestRunId(effectiveAgentId, task);
@@ -6941,14 +8094,57 @@ function createAgentRunHandlers(deps) {
6941
8094
  const filterOpts = { agent_id: agentId, status, task, search };
6942
8095
  const runs = listRuns({ ...filterOpts, limit, offset });
6943
8096
  const total = countRuns(filterOpts);
6944
- return { body: { runs, total, offset, limit } };
8097
+ return { body: { runs: runs.map((run) => serializeRun(run)), total, offset, limit } };
6945
8098
  }
6946
8099
  async function handleGetRun(req) {
6947
8100
  const run = getRun(req.params.id);
6948
8101
  if (!run) {
6949
8102
  return { status: 404, body: { error: "Run not found" } };
6950
8103
  }
6951
- 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 } };
6952
8148
  }
6953
8149
  async function handleGetRunReports(req) {
6954
8150
  const reports = listReports(req.params.id);
@@ -6958,18 +8154,376 @@ function createAgentRunHandlers(deps) {
6958
8154
  const turns = listTurnsByRun(req.params.id);
6959
8155
  return { body: turns };
6960
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
+ }
6961
8173
  return {
6962
8174
  handleRun,
6963
8175
  handleListRuns,
6964
8176
  handleGetRun,
8177
+ handleResumeRun,
6965
8178
  handleGetRunReports,
6966
- handleGetRunTurns
8179
+ handleGetRunTurns,
8180
+ handleGetRunWriteIntents,
8181
+ handleGetRunAudit
6967
8182
  };
6968
8183
  }
6969
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
8319
+ };
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
+ }
8523
+
6970
8524
  // src/daemon/api/attachments.ts
6971
- import fs18 from "fs";
6972
- import path17 from "path";
8525
+ import fs19 from "fs";
8526
+ import path19 from "path";
6973
8527
  var ATTACHMENT_MEDIA_TYPES = {
6974
8528
  png: "image/png",
6975
8529
  jpg: "image/jpeg",
@@ -6989,14 +8543,14 @@ function createAttachmentHandler(deps) {
6989
8543
  const contentType2 = att.media_type ?? "application/octet-stream";
6990
8544
  return { status: 200, headers: { "Content-Type": contentType2 }, body: att.data };
6991
8545
  }
6992
- const filePath = path17.join(vaultDir, "attachments", filename);
8546
+ const filePath = path19.join(vaultDir, "attachments", filename);
6993
8547
  let diskData;
6994
8548
  try {
6995
- diskData = fs18.readFileSync(filePath);
8549
+ diskData = fs19.readFileSync(filePath);
6996
8550
  } catch {
6997
8551
  return { status: 404, body: { error: "not_found" } };
6998
8552
  }
6999
- const ext = path17.extname(filename).slice(1).toLowerCase();
8553
+ const ext = path19.extname(filename).slice(1).toLowerCase();
7000
8554
  const contentType = ATTACHMENT_MEDIA_TYPES[ext] ?? "application/octet-stream";
7001
8555
  return { status: 200, headers: { "Content-Type": contentType }, body: diskData };
7002
8556
  }
@@ -7004,19 +8558,19 @@ function createAttachmentHandler(deps) {
7004
8558
  }
7005
8559
 
7006
8560
  // src/daemon/log-reconcile.ts
7007
- import fs19 from "fs";
7008
- import path18 from "path";
8561
+ import fs20 from "fs";
8562
+ import path20 from "path";
7009
8563
  function reconcileLogBuffer(logDir, sinceTimestamp) {
7010
8564
  let replayed = 0;
7011
8565
  const files = [];
7012
8566
  for (let i = 3; i >= 1; i--) {
7013
- const rotated = path18.join(logDir, `daemon.${i}.log`);
7014
- if (fs19.existsSync(rotated)) files.push(rotated);
8567
+ const rotated = path20.join(logDir, `daemon.${i}.log`);
8568
+ if (fs20.existsSync(rotated)) files.push(rotated);
7015
8569
  }
7016
- const current = path18.join(logDir, "daemon.log");
7017
- if (fs19.existsSync(current)) files.push(current);
8570
+ const current = path20.join(logDir, "daemon.log");
8571
+ if (fs20.existsSync(current)) files.push(current);
7018
8572
  for (const file of files) {
7019
- const content = fs19.readFileSync(file, "utf-8");
8573
+ const content = fs20.readFileSync(file, "utf-8");
7020
8574
  for (const line of content.split("\n")) {
7021
8575
  if (!line.trim()) continue;
7022
8576
  try {
@@ -7044,11 +8598,14 @@ function reconcileLogBuffer(logDir, sinceTimestamp) {
7044
8598
  // src/config/focus.ts
7045
8599
  var CONFIG_FOCUS_SECTION_PARAM = "configSection";
7046
8600
  var CONFIG_FOCUS_FIELD_PARAM = "configField";
8601
+ var CONFIG_FOCUS_TAB_PARAM = "tab";
7047
8602
  var CONFIG_SECTION_IDS = {
7048
8603
  appearance: "config-section-appearance",
8604
+ cortexInstructions: "config-section-cortex-instructions",
8605
+ cortexBuilder: "config-section-cortex-builder",
8606
+ cortexDigest: "config-section-cortex-digest",
7049
8607
  settingsAgent: "config-section-settings-agent",
7050
8608
  settingsEmbedding: "config-section-settings-embedding",
7051
- settingsContextInjection: "config-section-settings-context-injection",
7052
8609
  settingsNotifications: "config-section-settings-notifications",
7053
8610
  settingsPlanCapture: "config-section-settings-plan-capture",
7054
8611
  settingsProject: "config-section-settings-project",
@@ -7075,11 +8632,30 @@ var SECTION_RULES = [
7075
8632
  sectionId: CONFIG_SECTION_IDS.settingsEmbedding,
7076
8633
  sectionLabel: "Embedding"
7077
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
+ },
7078
8654
  {
7079
8655
  prefix: "context",
7080
- page: "/settings",
7081
- sectionId: CONFIG_SECTION_IDS.settingsContextInjection,
7082
- sectionLabel: "Context Injection"
8656
+ page: "/cortex",
8657
+ sectionId: CONFIG_SECTION_IDS.cortexInstructions,
8658
+ sectionLabel: "Instructions"
7083
8659
  },
7084
8660
  {
7085
8661
  prefix: "notifications",
@@ -7146,7 +8722,9 @@ var EXACT_FIELD_LABELS = {
7146
8722
  "embedding.provider": "Provider",
7147
8723
  "embedding.model": "Model",
7148
8724
  "embedding.base_url": "Base URL",
7149
- "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",
7150
8728
  "context.prompt_search": "Prompt Search",
7151
8729
  "context.prompt_max_spores": "Max Spores per Prompt",
7152
8730
  "notifications.enabled": "Notifications",
@@ -7167,8 +8745,8 @@ var EXACT_FIELD_LABELS = {
7167
8745
  var DYNAMIC_FIELD_LABEL_RULES = [
7168
8746
  {
7169
8747
  prefix: "notifications.domains.",
7170
- format: (path23) => {
7171
- const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path23);
8748
+ format: (path25) => {
8749
+ const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path25);
7172
8750
  if (!match) return null;
7173
8751
  const [, domain, leaf] = match;
7174
8752
  const domainLabel = humanizeToken(domain);
@@ -7177,13 +8755,13 @@ var DYNAMIC_FIELD_LABEL_RULES = [
7177
8755
  }
7178
8756
  ];
7179
8757
  var SAVE_MESSAGE_LABEL_LIMIT = 3;
7180
- function resolveConfigFocusTarget(path23) {
7181
- const section = findSectionRule(path23);
8758
+ function resolveConfigFocusTarget(path25) {
8759
+ const section = findSectionRule(path25);
7182
8760
  if (!section) return null;
7183
8761
  return {
7184
8762
  ...section,
7185
- fieldPath: path23,
7186
- fieldLabel: resolveFieldLabel(path23)
8763
+ fieldPath: path25,
8764
+ fieldLabel: resolveFieldLabel(path25)
7187
8765
  };
7188
8766
  }
7189
8767
  function buildConfigFocusLink(target) {
@@ -7218,25 +8796,25 @@ function buildScopedConfigSaveNotification(scope, touchedPaths) {
7218
8796
  }
7219
8797
  };
7220
8798
  }
7221
- function findSectionRule(path23) {
8799
+ function findSectionRule(path25) {
7222
8800
  for (const rule of SECTION_RULES) {
7223
- if (path23 === rule.prefix || path23.startsWith(`${rule.prefix}.`)) {
8801
+ if (path25 === rule.prefix || path25.startsWith(`${rule.prefix}.`)) {
7224
8802
  const { page, sectionId, sectionLabel, searchParams } = rule;
7225
8803
  return { page, sectionId, sectionLabel, searchParams };
7226
8804
  }
7227
8805
  }
7228
8806
  return null;
7229
8807
  }
7230
- function resolveFieldLabel(path23) {
7231
- const exact = EXACT_FIELD_LABELS[path23];
8808
+ function resolveFieldLabel(path25) {
8809
+ const exact = EXACT_FIELD_LABELS[path25];
7232
8810
  if (exact) return exact;
7233
8811
  for (const rule of DYNAMIC_FIELD_LABEL_RULES) {
7234
- if (path23 === rule.prefix || path23.startsWith(rule.prefix)) {
7235
- const label = rule.format(path23);
8812
+ if (path25 === rule.prefix || path25.startsWith(rule.prefix)) {
8813
+ const label = rule.format(path25);
7236
8814
  if (label) return label;
7237
8815
  }
7238
8816
  }
7239
- return humanizeToken(path23.split(".").pop() ?? "setting");
8817
+ return humanizeToken(path25.split(".").pop() ?? "setting");
7240
8818
  }
7241
8819
  function humanizeToken(value) {
7242
8820
  return value.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
@@ -7356,6 +8934,48 @@ var PowerManager = class {
7356
8934
  }
7357
8935
  };
7358
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
+
7359
8979
  // src/daemon/jobs/session-maintenance.ts
7360
8980
  function completeStaleActiveSessions(thresholdSeconds = STALE_SESSION_THRESHOLD_MS / MS_PER_SECOND) {
7361
8981
  const db = getDatabase();
@@ -7500,8 +9120,8 @@ function registerPowerJobs(powerManager, deps) {
7500
9120
  }
7501
9121
 
7502
9122
  // src/daemon/reconciliation.ts
7503
- import fs20 from "fs";
7504
- import path19 from "path";
9123
+ import fs21 from "fs";
9124
+ import path21 from "path";
7505
9125
 
7506
9126
  // src/daemon/event-handlers.ts
7507
9127
  var TOOL_INPUT_STORE_LIMIT = 4e3;
@@ -7670,10 +9290,10 @@ function createReconciler({ bufferDir, logger }) {
7670
9290
  function reconcileSession(sessionId) {
7671
9291
  if (reconciledSessions.has(sessionId)) return;
7672
9292
  reconciledSessions.add(sessionId);
7673
- const bufferPath = path19.join(bufferDir, `${sessionId}.jsonl`);
9293
+ const bufferPath = path21.join(bufferDir, `${sessionId}.jsonl`);
7674
9294
  let content;
7675
9295
  try {
7676
- content = fs20.readFileSync(bufferPath, "utf-8").trim();
9296
+ content = fs21.readFileSync(bufferPath, "utf-8").trim();
7677
9297
  } catch {
7678
9298
  return;
7679
9299
  }
@@ -7742,7 +9362,19 @@ function createReconciler({ bufferDir, logger }) {
7742
9362
  }
7743
9363
 
7744
9364
  // src/daemon/stop-processing.ts
7745
- 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
+ }
7746
9378
 
7747
9379
  // src/daemon/capture-images.ts
7748
9380
  var SESSION_SHORT_LEN = 6;
@@ -7779,80 +9411,8 @@ function captureBatchImages(input) {
7779
9411
  }
7780
9412
  }
7781
9413
 
7782
- // src/daemon/plan-capture.ts
7783
- import { createHash as createHash4 } from "crypto";
7784
- import os8 from "os";
7785
- import path20 from "path";
7786
- function extractTaggedPlans(text, tags) {
7787
- const results = [];
7788
- for (const tag of tags) {
7789
- const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
7790
- let match;
7791
- while ((match = regex.exec(text)) !== null) {
7792
- const content = match[1].trim();
7793
- if (content) results.push({ tag, content });
7794
- }
7795
- }
7796
- return results;
7797
- }
7798
- var TRANSCRIPT_SOURCE_PREFIX = "transcript:";
7799
- var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
7800
- "Write",
7801
- "Edit",
7802
- "Create",
7803
- "write",
7804
- "edit",
7805
- "patch",
7806
- "create"
7807
- ]);
7808
- var HEADING_REGEX = /^#\s+(.+)$/m;
7809
- var PLAN_ID_HASH_LENGTH = 16;
7810
- function isInPlanDirectory(filePath, watchDirs, projectRoot) {
7811
- const abs = path20.isAbsolute(filePath) ? filePath : path20.resolve(projectRoot, filePath);
7812
- return watchDirs.some((dir) => {
7813
- const expanded = dir.startsWith("~/") ? path20.join(os8.homedir(), dir.slice(2)) : dir;
7814
- const absDir = path20.isAbsolute(expanded) ? expanded : path20.resolve(projectRoot, expanded);
7815
- const prefix = absDir.endsWith(path20.sep) ? absDir : absDir + path20.sep;
7816
- return abs === absDir || abs.startsWith(prefix);
7817
- });
7818
- }
7819
- function isPlanWriteEvent(toolName, toolInput, config) {
7820
- if (!FILE_WRITE_TOOLS.has(toolName)) return null;
7821
- const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
7822
- if (typeof filePath !== "string") return null;
7823
- if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
7824
- if (config.extensions?.length) {
7825
- const ext = path20.extname(filePath).toLowerCase();
7826
- if (!config.extensions.includes(ext)) return null;
7827
- }
7828
- return filePath;
7829
- }
7830
- function parsePlanTitle(content, filename) {
7831
- const match = HEADING_REGEX.exec(content);
7832
- if (match) return match[1].trim();
7833
- return filename ?? null;
7834
- }
7835
- function capturePlan(input) {
7836
- const now = Math.floor(Date.now() / 1e3);
7837
- const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
7838
- const id = createHash4("md5").update(input.sourcePath).digest("hex").slice(0, PLAN_ID_HASH_LENGTH);
7839
- const title = parsePlanTitle(input.content, path20.basename(input.sourcePath));
7840
- return upsertPlan({
7841
- id,
7842
- title,
7843
- content: input.content,
7844
- source_path: input.sourcePath,
7845
- session_id: input.sessionId,
7846
- prompt_batch_id: input.promptBatchId ?? null,
7847
- content_hash: contentHash,
7848
- status: "active",
7849
- created_at: now,
7850
- updated_at: now
7851
- });
7852
- }
7853
-
7854
9414
  // src/daemon/skill-usage.ts
7855
- import crypto from "crypto";
9415
+ import crypto2 from "crypto";
7856
9416
  var SKILL_USAGE_DETECTION_ENABLED = false;
7857
9417
  var MAX_ACTIVE_SKILLS_CHECK = 1e3;
7858
9418
  function detectSkillUsage(sessionId, transcriptContent) {
@@ -7872,7 +9432,7 @@ function detectSkillUsage(sessionId, transcriptContent) {
7872
9432
  if (!pattern.test(transcriptContent)) continue;
7873
9433
  if (hasUsageForSkillAndSession(skill.id, sessionId)) continue;
7874
9434
  insertSkillUsage({
7875
- id: crypto.randomUUID(),
9435
+ id: crypto2.randomUUID(),
7876
9436
  skill_id: skill.id,
7877
9437
  session_id: sessionId,
7878
9438
  detected_at: now
@@ -7887,6 +9447,46 @@ function escapeRegex(s) {
7887
9447
  }
7888
9448
 
7889
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
+ }
7890
9490
  function enrichTurnsWithToolMetadata(turns, events) {
7891
9491
  if (events.length === 0 || turns.length === 0) return;
7892
9492
  const toolEvents = events.filter((e) => e.type === "tool_use");
@@ -7914,7 +9514,7 @@ function enrichTurnsWithToolMetadata(turns, events) {
7914
9514
  }
7915
9515
  }
7916
9516
  function createStopProcessor(deps) {
7917
- const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir } = deps;
9517
+ const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir, planWatchConfig } = deps;
7918
9518
  let activeStopProcessing = null;
7919
9519
  const sessionTitleCache = /* @__PURE__ */ new Map();
7920
9520
  const StopBody = external_exports.object({
@@ -8016,7 +9616,7 @@ function createStopProcessor(deps) {
8016
9616
  let transcriptText = null;
8017
9617
  if (hookTranscriptPath) {
8018
9618
  try {
8019
- transcriptText = fs21.readFileSync(hookTranscriptPath, "utf-8");
9619
+ transcriptText = fs22.readFileSync(hookTranscriptPath, "utf-8");
8020
9620
  } catch {
8021
9621
  }
8022
9622
  }
@@ -8048,11 +9648,12 @@ function createStopProcessor(deps) {
8048
9648
  const taggedPlans = extractTaggedPlans(turn.aiResponse, deps.planTags);
8049
9649
  for (const { tag, content } of taggedPlans) {
8050
9650
  try {
8051
- capturePlan({
8052
- sourcePath: `${TRANSCRIPT_SOURCE_PREFIX}${tag}`,
9651
+ captureTaggedPlan({
9652
+ tag,
8053
9653
  content,
8054
9654
  sessionId,
8055
- promptBatchId: latestBatch?.id ?? null
9655
+ promptBatchId: latestBatch?.id ?? null,
9656
+ logger
8056
9657
  });
8057
9658
  logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from transcript tag", {
8058
9659
  session_id: sessionId,
@@ -8069,6 +9670,50 @@ function createStopProcessor(deps) {
8069
9670
  }
8070
9671
  }
8071
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
+ }
8072
9717
  if (!hasTitle) {
8073
9718
  triggerTitleSummary2(sessionId);
8074
9719
  }
@@ -8115,12 +9760,11 @@ function createStopProcessor(deps) {
8115
9760
  last_assistant_message: lastAssistantMessage
8116
9761
  } = StopBody.parse(req.body);
8117
9762
  if (hookTranscriptPath) {
8118
- const transcriptMeta = readTranscriptMeta(hookTranscriptPath) ?? void 0;
8119
9763
  const detectedAgent = agent ?? getSession(sessionId)?.agent ?? "claude-code";
8120
- const decision = evaluateSessionCaptureRules(loadManifests(), detectedAgent, {
8121
- transcriptPath: hookTranscriptPath,
8122
- transcriptMeta
8123
- });
9764
+ const { decision } = gateEventByCaptureRules(
9765
+ { agent: detectedAgent, transcriptPath: hookTranscriptPath },
9766
+ { manifests: loadManifests() }
9767
+ );
8124
9768
  if (decision.action === "drop") {
8125
9769
  const deleted = cleanupInvalidCapturedSession(sessionId);
8126
9770
  logger.info(LOG_KINDS.HOOKS_STOP, "Stop ignored \u2014 invalid captured session", {
@@ -8175,9 +9819,10 @@ function createStopProcessor(deps) {
8175
9819
  }
8176
9820
 
8177
9821
  // src/daemon/event-dispatch.ts
8178
- import fs22 from "fs";
8179
- import path21 from "path";
9822
+ import fs23 from "fs";
9823
+ import path23 from "path";
8180
9824
  var EventBody = external_exports.object({ type: external_exports.string(), session_id: external_exports.string() }).passthrough();
9825
+ var DROP_DECISION_CACHE_MAX = 1024;
8181
9826
  function createEventDispatcher(deps) {
8182
9827
  const {
8183
9828
  registry,
@@ -8192,6 +9837,39 @@ function createEventDispatcher(deps) {
8192
9837
  triggerTitleSummary: triggerTitleSummary2
8193
9838
  } = deps;
8194
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
+ }
8195
9873
  return async (req) => {
8196
9874
  const validated = EventBody.parse(req.body);
8197
9875
  const event = {
@@ -8200,13 +9878,38 @@ function createEventDispatcher(deps) {
8200
9878
  };
8201
9879
  logger.debug(LOG_KINDS.HOOKS_EVENT, "Event received", { type: event.type, session_id: event.session_id });
8202
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
+ }
8203
9906
  registry.register(event.session_id, { started_at: event.timestamp });
8204
9907
  logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Auto-registered session from event", { session_id: event.session_id });
8205
9908
  const now = epochSeconds();
8206
9909
  const startedEpoch = Math.floor(new Date(event.timestamp).getTime() / 1e3);
8207
9910
  upsertSession({
8208
9911
  id: event.session_id,
8209
- agent: event.agent ?? "claude-code",
9912
+ agent: event.agent ?? DEFAULT_SYMBIONT_NAME,
8210
9913
  status: "active",
8211
9914
  started_at: startedEpoch,
8212
9915
  created_at: now,
@@ -8215,7 +9918,7 @@ function createEventDispatcher(deps) {
8215
9918
  reconcileSession(event.session_id);
8216
9919
  }
8217
9920
  if (!sessionBuffers.has(event.session_id)) {
8218
- const bufferDir = path21.join(vaultDir, "buffer");
9921
+ const bufferDir = path23.join(vaultDir, "buffer");
8219
9922
  sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));
8220
9923
  }
8221
9924
  sessionBuffers.get(event.session_id).append(event);
@@ -8241,6 +9944,29 @@ function createEventDispatcher(deps) {
8241
9944
  try {
8242
9945
  const { batchId, promptNumber } = handleUserPrompt(event.session_id, promptText || void 0);
8243
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
+ }
8244
9970
  const eventImages = event.images;
8245
9971
  if (Array.isArray(eventImages) && eventImages.length > 0) {
8246
9972
  captureBatchImages({
@@ -8274,13 +10000,15 @@ function createEventDispatcher(deps) {
8274
10000
  );
8275
10001
  if (planFilePath) {
8276
10002
  const captureSessionId = event.session_id;
8277
- fs22.promises.readFile(planFilePath, "utf-8").then((planContent) => {
10003
+ fs23.promises.readFile(planFilePath, "utf-8").then((planContent) => {
8278
10004
  const latestBatch = getLatestBatch(captureSessionId);
8279
10005
  capturePlan({
8280
- sourcePath: path21.relative(projectRoot, planFilePath),
10006
+ sourcePath: planFilePath,
10007
+ projectRoot,
8281
10008
  content: planContent,
8282
10009
  sessionId: captureSessionId,
8283
- promptBatchId: latestBatch?.id ?? null
10010
+ promptBatchId: latestBatch?.id ?? null,
10011
+ logger
8284
10012
  });
8285
10013
  logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured", {
8286
10014
  session_id: captureSessionId,
@@ -8440,8 +10168,8 @@ function createConfigReactionRegistry(logger) {
8440
10168
  function shouldFire(registeredPaths, touched) {
8441
10169
  if (registeredPaths.length === 0) return true;
8442
10170
  for (const prefix of registeredPaths) {
8443
- for (const path23 of touched) {
8444
- if (path23 === prefix || path23.startsWith(`${prefix}.`)) return true;
10171
+ for (const path25 of touched) {
10172
+ if (path25 === prefix || path25.startsWith(`${prefix}.`)) return true;
8445
10173
  }
8446
10174
  }
8447
10175
  return false;
@@ -8475,13 +10203,13 @@ function createPlanWatchReaction(deps) {
8475
10203
  }
8476
10204
 
8477
10205
  // src/daemon/main.ts
8478
- import fs23 from "fs";
8479
- import path22 from "path";
10206
+ import fs24 from "fs";
10207
+ import path24 from "path";
8480
10208
  function killStaleDaemon(vaultDir, logger) {
8481
- const daemonJsonPath = path22.join(vaultDir, "daemon.json");
10209
+ const daemonJsonPath = path24.join(vaultDir, "daemon.json");
8482
10210
  try {
8483
- if (!fs23.existsSync(daemonJsonPath)) return;
8484
- 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"));
8485
10213
  if (!info.pid) return;
8486
10214
  if (info.pid === process.pid) return;
8487
10215
  try {
@@ -8490,7 +10218,7 @@ function killStaleDaemon(vaultDir, logger) {
8490
10218
  logger.info(LOG_KINDS.DAEMON_START, "Killed stale daemon", { pid: info.pid });
8491
10219
  } catch {
8492
10220
  }
8493
- fs23.unlinkSync(daemonJsonPath);
10221
+ fs24.unlinkSync(daemonJsonPath);
8494
10222
  } catch {
8495
10223
  }
8496
10224
  }
@@ -8500,7 +10228,7 @@ async function main() {
8500
10228
  process.stderr.write("Usage: mycod --vault <path>\n");
8501
10229
  process.exit(1);
8502
10230
  }
8503
- const vaultDir = path22.resolve(vaultArg);
10231
+ const vaultDir = path24.resolve(vaultArg);
8504
10232
  loadSecrets(vaultDir);
8505
10233
  const config = loadMergedConfig(vaultDir);
8506
10234
  const liveConfig = { current: config };
@@ -8513,7 +10241,7 @@ async function main() {
8513
10241
  projectRoot,
8514
10242
  extensions: config.capture.artifact_extensions
8515
10243
  };
8516
- const logger = new DaemonLogger(path22.join(vaultDir, "logs"), {
10244
+ const logger = new DaemonLogger(path24.join(vaultDir, "logs"), {
8517
10245
  level: config.daemon.log_level
8518
10246
  });
8519
10247
  if (config.daemon.log_level === "debug") {
@@ -8542,7 +10270,7 @@ async function main() {
8542
10270
  const devCliEntry = detectDevBuild(
8543
10271
  globalPrefix,
8544
10272
  process.argv[1],
8545
- fs23.realpathSync
10273
+ fs24.realpathSync
8546
10274
  );
8547
10275
  if (devCliEntry) {
8548
10276
  setDevBuildCliEntry(devCliEntry);
@@ -8554,13 +10282,19 @@ async function main() {
8554
10282
  const db = initDatabase(vaultDbPath(vaultDir));
8555
10283
  createSchema(db, machineId);
8556
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
+ }
8557
10291
  logger.info(LOG_KINDS.DAEMON_START, "SQLite initialized", { vault: vaultDir });
8558
10292
  {
8559
- const reasonPath = path22.join(vaultDir, RESTART_REASON_FILENAME);
10293
+ const reasonPath = path24.join(vaultDir, RESTART_REASON_FILENAME);
8560
10294
  try {
8561
- if (fs23.existsSync(reasonPath)) {
8562
- const raw = JSON.parse(fs23.readFileSync(reasonPath, "utf-8"));
8563
- fs23.unlinkSync(reasonPath);
10295
+ if (fs24.existsSync(reasonPath)) {
10296
+ const raw = JSON.parse(fs24.readFileSync(reasonPath, "utf-8"));
10297
+ fs24.unlinkSync(reasonPath);
8564
10298
  if (raw.reason === "version_sync" && raw.to_version) {
8565
10299
  const message = raw.local_update_ran ? "Restarted and updated local project hooks." : "Restarted to pick up the latest version.";
8566
10300
  notify(vaultDir, {
@@ -8602,13 +10336,13 @@ async function main() {
8602
10336
  });
8603
10337
  const lastLogTimestamp = getMaxTimestamp();
8604
10338
  if (lastLogTimestamp) {
8605
- const logDir = path22.join(vaultDir, "logs");
10339
+ const logDir = path24.join(vaultDir, "logs");
8606
10340
  const replayedCount = reconcileLogBuffer(logDir, lastLogTimestamp);
8607
10341
  if (replayedCount > 0) {
8608
10342
  logger.info(LOG_KINDS.DAEMON_RECONCILE, `Replayed ${replayedCount} log entries from buffer`, { replayed: replayedCount });
8609
10343
  }
8610
10344
  }
8611
- const vectorsDbPath = path22.join(vaultDir, "vectors.db");
10345
+ const vectorsDbPath = path24.join(vaultDir, "vectors.db");
8612
10346
  const vectorStore = new SqliteVecVectorStore(vectorsDbPath);
8613
10347
  const llmProvider = createEmbeddingProvider(config.embedding);
8614
10348
  const embeddingProvider = new EmbeddingProviderAdapter(llmProvider, config.embedding);
@@ -8618,7 +10352,7 @@ async function main() {
8618
10352
  const databaseManager = new DatabaseMaintenanceManager(vaultDbPath(vaultDir), vaultDir, logger);
8619
10353
  let definitionsDir;
8620
10354
  try {
8621
- const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-TSB5M7FD.js");
10355
+ const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-NAVVZK63.js");
8622
10356
  definitionsDir = resolveDefinitionsDir2();
8623
10357
  await registerBuiltInAgentsAndTasks(definitionsDir, vaultDir);
8624
10358
  logger.info(LOG_KINDS.AGENT_TASK, "Built-in agents and tasks registered");
@@ -8655,10 +10389,10 @@ async function main() {
8655
10389
  }
8656
10390
  let uiDir = null;
8657
10391
  {
8658
- const root = findPackageRoot(path22.dirname(new URL(import.meta.url).pathname));
10392
+ const root = findPackageRoot(path24.dirname(new URL(import.meta.url).pathname));
8659
10393
  if (root) {
8660
- const candidate = path22.join(root, "dist", "ui");
8661
- if (fs23.existsSync(candidate)) uiDir = candidate;
10394
+ const candidate = path24.join(root, "dist", "ui");
10395
+ if (fs24.existsSync(candidate)) uiDir = candidate;
8662
10396
  }
8663
10397
  }
8664
10398
  if (uiDir) {
@@ -8672,6 +10406,7 @@ async function main() {
8672
10406
  sleepIntervalMs: POWER_SLEEP_INTERVAL_MS,
8673
10407
  logger
8674
10408
  });
10409
+ const inflightRuns = new InflightRunRegistry();
8675
10410
  const server = new DaemonServer({
8676
10411
  vaultDir,
8677
10412
  logger,
@@ -8691,7 +10426,7 @@ async function main() {
8691
10426
  (p) => createPerProjectAdapter(p, claudeCodeAdapter.parseTurns)
8692
10427
  )
8693
10428
  });
8694
- const bufferDir = path22.join(vaultDir, "buffer");
10429
+ const bufferDir = path24.join(vaultDir, "buffer");
8695
10430
  const sessionBuffers = /* @__PURE__ */ new Map();
8696
10431
  const reconciler = createReconciler({ bufferDir, logger });
8697
10432
  reconciler.runStartupReconciliation();
@@ -8703,7 +10438,8 @@ async function main() {
8703
10438
  logger,
8704
10439
  liveConfig,
8705
10440
  vaultDir,
8706
- planTags: symbiontPlanTags
10441
+ planTags: symbiontPlanTags,
10442
+ planWatchConfig
8707
10443
  });
8708
10444
  const sessionLifecycle = createSessionLifecycleHandlers({
8709
10445
  registry,
@@ -8733,14 +10469,32 @@ async function main() {
8733
10469
  });
8734
10470
  server.registerRoute("POST", "/events", eventDispatcher);
8735
10471
  server.registerRoute("POST", "/events/stop", stopProcessor.handleStopRoute);
8736
- const contextDeps = { embeddingManager, liveConfig, logger };
10472
+ let teamSync;
10473
+ const contextDeps = {
10474
+ vaultDir,
10475
+ embeddingManager,
10476
+ liveConfig,
10477
+ logger,
10478
+ getTeamClient: () => teamSync.getTeamClient()
10479
+ };
8737
10480
  server.registerRoute("POST", "/context", createSessionContextHandler(contextDeps));
8738
10481
  server.registerRoute("POST", "/context/resume", createResumeContextHandler(contextDeps));
8739
10482
  server.registerRoute("POST", "/context/prompt", createPromptContextHandler(contextDeps));
8740
10483
  const progressTracker = new ProgressTracker();
8741
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
+ });
8742
10492
  server.registerRoute("GET", "/api/config", async () => handleGetConfig(vaultDir));
8743
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);
8744
10498
  server.registerRoute("GET", "/api/config/merged", async () => handleGetMergedConfig(vaultDir));
8745
10499
  server.registerRoute("GET", "/api/config/local", async () => handleGetLocalConfig(vaultDir));
8746
10500
  const symbiontPlanDirsByAgent = {};
@@ -8756,7 +10510,7 @@ async function main() {
8756
10510
  liveConfig.current = ctx;
8757
10511
  });
8758
10512
  reactions.on(["capture", "symbionts"], (ctx) => {
8759
- reconcileConfiguredSymbionts(path22.dirname(vaultDir), vaultDir, ctx);
10513
+ reconcileConfiguredSymbionts(path24.dirname(vaultDir), vaultDir, ctx);
8760
10514
  });
8761
10515
  reactions.on(["capture"], createPlanWatchReaction({
8762
10516
  symbiontPlanDirs,
@@ -8771,7 +10525,14 @@ async function main() {
8771
10525
  }
8772
10526
  });
8773
10527
  async function syncScheduledTasks() {
8774
- 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
+ });
8775
10536
  }
8776
10537
  reactions.on(["agent.tasks"], async () => {
8777
10538
  await syncScheduledTasks();
@@ -8825,7 +10586,7 @@ async function main() {
8825
10586
  server.registerRoute("POST", "/api/log", createLogIngestionHandler(logger));
8826
10587
  server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req));
8827
10588
  server.registerRoute("POST", "/api/restart", async (req) => handleRestart({ vaultDir, progressTracker }, req.body));
8828
- const updateProjectRoot = path22.dirname(vaultDir);
10589
+ const updateProjectRoot = path24.dirname(vaultDir);
8829
10590
  const updateHandlers = createUpdateHandlers({
8830
10591
  vaultDir,
8831
10592
  projectRoot: updateProjectRoot,
@@ -8848,6 +10609,7 @@ async function main() {
8848
10609
  server.registerRoute("GET", "/api/sessions/:id/impact", sessionMutations.handleGetSessionImpact);
8849
10610
  server.registerRoute("POST", "/api/sessions/:id/complete", sessionMutations.handleCompleteSession);
8850
10611
  server.registerRoute("DELETE", "/api/sessions/:id", sessionMutations.handleDeleteSession);
10612
+ server.registerRoute("DELETE", "/api/plans/:id", sessionMutations.handleDeletePlan);
8851
10613
  server.registerRoute("GET", "/api/sessions/:id/batches", handleGetSessionBatches);
8852
10614
  server.registerRoute("GET", "/api/batches/:id/activities", handleGetBatchActivities);
8853
10615
  server.registerRoute("GET", "/api/sessions/:id/attachments", handleGetSessionAttachments);
@@ -8868,12 +10630,27 @@ async function main() {
8868
10630
  server.registerRoute("GET", "/api/digest", handleGetDigest);
8869
10631
  const attachments = createAttachmentHandler({ vaultDir });
8870
10632
  server.registerRoute("GET", "/api/attachments/:filename", attachments.handleGetAttachment);
8871
- const agentRunHandlers = createAgentRunHandlers({ vaultDir, embeddingManager, logger });
10633
+ const agentRunHandlers = createAgentRunHandlers({
10634
+ vaultDir,
10635
+ embeddingManager,
10636
+ logger,
10637
+ getTeamClient: () => teamSync.getTeamClient()
10638
+ });
8872
10639
  server.registerRoute("POST", "/api/agent/run", agentRunHandlers.handleRun);
8873
10640
  server.registerRoute("GET", "/api/agent/runs", agentRunHandlers.handleListRuns);
8874
10641
  server.registerRoute("GET", "/api/agent/runs/:id", agentRunHandlers.handleGetRun);
10642
+ server.registerRoute("POST", "/api/agent/runs/:id/resume", agentRunHandlers.handleResumeRun);
8875
10643
  server.registerRoute("GET", "/api/agent/runs/:id/reports", agentRunHandlers.handleGetRunReports);
8876
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);
8877
10654
  server.registerRoute("GET", "/api/agent/tasks", async (req) => handleListTasks(req, vaultDir));
8878
10655
  server.registerRoute("GET", "/api/agent/tasks/:id", async (req) => handleGetTask(req, vaultDir));
8879
10656
  server.registerRoute("GET", "/api/agent/tasks/:id/yaml", async (req) => handleGetTaskYaml(req, vaultDir));
@@ -8915,11 +10692,15 @@ async function main() {
8915
10692
  });
8916
10693
  server.registerRoute("GET", "/api/providers", async () => handleGetProviders());
8917
10694
  server.registerRoute("POST", "/api/providers/test", async (req) => handleTestProvider(req));
8918
- 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 });
8919
10699
  server.registerRoute("POST", "/api/mcp/remember", mcpProxy.handleRemember);
8920
10700
  server.registerRoute("POST", "/api/mcp/supersede", mcpProxy.handleSupersede);
8921
10701
  server.registerRoute("POST", "/api/mcp/consolidate", mcpProxy.handleConsolidate);
8922
10702
  server.registerRoute("GET", "/api/mcp/plans", mcpProxy.handlePlans);
10703
+ server.registerRoute("POST", "/api/mcp/plans", mcpProxy.handleSavePlan);
8923
10704
  server.registerRoute("GET", "/api/mcp/sessions", mcpProxy.handleSessions);
8924
10705
  server.registerRoute("GET", "/api/mcp/team", mcpProxy.handleTeam);
8925
10706
  const backupHandlers = createBackupHandlers({ db, machineId, vaultDir, liveConfig });
@@ -8936,7 +10717,7 @@ async function main() {
8936
10717
  }
8937
10718
  return result;
8938
10719
  });
8939
- const teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
10720
+ teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
8940
10721
  reactions.on(["team"], async () => {
8941
10722
  await teamSync.reconcileClient();
8942
10723
  });
@@ -8945,7 +10726,7 @@ async function main() {
8945
10726
  vaultDir,
8946
10727
  machineId,
8947
10728
  logger,
8948
- getTeamClient: teamSync.getTeamClient,
10729
+ getTeamClient: () => teamSync.getTeamClient(),
8949
10730
  setTeamClient: teamSync.setTeamClient
8950
10731
  });
8951
10732
  server.registerRoute("POST", "/api/team/connect", async (req) => {
@@ -8968,14 +10749,14 @@ async function main() {
8968
10749
  server.registerRoute("POST", "/api/team/upgrade-worker", teamHandlers.handleUpgradeWorker);
8969
10750
  server.registerRoute("POST", "/api/team/rotate-mcp-token", teamHandlers.handleRotateMcpToken);
8970
10751
  const collectiveHandlers = createCollectiveHandlers({
8971
- getTeamClient: teamSync.getTeamClient
10752
+ getTeamClient: () => teamSync.getTeamClient()
8972
10753
  });
8973
10754
  server.registerRoute("GET", "/api/collective/status", collectiveHandlers.handleStatus);
8974
10755
  server.registerRoute("GET", "/api/collective/search", collectiveHandlers.handleSearch);
8975
10756
  server.registerRoute("GET", "/api/collective/projects", collectiveHandlers.handleProjects);
8976
10757
  server.registerRoute("GET", "/api/collective/project", collectiveHandlers.handleProject);
8977
10758
  server.registerRoute("GET", "/api/collective/settings", collectiveHandlers.handleSettings);
8978
- server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: teamSync.getTeamClient, machineId }));
10759
+ server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: () => teamSync.getTeamClient(), machineId }));
8979
10760
  server.registerRoute("GET", "/api/activity", handleGetFeed);
8980
10761
  server.registerRoute("GET", "/api/embedding/status", async () => handleGetEmbeddingStatus(vaultDir));
8981
10762
  server.registerRoute("GET", "/api/embedding/details", async () => handleEmbeddingDetails(embeddingManager));
@@ -9013,9 +10794,25 @@ async function main() {
9013
10794
  logger.warn(LOG_KINDS.DAEMON_CONFIG, "Failed to persist auto-derived port", { error: err.message });
9014
10795
  }
9015
10796
  }
9016
- 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
+ });
9017
10807
  teamSync.registerFlushJob(powerManager);
9018
- 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
+ });
9019
10816
  powerManager.start();
9020
10817
  const shutdown = async (signal) => {
9021
10818
  logger.info(LOG_KINDS.DAEMON_START, `${signal} received`);
@@ -9025,6 +10822,17 @@ async function main() {
9025
10822
  logger.info(LOG_KINDS.DAEMON_START, "Waiting for active stop processing to complete...");
9026
10823
  await activeStopProcessing;
9027
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
+ }
9028
10836
  registry.destroy();
9029
10837
  await server.stop();
9030
10838
  vectorStore.close();
@@ -9047,4 +10855,4 @@ export {
9047
10855
  handleUserPrompt,
9048
10856
  main
9049
10857
  };
9050
- //# sourceMappingURL=main-E7HU4QYR.js.map
10858
+ //# sourceMappingURL=main-5PRQNEEE.js.map