@rubytech/create-maxy-code 0.1.446 → 0.1.447

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 (232) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/graph-style/dist/index.js +6 -0
  4. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  5. package/payload/platform/lib/graph-style/src/index.ts +7 -0
  6. package/payload/platform/neo4j/schema.cypher +32 -1
  7. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  8. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  9. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +42 -5
  10. package/payload/platform/plugins/docs/references/calendar-booking.md +18 -1
  11. package/payload/platform/plugins/docs/references/claudeai-connectors.md +15 -0
  12. package/payload/platform/plugins/docs/references/outlook-guide.md +8 -3
  13. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +4 -4
  14. package/payload/platform/plugins/memory/references/schema-construction.md +7 -2
  15. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  16. package/payload/platform/plugins/outlook/PLUGIN.md +23 -5
  17. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.d.ts +2 -0
  18. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.d.ts.map +1 -0
  19. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js +70 -0
  20. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js.map +1 -0
  21. package/payload/platform/plugins/outlook/mcp/dist/__tests__/html-to-text.test.d.ts +2 -0
  22. package/payload/platform/plugins/outlook/mcp/dist/__tests__/html-to-text.test.d.ts.map +1 -0
  23. package/payload/platform/plugins/outlook/mcp/dist/__tests__/html-to-text.test.js +22 -0
  24. package/payload/platform/plugins/outlook/mcp/dist/__tests__/html-to-text.test.js.map +1 -0
  25. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-delete.test.d.ts +2 -0
  26. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-delete.test.d.ts.map +1 -0
  27. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-delete.test.js +60 -0
  28. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-delete.test.js.map +1 -0
  29. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-fetch-body.test.d.ts +2 -0
  30. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-fetch-body.test.d.ts.map +1 -0
  31. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-fetch-body.test.js +80 -0
  32. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-fetch-body.test.js.map +1 -0
  33. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-otp-extract.test.d.ts +2 -0
  34. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-otp-extract.test.d.ts.map +1 -0
  35. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-otp-extract.test.js +90 -0
  36. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-otp-extract.test.js.map +1 -0
  37. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-reply.test.d.ts +2 -0
  38. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-reply.test.d.ts.map +1 -0
  39. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-reply.test.js +194 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-reply.test.js.map +1 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/otp.test.d.ts +2 -0
  42. package/payload/platform/plugins/outlook/mcp/dist/__tests__/otp.test.d.ts.map +1 -0
  43. package/payload/platform/plugins/outlook/mcp/dist/__tests__/otp.test.js +16 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/__tests__/otp.test.js.map +1 -0
  45. package/payload/platform/plugins/outlook/mcp/dist/__tests__/outbound-attachments.test.d.ts +2 -0
  46. package/payload/platform/plugins/outlook/mcp/dist/__tests__/outbound-attachments.test.d.ts.map +1 -0
  47. package/payload/platform/plugins/outlook/mcp/dist/__tests__/outbound-attachments.test.js +103 -0
  48. package/payload/platform/plugins/outlook/mcp/dist/__tests__/outbound-attachments.test.js.map +1 -0
  49. package/payload/platform/plugins/outlook/mcp/dist/index.js +88 -0
  50. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  51. package/payload/platform/plugins/outlook/mcp/dist/lib/html-to-text.d.ts +9 -0
  52. package/payload/platform/plugins/outlook/mcp/dist/lib/html-to-text.d.ts.map +1 -0
  53. package/payload/platform/plugins/outlook/mcp/dist/lib/html-to-text.js +24 -0
  54. package/payload/platform/plugins/outlook/mcp/dist/lib/html-to-text.js.map +1 -0
  55. package/payload/platform/plugins/outlook/mcp/dist/lib/otp.d.ts +2 -0
  56. package/payload/platform/plugins/outlook/mcp/dist/lib/otp.d.ts.map +1 -0
  57. package/payload/platform/plugins/outlook/mcp/dist/lib/otp.js +49 -0
  58. package/payload/platform/plugins/outlook/mcp/dist/lib/otp.js.map +1 -0
  59. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts +58 -0
  60. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts.map +1 -0
  61. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js +127 -0
  62. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js.map +1 -0
  63. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts +28 -0
  64. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts.map +1 -0
  65. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js +54 -0
  66. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js.map +1 -0
  67. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts +21 -0
  68. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +38 -0
  70. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-delete.d.ts +14 -0
  72. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-delete.d.ts.map +1 -0
  73. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-delete.js +39 -0
  74. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-delete.js.map +1 -0
  75. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts +23 -0
  76. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts.map +1 -0
  77. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js +44 -0
  78. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js.map +1 -0
  79. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-otp-extract.d.ts +22 -0
  80. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-otp-extract.d.ts.map +1 -0
  81. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-otp-extract.js +90 -0
  82. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-otp-extract.js.map +1 -0
  83. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts +27 -0
  84. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +75 -0
  86. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -0
  87. package/payload/platform/plugins/outlook/references/graph-surfaces.md +57 -1
  88. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +15 -4
  89. package/payload/platform/plugins/scheduling/PLUGIN.md +15 -0
  90. package/payload/platform/plugins/scheduling/mcp/dist/index.js +29 -1
  91. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  92. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +53 -1
  93. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -1
  94. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +21 -0
  95. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
  96. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +80 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
  98. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +4 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -1
  100. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +68 -1
  101. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  102. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.d.ts +2 -0
  103. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.d.ts.map +1 -0
  104. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js +75 -0
  105. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js.map +1 -0
  106. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts +31 -0
  107. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts.map +1 -0
  108. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js +41 -0
  109. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js.map +1 -0
  110. package/payload/platform/plugins/voice-mirror/.claude-plugin/plugin.json +21 -0
  111. package/payload/platform/plugins/voice-mirror/PLUGIN.md +72 -0
  112. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/index.js +10 -10
  113. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/index.js.map +1 -1
  114. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/neo4j.d.ts +1 -1
  115. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/neo4j.js +4 -4
  116. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/neo4j.js.map +1 -1
  117. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-record-feedback.js +1 -1
  118. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-record-feedback.js.map +1 -1
  119. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/package-lock.json +2 -2
  120. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/package.json +1 -1
  121. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/scripts/smoke.mjs +9 -9
  122. package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/skills/voice-mirror/SKILL.md +1 -1
  123. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  124. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +12 -0
  125. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  126. package/payload/platform/services/claude-session-manager/dist/http-server.js +1 -1
  127. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  128. package/payload/platform/templates/agents/admin/AGENTS.md +1 -1
  129. package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
  130. package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -2
  131. package/payload/premium-plugins/writer-craft/.claude-plugin/plugin.json +1 -14
  132. package/payload/premium-plugins/writer-craft/PLUGIN.md +2 -51
  133. package/payload/server/public/assets/{AdminLoginScreens-D9L-CuWi.js → AdminLoginScreens-Bvf4HKmq.js} +1 -1
  134. package/payload/server/public/assets/AdminShell-CaGc8ODG.js +2 -0
  135. package/payload/server/public/assets/{Checkbox-CFdt8ljo.js → Checkbox-2SswiICc.js} +1 -1
  136. package/payload/server/public/assets/admin-B7GPCzpr.js +1 -0
  137. package/payload/server/public/assets/browser-CodHC_MO.js +1 -0
  138. package/payload/server/public/assets/calendar-DH7cyXKF.js +1 -0
  139. package/payload/server/public/assets/chat-BQyPAEMv.js +1 -0
  140. package/payload/server/public/assets/chevron-left-Cmev0R9v.js +1 -0
  141. package/payload/server/public/assets/data-DWxZzMfL.js +1 -0
  142. package/payload/server/public/assets/{graph-DrE7KupQ.js → graph-BToB78LG.js} +2 -2
  143. package/payload/server/public/assets/graph-labels-JRGrstzM.js +1 -0
  144. package/payload/server/public/assets/maximize-2-CEQRv64B.js +1 -0
  145. package/payload/server/public/assets/operator-Cx8cq2wf.js +1 -0
  146. package/payload/server/public/assets/page-D6kzsk18.js +32 -0
  147. package/payload/server/public/assets/page-TCsQy4eP.js +1 -0
  148. package/payload/server/public/assets/public-B61xcmAr.js +1 -0
  149. package/payload/server/public/assets/{rotate-ccw-Cqp4oqHV.js → rotate-ccw-7_3ol0Z4.js} +1 -1
  150. package/payload/server/public/assets/{tasks-bQKOhMga.js → tasks-BY9ajKXf.js} +1 -1
  151. package/payload/server/public/assets/{time-entry-format-BIL4ZaDK.js → time-entry-format-DmNhIJnM.js} +1 -1
  152. package/payload/server/public/assets/useCopyFeedback-C5-GOl5l.js +1 -0
  153. package/payload/server/public/assets/useSelectionMode-UOUVnP9B.js +1 -0
  154. package/payload/server/public/assets/{useSubAccountSwitcher-CVHKTSst.css → useSubAccountSwitcher-DgM1ergs.css} +1 -1
  155. package/payload/server/public/assets/useVoiceRecorder-BAP3yGxs.js +2 -0
  156. package/payload/server/public/browser.html +5 -5
  157. package/payload/server/public/calendar.html +6 -6
  158. package/payload/server/public/chat.html +12 -12
  159. package/payload/server/public/data.html +10 -10
  160. package/payload/server/public/graph.html +11 -11
  161. package/payload/server/public/index.html +13 -13
  162. package/payload/server/public/operator.html +14 -14
  163. package/payload/server/public/public.html +12 -12
  164. package/payload/server/public/tasks.html +5 -5
  165. package/payload/server/server.js +349 -171
  166. package/payload/premium-plugins/writer-craft/lib/mcp-lifeline/dist/index.d.ts +0 -8
  167. package/payload/premium-plugins/writer-craft/lib/mcp-lifeline/dist/index.d.ts.map +0 -1
  168. package/payload/premium-plugins/writer-craft/lib/mcp-lifeline/dist/index.js +0 -71
  169. package/payload/premium-plugins/writer-craft/lib/mcp-lifeline/dist/index.js.map +0 -1
  170. package/payload/premium-plugins/writer-craft/lib/mcp-lifeline/package.json +0 -7
  171. package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts +0 -51
  172. package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts.map +0 -1
  173. package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js +0 -196
  174. package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js.map +0 -1
  175. package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/package.json +0 -7
  176. package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts +0 -3
  177. package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts.map +0 -1
  178. package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js +0 -58
  179. package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js.map +0 -1
  180. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +0 -457
  181. package/payload/premium-plugins/writer-craft/mcp/src/lib/neo4j.ts +0 -56
  182. package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +0 -154
  183. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +0 -833
  184. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-ingest-session-text.ts +0 -153
  185. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-record-feedback.ts +0 -148
  186. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +0 -270
  187. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-tag-content.ts +0 -181
  188. package/payload/premium-plugins/writer-craft/mcp/tsconfig.json +0 -8
  189. package/payload/server/public/assets/AdminShell-DSQrvZXq.js +0 -2
  190. package/payload/server/public/assets/admin-Ch4n-G0j.js +0 -1
  191. package/payload/server/public/assets/browser-B2AIxiuk.js +0 -1
  192. package/payload/server/public/assets/calendar-BvItH3B0.js +0 -1
  193. package/payload/server/public/assets/chat-eQFMaX9y.js +0 -1
  194. package/payload/server/public/assets/chevron-left-D2AnCEyE.js +0 -1
  195. package/payload/server/public/assets/data-BzOmbHoe.js +0 -1
  196. package/payload/server/public/assets/file-download-CK9j_Jx2.js +0 -1
  197. package/payload/server/public/assets/graph-labels-CwEtvIRV.js +0 -1
  198. package/payload/server/public/assets/maximize-2-CuzEom3K.js +0 -1
  199. package/payload/server/public/assets/operator-DlboYPPu.js +0 -1
  200. package/payload/server/public/assets/page-BuWH5gNK.js +0 -1
  201. package/payload/server/public/assets/page-Co9E8U5x.js +0 -32
  202. package/payload/server/public/assets/public-DtFpPMTG.js +0 -1
  203. package/payload/server/public/assets/useCopyFeedback-rB8QuqyH.js +0 -1
  204. package/payload/server/public/assets/useVoiceRecorder-Ctny8qWx.js +0 -2
  205. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/lib/mcp-spawn-tee/index.js +0 -0
  206. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/lib/mcp-spawn-tee/package.json +0 -0
  207. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/index.d.ts +0 -0
  208. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/index.d.ts.map +0 -0
  209. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/neo4j.d.ts.map +0 -0
  210. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/voice-corpus.d.ts +0 -0
  211. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/voice-corpus.d.ts.map +0 -0
  212. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/voice-corpus.js +0 -0
  213. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/lib/voice-corpus.js.map +0 -0
  214. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-distil-profile.d.ts +0 -0
  215. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-distil-profile.d.ts.map +0 -0
  216. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-distil-profile.js +0 -0
  217. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-distil-profile.js.map +0 -0
  218. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-ingest-session-text.d.ts +0 -0
  219. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-ingest-session-text.d.ts.map +0 -0
  220. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-ingest-session-text.js +0 -0
  221. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-ingest-session-text.js.map +0 -0
  222. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-record-feedback.d.ts +0 -0
  223. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-record-feedback.d.ts.map +0 -0
  224. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-retrieve-conditioning.d.ts +0 -0
  225. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +0 -0
  226. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-retrieve-conditioning.js +0 -0
  227. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-retrieve-conditioning.js.map +0 -0
  228. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-tag-content.d.ts +0 -0
  229. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-tag-content.d.ts.map +0 -0
  230. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-tag-content.js +0 -0
  231. /package/payload/{premium-plugins/writer-craft → platform/plugins/voice-mirror}/mcp/dist/tools/voice-tag-content.js.map +0 -0
  232. /package/payload/server/public/assets/{useSubAccountSwitcher-CHEt_m6q.js → useSubAccountSwitcher-DF6bohQA.js} +0 -0
@@ -1,457 +0,0 @@
1
- import { initStderrTee } from "../../../../platform/lib/mcp-stderr-tee/dist/index.js";
2
- import { lifelineTool } from "../../../../platform/lib/mcp-lifeline/dist/index.js";
3
- initStderrTee("writer-craft");
4
-
5
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import { z } from "zod";
8
- import { voiceTagContent, resolveVoiceAuthor, AUTHORSHIP_MODES, type AuthorshipMode } from "./tools/voice-tag-content.js";
9
- import type { VoiceFormat } from "./lib/voice-corpus.js";
10
- import { voiceDistilProfile } from "./tools/voice-distil-profile.js";
11
- import { voiceRetrieveConditioning } from "./tools/voice-retrieve-conditioning.js";
12
- import { voiceRecordFeedback } from "./tools/voice-record-feedback.js";
13
- import { voiceIngestSessionText } from "./tools/voice-ingest-session-text.js";
14
- import { FORMAT_VALUES } from "./lib/voice-corpus.js";
15
- import { closeDriver } from "./lib/neo4j.js";
16
-
17
- const server = new McpServer({
18
- name: "maxy-writer-craft",
19
- version: "0.1.0",
20
- });
21
-
22
- // Plugin-system boot tolerance — module-init must never throw. Per-call
23
- // refusal preserves account isolation. Mirrors the memory plugin's
24
- // pattern (Task 202).
25
- const accountId = process.env.ACCOUNT_ID ?? null;
26
- const userIdEnv = process.env.ADMIN_USER_ID ?? null;
27
- process.stderr.write(
28
- `[writer-craft] boot accountId=${accountId ?? "null"} userId=${userIdEnv ?? "null"}\n`,
29
- );
30
-
31
- function refuseNoAccount(toolName: string) {
32
- process.stderr.write(
33
- `[writer-craft] tool=${toolName} refuse reason=no-account-context\n`,
34
- );
35
- return {
36
- content: [
37
- {
38
- type: "text" as const,
39
- text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID. Per-account graph access requires ACCOUNT_ID at spawn.`,
40
- },
41
- ],
42
- isError: true as const,
43
- };
44
- }
45
-
46
- function resolveUserId(): string | null {
47
- // ADMIN_USER_ID is stamped by the admin session manager at spawn time.
48
- // When absent (e.g. specialist subagents), the operator must pass it as
49
- // a tool argument or the tools refuse rather than orphan a profile.
50
- return userIdEnv;
51
- }
52
-
53
- // Format enum schema — shared across tools that accept VoiceFormat.
54
- const zFormat = z.enum([...FORMAT_VALUES] as [string, ...string[]]).describe(
55
- "Writing format: text | email | social-post | article | marketing-copy | note. One :VoiceProfile per (accountId, userId, format).",
56
- );
57
-
58
- // Scope enum schema — shared across distil / retrieve / feedback (Task 676).
59
- const zScope = z
60
- .enum(["personal", "org"])
61
- .optional()
62
- .describe(
63
- "Voice scope: 'personal' (default) = the operator's own voice, distilled from their author-tagged content and anchored on :AdminUser. 'org' = the account/house voice, distilled from the whole account and anchored on :LocalBusiness under the reserved userId '__org__'.",
64
- );
65
-
66
- lifelineTool(server,
67
- "voice-tag-content",
68
- "Stamp authorshipMode and format on one or many content nodes (:KnowledgeDocument | :Message | :SocialPost) the operator owns. Email threads live as :KnowledgeDocument {source:'email'} since Task 321. Valid modes: human-only, human-led-agent-assisted, agent-led-human-reviewed, agent-only, unknown. Valid formats: text, email, social-post, article, marketing-copy, note. format is editorial — the operator chooses it per node; the tool does not infer it.",
69
- {
70
- nodeIds: z.array(z.string()).min(1).describe("Element IDs of nodes to tag"),
71
- mode: z
72
- .enum([
73
- "human-only",
74
- "human-led-agent-assisted",
75
- "agent-led-human-reviewed",
76
- "agent-only",
77
- "unknown",
78
- ])
79
- .describe("Authorship mode to apply"),
80
- format: zFormat.describe(
81
- "Writing format. Required — the operator's editorial classification of the content.",
82
- ),
83
- userId: z
84
- .string()
85
- .optional()
86
- .describe(
87
- "Operator AdminUser id. Defaults to ADMIN_USER_ID env. Used as the default author when `author` is omitted.",
88
- ),
89
- author: z
90
- .string()
91
- .optional()
92
- .describe(
93
- "voiceAuthor userId to stamp on the nodes. Omit to attribute to the tagging operator — single-operator accounts get personal attribution for free.",
94
- ),
95
- },
96
- async ({ nodeIds, mode, format, userId, author }) => {
97
- if (!accountId) return refuseNoAccount("voice-tag-content");
98
- let voiceAuthor: string;
99
- try {
100
- voiceAuthor = resolveVoiceAuthor(author, userId ?? resolveUserId());
101
- } catch (err) {
102
- return {
103
- content: [
104
- {
105
- type: "text" as const,
106
- text: err instanceof Error ? err.message : String(err),
107
- },
108
- ],
109
- isError: true,
110
- };
111
- }
112
- try {
113
- const result = await voiceTagContent({
114
- nodeIds,
115
- mode: mode as AuthorshipMode,
116
- accountId,
117
- format: format as VoiceFormat,
118
- voiceAuthor,
119
- });
120
- return {
121
- content: [
122
- {
123
- type: "text" as const,
124
- text: `voice-tag: mode=${mode} format=${format} author=${voiceAuthor} updated=${result.updated} skipped=${result.skipped}`,
125
- },
126
- ],
127
- };
128
- } catch (err) {
129
- return {
130
- content: [
131
- {
132
- type: "text" as const,
133
- text: `voice-tag-content failed: ${err instanceof Error ? err.message : String(err)}`,
134
- },
135
- ],
136
- isError: true,
137
- };
138
- }
139
- },
140
- );
141
-
142
- lifelineTool(server,
143
- "voice-distil-profile",
144
- "Three-mode tool. mode='sample' (default) walks the operator's full per-format human-only corpus and returns exemplars + recent edit intents for the agent to compose a fresh style card; cadence-guarded (≥20% corpus growth OR ≥30 days). mode='amend' (Task 472) reads only the named documents plus the existing :VoiceProfile.styleCard so the agent can decide whether one or more specific documents move the profile — operator-initiated, no cadence guard. mode='write' persists the YAML style card the agent composed; supply amendedFromNodeIds to attribute the write to specific documents (Task 472, bypasses cadence + corpus walk; existing LEARNED_FROM edges remain via MERGE). Trashed nodes are excluded from every walk. When format is omitted on 'sample', enumerates all formats in the corpus and distils each. scope (Task 676): 'personal' (default) distils the operator's author-tagged content; 'org' distils the whole account into the house voice anchored on the business.",
145
- {
146
- userId: z
147
- .string()
148
- .optional()
149
- .describe(
150
- "Operator AdminUser identifier. Defaults to ADMIN_USER_ID env from spawn context.",
151
- ),
152
- format: zFormat.optional().describe(
153
- "Writing format to distil. Required for mode='amend' and mode='write'. When omitted on 'sample', enumerates corpus formats and distils each that fires the cadence guard.",
154
- ),
155
- force: z
156
- .boolean()
157
- .optional()
158
- .describe("Override cadence guard on full-corpus sample/write; ignored by mode='amend' (always proceeds)."),
159
- mode: z
160
- .enum(["sample", "amend", "write"])
161
- .optional()
162
- .describe("'sample' returns full-corpus exemplars + feedback for in-turn distillation; 'amend' returns existing styleCard + bodies of one or more named documents for incremental update; 'write' persists the agent-produced styleCardYaml."),
163
- styleCardYaml: z
164
- .string()
165
- .optional()
166
- .describe("Required for mode='write'. The YAML style card the agent composed (from the sample exemplars + feedback intents, or from the amend bodies + existing card)."),
167
- nodeIds: z
168
- .array(z.string())
169
- .optional()
170
- .describe(
171
- "Required for mode='amend'. ElementIds of the documents the operator wants folded into the per-format profile (one or more). Each id is eligibility-checked; ineligible ones come back in the ineligible[] payload.",
172
- ),
173
- amendedFromNodeIds: z
174
- .array(z.string())
175
- .optional()
176
- .describe(
177
- "Optional for mode='write' after an amend sample. ElementIds the agent decided actually moved the profile. Each becomes a LEARNED_FROM edge from the persisted :VoiceProfile. Setting this routes the write through the amend-write path (no corpus walk, cadence bypassed).",
178
- ),
179
- scope: zScope,
180
- },
181
- async ({ userId, format, force, mode, styleCardYaml, nodeIds, amendedFromNodeIds, scope }) => {
182
- if (!accountId) return refuseNoAccount("voice-distil-profile");
183
- const resolvedUserId = userId ?? resolveUserId();
184
- if (!resolvedUserId) {
185
- return {
186
- content: [
187
- {
188
- type: "text" as const,
189
- text:
190
- "voice-distil-profile: userId required (no ADMIN_USER_ID in spawn env).",
191
- },
192
- ],
193
- isError: true,
194
- };
195
- }
196
- try {
197
- const result = await voiceDistilProfile({
198
- accountId,
199
- userId: resolvedUserId,
200
- format: format as import("./lib/voice-corpus.js").VoiceFormat | undefined,
201
- force: force === true,
202
- mode,
203
- styleCardYaml,
204
- nodeIds,
205
- amendedFromNodeIds,
206
- scope: scope as import("./lib/voice-corpus.js").VoiceScope | undefined,
207
- });
208
-
209
- // Summarise single-format or multi-format result.
210
- const summariseOne = (r: import("./tools/voice-distil-profile.js").VoiceDistilProfileResult, fmt?: string): string => {
211
- const tag = fmt ? ` format=${fmt}` : "";
212
- if (r.eligible !== undefined) {
213
- // amend sample
214
- return `voice-distil amend${tag}: eligible=${r.eligible} ineligible=${r.ineligible?.length ?? 0} exemplars=${r.exemplars?.length ?? 0} existingProfile=${r.profileId ?? "none"} feedbackEntries=${r.feedbackEntries}\n${JSON.stringify({ existingStyleCard: r.existingStyleCard, exemplars: r.exemplars, feedbackIntents: r.feedbackIntents, ineligible: r.ineligible }, null, 2)}`;
215
- }
216
- if (r.skipped && r.exemplars) {
217
- return `voice-distil sample${tag}: corpusSize=${r.corpusSize} exemplars=${r.exemplars.length} feedbackEntries=${r.feedbackEntries}\n${JSON.stringify({ exemplars: r.exemplars, feedbackIntents: r.feedbackIntents }, null, 2)}`;
218
- }
219
- if (r.skipped) {
220
- return `voice-distil${tag}: skipped reason=${r.skipReason} corpusSize=${r.corpusSize}`;
221
- }
222
- return `voice-distil${tag}: ok profileId=${r.profileId} corpusSize=${r.corpusSize} feedbackEntries=${r.feedbackEntries} generatedAt=${r.generatedAt}`;
223
- };
224
-
225
- let summary: string;
226
- if (Array.isArray(result)) {
227
- summary = result.map((r) => summariseOne(r, r.format)).join("\n---\n");
228
- } else {
229
- summary = summariseOne(result, format);
230
- }
231
-
232
- return { content: [{ type: "text" as const, text: summary }] };
233
- } catch (err) {
234
- return {
235
- content: [
236
- {
237
- type: "text" as const,
238
- text: `voice-distil-profile failed: ${err instanceof Error ? err.message : String(err)}`,
239
- },
240
- ],
241
- isError: true,
242
- };
243
- }
244
- },
245
- );
246
-
247
- lifelineTool(server,
248
- "voice-retrieve-conditioning",
249
- "Return the operator's per-format style card and K voice-matched exemplars for a drafting brief. K=5 short-form (length='short'), K=15 long-form (length='long'). Token-budget bounded. format is required (the writing type: text|email|social-post|article|marketing-copy|note). scope (Task 676): 'personal' (default) returns the caller's own voice; 'org' returns the account/house voice. A personal request with no personal profile/corpus falls back to the org profile (status 'fallback-org'). Returns {styleCard, exemplars, status} where status is one of: 'ok', 'fallback-org' (personal asked, org returned), 'no-data' (no profile and empty corpus for the scope), 'error' (lookup failed — carries a message). Drafting falls back to default register on 'no-data' and 'error' and injects the org card on 'fallback-org'; only 'no-data' means 'you have no voice profile yet' — never report no-profile on 'error'.",
250
- {
251
- userId: z
252
- .string()
253
- .optional()
254
- .describe("Operator AdminUser identifier. Defaults to ADMIN_USER_ID env."),
255
- format: zFormat.describe("Writing format for corpus and profile scoping."),
256
- topic: z
257
- .string()
258
- .optional()
259
- .describe("Drafting topic keyword (filters exemplars to relevant ones)"),
260
- length: z
261
- .enum(["short", "long"])
262
- .optional()
263
- .describe("K-selector: short = K=5 (16k tokens, email/post); long = K=15 (96k tokens, brochure/prospectus). Defaults to short."),
264
- register: z.string().optional().describe("Optional register hint"),
265
- scope: zScope,
266
- tokenBudget: z.number().int().positive().optional(),
267
- },
268
- async ({ userId, format, topic, length, register, scope, tokenBudget }) => {
269
- if (!accountId) return refuseNoAccount("voice-retrieve-conditioning");
270
- const resolvedUserId = userId ?? resolveUserId();
271
- // org scope is account-wide (keyed on ORG_USER_ID + accountId) and never
272
- // reads a userId, so a specialist spawn that carries ACCOUNT_ID but no
273
- // ADMIN_USER_ID can still retrieve the house voice. Only personal scope
274
- // requires a resolved userId (Task 975).
275
- if ((scope ?? "personal") !== "org" && !resolvedUserId) {
276
- // Soft (no isError) so drafting degrades, but status:error keeps this
277
- // distinct from genuine no-data (Task 493).
278
- return {
279
- content: [
280
- {
281
- type: "text" as const,
282
- text: JSON.stringify({
283
- styleCard: null,
284
- exemplars: [],
285
- status: "error",
286
- error: "no userId (no ADMIN_USER_ID in spawn env)",
287
- }),
288
- },
289
- ],
290
- };
291
- }
292
- try {
293
- const result = await voiceRetrieveConditioning({
294
- accountId,
295
- // org scope ignores userId; pass through for personal scope.
296
- userId: resolvedUserId ?? "",
297
- brief: {
298
- format: format as import("./lib/voice-corpus.js").VoiceFormat,
299
- topic,
300
- length: length as "short" | "long" | undefined,
301
- register,
302
- scope: scope as import("./lib/voice-corpus.js").VoiceScope | undefined,
303
- },
304
- tokenBudget,
305
- });
306
- return {
307
- content: [
308
- { type: "text" as const, text: JSON.stringify(result) },
309
- ],
310
- };
311
- } catch (err) {
312
- // Graceful degradation: never an error response (no isError) so drafting
313
- // does not crash — but the payload carries status:error so the failure
314
- // is not masquerading as no-data (Task 493).
315
- const message = err instanceof Error ? err.message : String(err);
316
- process.stderr.write(`[voice-retrieve] tool error: ${message}\n`);
317
- return {
318
- content: [
319
- {
320
- type: "text" as const,
321
- text: JSON.stringify({
322
- styleCard: null,
323
- exemplars: [],
324
- status: "error",
325
- error: message,
326
- }),
327
- },
328
- ],
329
- };
330
- }
331
- },
332
- );
333
-
334
- lifelineTool(server,
335
- "voice-record-feedback",
336
- "Capture an operator's edit on an agent draft. Writes :VoiceEdit linked to the per-format :VoiceProfile via :FEEDBACK_FOR. The calling agent supplies `intent` (one sentence describing what changed and what voice preference it reveals — Task 424) and `format` (the writing format of the draft). scope (Task 676): 'org' routes the edit to the account/house profile; 'personal' (default) to the operator's own. The editor is always preserved via the AUTHORED edge.",
337
- {
338
- userId: z.string().optional(),
339
- format: zFormat.describe("Writing format of the draft being edited."),
340
- originalText: z.string().min(1),
341
- editedText: z.string().min(1),
342
- intent: z
343
- .string()
344
- .min(1)
345
- .describe("One sentence summarising what changed and what voice preference it reveals — produced by the calling agent in-turn from originalText vs editedText (Task 424)."),
346
- skill: z.string().optional().describe("Calling skill name, for provenance"),
347
- brief: z.string().optional().describe("Drafting brief that produced the original"),
348
- scope: zScope,
349
- },
350
- async ({ userId, format, originalText, editedText, intent, skill, brief, scope }) => {
351
- if (!accountId) return refuseNoAccount("voice-record-feedback");
352
- const resolvedUserId = userId ?? resolveUserId();
353
- if (!resolvedUserId) {
354
- return {
355
- content: [
356
- {
357
- type: "text" as const,
358
- text: "voice-record-feedback: userId required (no ADMIN_USER_ID in spawn env).",
359
- },
360
- ],
361
- isError: true,
362
- };
363
- }
364
- try {
365
- const result = await voiceRecordFeedback({
366
- accountId,
367
- userId: resolvedUserId,
368
- format: format as import("./lib/voice-corpus.js").VoiceFormat,
369
- originalText,
370
- editedText,
371
- intent,
372
- scope: scope as import("./lib/voice-corpus.js").VoiceScope | undefined,
373
- context: { skill, brief },
374
- });
375
- return {
376
- content: [
377
- {
378
- type: "text" as const,
379
- text: `voice-record-feedback: editId=${result.editId} format=${format} intent="${result.intent}" hadProfile=${result.hadProfile}`,
380
- },
381
- ],
382
- };
383
- } catch (err) {
384
- return {
385
- content: [
386
- {
387
- type: "text" as const,
388
- text: `voice-record-feedback failed: ${err instanceof Error ? err.message : String(err)}`,
389
- },
390
- ],
391
- isError: true,
392
- };
393
- }
394
- },
395
- );
396
-
397
- lifelineTool(server,
398
- "voice-ingest-session-text",
399
- "Write operator turns from the current session as :Message {format:'text', source:'admin-pty', authorshipMode:'human-only'} corpus nodes. The admin agent reads its own operator turns from the live conversation context and passes them as `turns`. Deduplication via SHA256 contentHash — re-running with the same turns is a no-op. Triggers voice-distil-profile {format:'text'} after ingest (cadence guard decides whether distillation runs). Invoked on demand by the voice-mirror skill when the operator asks to capture this session's voice.",
400
- {
401
- turns: z.array(z.string()).describe("Operator turns the agent read from its own session context (one string per operator message). Slash commands, harness injections, and large paste blocks are filtered out by the tool."),
402
- sessionId: z.string().optional().describe("Session ID for provenance."),
403
- userId: z.string().optional().describe("Operator AdminUser identifier. Defaults to ADMIN_USER_ID env."),
404
- },
405
- async ({ turns, sessionId, userId }) => {
406
- if (!accountId) return refuseNoAccount("voice-ingest-session-text");
407
- const resolvedUserId = userId ?? resolveUserId();
408
- if (!resolvedUserId) {
409
- return {
410
- content: [
411
- {
412
- type: "text" as const,
413
- text: "voice-ingest-session-text: userId required (no ADMIN_USER_ID in spawn env).",
414
- },
415
- ],
416
- isError: true,
417
- };
418
- }
419
- try {
420
- const result = await voiceIngestSessionText({
421
- turns,
422
- accountId,
423
- userId: resolvedUserId,
424
- sessionId,
425
- });
426
- return {
427
- content: [
428
- {
429
- type: "text" as const,
430
- text: `voice-ingest-session-text: ingestedCount=${result.ingestedCount} skippedCount=${result.skippedCount} sessionId=${result.sessionId ?? "unknown"}`,
431
- },
432
- ],
433
- };
434
- } catch (err) {
435
- return {
436
- content: [
437
- {
438
- type: "text" as const,
439
- text: `voice-ingest-session-text failed: ${err instanceof Error ? err.message : String(err)}`,
440
- },
441
- ],
442
- isError: true,
443
- };
444
- }
445
- },
446
- );
447
-
448
- const transport = new StdioServerTransport();
449
- await server.connect(transport);
450
-
451
- process.on("SIGTERM", async () => {
452
- await closeDriver();
453
- process.exit(0);
454
- });
455
-
456
- // Surface AUTHORSHIP_MODES for downstream callers / docs.
457
- export { AUTHORSHIP_MODES };
@@ -1,56 +0,0 @@
1
- /**
2
- * Minimal neo4j driver wrapper for the writer-craft MCP server.
3
- *
4
- * Mirrors the public surface of `platform/plugins/memory/mcp/src/lib/neo4j.ts`:
5
- * `getSession()` returns a session, `closeDriver()` releases the singleton.
6
- * Password resolution follows the same `PLATFORM_ROOT/config/.neo4j-password`
7
- * convention, with an explicit `NEO4J_URI` requirement to avoid silent
8
- * cross-account orphaning.
9
- */
10
- import neo4j, { Driver, Session } from "neo4j-driver";
11
- import { readFileSync } from "node:fs";
12
- import { resolve } from "node:path";
13
-
14
- let driver: Driver | null = null;
15
-
16
- function readPassword(): string {
17
- if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
18
- const passwordFile = resolve(
19
- process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../../../platform"),
20
- "config/.neo4j-password",
21
- );
22
- try {
23
- return readFileSync(passwordFile, "utf-8").trim();
24
- } catch {
25
- throw new Error(
26
- `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`,
27
- );
28
- }
29
- }
30
-
31
- export function getDriver(): Driver {
32
- if (!driver) {
33
- const uri = process.env.NEO4J_URI;
34
- if (!uri) {
35
- throw new Error(
36
- "[writer-craft] NEO4J_URI unset — refusing to default to bolt://localhost:7687",
37
- );
38
- }
39
- const user = process.env.NEO4J_USER ?? "neo4j";
40
- const password = readPassword();
41
- process.stderr.write(`[writer-craft] resolved neo4j_uri=${uri}\n`);
42
- driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
43
- }
44
- return driver;
45
- }
46
-
47
- export function getSession(): Session {
48
- return getDriver().session();
49
- }
50
-
51
- export async function closeDriver(): Promise<void> {
52
- if (driver) {
53
- await driver.close();
54
- driver = null;
55
- }
56
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Cypher fragment that excludes trashed nodes for the given alias.
3
- *
4
- * Mirrors the platform's soft-delete model: a node is trashed if it
5
- * carries the `:Trashed` label OR a non-null `deletedAt` timestamp.
6
- * Inlined here (Task 473) instead of imported from
7
- * `platform/lib/graph-trash` — the cross-package import path did not
8
- * survive `tsc` emit (the `rootDir: "src"` config collapsed
9
- * `../../../../../platform/...` to `../../...`, pointing the dist
10
- * file at a non-existent location and crashing the MCP on boot). The
11
- * predicate is one line; copying it costs less than the build coupling.
12
- */
13
- export function notTrashed(alias: string): string {
14
- return `(NOT \`${alias}\`:Trashed AND \`${alias}\`.deletedAt IS NULL)`;
15
- }
16
-
17
- /**
18
- * Shared corpus-filter clauses and format helpers for the voice-mirror tools.
19
- *
20
- * Imported by `voice-distil-profile` (count + sample walks),
21
- * `voice-retrieve-conditioning` (exemplar walk), `voice-tag-content` (tag
22
- * writes), and `voice-ingest-session-text` (on-demand session-text ingest). The single
23
- * load-bearing filter — when the corpus rules change, this is the only place
24
- * to edit.
25
- *
26
- * Three predicates in VOICE_CORPUS_WHERE:
27
- *
28
- * 1. `authorshipMode IN ['human-only', 'human-led-agent-assisted']`.
29
- * `human-only` is the operator's own writing verbatim;
30
- * `human-led-agent-assisted` is operator-authored with light agent
31
- * polish, which still carries voice signal (the operator chose the
32
- * claims and the structure). Including this expands the available
33
- * corpus to match how most modern writing actually happens.
34
- *
35
- * 2. Label is one of the four primary content labels OR is a
36
- * conversation-document `:Section` chunk (unguarded chat voice from
37
- * WhatsApp / Telegram / Signal / Granola / Otter / Circleback etc.
38
- * archives). After the Task 324 collapse, conversation transcripts
39
- * land as `:KnowledgeDocument` parents carrying `conversationIdentity`
40
- * with plain `:Section` children — no `:Conversation` secondary label.
41
- * Conversation-document sections are identified by their parent's
42
- * `conversationIdentity` property via an EXISTS sub-pattern. The
43
- * conversation parent must itself be live — a trashed parent excludes
44
- * its child chunks.
45
- * Chat captures voice in a way email rarely does — this is the
46
- * single highest-leverage corpus addition for voice fidelity.
47
- *
48
- * 3. The node is not trashed — `notTrashed("n")` filters both the
49
- * `:Trashed` label and the `deletedAt` property (Task 472). This is
50
- * the read-side equivalent of `graph-trash`'s soft-delete model: a
51
- * node marked by either signal is excluded from every voice-mirror
52
- * walk that goes through VOICE_CORPUS_WHERE.
53
- *
54
- * The clause expects `$accountId` to be bound in the calling Cypher
55
- * parameters. It does not bind any other parameters.
56
- */
57
- export const VOICE_CORPUS_WHERE = `n.accountId = $accountId
58
- AND n.authorshipMode IN ['human-only', 'human-led-agent-assisted']
59
- AND ${notTrashed("n")}
60
- AND (
61
- any(lbl IN labels(n) WHERE lbl IN ['KnowledgeDocument', 'Message', 'SocialPost'])
62
- OR ('Section' IN labels(n) AND EXISTS {
63
- MATCH (parent:KnowledgeDocument)-[:HAS_SECTION]->(n)
64
- WHERE parent.conversationIdentity IS NOT NULL
65
- AND ${notTrashed("parent")}
66
- })
67
- )`;
68
-
69
- /**
70
- * The label set the tag tool accepts when stamping `authorshipMode`.
71
- * Mirrors the label predicate inside VOICE_CORPUS_WHERE so the tag tool
72
- * and the corpus walk stay in lockstep. The tag tool does not filter
73
- * by authorshipMode (that's the property it's setting).
74
- *
75
- * `:Email` retired with Task 321 — email threads now land as
76
- * `:KnowledgeDocument {source:'email'}` and are already covered.
77
- */
78
- export const TAGGABLE_PRIMARY_LABELS = [
79
- "KnowledgeDocument",
80
- "Message",
81
- "SocialPost",
82
- ] as const;
83
-
84
- /**
85
- * Closed enum of writing-format values. One `:VoiceProfile` exists per
86
- * `(accountId, userId, format)`. Adding a new value is a separate task.
87
- *
88
- * Format is an editorial classification set by the operator at tag time.
89
- * The only auto-tagged path is `voice-ingest-session-text`, which hard-codes
90
- * `format='text'` on `:Message` because chat turns are unambiguously text and
91
- * the operator is not in the loop on session end.
92
- */
93
- export const FORMAT_VALUES = [
94
- "text",
95
- "email",
96
- "social-post",
97
- "article",
98
- "marketing-copy",
99
- "note",
100
- ] as const;
101
-
102
- export type VoiceFormat = (typeof FORMAT_VALUES)[number];
103
-
104
- /**
105
- * Returns VOICE_CORPUS_WHERE with an additional `n.format = $format`
106
- * predicate appended. Requires `$format` to be bound alongside `$accountId`
107
- * in the calling Cypher parameters.
108
- *
109
- * Format-discovery queries (which enumerate distinct formats from the
110
- * corpus) use the base `VOICE_CORPUS_WHERE` directly; per-format corpus
111
- * walks use this function.
112
- */
113
- export function voiceCorpusWhereWithFormat(format: VoiceFormat): string {
114
- return `${VOICE_CORPUS_WHERE}\n AND n.format = $format`;
115
- }
116
-
117
- /**
118
- * Reserved sentinel userId for the per-account org voice profile (Task 676).
119
- *
120
- * Keeps the `(accountId, userId, format)` UNIQUE constraint valid with no
121
- * migration: exactly one org profile per `(account, format)`. A real
122
- * `:AdminUser` must never hold this userId — `voice-distil-profile` surfaces
123
- * a Neo4j constraint error (does not swallow it) if one does.
124
- */
125
- export const ORG_USER_ID = "__org__";
126
-
127
- /**
128
- * Voice profile scope. `personal` = one operator's own voice, keyed
129
- * `(accountId, userId, format)` and anchored on `:AdminUser`. `org` = the
130
- * account/house voice, keyed `(accountId, '__org__', format)` and anchored on
131
- * `:LocalBusiness`.
132
- */
133
- export type VoiceScope = "personal" | "org";
134
-
135
- /**
136
- * The profile-key userId for a scope: the operator's own id for personal, the
137
- * reserved sentinel for org. The single place that maps scope → key userId, so
138
- * distillation, retrieval, and feedback agree on where an org profile lives.
139
- */
140
- export function profileUserIdForScope(scope: VoiceScope, userId: string): string {
141
- return scope === "org" ? ORG_USER_ID : userId;
142
- }
143
-
144
- /**
145
- * `VOICE_CORPUS_WHERE` + format predicate + author predicate. Requires both
146
- * `$format` and `$voiceAuthor` bound in the calling Cypher parameters.
147
- *
148
- * Personal distillation and personal retrieval use this to narrow the
149
- * account-wide corpus to one author's content; org walks use the unchanged
150
- * account-wide `voiceCorpusWhereWithFormat`.
151
- */
152
- export function voiceCorpusWhereWithFormatAndAuthor(format: VoiceFormat): string {
153
- return `${VOICE_CORPUS_WHERE}\n AND n.format = $format\n AND n.voiceAuthor = $voiceAuthor`;
154
- }