@rubytech/create-maxy-code 0.1.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 (1821) hide show
  1. package/dist/__tests__/account-id-env.test.js +48 -0
  2. package/dist/__tests__/apt-resolve.test.js +179 -0
  3. package/dist/__tests__/brew-install.test.js +141 -0
  4. package/dist/__tests__/brew-resolve.test.js +103 -0
  5. package/dist/__tests__/cdp-port-no-silent-fallback.test.js +53 -0
  6. package/dist/__tests__/launchd-plist.test.js +149 -0
  7. package/dist/__tests__/macos-version.test.js +96 -0
  8. package/dist/__tests__/peer-brand-detect.test.js +103 -0
  9. package/dist/__tests__/platform-detect.test.js +50 -0
  10. package/dist/__tests__/port-canonicalisation.test.js +200 -0
  11. package/dist/__tests__/preflight-port-classifier.test.js +330 -0
  12. package/dist/__tests__/snap-chromium.test.js +115 -0
  13. package/dist/apt-resolve.js +73 -0
  14. package/dist/brew-install.js +175 -0
  15. package/dist/brew-resolve.js +68 -0
  16. package/dist/index.js +3325 -0
  17. package/dist/launchd-plist.js +68 -0
  18. package/dist/macos-version.js +53 -0
  19. package/dist/peer-brand-detect.js +39 -0
  20. package/dist/pinned-binaries.js +12 -0
  21. package/dist/platform-detect.js +36 -0
  22. package/dist/port-resolution.js +153 -0
  23. package/dist/preflight-port-classifier.js +222 -0
  24. package/dist/snap-chromium.js +89 -0
  25. package/dist/uninstall.js +861 -0
  26. package/package.json +32 -0
  27. package/payload/platform/config/brand-registry.json +20 -0
  28. package/payload/platform/config/brand.json +54 -0
  29. package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.d.ts +2 -0
  30. package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.d.ts.map +1 -0
  31. package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.js +88 -0
  32. package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.js.map +1 -0
  33. package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.d.ts +2 -0
  34. package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.d.ts.map +1 -0
  35. package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.js +55 -0
  36. package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.js.map +1 -0
  37. package/payload/platform/lib/account-enumeration/dist/index.d.ts +49 -0
  38. package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -0
  39. package/payload/platform/lib/account-enumeration/dist/index.js +109 -0
  40. package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -0
  41. package/payload/platform/lib/account-enumeration/src/__tests__/enumerate.test.ts +94 -0
  42. package/payload/platform/lib/account-enumeration/src/__tests__/validate-env.test.ts +57 -0
  43. package/payload/platform/lib/account-enumeration/src/index.ts +140 -0
  44. package/payload/platform/lib/account-enumeration/tsconfig.json +8 -0
  45. package/payload/platform/lib/admins-write/dist/index.d.ts +87 -0
  46. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -0
  47. package/payload/platform/lib/admins-write/dist/index.js +248 -0
  48. package/payload/platform/lib/admins-write/dist/index.js.map +1 -0
  49. package/payload/platform/lib/admins-write/src/index.ts +311 -0
  50. package/payload/platform/lib/admins-write/tsconfig.json +8 -0
  51. package/payload/platform/lib/anthropic-key/dist/index.d.ts +22 -0
  52. package/payload/platform/lib/anthropic-key/dist/index.d.ts.map +1 -0
  53. package/payload/platform/lib/anthropic-key/dist/index.js +232 -0
  54. package/payload/platform/lib/anthropic-key/dist/index.js.map +1 -0
  55. package/payload/platform/lib/brand-templating/dist/index.d.ts +18 -0
  56. package/payload/platform/lib/brand-templating/dist/index.d.ts.map +1 -0
  57. package/payload/platform/lib/brand-templating/dist/index.js +69 -0
  58. package/payload/platform/lib/brand-templating/dist/index.js.map +1 -0
  59. package/payload/platform/lib/brand-templating/src/index.ts +76 -0
  60. package/payload/platform/lib/brand-templating/tsconfig.json +8 -0
  61. package/payload/platform/lib/device-url/dist/index.d.ts +44 -0
  62. package/payload/platform/lib/device-url/dist/index.d.ts.map +1 -0
  63. package/payload/platform/lib/device-url/dist/index.js +68 -0
  64. package/payload/platform/lib/device-url/dist/index.js.map +1 -0
  65. package/payload/platform/lib/device-url/src/index.ts +78 -0
  66. package/payload/platform/lib/device-url/tsconfig.json +8 -0
  67. package/payload/platform/lib/entitlement/PUBKEY-HASH.txt +1 -0
  68. package/payload/platform/lib/entitlement/dist/canonicalize.d.ts +26 -0
  69. package/payload/platform/lib/entitlement/dist/canonicalize.d.ts.map +1 -0
  70. package/payload/platform/lib/entitlement/dist/canonicalize.js +54 -0
  71. package/payload/platform/lib/entitlement/dist/canonicalize.js.map +1 -0
  72. package/payload/platform/lib/entitlement/dist/index.d.ts +76 -0
  73. package/payload/platform/lib/entitlement/dist/index.d.ts.map +1 -0
  74. package/payload/platform/lib/entitlement/dist/index.js +293 -0
  75. package/payload/platform/lib/entitlement/dist/index.js.map +1 -0
  76. package/payload/platform/lib/entitlement/rubytech-pubkey.pem +3 -0
  77. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts +2 -0
  78. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts.map +1 -0
  79. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js +97 -0
  80. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js.map +1 -0
  81. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts +2 -0
  82. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts.map +1 -0
  83. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js +112 -0
  84. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js.map +1 -0
  85. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts +2 -0
  86. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts.map +1 -0
  87. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js +163 -0
  88. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js.map +1 -0
  89. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts +2 -0
  90. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts.map +1 -0
  91. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js +89 -0
  92. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js.map +1 -0
  93. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts +2 -0
  94. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts.map +1 -0
  95. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js +140 -0
  96. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js.map +1 -0
  97. package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.d.ts +37 -0
  98. package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.d.ts.map +1 -0
  99. package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.js +333 -0
  100. package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.js.map +1 -0
  101. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts +85 -0
  102. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts.map +1 -0
  103. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js +93 -0
  104. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js.map +1 -0
  105. package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.d.ts +71 -0
  106. package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.d.ts.map +1 -0
  107. package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.js +168 -0
  108. package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.js.map +1 -0
  109. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +50 -0
  110. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -0
  111. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +197 -0
  112. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -0
  113. package/payload/platform/lib/graph-mcp/dist/index.d.ts +26 -0
  114. package/payload/platform/lib/graph-mcp/dist/index.d.ts.map +1 -0
  115. package/payload/platform/lib/graph-mcp/dist/index.js +847 -0
  116. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -0
  117. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +75 -0
  118. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -0
  119. package/payload/platform/lib/graph-mcp/dist/schema-cache.js +217 -0
  120. package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -0
  121. package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts +42 -0
  122. package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts.map +1 -0
  123. package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js +87 -0
  124. package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js.map +1 -0
  125. package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate-write.test.ts +150 -0
  126. package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate.test.ts +141 -0
  127. package/payload/platform/lib/graph-mcp/src/__tests__/schema-cache.test.ts +169 -0
  128. package/payload/platform/lib/graph-mcp/src/__tests__/schema-cypher-parser.test.ts +99 -0
  129. package/payload/platform/lib/graph-mcp/src/__tests__/warnings-envelope.test.ts +151 -0
  130. package/payload/platform/lib/graph-mcp/src/cypher-rewrite-stamp.ts +349 -0
  131. package/payload/platform/lib/graph-mcp/src/cypher-shim-read.ts +141 -0
  132. package/payload/platform/lib/graph-mcp/src/cypher-shim-write.ts +240 -0
  133. package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +249 -0
  134. package/payload/platform/lib/graph-mcp/src/index.ts +1076 -0
  135. package/payload/platform/lib/graph-mcp/src/schema-cache.ts +242 -0
  136. package/payload/platform/lib/graph-mcp/src/schema-cypher-parser.ts +84 -0
  137. package/payload/platform/lib/graph-mcp/tsconfig.json +8 -0
  138. package/payload/platform/lib/graph-search/dist/index.d.ts +227 -0
  139. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -0
  140. package/payload/platform/lib/graph-search/dist/index.js +525 -0
  141. package/payload/platform/lib/graph-search/dist/index.js.map +1 -0
  142. package/payload/platform/lib/graph-search/src/__tests__/bm25-label-gate.test.ts +88 -0
  143. package/payload/platform/lib/graph-search/src/__tests__/bm25-only.test.ts +129 -0
  144. package/payload/platform/lib/graph-search/src/__tests__/bm25-strong-bypass-threshold.test.ts +126 -0
  145. package/payload/platform/lib/graph-search/src/__tests__/brochure-threshold.test.ts +136 -0
  146. package/payload/platform/lib/graph-search/src/__tests__/escape-and-normalise.test.ts +53 -0
  147. package/payload/platform/lib/graph-search/src/__tests__/expand-batch.test.ts +206 -0
  148. package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +280 -0
  149. package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +262 -0
  150. package/payload/platform/lib/graph-search/src/__tests__/vector-threshold.test.ts +170 -0
  151. package/payload/platform/lib/graph-search/src/index.ts +718 -0
  152. package/payload/platform/lib/graph-search/tsconfig.json +9 -0
  153. package/payload/platform/lib/graph-search/vitest.config.ts +9 -0
  154. package/payload/platform/lib/graph-trash/dist/index.d.ts +99 -0
  155. package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -0
  156. package/payload/platform/lib/graph-trash/dist/index.js +333 -0
  157. package/payload/platform/lib/graph-trash/dist/index.js.map +1 -0
  158. package/payload/platform/lib/graph-trash/src/index.ts +475 -0
  159. package/payload/platform/lib/graph-trash/tsconfig.json +8 -0
  160. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.d.ts +2 -0
  161. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.d.ts.map +1 -0
  162. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js +165 -0
  163. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js.map +1 -0
  164. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts +2 -0
  165. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts.map +1 -0
  166. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +226 -0
  167. package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -0
  168. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts +2 -0
  169. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts.map +1 -0
  170. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js +147 -0
  171. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js.map +1 -0
  172. package/payload/platform/lib/graph-write/dist/audit.d.ts +84 -0
  173. package/payload/platform/lib/graph-write/dist/audit.d.ts.map +1 -0
  174. package/payload/platform/lib/graph-write/dist/audit.js +129 -0
  175. package/payload/platform/lib/graph-write/dist/audit.js.map +1 -0
  176. package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +26 -0
  177. package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts.map +1 -0
  178. package/payload/platform/lib/graph-write/dist/conversation-provenance.js +81 -0
  179. package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -0
  180. package/payload/platform/lib/graph-write/dist/index.d.ts +124 -0
  181. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -0
  182. package/payload/platform/lib/graph-write/dist/index.js +288 -0
  183. package/payload/platform/lib/graph-write/dist/index.js.map +1 -0
  184. package/payload/platform/lib/graph-write/src/__tests__/account-id-gate.test.ts +189 -0
  185. package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +252 -0
  186. package/payload/platform/lib/graph-write/src/__tests__/audit.test.ts +162 -0
  187. package/payload/platform/lib/graph-write/src/audit.ts +182 -0
  188. package/payload/platform/lib/graph-write/src/conversation-provenance.ts +140 -0
  189. package/payload/platform/lib/graph-write/src/index.ts +386 -0
  190. package/payload/platform/lib/graph-write/tsconfig.json +8 -0
  191. package/payload/platform/lib/mcp-eager/dist/index.d.ts +61 -0
  192. package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -0
  193. package/payload/platform/lib/mcp-eager/dist/index.js +49 -0
  194. package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -0
  195. package/payload/platform/lib/mcp-eager/src/index.ts +78 -0
  196. package/payload/platform/lib/mcp-eager/tsconfig.json +8 -0
  197. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +53 -0
  198. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -0
  199. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +132 -0
  200. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -0
  201. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +134 -0
  202. package/payload/platform/lib/mcp-spawn-tee/tsconfig.json +8 -0
  203. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
  204. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
  205. package/payload/platform/lib/mcp-stderr-tee/dist/index.js +196 -0
  206. package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -0
  207. package/payload/platform/lib/mcp-stderr-tee/src/index.ts +206 -0
  208. package/payload/platform/lib/mcp-stderr-tee/tsconfig.json +8 -0
  209. package/payload/platform/lib/models/dist/index.d.ts +7 -0
  210. package/payload/platform/lib/models/dist/index.d.ts.map +1 -0
  211. package/payload/platform/lib/models/dist/index.js +20 -0
  212. package/payload/platform/lib/models/dist/index.js.map +1 -0
  213. package/payload/platform/lib/models/src/index.ts +18 -0
  214. package/payload/platform/lib/models/tsconfig.json +8 -0
  215. package/payload/platform/lib/oauth-llm/dist/index.d.ts +118 -0
  216. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -0
  217. package/payload/platform/lib/oauth-llm/dist/index.js +388 -0
  218. package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -0
  219. package/payload/platform/lib/oauth-llm/src/index.ts +585 -0
  220. package/payload/platform/lib/oauth-llm/tsconfig.json +8 -0
  221. package/payload/platform/lib/persistent-components/dist/index.d.ts +21 -0
  222. package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -0
  223. package/payload/platform/lib/persistent-components/dist/index.js +32 -0
  224. package/payload/platform/lib/persistent-components/dist/index.js.map +1 -0
  225. package/payload/platform/lib/persistent-components/src/index.ts +28 -0
  226. package/payload/platform/lib/persistent-components/tsconfig.json +8 -0
  227. package/payload/platform/lib/screening-patterns/dist/index.d.ts +29 -0
  228. package/payload/platform/lib/screening-patterns/dist/index.d.ts.map +1 -0
  229. package/payload/platform/lib/screening-patterns/dist/index.js +48 -0
  230. package/payload/platform/lib/screening-patterns/dist/index.js.map +1 -0
  231. package/payload/platform/lib/screening-patterns/src/index.ts +51 -0
  232. package/payload/platform/lib/screening-patterns/tsconfig.json +8 -0
  233. package/payload/platform/lib/task-secrets/dist/index.d.ts +40 -0
  234. package/payload/platform/lib/task-secrets/dist/index.d.ts.map +1 -0
  235. package/payload/platform/lib/task-secrets/dist/index.js +44 -0
  236. package/payload/platform/lib/task-secrets/dist/index.js.map +1 -0
  237. package/payload/platform/lib/task-secrets/src/__tests__/redact-secrets.test.ts +127 -0
  238. package/payload/platform/lib/task-secrets/src/index.ts +77 -0
  239. package/payload/platform/lib/task-secrets/tsconfig.json +9 -0
  240. package/payload/platform/lib/task-secrets/vitest.config.ts +9 -0
  241. package/payload/platform/neo4j/edge-annotations.json +154 -0
  242. package/payload/platform/neo4j/schema.cypher +1104 -0
  243. package/payload/platform/package-lock.json +3576 -0
  244. package/payload/platform/package.json +25 -0
  245. package/payload/platform/plugins/admin/PLUGIN.md +81 -0
  246. package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +191 -0
  247. package/payload/platform/plugins/admin/hooks/__tests__/playwright-file-guard.test.sh +278 -0
  248. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +204 -0
  249. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +225 -0
  250. package/payload/platform/plugins/admin/hooks/playwright-file-guard.sh +214 -0
  251. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +294 -0
  252. package/payload/platform/plugins/admin/hooks/webfetch-preflight.mjs +363 -0
  253. package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts +2 -0
  254. package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts.map +1 -0
  255. package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js +91 -0
  256. package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map +1 -0
  257. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts +2 -0
  258. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts.map +1 -0
  259. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js +98 -0
  260. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js.map +1 -0
  261. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.d.ts +2 -0
  262. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.d.ts.map +1 -0
  263. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.js +141 -0
  264. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.js.map +1 -0
  265. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.d.ts +2 -0
  266. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.d.ts.map +1 -0
  267. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.js +88 -0
  268. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.js.map +1 -0
  269. package/payload/platform/plugins/admin/mcp/dist/index.d.ts +2 -0
  270. package/payload/platform/plugins/admin/mcp/dist/index.d.ts.map +1 -0
  271. package/payload/platform/plugins/admin/mcp/dist/index.js +3495 -0
  272. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -0
  273. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.d.ts +5 -0
  274. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.d.ts.map +1 -0
  275. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js +40 -0
  276. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js.map +1 -0
  277. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts +39 -0
  278. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts.map +1 -0
  279. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js +249 -0
  280. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js.map +1 -0
  281. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts +15 -0
  282. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts.map +1 -0
  283. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js +73 -0
  284. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js.map +1 -0
  285. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +35 -0
  286. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -0
  287. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +140 -0
  288. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -0
  289. package/payload/platform/plugins/admin/mcp/package.json +23 -0
  290. package/payload/platform/plugins/admin/mcp/vitest.config.ts +9 -0
  291. package/payload/platform/plugins/admin/references/chat-ui-guide.md +31 -0
  292. package/payload/platform/plugins/admin/references/contextual-ui.md +107 -0
  293. package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +241 -0
  294. package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +28 -0
  295. package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +197 -0
  296. package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +53 -0
  297. package/payload/platform/plugins/admin/skills/datetime/SKILL.md +91 -0
  298. package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +418 -0
  299. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +255 -0
  300. package/payload/platform/plugins/admin/skills/plainly/SKILL.md +105 -0
  301. package/payload/platform/plugins/admin/skills/plainly/references/worked-examples.md +301 -0
  302. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +99 -0
  303. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +277 -0
  304. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +72 -0
  305. package/payload/platform/plugins/admin/skills/qr-code/SKILL.md +35 -0
  306. package/payload/platform/plugins/admin/skills/qr-code/references/data-formats.md +113 -0
  307. package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +113 -0
  308. package/payload/platform/plugins/admin/skills/skill-builder/references/lean-pattern.md +110 -0
  309. package/payload/platform/plugins/admin/skills/skill-builder/references/pdf-generation.md +30 -0
  310. package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +44 -0
  311. package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +71 -0
  312. package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +189 -0
  313. package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +79 -0
  314. package/payload/platform/plugins/admin/skills/unzip-attachment/__tests__/preflight.sh +148 -0
  315. package/payload/platform/plugins/admin/skills/unzip-attachment/references/safety.md +116 -0
  316. package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +47 -0
  317. package/payload/platform/plugins/anthropic/PLUGIN.md +40 -0
  318. package/payload/platform/plugins/anthropic/references/console-api.md +186 -0
  319. package/payload/platform/plugins/anthropic/references/setup-guide.md +36 -0
  320. package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +138 -0
  321. package/payload/platform/plugins/business-assistant/PLUGIN.md +59 -0
  322. package/payload/platform/plugins/business-assistant/references/crm.md +112 -0
  323. package/payload/platform/plugins/business-assistant/references/document-management.md +96 -0
  324. package/payload/platform/plugins/business-assistant/references/escalation.md +126 -0
  325. package/payload/platform/plugins/business-assistant/references/invoicing.md +163 -0
  326. package/payload/platform/plugins/business-assistant/references/profiling.md +50 -0
  327. package/payload/platform/plugins/business-assistant/references/quoting.md +56 -0
  328. package/payload/platform/plugins/business-assistant/references/scheduling.md +127 -0
  329. package/payload/platform/plugins/business-assistant/references/task-management.md +163 -0
  330. package/payload/platform/plugins/cloudflare/PLUGIN.md +73 -0
  331. package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts +2 -0
  332. package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts.map +1 -0
  333. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +29 -0
  334. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -0
  335. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +283 -0
  336. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -0
  337. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +1155 -0
  338. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -0
  339. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts +90 -0
  340. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts.map +1 -0
  341. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js +550 -0
  342. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js.map +1 -0
  343. package/payload/platform/plugins/cloudflare/mcp/package.json +18 -0
  344. package/payload/platform/plugins/cloudflare/mcp/vitest.config.ts +10 -0
  345. package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +108 -0
  346. package/payload/platform/plugins/cloudflare/references/manual-setup.md +481 -0
  347. package/payload/platform/plugins/cloudflare/references/reset-guide.md +118 -0
  348. package/payload/platform/plugins/cloudflare/scripts/_stream-log.sh +154 -0
  349. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.sh +98 -0
  350. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +751 -0
  351. package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +107 -0
  352. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +826 -0
  353. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +107 -0
  354. package/payload/platform/plugins/contacts/PLUGIN.md +31 -0
  355. package/payload/platform/plugins/contacts/mcp/dist/index.d.ts +2 -0
  356. package/payload/platform/plugins/contacts/mcp/dist/index.d.ts.map +1 -0
  357. package/payload/platform/plugins/contacts/mcp/dist/index.js +433 -0
  358. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -0
  359. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts +5 -0
  360. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts.map +1 -0
  361. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js +40 -0
  362. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js.map +1 -0
  363. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts +33 -0
  364. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts.map +1 -0
  365. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js +53 -0
  366. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js.map +1 -0
  367. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +23 -0
  368. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -0
  369. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +123 -0
  370. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -0
  371. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts +28 -0
  372. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts.map +1 -0
  373. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js +39 -0
  374. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js.map +1 -0
  375. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +41 -0
  376. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -0
  377. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +142 -0
  378. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -0
  379. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts +52 -0
  380. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts.map +1 -0
  381. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +119 -0
  382. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js.map +1 -0
  383. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts +23 -0
  384. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts.map +1 -0
  385. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js +49 -0
  386. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js.map +1 -0
  387. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +21 -0
  388. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -0
  389. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +70 -0
  390. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -0
  391. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts +14 -0
  392. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts.map +1 -0
  393. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js +43 -0
  394. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js.map +1 -0
  395. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts +18 -0
  396. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -0
  397. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +95 -0
  398. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -0
  399. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts +15 -0
  400. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -0
  401. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +72 -0
  402. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -0
  403. package/payload/platform/plugins/contacts/mcp/package.json +19 -0
  404. package/payload/platform/plugins/deep-research/PLUGIN.md +13 -0
  405. package/payload/platform/plugins/deep-research/skills/deep-research/SKILL.md +46 -0
  406. package/payload/platform/plugins/deep-research/skills/deep-research/references/citation-styles.md +52 -0
  407. package/payload/platform/plugins/deep-research/skills/deep-research/references/research-modes.md +22 -0
  408. package/payload/platform/plugins/deep-research/skills/deep-research/references/search-strategy.md +24 -0
  409. package/payload/platform/plugins/docs/PLUGIN.md +52 -0
  410. package/payload/platform/plugins/docs/references/access-control.md +73 -0
  411. package/payload/platform/plugins/docs/references/admin-session.md +80 -0
  412. package/payload/platform/plugins/docs/references/attachments.md +44 -0
  413. package/payload/platform/plugins/docs/references/cloudflare.md +111 -0
  414. package/payload/platform/plugins/docs/references/contacts-guide.md +102 -0
  415. package/payload/platform/plugins/docs/references/deployment.md +150 -0
  416. package/payload/platform/plugins/docs/references/getting-started.md +82 -0
  417. package/payload/platform/plugins/docs/references/graph.md +149 -0
  418. package/payload/platform/plugins/docs/references/internals.md +512 -0
  419. package/payload/platform/plugins/docs/references/memory-guide.md +119 -0
  420. package/payload/platform/plugins/docs/references/migration-guide.md +90 -0
  421. package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
  422. package/payload/platform/plugins/docs/references/platform.md +111 -0
  423. package/payload/platform/plugins/docs/references/plugins-guide.md +174 -0
  424. package/payload/platform/plugins/docs/references/projects-guide.md +73 -0
  425. package/payload/platform/plugins/docs/references/settings.md +82 -0
  426. package/payload/platform/plugins/docs/references/telegram-guide.md +58 -0
  427. package/payload/platform/plugins/docs/references/troubleshooting.md +532 -0
  428. package/payload/platform/plugins/email/PLUGIN.md +49 -0
  429. package/payload/platform/plugins/email/mcp/dist/index.d.ts +2 -0
  430. package/payload/platform/plugins/email/mcp/dist/index.d.ts.map +1 -0
  431. package/payload/platform/plugins/email/mcp/dist/index.js +291 -0
  432. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -0
  433. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +118 -0
  434. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -0
  435. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +364 -0
  436. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -0
  437. package/payload/platform/plugins/email/mcp/dist/lib/embedding.d.ts +2 -0
  438. package/payload/platform/plugins/email/mcp/dist/lib/embedding.d.ts.map +1 -0
  439. package/payload/platform/plugins/email/mcp/dist/lib/embedding.js +24 -0
  440. package/payload/platform/plugins/email/mcp/dist/lib/embedding.js.map +1 -0
  441. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +87 -0
  442. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -0
  443. package/payload/platform/plugins/email/mcp/dist/lib/graph.js +324 -0
  444. package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -0
  445. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +215 -0
  446. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -0
  447. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +735 -0
  448. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -0
  449. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.d.ts +5 -0
  450. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.d.ts.map +1 -0
  451. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js +40 -0
  452. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js.map +1 -0
  453. package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +32 -0
  454. package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -0
  455. package/payload/platform/plugins/email/mcp/dist/lib/providers.js +569 -0
  456. package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -0
  457. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts +29 -0
  458. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts.map +1 -0
  459. package/payload/platform/plugins/email/mcp/dist/lib/screening.js +105 -0
  460. package/payload/platform/plugins/email/mcp/dist/lib/screening.js.map +1 -0
  461. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +21 -0
  462. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -0
  463. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +77 -0
  464. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -0
  465. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts +38 -0
  466. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts.map +1 -0
  467. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +894 -0
  468. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +1 -0
  469. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts +25 -0
  470. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts.map +1 -0
  471. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +227 -0
  472. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -0
  473. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts +19 -0
  474. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts.map +1 -0
  475. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js +151 -0
  476. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js.map +1 -0
  477. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts +22 -0
  478. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts.map +1 -0
  479. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js +188 -0
  480. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js.map +1 -0
  481. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.d.ts +15 -0
  482. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.d.ts.map +1 -0
  483. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js +142 -0
  484. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js.map +1 -0
  485. package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts +14 -0
  486. package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -0
  487. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +75 -0
  488. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -0
  489. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +10 -0
  490. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -0
  491. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +83 -0
  492. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -0
  493. package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts +15 -0
  494. package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -0
  495. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +63 -0
  496. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -0
  497. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +10 -0
  498. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -0
  499. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +31 -0
  500. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -0
  501. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts +22 -0
  502. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -0
  503. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +183 -0
  504. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -0
  505. package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts +6 -0
  506. package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts.map +1 -0
  507. package/payload/platform/plugins/email/mcp/dist/tools/email-status.js +43 -0
  508. package/payload/platform/plugins/email/mcp/dist/tools/email-status.js.map +1 -0
  509. package/payload/platform/plugins/email/mcp/package.json +23 -0
  510. package/payload/platform/plugins/email/references/email-reference.md +204 -0
  511. package/payload/platform/plugins/linkedin-import/PLUGIN.md +27 -0
  512. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +142 -0
  513. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +135 -0
  514. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +95 -0
  515. package/payload/platform/plugins/memory/PLUGIN.md +146 -0
  516. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +879 -0
  517. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +138 -0
  518. package/payload/platform/plugins/memory/mcp/dist/index.d.ts +2 -0
  519. package/payload/platform/plugins/memory/mcp/dist/index.d.ts.map +1 -0
  520. package/payload/platform/plugins/memory/mcp/dist/index.js +1813 -0
  521. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -0
  522. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts +2 -0
  523. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts.map +1 -0
  524. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js +92 -0
  525. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js.map +1 -0
  526. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts +2 -0
  527. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts.map +1 -0
  528. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +225 -0
  529. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -0
  530. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts +2 -0
  531. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts.map +1 -0
  532. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +100 -0
  533. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -0
  534. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts +2 -0
  535. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts.map +1 -0
  536. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +448 -0
  537. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -0
  538. package/payload/platform/plugins/memory/mcp/dist/lib/attachments.d.ts +37 -0
  539. package/payload/platform/plugins/memory/mcp/dist/lib/attachments.d.ts.map +1 -0
  540. package/payload/platform/plugins/memory/mcp/dist/lib/attachments.js +69 -0
  541. package/payload/platform/plugins/memory/mcp/dist/lib/attachments.js.map +1 -0
  542. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +5 -0
  543. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -0
  544. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +30 -0
  545. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -0
  546. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts +49 -0
  547. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts.map +1 -0
  548. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js +35 -0
  549. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js.map +1 -0
  550. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +47 -0
  551. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -0
  552. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +31 -0
  553. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -0
  554. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts +3 -0
  555. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -0
  556. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +155 -0
  557. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -0
  558. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts +11 -0
  559. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -0
  560. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +20 -0
  561. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -0
  562. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +14 -0
  563. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -0
  564. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +38 -0
  565. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -0
  566. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +16 -0
  567. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -0
  568. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +59 -0
  569. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -0
  570. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts +9 -0
  571. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts.map +1 -0
  572. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js +32 -0
  573. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js.map +1 -0
  574. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts +3 -0
  575. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -0
  576. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +29 -0
  577. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -0
  578. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts +45 -0
  579. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts.map +1 -0
  580. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js +125 -0
  581. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js.map +1 -0
  582. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts +9 -0
  583. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -0
  584. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +61 -0
  585. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -0
  586. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +3 -0
  587. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -0
  588. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +29 -0
  589. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -0
  590. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts +36 -0
  591. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts.map +1 -0
  592. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js +86 -0
  593. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js.map +1 -0
  594. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts +42 -0
  595. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts.map +1 -0
  596. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js +114 -0
  597. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js.map +1 -0
  598. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +38 -0
  599. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -0
  600. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +89 -0
  601. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -0
  602. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts +136 -0
  603. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts.map +1 -0
  604. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js +180 -0
  605. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js.map +1 -0
  606. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +246 -0
  607. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -0
  608. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +828 -0
  609. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -0
  610. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +63 -0
  611. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -0
  612. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +210 -0
  613. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -0
  614. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +5 -0
  615. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -0
  616. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +40 -0
  617. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -0
  618. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +113 -0
  619. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -0
  620. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +455 -0
  621. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -0
  622. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts +83 -0
  623. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts.map +1 -0
  624. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js +209 -0
  625. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -0
  626. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts +3 -0
  627. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts.map +1 -0
  628. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js +12 -0
  629. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js.map +1 -0
  630. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +2 -0
  631. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -0
  632. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +97 -0
  633. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -0
  634. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts +2 -0
  635. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts.map +1 -0
  636. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +184 -0
  637. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -0
  638. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts +2 -0
  639. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts.map +1 -0
  640. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +73 -0
  641. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -0
  642. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts +2 -0
  643. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts.map +1 -0
  644. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +109 -0
  645. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -0
  646. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts +2 -0
  647. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts.map +1 -0
  648. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +84 -0
  649. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -0
  650. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts +2 -0
  651. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts.map +1 -0
  652. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +106 -0
  653. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -0
  654. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.d.ts +2 -0
  655. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.d.ts.map +1 -0
  656. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.js +87 -0
  657. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.js.map +1 -0
  658. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts +2 -0
  659. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts.map +1 -0
  660. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +148 -0
  661. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -0
  662. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +89 -0
  663. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -0
  664. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +542 -0
  665. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -0
  666. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts +41 -0
  667. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts.map +1 -0
  668. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js +116 -0
  669. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js.map +1 -0
  670. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts +8 -0
  671. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -0
  672. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +7 -0
  673. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -0
  674. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts +7 -0
  675. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts.map +1 -0
  676. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js +28 -0
  677. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js.map +1 -0
  678. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts +7 -0
  679. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts.map +1 -0
  680. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js +7 -0
  681. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js.map +1 -0
  682. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts +7 -0
  683. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts.map +1 -0
  684. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js +27 -0
  685. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js.map +1 -0
  686. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +54 -0
  687. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -0
  688. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +231 -0
  689. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -0
  690. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts +34 -0
  691. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +1 -0
  692. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +58 -0
  693. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -0
  694. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +57 -0
  695. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -0
  696. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +106 -0
  697. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -0
  698. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts +16 -0
  699. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts.map +1 -0
  700. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js +91 -0
  701. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js.map +1 -0
  702. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +22 -0
  703. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -0
  704. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +36 -0
  705. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -0
  706. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +58 -0
  707. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -0
  708. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +125 -0
  709. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -0
  710. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts +28 -0
  711. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -0
  712. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +93 -0
  713. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -0
  714. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts +20 -0
  715. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -0
  716. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +87 -0
  717. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -0
  718. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +129 -0
  719. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -0
  720. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +827 -0
  721. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -0
  722. package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.d.ts +19 -0
  723. package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.d.ts.map +1 -0
  724. package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.js +125 -0
  725. package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.js.map +1 -0
  726. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts +61 -0
  727. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts.map +1 -0
  728. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +102 -0
  729. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +1 -0
  730. package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.d.ts +12 -0
  731. package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.d.ts.map +1 -0
  732. package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.js +100 -0
  733. package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.js.map +1 -0
  734. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts +9 -0
  735. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -0
  736. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +84 -0
  737. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -0
  738. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.d.ts +13 -0
  739. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.d.ts.map +1 -0
  740. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js +63 -0
  741. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js.map +1 -0
  742. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts +24 -0
  743. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts.map +1 -0
  744. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js +40 -0
  745. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js.map +1 -0
  746. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +5 -0
  747. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -0
  748. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +42 -0
  749. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -0
  750. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +13 -0
  751. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -0
  752. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +77 -0
  753. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -0
  754. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +47 -0
  755. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -0
  756. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +173 -0
  757. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -0
  758. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +24 -0
  759. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -0
  760. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +31 -0
  761. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -0
  762. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +44 -0
  763. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -0
  764. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +322 -0
  765. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -0
  766. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +65 -0
  767. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -0
  768. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +369 -0
  769. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -0
  770. package/payload/platform/plugins/memory/mcp/package.json +24 -0
  771. package/payload/platform/plugins/memory/mcp/scripts/boot-smoke.sh +69 -0
  772. package/payload/platform/plugins/memory/mcp/scripts/graph/accept.sh +217 -0
  773. package/payload/platform/plugins/memory/mcp/scripts/graph/fixture.cypher +59 -0
  774. package/payload/platform/plugins/memory/mcp/vitest.config.ts +15 -0
  775. package/payload/platform/plugins/memory/references/graph-primitives.md +342 -0
  776. package/payload/platform/plugins/memory/references/schema-base.md +234 -0
  777. package/payload/platform/plugins/memory/references/schema-creator.md +35 -0
  778. package/payload/platform/plugins/memory/references/schema-estate-agent.md +35 -0
  779. package/payload/platform/plugins/memory/references/schema-food-beverage.md +32 -0
  780. package/payload/platform/plugins/memory/references/schema-hospitality.md +31 -0
  781. package/payload/platform/plugins/memory/references/schema-logistics.md +30 -0
  782. package/payload/platform/plugins/memory/references/schema-professional-services.md +33 -0
  783. package/payload/platform/plugins/memory/references/schema-retail.md +33 -0
  784. package/payload/platform/plugins/memory/references/schema-trades.md +36 -0
  785. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +202 -0
  786. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +159 -0
  787. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +108 -0
  788. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +267 -0
  789. package/payload/platform/plugins/outlook/PLUGIN.md +48 -0
  790. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts +2 -0
  791. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts.map +1 -0
  792. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +94 -0
  793. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -0
  794. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts +2 -0
  795. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts.map +1 -0
  796. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js +31 -0
  797. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js.map +1 -0
  798. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +2 -0
  799. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts.map +1 -0
  800. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +213 -0
  801. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -0
  802. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts +2 -0
  803. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
  804. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +130 -0
  805. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -0
  806. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +65 -0
  807. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -0
  808. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +261 -0
  809. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -0
  810. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +61 -0
  811. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -0
  812. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +170 -0
  813. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -0
  814. package/payload/platform/plugins/outlook/mcp/dist/index.d.ts +18 -0
  815. package/payload/platform/plugins/outlook/mcp/dist/index.d.ts.map +1 -0
  816. package/payload/platform/plugins/outlook/mcp/dist/index.js +152 -0
  817. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -0
  818. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +60 -0
  819. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -0
  820. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +189 -0
  821. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -0
  822. package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts +23 -0
  823. package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts.map +1 -0
  824. package/payload/platform/plugins/outlook/mcp/dist/lib/log.js +53 -0
  825. package/payload/platform/plugins/outlook/mcp/dist/lib/log.js.map +1 -0
  826. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +26 -0
  827. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -0
  828. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +50 -0
  829. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -0
  830. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts +12 -0
  831. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -0
  832. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +32 -0
  833. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -0
  834. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts +59 -0
  835. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts.map +1 -0
  836. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js +54 -0
  837. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js.map +1 -0
  838. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts +14 -0
  839. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts.map +1 -0
  840. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js +45 -0
  841. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js.map +1 -0
  842. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +15 -0
  843. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -0
  844. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +48 -0
  845. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -0
  846. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +8 -0
  847. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -0
  848. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +49 -0
  849. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -0
  850. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +19 -0
  851. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -0
  852. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +58 -0
  853. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -0
  854. package/payload/platform/plugins/outlook/mcp/package.json +20 -0
  855. package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +109 -0
  856. package/payload/platform/plugins/outlook/references/auth.md +118 -0
  857. package/payload/platform/plugins/outlook/references/graph-surfaces.md +114 -0
  858. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +65 -0
  859. package/payload/platform/plugins/projects/PLUGIN.md +90 -0
  860. package/payload/platform/plugins/projects/references/investigation.md +63 -0
  861. package/payload/platform/plugins/projects/references/retrospective.md +71 -0
  862. package/payload/platform/plugins/projects/references/review.md +51 -0
  863. package/payload/platform/plugins/projects/references/sprint.md +168 -0
  864. package/payload/platform/plugins/replicate/PLUGIN.md +23 -0
  865. package/payload/platform/plugins/replicate/mcp/dist/index.d.ts +2 -0
  866. package/payload/platform/plugins/replicate/mcp/dist/index.d.ts.map +1 -0
  867. package/payload/platform/plugins/replicate/mcp/dist/index.js +106 -0
  868. package/payload/platform/plugins/replicate/mcp/dist/index.js.map +1 -0
  869. package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.d.ts +15 -0
  870. package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.d.ts.map +1 -0
  871. package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.js +73 -0
  872. package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.js.map +1 -0
  873. package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.d.ts +14 -0
  874. package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.d.ts.map +1 -0
  875. package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.js +191 -0
  876. package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.js.map +1 -0
  877. package/payload/platform/plugins/replicate/mcp/package.json +21 -0
  878. package/payload/platform/plugins/sales/PLUGIN.md +106 -0
  879. package/payload/platform/plugins/sales/references/close-tracking.md +69 -0
  880. package/payload/platform/plugins/sales/references/comparisons.md +99 -0
  881. package/payload/platform/plugins/sales/references/competitive-positioning.md +51 -0
  882. package/payload/platform/plugins/sales/references/faq.md +77 -0
  883. package/payload/platform/plugins/sales/references/objection-handling.md +157 -0
  884. package/payload/platform/plugins/sales/references/pricing.md +101 -0
  885. package/payload/platform/plugins/scheduling/PLUGIN.md +103 -0
  886. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.d.ts +2 -0
  887. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.d.ts.map +1 -0
  888. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.js +16 -0
  889. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.js.map +1 -0
  890. package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts +2 -0
  891. package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts.map +1 -0
  892. package/payload/platform/plugins/scheduling/mcp/dist/index.js +315 -0
  893. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -0
  894. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts +2 -0
  895. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts.map +1 -0
  896. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js +119 -0
  897. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map +1 -0
  898. package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.d.ts +2 -0
  899. package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.d.ts.map +1 -0
  900. package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.js +19 -0
  901. package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.js.map +1 -0
  902. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts +47 -0
  903. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts.map +1 -0
  904. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js +362 -0
  905. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js.map +1 -0
  906. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts +30 -0
  907. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts.map +1 -0
  908. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +89 -0
  909. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -0
  910. package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.d.ts +48 -0
  911. package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.d.ts.map +1 -0
  912. package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.js +140 -0
  913. package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.js.map +1 -0
  914. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +20 -0
  915. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -0
  916. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +568 -0
  917. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -0
  918. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts +7 -0
  919. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -0
  920. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +23 -0
  921. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -0
  922. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +25 -0
  923. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -0
  924. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +121 -0
  925. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -0
  926. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.d.ts +9 -0
  927. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.d.ts.map +1 -0
  928. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js +76 -0
  929. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js.map +1 -0
  930. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +25 -0
  931. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -0
  932. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +53 -0
  933. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -0
  934. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.d.ts +8 -0
  935. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.d.ts.map +1 -0
  936. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.js +48 -0
  937. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.js.map +1 -0
  938. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +20 -0
  939. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -0
  940. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +76 -0
  941. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -0
  942. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts +18 -0
  943. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -0
  944. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +167 -0
  945. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -0
  946. package/payload/platform/plugins/scheduling/mcp/package.json +23 -0
  947. package/payload/platform/plugins/scheduling/mcp/vitest.config.ts +9 -0
  948. package/payload/platform/plugins/tasks/.claude-plugin/plugin.json +17 -0
  949. package/payload/platform/plugins/tasks/.mcp.json +13 -0
  950. package/payload/platform/plugins/tasks/PLUGIN.md +81 -0
  951. package/payload/platform/plugins/tasks/mcp/dist/index.d.ts +2 -0
  952. package/payload/platform/plugins/tasks/mcp/dist/index.d.ts.map +1 -0
  953. package/payload/platform/plugins/tasks/mcp/dist/index.js +503 -0
  954. package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -0
  955. package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.d.ts +7 -0
  956. package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.d.ts.map +1 -0
  957. package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.js +24 -0
  958. package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.js.map +1 -0
  959. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.d.ts +5 -0
  960. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.d.ts.map +1 -0
  961. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js +40 -0
  962. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js.map +1 -0
  963. package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.d.ts +17 -0
  964. package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.d.ts.map +1 -0
  965. package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.js +76 -0
  966. package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.js.map +1 -0
  967. package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.d.ts +29 -0
  968. package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.d.ts.map +1 -0
  969. package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.js +235 -0
  970. package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.js.map +1 -0
  971. package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.d.ts +40 -0
  972. package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.d.ts.map +1 -0
  973. package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.js +125 -0
  974. package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.js.map +1 -0
  975. package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.d.ts +26 -0
  976. package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.d.ts.map +1 -0
  977. package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.js +81 -0
  978. package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.js.map +1 -0
  979. package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.d.ts +19 -0
  980. package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.d.ts.map +1 -0
  981. package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.js +102 -0
  982. package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.js.map +1 -0
  983. package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.d.ts +20 -0
  984. package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.d.ts.map +1 -0
  985. package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.js +37 -0
  986. package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.js.map +1 -0
  987. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.d.ts +12 -0
  988. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.d.ts.map +1 -0
  989. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js +28 -0
  990. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js.map +1 -0
  991. package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.d.ts +16 -0
  992. package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.d.ts.map +1 -0
  993. package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.js +33 -0
  994. package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.js.map +1 -0
  995. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts +63 -0
  996. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts.map +1 -0
  997. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js +141 -0
  998. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js.map +1 -0
  999. package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.d.ts +19 -0
  1000. package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.d.ts.map +1 -0
  1001. package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.js +51 -0
  1002. package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.js.map +1 -0
  1003. package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.d.ts +18 -0
  1004. package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.d.ts.map +1 -0
  1005. package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.js +66 -0
  1006. package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.js.map +1 -0
  1007. package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.d.ts +21 -0
  1008. package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.d.ts.map +1 -0
  1009. package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.js +54 -0
  1010. package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.js.map +1 -0
  1011. package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.d.ts +12 -0
  1012. package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.d.ts.map +1 -0
  1013. package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.js +59 -0
  1014. package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.js.map +1 -0
  1015. package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts +32 -0
  1016. package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts.map +1 -0
  1017. package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js +112 -0
  1018. package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js.map +1 -0
  1019. package/payload/platform/plugins/tasks/mcp/package.json +20 -0
  1020. package/payload/platform/plugins/telegram/PLUGIN.md +35 -0
  1021. package/payload/platform/plugins/telegram/mcp/dist/index.d.ts +2 -0
  1022. package/payload/platform/plugins/telegram/mcp/dist/index.d.ts.map +1 -0
  1023. package/payload/platform/plugins/telegram/mcp/dist/index.js +198 -0
  1024. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -0
  1025. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +41 -0
  1026. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -0
  1027. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +70 -0
  1028. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -0
  1029. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts +16 -0
  1030. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts.map +1 -0
  1031. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +68 -0
  1032. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -0
  1033. package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +20 -0
  1034. package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -0
  1035. package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +34 -0
  1036. package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -0
  1037. package/payload/platform/plugins/telegram/mcp/package.json +19 -0
  1038. package/payload/platform/plugins/telegram/references/setup-guide.md +50 -0
  1039. package/payload/platform/plugins/waitlist/PLUGIN.md +63 -0
  1040. package/payload/platform/plugins/waitlist/mcp/dist/index.d.ts +2 -0
  1041. package/payload/platform/plugins/waitlist/mcp/dist/index.d.ts.map +1 -0
  1042. package/payload/platform/plugins/waitlist/mcp/dist/index.js +328 -0
  1043. package/payload/platform/plugins/waitlist/mcp/dist/index.js.map +1 -0
  1044. package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.d.ts +3 -0
  1045. package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.d.ts.map +1 -0
  1046. package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.js +48 -0
  1047. package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.js.map +1 -0
  1048. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.d.ts +5 -0
  1049. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.d.ts.map +1 -0
  1050. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js +40 -0
  1051. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js.map +1 -0
  1052. package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.d.ts +2 -0
  1053. package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.d.ts.map +1 -0
  1054. package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.js +70 -0
  1055. package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.js.map +1 -0
  1056. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.d.ts +33 -0
  1057. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.d.ts.map +1 -0
  1058. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.js +124 -0
  1059. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.js.map +1 -0
  1060. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.d.ts +23 -0
  1061. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.d.ts.map +1 -0
  1062. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.js +58 -0
  1063. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.js.map +1 -0
  1064. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.d.ts +83 -0
  1065. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.d.ts.map +1 -0
  1066. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.js +433 -0
  1067. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.js.map +1 -0
  1068. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.d.ts +19 -0
  1069. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.d.ts.map +1 -0
  1070. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.js +81 -0
  1071. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.js.map +1 -0
  1072. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.d.ts +28 -0
  1073. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.d.ts.map +1 -0
  1074. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.js +50 -0
  1075. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.js.map +1 -0
  1076. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.d.ts +33 -0
  1077. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.d.ts.map +1 -0
  1078. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.js +335 -0
  1079. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.js.map +1 -0
  1080. package/payload/platform/plugins/waitlist/mcp/package.json +23 -0
  1081. package/payload/platform/plugins/waitlist/mcp/vitest.config.ts +9 -0
  1082. package/payload/platform/plugins/whatsapp/PLUGIN.md +71 -0
  1083. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts +14 -0
  1084. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts.map +1 -0
  1085. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js +42 -0
  1086. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js.map +1 -0
  1087. package/payload/platform/plugins/whatsapp/mcp/dist/index.d.ts +8 -0
  1088. package/payload/platform/plugins/whatsapp/mcp/dist/index.d.ts.map +1 -0
  1089. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +469 -0
  1090. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -0
  1091. package/payload/platform/plugins/whatsapp/mcp/package.json +19 -0
  1092. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +257 -0
  1093. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +82 -0
  1094. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +99 -0
  1095. package/payload/platform/plugins/workflows/.claude-plugin/plugin.json +16 -0
  1096. package/payload/platform/plugins/workflows/.mcp.json +12 -0
  1097. package/payload/platform/plugins/workflows/PLUGIN.md +297 -0
  1098. package/payload/platform/plugins/workflows/mcp/dist/index.d.ts +2 -0
  1099. package/payload/platform/plugins/workflows/mcp/dist/index.d.ts.map +1 -0
  1100. package/payload/platform/plugins/workflows/mcp/dist/index.js +536 -0
  1101. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -0
  1102. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts +38 -0
  1103. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts.map +1 -0
  1104. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js +83 -0
  1105. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js.map +1 -0
  1106. package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts +2 -0
  1107. package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts.map +1 -0
  1108. package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.js +19 -0
  1109. package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.js.map +1 -0
  1110. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts +151 -0
  1111. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts.map +1 -0
  1112. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js +299 -0
  1113. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js.map +1 -0
  1114. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.d.ts +5 -0
  1115. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.d.ts.map +1 -0
  1116. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js +40 -0
  1117. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js.map +1 -0
  1118. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts +153 -0
  1119. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts.map +1 -0
  1120. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js +273 -0
  1121. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js.map +1 -0
  1122. package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.d.ts +32 -0
  1123. package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.d.ts.map +1 -0
  1124. package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.js +97 -0
  1125. package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.js.map +1 -0
  1126. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts +48 -0
  1127. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts.map +1 -0
  1128. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +166 -0
  1129. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -0
  1130. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts +28 -0
  1131. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts.map +1 -0
  1132. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js +57 -0
  1133. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js.map +1 -0
  1134. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts +64 -0
  1135. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -0
  1136. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +605 -0
  1137. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -0
  1138. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts +29 -0
  1139. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts.map +1 -0
  1140. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js +55 -0
  1141. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js.map +1 -0
  1142. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts +16 -0
  1143. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts.map +1 -0
  1144. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js +44 -0
  1145. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js.map +1 -0
  1146. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts +28 -0
  1147. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts.map +1 -0
  1148. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js +71 -0
  1149. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js.map +1 -0
  1150. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts +11 -0
  1151. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -0
  1152. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +198 -0
  1153. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -0
  1154. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts +13 -0
  1155. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts.map +1 -0
  1156. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js +71 -0
  1157. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js.map +1 -0
  1158. package/payload/platform/plugins/workflows/mcp/package.json +20 -0
  1159. package/payload/platform/plugins/workflows/mcp/test-runner.mjs +710 -0
  1160. package/payload/platform/plugins/workflows/mcp/test-workflows.sh +77 -0
  1161. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +88 -0
  1162. package/payload/platform/scripts/__tests__/admin-persist-audit.test.ts +182 -0
  1163. package/payload/platform/scripts/__tests__/first-token-creates-stream-log.test.sh +37 -0
  1164. package/payload/platform/scripts/__tests__/logs-read-prefix.sh +237 -0
  1165. package/payload/platform/scripts/admin-conversation-recover.mjs +386 -0
  1166. package/payload/platform/scripts/admin-persist-audit.ts +217 -0
  1167. package/payload/platform/scripts/check-no-conversation-id-leaks.mjs +165 -0
  1168. package/payload/platform/scripts/check-no-task-id-leaks.mjs +110 -0
  1169. package/payload/platform/scripts/check-sdk-oauth.mjs +185 -0
  1170. package/payload/platform/scripts/check-skill-load-coverage.mjs +100 -0
  1171. package/payload/platform/scripts/component-knowledgedoc-backfill.ts +214 -0
  1172. package/payload/platform/scripts/conversation-id-allowlist.txt +151 -0
  1173. package/payload/platform/scripts/dedupe-userprofile-ghosts.sh +388 -0
  1174. package/payload/platform/scripts/generate-entitlement-fixture.mjs +152 -0
  1175. package/payload/platform/scripts/installer-device-verify.sh +249 -0
  1176. package/payload/platform/scripts/lib/resolve-account-dir.sh +186 -0
  1177. package/payload/platform/scripts/log-adherence-check.sh +125 -0
  1178. package/payload/platform/scripts/logs-read.sh +577 -0
  1179. package/payload/platform/scripts/logs-read.test.sh +159 -0
  1180. package/payload/platform/scripts/migrate-import.sh +437 -0
  1181. package/payload/platform/scripts/redact-install-logs.sh +87 -0
  1182. package/payload/platform/scripts/resume-tunnel.sh +117 -0
  1183. package/payload/platform/scripts/seed-neo4j.sh +590 -0
  1184. package/payload/platform/scripts/taskmaster-export.sh +388 -0
  1185. package/payload/platform/scripts/test-laptop-vnc-boot.sh +88 -0
  1186. package/payload/platform/scripts/verify-skill-tool-surface.sh +255 -0
  1187. package/payload/platform/scripts/vnc.sh +475 -0
  1188. package/payload/platform/scripts/wifi-provision-server/server.js +743 -0
  1189. package/payload/platform/scripts/wifi-provision.sh +492 -0
  1190. package/payload/platform/services/claude-session-manager/dist/config.d.ts +12 -0
  1191. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -0
  1192. package/payload/platform/services/claude-session-manager/dist/config.js +27 -0
  1193. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -0
  1194. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +10 -0
  1195. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -0
  1196. package/payload/platform/services/claude-session-manager/dist/http-server.js +186 -0
  1197. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -0
  1198. package/payload/platform/services/claude-session-manager/dist/index.d.ts +2 -0
  1199. package/payload/platform/services/claude-session-manager/dist/index.d.ts.map +1 -0
  1200. package/payload/platform/services/claude-session-manager/dist/index.js +64 -0
  1201. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -0
  1202. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +4 -0
  1203. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -0
  1204. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +31 -0
  1205. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -0
  1206. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +34 -0
  1207. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -0
  1208. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +91 -0
  1209. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -0
  1210. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +23 -0
  1211. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -0
  1212. package/payload/platform/services/claude-session-manager/dist/session-store.js +31 -0
  1213. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -0
  1214. package/payload/platform/services/claude-session-manager/dist/types.d.ts +33 -0
  1215. package/payload/platform/services/claude-session-manager/dist/types.d.ts.map +1 -0
  1216. package/payload/platform/services/claude-session-manager/dist/types.js +2 -0
  1217. package/payload/platform/services/claude-session-manager/dist/types.js.map +1 -0
  1218. package/payload/platform/services/claude-session-manager/dist/url-capture.d.ts +12 -0
  1219. package/payload/platform/services/claude-session-manager/dist/url-capture.d.ts.map +1 -0
  1220. package/payload/platform/services/claude-session-manager/dist/url-capture.js +68 -0
  1221. package/payload/platform/services/claude-session-manager/dist/url-capture.js.map +1 -0
  1222. package/payload/platform/services/claude-session-manager/package.json +22 -0
  1223. package/payload/platform/templates/account.json +12 -0
  1224. package/payload/platform/templates/agents/admin/AGENTS.md +12 -0
  1225. package/payload/platform/templates/agents/admin/IDENTITY.md +320 -0
  1226. package/payload/platform/templates/agents/admin/LEARNINGS.md +3 -0
  1227. package/payload/platform/templates/agents/admin/SOUL.md +23 -0
  1228. package/payload/platform/templates/agents/public/IDENTITY.md +56 -0
  1229. package/payload/platform/templates/agents/public/SOUL.md +19 -0
  1230. package/payload/platform/templates/agents/public/config.json +9 -0
  1231. package/payload/platform/templates/specialists/.claude-plugin/plugin.json +4 -0
  1232. package/payload/platform/templates/specialists/agents/content-producer.md +104 -0
  1233. package/payload/platform/templates/specialists/agents/database-operator.md +199 -0
  1234. package/payload/platform/templates/specialists/agents/personal-assistant.md +209 -0
  1235. package/payload/platform/templates/specialists/agents/project-manager.md +132 -0
  1236. package/payload/platform/templates/specialists/agents/research-assistant.md +115 -0
  1237. package/payload/platform/templates/systemd/edge.service.template +38 -0
  1238. package/payload/platform/tsconfig.base.json +18 -0
  1239. package/payload/premium-plugins/real-agency/BUNDLE.md +44 -0
  1240. package/payload/premium-plugins/real-agency/agents/buyer-enquiry/IDENTITY.md +13 -0
  1241. package/payload/premium-plugins/real-agency/agents/buyer-enquiry/SOUL.md +9 -0
  1242. package/payload/premium-plugins/real-agency/agents/buyer-enquiry/template.json +9 -0
  1243. package/payload/premium-plugins/real-agency/agents/compliance.md +303 -0
  1244. package/payload/premium-plugins/real-agency/agents/negotiator.md +145 -0
  1245. package/payload/premium-plugins/real-agency/agents/valuer.md +140 -0
  1246. package/payload/premium-plugins/real-agency/plugins/brochures/PLUGIN.md +36 -0
  1247. package/payload/premium-plugins/real-agency/plugins/brochures/commands/make-brochure.md +11 -0
  1248. package/payload/premium-plugins/real-agency/plugins/brochures/skills/a4-print-documents/SKILL.md +478 -0
  1249. package/payload/premium-plugins/real-agency/plugins/brochures/skills/brand-design/SKILL.md +192 -0
  1250. package/payload/premium-plugins/real-agency/plugins/brochures/skills/make-brochure/SKILL.md +354 -0
  1251. package/payload/premium-plugins/real-agency/plugins/brochures/skills/make-brochure/references/seller-brief-template.md +115 -0
  1252. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/SKILL.md +119 -0
  1253. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/build.md +270 -0
  1254. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/copy.md +211 -0
  1255. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/images.md +166 -0
  1256. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/page-landing.md +376 -0
  1257. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/page.html +1288 -0
  1258. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/placeholders.md +250 -0
  1259. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/registers.md +47 -0
  1260. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/seller-brief.md +56 -0
  1261. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/structure.md +249 -0
  1262. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/template.html +2370 -0
  1263. package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-extract/SKILL.md +372 -0
  1264. package/payload/premium-plugins/real-agency/plugins/buyers/PLUGIN.md +35 -0
  1265. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-feedback/SKILL.md +109 -0
  1266. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/SKILL.md +42 -0
  1267. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-qualification-questions.md +16 -0
  1268. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-qualification.md +59 -0
  1269. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-scripts.md +63 -0
  1270. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-working-scripts.md +54 -0
  1271. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/feedback-collection.md +42 -0
  1272. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/offer-capture.md +38 -0
  1273. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/viewing-booking.md +32 -0
  1274. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/viewing-management.md +52 -0
  1275. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/SKILL.md +407 -0
  1276. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/care-fees-guide.md +68 -0
  1277. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/divorce-sales-guide.md +61 -0
  1278. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/downsizing-guide.md +45 -0
  1279. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/first-time-buyers.md +92 -0
  1280. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/first-time-sellers.md +78 -0
  1281. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/probate-guide.md +53 -0
  1282. package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/upsizing-guide.md +41 -0
  1283. package/payload/premium-plugins/real-agency/plugins/buyers/skills/property-enquiry/SKILL.md +126 -0
  1284. package/payload/premium-plugins/real-agency/plugins/buyers/skills/viewing-management/SKILL.md +111 -0
  1285. package/payload/premium-plugins/real-agency/plugins/estate-business/PLUGIN.md +34 -0
  1286. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/SKILL.md +133 -0
  1287. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/buy-back-your-time.md +37 -0
  1288. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/firewave-gost-scorecards.md +14 -0
  1289. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/keller-org-model.md +17 -0
  1290. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/lencioni-team-models.md +22 -0
  1291. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/listing-management-system.md +11 -0
  1292. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/net-figure-form.md +11 -0
  1293. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/serhant-bizinbox-notes.md +13 -0
  1294. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/team-roles-commission.md +14 -0
  1295. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/va-2026-ops.md +43 -0
  1296. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/wingman-structure.md +13 -0
  1297. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/SKILL.md +32 -0
  1298. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/crm-systems.md +57 -0
  1299. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/hiring-guide.md +59 -0
  1300. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/impact-framework.md +47 -0
  1301. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/minutes-equal-money.md +55 -0
  1302. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/team-management.md +48 -0
  1303. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/SKILL.md +52 -0
  1304. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/12-reasons.md +39 -0
  1305. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/95-5-system.md +66 -0
  1306. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/agent-attraction-scripts.md +90 -0
  1307. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/business-partnership.md +92 -0
  1308. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/exp-model-overview.md +66 -0
  1309. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/model-comparison.md +66 -0
  1310. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/revenue-share-explained.md +57 -0
  1311. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/SKILL.md +117 -0
  1312. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/attraction-agent-notes.md +31 -0
  1313. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/attraction-agent.md +58 -0
  1314. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/authenticity-boundaries.md +28 -0
  1315. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/become-a-brand-leader-notes.md +19 -0
  1316. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/blast-formula.md +42 -0
  1317. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/brand-leader.md +48 -0
  1318. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/brand-strategy-system.md +59 -0
  1319. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/content-engine.md +49 -0
  1320. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/firewave-blast-and-blogging.md +23 -0
  1321. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/gary-v-content.md +52 -0
  1322. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/gary-v-principles.md +20 -0
  1323. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/oversubscribed-positioning.md +18 -0
  1324. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/platforms.md +41 -0
  1325. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/priestley-oversubscribed.md +54 -0
  1326. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/storeys-style-examples.md +25 -0
  1327. package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/visual-identity.md +27 -0
  1328. package/payload/premium-plugins/real-agency/plugins/estate-coaching/PLUGIN.md +55 -0
  1329. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/SKILL.md +371 -0
  1330. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/atomic-habits.md +52 -0
  1331. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/daily-routine-scorecard.md +104 -0
  1332. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/hp6-model.md +63 -0
  1333. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/twelve-week-year.md +71 -0
  1334. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/SKILL.md +36 -0
  1335. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/coaching-boundaries.md +56 -0
  1336. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/feedback-framework.md +61 -0
  1337. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/performance-framework.md +109 -0
  1338. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/SKILL.md +421 -0
  1339. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/coaching-exercises.md +86 -0
  1340. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/goal-setting.md +78 -0
  1341. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/one-to-one-framework.md +92 -0
  1342. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/soi-workbook.md +103 -0
  1343. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/SKILL.md +410 -0
  1344. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/agent-training-guide.md +70 -0
  1345. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/business-in-a-box.md +72 -0
  1346. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/buyers-guide.md +53 -0
  1347. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/codo-method.md +72 -0
  1348. package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/website-planning-guide.md +79 -0
  1349. package/payload/premium-plugins/real-agency/plugins/estate-onboarding/PLUGIN.md +31 -0
  1350. package/payload/premium-plugins/real-agency/plugins/estate-onboarding/skills/bootstrap/SKILL.md +26 -0
  1351. package/payload/premium-plugins/real-agency/plugins/estate-onboarding/skills/bootstrap/references/onboarding-flow.md +63 -0
  1352. package/payload/premium-plugins/real-agency/plugins/estate-sales/PLUGIN.md +34 -0
  1353. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/SKILL.md +35 -0
  1354. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/deal-saving.md +47 -0
  1355. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-deep-guide.md +64 -0
  1356. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-prep-principles.md +29 -0
  1357. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-techniques.md +42 -0
  1358. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/offer-presentation.md +43 -0
  1359. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-closer/SKILL.md +24 -0
  1360. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-closer/references/serhant-emotion-stages.md +36 -0
  1361. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/SKILL.md +30 -0
  1362. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/chris-voss-discovery.md +88 -0
  1363. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/firewave-gost-journey.md +68 -0
  1364. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/phil-jones-openers.md +78 -0
  1365. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/pre-listing-checklist.md +77 -0
  1366. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/serhant-improv.md +22 -0
  1367. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/tom-ferry-discovery.md +103 -0
  1368. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/vendor-motivation-competitor.md +52 -0
  1369. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/SKILL.md +29 -0
  1370. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/chris-voss-negotiation.md +70 -0
  1371. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/phil-jones-price-words.md +40 -0
  1372. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/serhant-negotiation-plus.md +55 -0
  1373. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/tom-panos-commission-pricing.md +57 -0
  1374. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/tony-morris-questioning.md +54 -0
  1375. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/SKILL.md +27 -0
  1376. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/references/conveyancing-guide.md +54 -0
  1377. package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/references/transaction-tracking.md +66 -0
  1378. package/payload/premium-plugins/real-agency/plugins/estate-teaching/PLUGIN.md +31 -0
  1379. package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/SKILL.md +39 -0
  1380. package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/references/module-delivery.md +65 -0
  1381. package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/references/progress-tracking.md +47 -0
  1382. package/payload/premium-plugins/real-agency/plugins/leads/PLUGIN.md +32 -0
  1383. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/SKILL.md +137 -0
  1384. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/buyer-search-letter.md +28 -0
  1385. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/buyer-search-letters.md +37 -0
  1386. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/database-reactivation.md +30 -0
  1387. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/email-nurture-sequences.md +45 -0
  1388. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/facebook-referrals.md +30 -0
  1389. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/firewave-email-nurture-sequences.md +41 -0
  1390. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/keller-33-touch.md +34 -0
  1391. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/neighbour-letters.md +31 -0
  1392. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/neighbour-notification-letter.md +20 -0
  1393. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/ofi-follow-up-dialogue.md +22 -0
  1394. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/ofi-follow-up.md +26 -0
  1395. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/serhant-three-fs-plus.md +21 -0
  1396. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sharran-10x10x10.md +18 -0
  1397. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sms-templates.md +40 -0
  1398. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sphere-of-influence-notes.md +34 -0
  1399. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sphere-of-influence.md +60 -0
  1400. package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/tom-panos-sms-templates.md +59 -0
  1401. package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/SKILL.md +33 -0
  1402. package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/database-matching.md +30 -0
  1403. package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/database-value.md +53 -0
  1404. package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/prospecting-dialogues.md +24 -0
  1405. package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/reactivation.md +34 -0
  1406. package/payload/premium-plugins/real-agency/plugins/listings/PLUGIN.md +33 -0
  1407. package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/SKILL.md +28 -0
  1408. package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/kerb-appeal.md +38 -0
  1409. package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/photo-day.md +59 -0
  1410. package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/situational-tips.md +50 -0
  1411. package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/staging-guide.md +52 -0
  1412. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/SKILL.md +286 -0
  1413. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/booking-script.md +51 -0
  1414. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/objection-scripts.md +193 -0
  1415. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/penhaul-presentation.md +123 -0
  1416. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/pre-listing-kit.md +139 -0
  1417. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/set-to-sell.md +55 -0
  1418. package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/sharran-frameworks.md +107 -0
  1419. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/SKILL.md +337 -0
  1420. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/auction-report-template.md +41 -0
  1421. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/coming-soon-campaign.md +43 -0
  1422. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/direct-mail-templates.md +121 -0
  1423. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/eoi-form-template.md +62 -0
  1424. package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/monthly-scorecard.md +63 -0
  1425. package/payload/premium-plugins/real-agency/plugins/loop/PLUGIN.md +73 -0
  1426. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.d.ts +2 -0
  1427. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.d.ts.map +1 -0
  1428. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.js +293 -0
  1429. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.js.map +1 -0
  1430. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.d.ts +10 -0
  1431. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.d.ts.map +1 -0
  1432. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.js +88 -0
  1433. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.js.map +1 -0
  1434. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.d.ts +82 -0
  1435. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -0
  1436. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.js +427 -0
  1437. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -0
  1438. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.d.ts +5 -0
  1439. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.d.ts.map +1 -0
  1440. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.js +40 -0
  1441. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.js.map +1 -0
  1442. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.d.ts +10 -0
  1443. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.d.ts.map +1 -0
  1444. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.js +24 -0
  1445. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.js.map +1 -0
  1446. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.d.ts +16 -0
  1447. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.d.ts.map +1 -0
  1448. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.js +35 -0
  1449. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.js.map +1 -0
  1450. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.d.ts +5 -0
  1451. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  1452. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.js +19 -0
  1453. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.js.map +1 -0
  1454. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.d.ts +4 -0
  1455. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.d.ts.map +1 -0
  1456. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.js +14 -0
  1457. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.js.map +1 -0
  1458. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.d.ts +9 -0
  1459. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.d.ts.map +1 -0
  1460. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.js +60 -0
  1461. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.js.map +1 -0
  1462. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts +13 -0
  1463. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts.map +1 -0
  1464. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.js +41 -0
  1465. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.js.map +1 -0
  1466. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts +9 -0
  1467. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts.map +1 -0
  1468. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.js +16 -0
  1469. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.js.map +1 -0
  1470. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts +15 -0
  1471. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts.map +1 -0
  1472. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.js +11 -0
  1473. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.js.map +1 -0
  1474. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.d.ts +10 -0
  1475. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.d.ts.map +1 -0
  1476. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.js +39 -0
  1477. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.js.map +1 -0
  1478. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.d.ts +9 -0
  1479. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.d.ts.map +1 -0
  1480. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.js +125 -0
  1481. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.js.map +1 -0
  1482. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.d.ts +18 -0
  1483. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.d.ts.map +1 -0
  1484. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.js +87 -0
  1485. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.js.map +1 -0
  1486. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.d.ts +10 -0
  1487. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.d.ts.map +1 -0
  1488. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.js +82 -0
  1489. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.js.map +1 -0
  1490. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.d.ts +12 -0
  1491. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -0
  1492. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.js +32 -0
  1493. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -0
  1494. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.d.ts +15 -0
  1495. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.d.ts.map +1 -0
  1496. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.js +11 -0
  1497. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.js.map +1 -0
  1498. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.d.ts +16 -0
  1499. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.d.ts.map +1 -0
  1500. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.js +41 -0
  1501. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.js.map +1 -0
  1502. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.d.ts +13 -0
  1503. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.d.ts.map +1 -0
  1504. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.js +49 -0
  1505. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.js.map +1 -0
  1506. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.d.ts +7 -0
  1507. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.d.ts.map +1 -0
  1508. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.js +19 -0
  1509. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.js.map +1 -0
  1510. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.d.ts +5 -0
  1511. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.d.ts.map +1 -0
  1512. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.js +32 -0
  1513. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.js.map +1 -0
  1514. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.d.ts +14 -0
  1515. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.d.ts.map +1 -0
  1516. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.js +11 -0
  1517. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.js.map +1 -0
  1518. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.d.ts +9 -0
  1519. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.d.ts.map +1 -0
  1520. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.js +85 -0
  1521. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.js.map +1 -0
  1522. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.d.ts +13 -0
  1523. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.d.ts.map +1 -0
  1524. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.js +44 -0
  1525. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.js.map +1 -0
  1526. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.d.ts +14 -0
  1527. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.d.ts.map +1 -0
  1528. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.js +18 -0
  1529. package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.js.map +1 -0
  1530. package/payload/premium-plugins/real-agency/plugins/loop/mcp/package-lock.json +2549 -0
  1531. package/payload/premium-plugins/real-agency/plugins/loop/mcp/package.json +21 -0
  1532. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/__tests__/loop-swagger.snapshot.json +26467 -0
  1533. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +153 -0
  1534. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/index.ts +444 -0
  1535. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/crypto.ts +105 -0
  1536. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/loop-api.ts +604 -0
  1537. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/neo4j.ts +51 -0
  1538. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/customer-preferences.ts +66 -0
  1539. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/feedback.ts +86 -0
  1540. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-deregister.ts +27 -0
  1541. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-list.ts +19 -0
  1542. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-register.ts +95 -0
  1543. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-enquiry.ts +113 -0
  1544. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match-batch.ts +53 -0
  1545. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match-request.ts +42 -0
  1546. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match.ts +84 -0
  1547. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/people-detail.ts +245 -0
  1548. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/people-search.ts +180 -0
  1549. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-detail.ts +145 -0
  1550. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-listed.ts +88 -0
  1551. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-request.ts +42 -0
  1552. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-search.ts +92 -0
  1553. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/supplier.ts +129 -0
  1554. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/team-availability.ts +52 -0
  1555. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/team-info.ts +95 -0
  1556. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-create.ts +41 -0
  1557. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-detail.ts +171 -0
  1558. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-search.ts +92 -0
  1559. package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-update.ts +53 -0
  1560. package/payload/premium-plugins/real-agency/plugins/loop/mcp/tsconfig.json +20 -0
  1561. package/payload/premium-plugins/real-agency/plugins/loop/mcp/vitest.config.ts +9 -0
  1562. package/payload/premium-plugins/real-agency/plugins/vendors/PLUGIN.md +34 -0
  1563. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/SKILL.md +42 -0
  1564. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/fee-protection-and-agenda.md +28 -0
  1565. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/listing-scripts.md +44 -0
  1566. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/negotiation-deep-guide.md +70 -0
  1567. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/price-alignment-scripts.md +33 -0
  1568. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/price-alignment.md +34 -0
  1569. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/scenario-scripts.md +38 -0
  1570. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/seller-engagement.md +51 -0
  1571. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/valuation-booking.md +76 -0
  1572. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/vendor-scripts.md +63 -0
  1573. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/vendor-updates.md +41 -0
  1574. package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-updates/SKILL.md +153 -0
  1575. package/payload/premium-plugins/teaching/PLUGIN.md +57 -0
  1576. package/payload/premium-plugins/teaching/skills/interactive-tutor/SKILL.md +59 -0
  1577. package/payload/premium-plugins/teaching/skills/interactive-tutor/references/assessment.md +70 -0
  1578. package/payload/premium-plugins/teaching/skills/interactive-tutor/references/classroom-conduct.md +43 -0
  1579. package/payload/premium-plugins/teaching/skills/interactive-tutor/references/teaching-modes.md +83 -0
  1580. package/payload/premium-plugins/teaching/skills/lesson-planner/SKILL.md +48 -0
  1581. package/payload/premium-plugins/teaching/skills/lesson-planner/references/context-gathering.md +41 -0
  1582. package/payload/premium-plugins/teaching/skills/lesson-planner/references/plan-structure.md +94 -0
  1583. package/payload/premium-plugins/teaching/skills/study-pack-builder/SKILL.md +52 -0
  1584. package/payload/premium-plugins/teaching/skills/study-pack-builder/references/disaggregation.md +49 -0
  1585. package/payload/premium-plugins/teaching/skills/study-pack-builder/references/materials.md +116 -0
  1586. package/payload/premium-plugins/writer-craft/PLUGIN.md +87 -0
  1587. package/payload/premium-plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +92 -0
  1588. package/payload/premium-plugins/writer-craft/skills/citation-style/SKILL.md +94 -0
  1589. package/payload/premium-plugins/writer-craft/skills/citation-style/references/book-and-chapter-models.md +77 -0
  1590. package/payload/premium-plugins/writer-craft/skills/citation-style/references/citation-rules.md +103 -0
  1591. package/payload/premium-plugins/writer-craft/skills/citation-style/references/journal-article-models.md +74 -0
  1592. package/payload/premium-plugins/writer-craft/skills/citation-style/references/other-source-models.md +146 -0
  1593. package/payload/premium-plugins/writer-craft/skills/citation-style/references/reference-list-rules.md +70 -0
  1594. package/payload/premium-plugins/writer-craft/skills/editorial-practice/SKILL.md +108 -0
  1595. package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/copyediting.md +73 -0
  1596. package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/developmental-editing.md +85 -0
  1597. package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/genre-specific-editing.md +78 -0
  1598. package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/line-editing.md +55 -0
  1599. package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/self-editing.md +89 -0
  1600. package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/SKILL.md +114 -0
  1601. package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/audience-analysis.md +73 -0
  1602. package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/crafting-persuasive-story.md +76 -0
  1603. package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/persuasion-case-studies.md +67 -0
  1604. package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/transformation-framework.md +86 -0
  1605. package/payload/premium-plugins/writer-craft/skills/point-of-view/SKILL.md +97 -0
  1606. package/payload/premium-plugins/writer-craft/skills/point-of-view/references/indirect-narration.md +72 -0
  1607. package/payload/premium-plugins/writer-craft/skills/point-of-view/references/pov-types-and-voice.md +91 -0
  1608. package/payload/premium-plugins/writer-craft/skills/point-of-view/references/protagonist-filter.md +71 -0
  1609. package/payload/premium-plugins/writer-craft/skills/point-of-view/references/tense-and-person.md +85 -0
  1610. package/payload/premium-plugins/writer-craft/skills/prose-craft/SKILL.md +100 -0
  1611. package/payload/premium-plugins/writer-craft/skills/prose-craft/references/punctuation-and-grammar.md +72 -0
  1612. package/payload/premium-plugins/writer-craft/skills/prose-craft/references/repetition.md +71 -0
  1613. package/payload/premium-plugins/writer-craft/skills/prose-craft/references/sound-and-rhythm.md +64 -0
  1614. package/payload/premium-plugins/writer-craft/skills/prose-craft/references/word-economy.md +93 -0
  1615. package/payload/premium-plugins/writer-craft/skills/reader-engagement/SKILL.md +100 -0
  1616. package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/cause-effect-setup-payoff.md +79 -0
  1617. package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/conflict-escalation.md +81 -0
  1618. package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/hooking-readers.md +67 -0
  1619. package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/neurochemistry-of-engagement.md +94 -0
  1620. package/payload/premium-plugins/writer-craft/skills/review-manuscript/SKILL.md +111 -0
  1621. package/payload/premium-plugins/writer-craft/skills/review-manuscript/references/review-manuscript-checklist.md +119 -0
  1622. package/payload/premium-plugins/writer-craft/skills/review-prose/SKILL.md +99 -0
  1623. package/payload/premium-plugins/writer-craft/skills/review-prose/references/prose-review-checklist.md +112 -0
  1624. package/payload/premium-plugins/writer-craft/skills/review-scene/SKILL.md +99 -0
  1625. package/payload/premium-plugins/writer-craft/skills/review-scene/references/scene-analysis-framework.md +95 -0
  1626. package/payload/premium-plugins/writer-craft/skills/story-architecture/SKILL.md +106 -0
  1627. package/payload/premium-plugins/writer-craft/skills/story-architecture/references/blueprinting-and-scene-cards.md +118 -0
  1628. package/payload/premium-plugins/writer-craft/skills/story-architecture/references/inner-issue-and-protagonist-goal.md +66 -0
  1629. package/payload/premium-plugins/writer-craft/skills/story-architecture/references/misbelief-desire-worldview.md +87 -0
  1630. package/payload/premium-plugins/writer-craft/skills/story-architecture/references/origin-scenes-and-escalation.md +82 -0
  1631. package/payload/premium-plugins/writer-craft/skills/story-blueprint/SKILL.md +133 -0
  1632. package/payload/premium-plugins/writer-craft/skills/story-blueprint/references/blueprinting-exercises.md +118 -0
  1633. package/payload/premium-plugins/writer-craft/skills/story-blueprint/references/blueprinting-process.md +128 -0
  1634. package/payload/server/adminuser-self-heal-QAWOZ3JV.js +45 -0
  1635. package/payload/server/chunk-5FM432JB.js +4148 -0
  1636. package/payload/server/chunk-6S5JTXAN.js +1544 -0
  1637. package/payload/server/chunk-JSBRDJBE.js +30 -0
  1638. package/payload/server/chunk-RNW625CL.js +759 -0
  1639. package/payload/server/cloudflare-task-tracker-VC7QVU5H.js +22 -0
  1640. package/payload/server/maxy-edge.js +1021 -0
  1641. package/payload/server/package.json +13 -0
  1642. package/payload/server/public/assets/Checkbox-C6ZCsPvl.js +1 -0
  1643. package/payload/server/public/assets/_baseFor-BHtDrjIo.js +1 -0
  1644. package/payload/server/public/assets/admin-CWMpccrR.css +1 -0
  1645. package/payload/server/public/assets/admin-DVGJmN-k.js +216 -0
  1646. package/payload/server/public/assets/arc-DMDAZHAN.js +1 -0
  1647. package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +1 -0
  1648. package/payload/server/public/assets/architectureDiagram-Q4EWVU46-DwN6H0y2.js +36 -0
  1649. package/payload/server/public/assets/array-DetWRiSa.js +1 -0
  1650. package/payload/server/public/assets/blockDiagram-DXYQGD6D-TUk_F7H6.js +132 -0
  1651. package/payload/server/public/assets/c4Diagram-AHTNJAMY-CTjGko0X.js +10 -0
  1652. package/payload/server/public/assets/channel-Cv-65bLZ.js +1 -0
  1653. package/payload/server/public/assets/chunk-2KRD3SAO-Di4bO8ir.js +1 -0
  1654. package/payload/server/public/assets/chunk-336JU56O-DulT46bV.js +2 -0
  1655. package/payload/server/public/assets/chunk-426QAEUC-BwKj8yqp.js +1 -0
  1656. package/payload/server/public/assets/chunk-4BX2VUAB-DyEhFk-Z.js +1 -0
  1657. package/payload/server/public/assets/chunk-4TB4RGXK-CewO8YaZ.js +206 -0
  1658. package/payload/server/public/assets/chunk-55IACEB6-BRJOZLpU.js +1 -0
  1659. package/payload/server/public/assets/chunk-5FUZZQ4R-B3IWYz-k.js +62 -0
  1660. package/payload/server/public/assets/chunk-5PVQY5BW-DyiDEtXY.js +2 -0
  1661. package/payload/server/public/assets/chunk-67CJDMHE-BG6-9r6c.js +1 -0
  1662. package/payload/server/public/assets/chunk-7N4EOEYR-BvMbitX7.js +1 -0
  1663. package/payload/server/public/assets/chunk-AA7GKIK3-CE8mGBD5.js +1 -0
  1664. package/payload/server/public/assets/chunk-BSJP7CBP-DP7LTBll.js +1 -0
  1665. package/payload/server/public/assets/chunk-CIAEETIT-VfnIdN-h.js +1 -0
  1666. package/payload/server/public/assets/chunk-DD-I1_y5.js +1 -0
  1667. package/payload/server/public/assets/chunk-EDXVE4YY-B3u7wU36.js +1 -0
  1668. package/payload/server/public/assets/chunk-ENJZ2VHE-BesS5YY4.js +10 -0
  1669. package/payload/server/public/assets/chunk-FMBD7UC4-w-yBZsN2.js +15 -0
  1670. package/payload/server/public/assets/chunk-FOC6F5B3-f9cFywhd.js +1 -0
  1671. package/payload/server/public/assets/chunk-ICPOFSXX-C5_hbB6H.js +122 -0
  1672. package/payload/server/public/assets/chunk-K5T4RW27-B_ZUrFUq.js +94 -0
  1673. package/payload/server/public/assets/chunk-KGLVRYIC-CcWTvRlI.js +1 -0
  1674. package/payload/server/public/assets/chunk-LIHQZDEY-D-5-peQw.js +1 -0
  1675. package/payload/server/public/assets/chunk-ORNJ4GCN-B4Z5L25I.js +1 -0
  1676. package/payload/server/public/assets/chunk-OYMX7WX6-CrL4rhBa.js +231 -0
  1677. package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +1 -0
  1678. package/payload/server/public/assets/chunk-U2HBQHQK-CQpbcqRS.js +70 -0
  1679. package/payload/server/public/assets/chunk-X2U36JSP-1HG7T4gX.js +1 -0
  1680. package/payload/server/public/assets/chunk-XPW4576I-m1Y_r88I.js +32 -0
  1681. package/payload/server/public/assets/chunk-YZCP3GAM-boN5wjX9.js +1 -0
  1682. package/payload/server/public/assets/chunk-ZZ45TVLE-D6CiPO0Q.js +1 -0
  1683. package/payload/server/public/assets/classDiagram-6PBFFD2Q-wQ2-BRyB.js +1 -0
  1684. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_YLNNHy.js +1 -0
  1685. package/payload/server/public/assets/clone-VLK-GPZZ.js +1 -0
  1686. package/payload/server/public/assets/cormorant-cyrillic-300-normal-CzPHYadL.woff +0 -0
  1687. package/payload/server/public/assets/cormorant-cyrillic-300-normal-DFUoTmrg.woff2 +0 -0
  1688. package/payload/server/public/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
  1689. package/payload/server/public/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
  1690. package/payload/server/public/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
  1691. package/payload/server/public/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
  1692. package/payload/server/public/assets/cormorant-cyrillic-ext-300-normal-BXl3lXsi.woff2 +0 -0
  1693. package/payload/server/public/assets/cormorant-cyrillic-ext-300-normal-DmxSOTe3.woff +0 -0
  1694. package/payload/server/public/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
  1695. package/payload/server/public/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
  1696. package/payload/server/public/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
  1697. package/payload/server/public/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
  1698. package/payload/server/public/assets/cormorant-latin-300-normal-CJ5dfen0.woff2 +0 -0
  1699. package/payload/server/public/assets/cormorant-latin-300-normal-DQZObO_3.woff +0 -0
  1700. package/payload/server/public/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
  1701. package/payload/server/public/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
  1702. package/payload/server/public/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
  1703. package/payload/server/public/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
  1704. package/payload/server/public/assets/cormorant-latin-ext-300-normal-CkiUx0UG.woff +0 -0
  1705. package/payload/server/public/assets/cormorant-latin-ext-300-normal-De3D72RL.woff2 +0 -0
  1706. package/payload/server/public/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
  1707. package/payload/server/public/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
  1708. package/payload/server/public/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
  1709. package/payload/server/public/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
  1710. package/payload/server/public/assets/cormorant-vietnamese-300-normal-BVqIp_mg.woff2 +0 -0
  1711. package/payload/server/public/assets/cormorant-vietnamese-300-normal-CEMS9Pw-.woff +0 -0
  1712. package/payload/server/public/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
  1713. package/payload/server/public/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
  1714. package/payload/server/public/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
  1715. package/payload/server/public/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
  1716. package/payload/server/public/assets/cose-bilkent-S5V4N54A-BhtgY3T7.js +1 -0
  1717. package/payload/server/public/assets/cytoscape.esm-C9yNhe1u.js +321 -0
  1718. package/payload/server/public/assets/dagre-CncXYNX1.js +1 -0
  1719. package/payload/server/public/assets/dagre-KV5264BT-C0CcgCHW.js +4 -0
  1720. package/payload/server/public/assets/data-Bt4Wsocg.js +1 -0
  1721. package/payload/server/public/assets/defaultLocale-_WRwicXn.js +1 -0
  1722. package/payload/server/public/assets/diagram-5BDNPKRD-CkUlWbsI.js +10 -0
  1723. package/payload/server/public/assets/diagram-G4DWMVQ6-DVZBG1Ul.js +24 -0
  1724. package/payload/server/public/assets/diagram-MMDJMWI5-BIb06ZkK.js +43 -0
  1725. package/payload/server/public/assets/diagram-TYMM5635-BcT0_WR8.js +24 -0
  1726. package/payload/server/public/assets/dist-Bd4S37oi.js +1 -0
  1727. package/payload/server/public/assets/dm-sans-latin-400-normal-BwCSEQnW.woff +0 -0
  1728. package/payload/server/public/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2 +0 -0
  1729. package/payload/server/public/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2 +0 -0
  1730. package/payload/server/public/assets/dm-sans-latin-500-normal-Dr3UlScf.woff +0 -0
  1731. package/payload/server/public/assets/dm-sans-latin-ext-400-normal-BjWJ59Pq.woff +0 -0
  1732. package/payload/server/public/assets/dm-sans-latin-ext-400-normal-BtiwyxMk.woff2 +0 -0
  1733. package/payload/server/public/assets/dm-sans-latin-ext-500-normal-BJfUCQsA.woff2 +0 -0
  1734. package/payload/server/public/assets/dm-sans-latin-ext-500-normal-DR84L5F-.woff +0 -0
  1735. package/payload/server/public/assets/erDiagram-SMLLAGMA-BHk6lxIT.js +85 -0
  1736. package/payload/server/public/assets/flatten-BsWEYbBB.js +1 -0
  1737. package/payload/server/public/assets/flowDiagram-DWJPFMVM-CjeJn490.js +162 -0
  1738. package/payload/server/public/assets/ganttDiagram-T4ZO3ILL-BGYvX3Lv.js +292 -0
  1739. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +1 -0
  1740. package/payload/server/public/assets/gitGraphDiagram-UUTBAWPF-Br4WLGzW.js +106 -0
  1741. package/payload/server/public/assets/graph-CRSLozxc.js +1 -0
  1742. package/payload/server/public/assets/graph-labels-CQyZQ0u6.js +1 -0
  1743. package/payload/server/public/assets/graphlib-BWd9sMeP.js +1 -0
  1744. package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +1 -0
  1745. package/payload/server/public/assets/infoDiagram-42DDH7IO-BjZeQoNZ.js +2 -0
  1746. package/payload/server/public/assets/init-sTEcj9YX.js +1 -0
  1747. package/payload/server/public/assets/isEmpty-BWl67LAZ.js +1 -0
  1748. package/payload/server/public/assets/ishikawaDiagram-UXIWVN3A-POMae6Ni.js +70 -0
  1749. package/payload/server/public/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  1750. package/payload/server/public/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  1751. package/payload/server/public/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  1752. package/payload/server/public/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  1753. package/payload/server/public/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  1754. package/payload/server/public/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  1755. package/payload/server/public/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  1756. package/payload/server/public/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  1757. package/payload/server/public/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  1758. package/payload/server/public/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  1759. package/payload/server/public/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  1760. package/payload/server/public/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  1761. package/payload/server/public/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  1762. package/payload/server/public/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  1763. package/payload/server/public/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  1764. package/payload/server/public/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  1765. package/payload/server/public/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  1766. package/payload/server/public/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  1767. package/payload/server/public/assets/journeyDiagram-VCZTEJTY-ledtLV6x.js +139 -0
  1768. package/payload/server/public/assets/jsx-runtime-DvanDPKm.css +1 -0
  1769. package/payload/server/public/assets/jsx-runtime-vPsBTwUp.js +9 -0
  1770. package/payload/server/public/assets/kanban-definition-6JOO6SKY-DI0T4W9z.js +89 -0
  1771. package/payload/server/public/assets/katex-s61Rgv6l.js +257 -0
  1772. package/payload/server/public/assets/lib-W5Jcz4p8.js +33 -0
  1773. package/payload/server/public/assets/line-D1281H12.js +1 -0
  1774. package/payload/server/public/assets/linear-0O14Y6uf.js +1 -0
  1775. package/payload/server/public/assets/mermaid-parser.core-Dt95U7zk.js +4 -0
  1776. package/payload/server/public/assets/mermaid.core-BuYSs1fU.js +11 -0
  1777. package/payload/server/public/assets/mindmap-definition-QFDTVHPH-CFE1lmfX.js +96 -0
  1778. package/payload/server/public/assets/ordinal-krseTxxN.js +1 -0
  1779. package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +1 -0
  1780. package/payload/server/public/assets/page-CSUcuVW0.js +1 -0
  1781. package/payload/server/public/assets/page-TARBO-Yr.js +50 -0
  1782. package/payload/server/public/assets/path-B0Ik7Tu9.js +1 -0
  1783. package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +1 -0
  1784. package/payload/server/public/assets/pieDiagram-DEJITSTG-BSd9xa7v.js +30 -0
  1785. package/payload/server/public/assets/public-5r6aRXrb.js +8 -0
  1786. package/payload/server/public/assets/quadrantDiagram-34T5L4WZ-xYehPVw5.js +7 -0
  1787. package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +1 -0
  1788. package/payload/server/public/assets/reduce-tk-xY6Fv.js +1 -0
  1789. package/payload/server/public/assets/requirementDiagram-MS252O5E-C6n77V1S.js +84 -0
  1790. package/payload/server/public/assets/rough.esm-DKRO8IF-.js +1 -0
  1791. package/payload/server/public/assets/sankeyDiagram-XADWPNL6-CWIfeO1M.js +10 -0
  1792. package/payload/server/public/assets/sequenceDiagram-FGHM5R23-DDv2DuMo.js +157 -0
  1793. package/payload/server/public/assets/src-B6XdH6xq.js +1 -0
  1794. package/payload/server/public/assets/stateDiagram-FHFEXIEX-BPZdmsww.js +1 -0
  1795. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-33eC4TwE.js +1 -0
  1796. package/payload/server/public/assets/timeline-definition-GMOUNBTQ-BpRT_wSX.js +120 -0
  1797. package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +1 -0
  1798. package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +1 -0
  1799. package/payload/server/public/assets/vennDiagram-DHZGUBPP-BG8ubucH.js +34 -0
  1800. package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +1 -0
  1801. package/payload/server/public/assets/wardleyDiagram-NUSXRM2D-D4E7PU1B.js +20 -0
  1802. package/payload/server/public/assets/xychartDiagram-5P7HB3ND-Ur2xVM-c.js +7 -0
  1803. package/payload/server/public/brand/claude.png +0 -0
  1804. package/payload/server/public/brand/favicon.ico +0 -0
  1805. package/payload/server/public/brand/maxy-black.png +0 -0
  1806. package/payload/server/public/brand/maxy-horizontal.png +0 -0
  1807. package/payload/server/public/brand/maxy-monochrome.png +0 -0
  1808. package/payload/server/public/brand/maxy-square.png +0 -0
  1809. package/payload/server/public/brand/maxy.png +0 -0
  1810. package/payload/server/public/brand/star.png +0 -0
  1811. package/payload/server/public/brand-constants.json +8 -0
  1812. package/payload/server/public/brand-defaults.css +12 -0
  1813. package/payload/server/public/data.html +18 -0
  1814. package/payload/server/public/favicon.ico +0 -0
  1815. package/payload/server/public/graph.html +19 -0
  1816. package/payload/server/public/index.html +23 -0
  1817. package/payload/server/public/public.html +19 -0
  1818. package/payload/server/public/robots.txt +5 -0
  1819. package/payload/server/public/vnc-popout.html +63 -0
  1820. package/payload/server/server-init.cjs +115 -0
  1821. package/payload/server/server.js +14960 -0
@@ -0,0 +1,3495 @@
1
+ import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
2
+ initStderrTee("admin");
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { readFile, writeFile } from "node:fs/promises";
8
+ import { resolve, join } from "node:path";
9
+ import { execFileSync } from "node:child_process";
10
+ import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
11
+ import { writeKey, validateKey, hasKey, keyFilePath, deleteKey } from "../../../../lib/anthropic-key/dist/index.js";
12
+ import { writeAdminEntry, removeAdminFromAccount } from "../../../../lib/admins-write/dist/index.js";
13
+ import { isPersistentComponent } from "../../../../lib/persistent-components/dist/index.js";
14
+ import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
15
+ import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
16
+ import { resolveEntitlement } from "../../../../lib/entitlement/dist/index.js";
17
+ import { createHash, randomInt, randomUUID } from "node:crypto";
18
+ import { createConnection } from "node:net";
19
+ import { homedir, hostname as osHostname } from "node:os";
20
+ import QRCode from "qrcode";
21
+ import { getSession, closeDriver } from "./lib/neo4j.js";
22
+ import { getOnboardingState, completeOnboardingStep } from "./lib/onboarding.js";
23
+ import { findSkillOwners, computePluginReadHint, loadSkill, parseRequiredInputs } from "./skill-resolution.js";
24
+ import { resolvePublicHostname } from "./lib/public-hostname.js";
25
+ const server = new McpServer({
26
+ name: "admin",
27
+ version: "0.1.0",
28
+ });
29
+ const PLATFORM_ROOT = process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../..");
30
+ const ACCOUNT_ID = process.env.ACCOUNT_ID;
31
+ if (!ACCOUNT_ID) {
32
+ throw new Error("ACCOUNT_ID environment variable is required");
33
+ }
34
+ const PLATFORM_PORT = process.env.PLATFORM_PORT;
35
+ if (!PLATFORM_PORT) {
36
+ throw new Error("PLATFORM_PORT environment variable is required — set by getMcpServers() in claude-agent.ts");
37
+ }
38
+ // Brand-aware config — reads configDir, productName, and commercialMode from
39
+ // brand.json stamped at install time. commercialMode (Task 831) gates the
40
+ // entitlement verifier: false (default) preserves personal-mode installs;
41
+ // true requires a Rubytech-signed entitlement.json or the install runs locked.
42
+ // No fallback: if brand.json is missing or incomplete, the platform wasn't properly installed.
43
+ function resolveBrandConfig() {
44
+ const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
45
+ if (!existsSync(brandPath)) {
46
+ throw new Error(`brand.json not found at ${brandPath} — platform not properly installed`);
47
+ }
48
+ try {
49
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
50
+ if (!brand.configDir) {
51
+ throw new Error(`brand.json at ${brandPath} is missing the configDir field`);
52
+ }
53
+ if (!brand.productName) {
54
+ throw new Error(`brand.json at ${brandPath} is missing the productName field`);
55
+ }
56
+ // Task 924 + 959 — brand.json is the single source of truth for the
57
+ // four brand-scoped port fields. An admin MCP probe must see the exact
58
+ // same numbers the rest of the platform binds; silent vncDisplay-derived
59
+ // fallback was a recurrence-class silent-fallback-masks-root-cause
60
+ // violation (Task 959) and is now loud-fail.
61
+ const brandLabel = String(brand.configDir).replace(/^\./, "");
62
+ if (typeof brand.vncDisplay !== "number") {
63
+ console.error(`[mcp:admin] error reason=cdp-port-unresolved brand=${brandLabel} path=${brandPath} field=vncDisplay json_keys=${Object.keys(brand).join(",")}`);
64
+ throw new Error(`brand.json at ${brandPath} missing required field: vncDisplay`);
65
+ }
66
+ for (const field of ["rfbPort", "websockifyPort", "cdpPort"]) {
67
+ if (typeof brand[field] !== "number") {
68
+ console.error(`[mcp:admin] error reason=cdp-port-unresolved brand=${brandLabel} path=${brandPath} field=${field} json_keys=${Object.keys(brand).join(",")}`);
69
+ throw new Error(`brand.json at ${brandPath} missing required field: ${field}`);
70
+ }
71
+ }
72
+ return {
73
+ configDir: brand.configDir,
74
+ productName: brand.productName,
75
+ commercialMode: brand.commercialMode === true,
76
+ vncDisplay: brand.vncDisplay,
77
+ rfbPort: brand.rfbPort,
78
+ websockifyPort: brand.websockifyPort,
79
+ cdpPort: brand.cdpPort,
80
+ };
81
+ }
82
+ catch (err) {
83
+ if (err instanceof SyntaxError) {
84
+ throw new Error(`brand.json at ${brandPath} is not valid JSON: ${err.message}`);
85
+ }
86
+ throw err;
87
+ }
88
+ }
89
+ const BRAND_CONFIG = resolveBrandConfig();
90
+ const CONFIG_DIR = resolve(homedir(), BRAND_CONFIG.configDir);
91
+ const BRAND_NAME = BRAND_CONFIG.productName;
92
+ // Entitlement input shape for the verifier. configDir is rooted under $HOME
93
+ // (where entitlement.json is delivered post-purchase). platformRoot is needed
94
+ // because the verifier runs in two bundle contexts (CJS dist for MCP, ESM tsup
95
+ // bundle for UI) — caller passes the root rather than the verifier guessing.
96
+ const ENTITLEMENT_BRAND = {
97
+ configDir: CONFIG_DIR,
98
+ platformRoot: PLATFORM_ROOT,
99
+ commercialMode: BRAND_CONFIG.commercialMode,
100
+ };
101
+ /** Resolve current effective entitlement. Memoized inside the verifier. */
102
+ async function currentEntitlement() {
103
+ const config = await readAccountConfig();
104
+ return resolveEntitlement(ENTITLEMENT_BRAND, {
105
+ accountId: typeof config.accountId === "string" ? config.accountId : "",
106
+ customerEmail: typeof config.customerEmail === "string" ? config.customerEmail : undefined,
107
+ tier: typeof config.tier === "string" ? config.tier : undefined,
108
+ purchasedPlugins: Array.isArray(config.purchasedPlugins)
109
+ ? config.purchasedPlugins
110
+ : undefined,
111
+ });
112
+ }
113
+ const REMOTE_PASSWORD_FILE = resolve(CONFIG_DIR, ".remote-password");
114
+ // Resolve account directory
115
+ function getAccountDir() {
116
+ const dir = resolve(PLATFORM_ROOT, "..", "data/accounts", ACCOUNT_ID);
117
+ if (!existsSync(dir)) {
118
+ throw new Error(`Account directory not found: ${dir}`);
119
+ }
120
+ return dir;
121
+ }
122
+ async function readAccountConfig() {
123
+ const configPath = join(getAccountDir(), "account.json");
124
+ const content = await readFile(configPath, "utf-8");
125
+ return JSON.parse(content);
126
+ }
127
+ const STAGING_ROOT = resolve(PLATFORM_ROOT, "../premium-plugins");
128
+ const PLUGINS_DIR = resolve(PLATFORM_ROOT, "plugins");
129
+ const CORE_PLUGINS = ["admin", "memory", "docs", "cloudflare", "anthropic"];
130
+ // users.json lives under CONFIG_DIR (persistent) — see paths.ts comment for
131
+ // the regression that motivated the move. Both platform/ui and this MCP plugin
132
+ // resolve the same file via brand-aware $HOME/<configDir>, so the device-level
133
+ // admin auth store is single-pathed across both processes (Task 904).
134
+ const USERS_FILE = resolve(CONFIG_DIR, "users.json");
135
+ /** Maximum number of admin users allowed per tier. */
136
+ const MAX_ADMINS_BY_TIER = {
137
+ solo: 1,
138
+ family: 5,
139
+ pro: 5,
140
+ };
141
+ const MAX_ADMINS_DEFAULT = 5;
142
+ function hashPin(pin) {
143
+ return createHash("sha256").update(pin).digest("hex");
144
+ }
145
+ function readUsersJson() {
146
+ if (!existsSync(USERS_FILE)) {
147
+ throw new Error("users.json not found — run the seed script first");
148
+ }
149
+ const raw = readFileSync(USERS_FILE, "utf-8").trim();
150
+ if (!raw)
151
+ return [];
152
+ return JSON.parse(raw);
153
+ }
154
+ function writeUsersJson(users) {
155
+ writeFileSync(USERS_FILE, JSON.stringify(users, null, 2) + "\n", "utf-8");
156
+ }
157
+ function generateUniquePin(users, maxRetries = 10) {
158
+ const existingPins = new Set(users.map(u => u.pin));
159
+ for (let i = 0; i < maxRetries; i++) {
160
+ const raw = String(randomInt(0, 10000)).padStart(4, "0");
161
+ const hashed = hashPin(raw);
162
+ if (!existingPins.has(hashed)) {
163
+ return raw;
164
+ }
165
+ console.error(`[admin] PIN collision during admin-add: rejecting duplicate (attempt ${i + 1}/${maxRetries})`);
166
+ }
167
+ throw new Error("Could not generate a unique PIN after multiple attempts. Specify a PIN manually.");
168
+ }
169
+ const VALID_PLUGIN_NAME = /^[a-z0-9-]+$/;
170
+ /** Parse YAML frontmatter from markdown content. Returns key-value pairs. */
171
+ function parseFrontmatter(content) {
172
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
173
+ if (!match)
174
+ return {};
175
+ const result = {};
176
+ let currentKey = "";
177
+ let currentArray = null;
178
+ for (const line of match[1].split("\n")) {
179
+ const kvMatch = line.match(/^(\w[\w-]*):\s*(.*)$/);
180
+ if (kvMatch) {
181
+ if (currentArray && currentKey) {
182
+ result[currentKey] = currentArray;
183
+ currentArray = null;
184
+ }
185
+ const [, key, rawValue] = kvMatch;
186
+ const value = rawValue.replace(/^["']|["']$/g, "").trim();
187
+ if (value) {
188
+ result[key] = value;
189
+ currentKey = key;
190
+ }
191
+ else {
192
+ // Key with no inline value — next lines may be array items
193
+ currentKey = key;
194
+ currentArray = [];
195
+ }
196
+ }
197
+ else if (currentArray !== null) {
198
+ const itemMatch = line.match(/^\s*-\s+(.+)$/);
199
+ if (itemMatch) {
200
+ currentArray.push(itemMatch[1].trim());
201
+ }
202
+ }
203
+ }
204
+ if (currentArray && currentKey) {
205
+ result[currentKey] = currentArray;
206
+ }
207
+ return result;
208
+ }
209
+ // ===================================================================
210
+ // System tools
211
+ // ===================================================================
212
+ function checkPort(port, timeoutMs = 1000) {
213
+ return new Promise((res) => {
214
+ const socket = createConnection(port, "127.0.0.1");
215
+ socket.setTimeout(timeoutMs);
216
+ socket.once("connect", () => { socket.destroy(); res(true); });
217
+ socket.once("error", () => { socket.destroy(); res(false); });
218
+ socket.once("timeout", () => { socket.destroy(); res(false); });
219
+ });
220
+ }
221
+ eagerTool(server, "system-status", "Check health of all Maxy platform services: Neo4j, Ollama, Cloudflare tunnel, crontab (Maxy cron entries), VNC, Chrome (CDP), specialist agents, and deployed brand identity.", {}, async () => {
222
+ const checks = {};
223
+ try {
224
+ // Task 580: NEO4J_URI must be explicit. The outer try/catch surfaces the
225
+ // unset condition as `checks.neo4j = "unreachable: NEO4J_URI unset..."`
226
+ // so system-status still reports cleanly — the misconfig appears in the
227
+ // check output instead of taking down the whole tool.
228
+ const neo4jUri = process.env.NEO4J_URI;
229
+ if (!neo4jUri) {
230
+ throw new Error("NEO4J_URI unset (Task 580 — no silent default)");
231
+ }
232
+ // NOTE: the port-replace below only works when the URI ends in :7687.
233
+ // For non-default bolt ports (e.g. :7688 on realagent) the HTTP port is
234
+ // bolt - 213 (see graph-proxy.ts). Pre-existing fragility — flagged as a
235
+ // follow-up in .docs/neo4j-uri-audit.md. Not in scope for Task 580.
236
+ const res = await fetch(neo4jUri.replace("bolt://", "http://").replace("7687", "7474"), { signal: AbortSignal.timeout(3000) });
237
+ checks.neo4j = res.ok ? "healthy" : `unhealthy (${res.status})`;
238
+ }
239
+ catch (err) {
240
+ checks.neo4j = `unreachable: ${err instanceof Error ? err.message : String(err)}`;
241
+ }
242
+ try {
243
+ const ollamaUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
244
+ const res = await fetch(`${ollamaUrl}/api/tags`, {
245
+ signal: AbortSignal.timeout(3000),
246
+ });
247
+ if (res.ok) {
248
+ const data = (await res.json());
249
+ const models = data.models?.map((m) => m.name).join(", ") ?? "none";
250
+ checks.ollama = `healthy (models: ${models})`;
251
+ }
252
+ else {
253
+ checks.ollama = `unhealthy (${res.status})`;
254
+ }
255
+ }
256
+ catch (err) {
257
+ checks.ollama = `unreachable: ${err instanceof Error ? err.message : String(err)}`;
258
+ }
259
+ // Cloudflare tunnel: lightweight process check (full status via tunnel-status in cloudflare plugin)
260
+ try {
261
+ execFileSync("pgrep", ["-x", "cloudflared"], { timeout: 2000 });
262
+ checks.cloudflare = "running";
263
+ }
264
+ catch {
265
+ // pgrep exits non-zero if no process found
266
+ try {
267
+ execFileSync("which", ["cloudflared"], { encoding: "utf-8", timeout: 3000 });
268
+ checks.cloudflare = "installed but not running";
269
+ }
270
+ catch {
271
+ checks.cloudflare = "not installed";
272
+ }
273
+ }
274
+ // Crontab — brand cron entries registered by the installer
275
+ try {
276
+ const crontabOutput = execFileSync("crontab", ["-l"], {
277
+ encoding: "utf-8",
278
+ timeout: 3000,
279
+ });
280
+ const cronBeginMarker = `# BEGIN ${BRAND_NAME.toUpperCase()} CRONS`;
281
+ const cronEndMarker = `# END ${BRAND_NAME.toUpperCase()} CRONS`;
282
+ const beginIdx = crontabOutput.indexOf(cronBeginMarker);
283
+ const endIdx = crontabOutput.indexOf(cronEndMarker);
284
+ if (beginIdx === -1 || endIdx === -1) {
285
+ checks.crontab = `no ${BRAND_NAME} crons registered — run the installer to register them`;
286
+ }
287
+ else {
288
+ const block = crontabOutput.slice(beginIdx, endIdx);
289
+ const entries = block
290
+ .split("\n")
291
+ .filter((line) => line.trim() && !line.startsWith("#"));
292
+ if (entries.length === 0) {
293
+ checks.crontab = `${BRAND_NAME} cron block present but empty`;
294
+ }
295
+ else {
296
+ const names = entries.map((line) => {
297
+ const commentMatch = line.match(/#\s*(.+)$/);
298
+ if (commentMatch)
299
+ return commentMatch[1].trim();
300
+ const scriptMatch = line.match(/([^/\s]+)\.js/);
301
+ if (scriptMatch)
302
+ return scriptMatch[1];
303
+ return line.trim().split(/\s+/).slice(-1)[0];
304
+ });
305
+ checks.crontab = `${entries.length} registered: ${names.join(", ")}`;
306
+ }
307
+ }
308
+ }
309
+ catch {
310
+ checks.crontab = "no crontab configured for this user";
311
+ }
312
+ // VNC display server (this brand's rfbPort — Task 924)
313
+ checks.vnc = (await checkPort(BRAND_CONFIG.rfbPort)) ? "running" : "not running";
314
+ // Chrome CDP (this brand's cdpPort — Task 924); personal-assistant
315
+ // browser tools depend on this.
316
+ const chromeUp = await checkPort(BRAND_CONFIG.cdpPort);
317
+ checks.chrome = chromeUp
318
+ ? "running"
319
+ : `not running — personal-assistant browser tools degraded (CDP port ${BRAND_CONFIG.cdpPort} not listening)`;
320
+ // WiFi — current connection state (Task 429)
321
+ try {
322
+ const wifiScan = execFileSync("nmcli", ["-t", "-f", "ACTIVE,SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
323
+ const activeLine = wifiScan.split("\n").find((l) => l.startsWith("yes:"));
324
+ if (activeLine) {
325
+ const fields = activeLine.split(/(?<!\\):/).map((f) => f.replace(/\\:/g, ":"));
326
+ checks.wifi = `connected to ${fields[1]} (${fields[2]}%)`;
327
+ }
328
+ else {
329
+ checks.wifi = "not connected";
330
+ }
331
+ }
332
+ catch {
333
+ checks.wifi = "not available";
334
+ }
335
+ // Specialist agents — read AGENTS.md registry, verify files exist
336
+ try {
337
+ const accountDir = getAccountDir();
338
+ const agentsMdPath = join(accountDir, "agents/admin/AGENTS.md");
339
+ const specialistsAgentsDir = join(accountDir, "specialists/agents");
340
+ if (!existsSync(agentsMdPath)) {
341
+ checks.specialists = "not configured (AGENTS.md missing)";
342
+ }
343
+ else {
344
+ const content = readFileSync(agentsMdPath, "utf-8");
345
+ const entries = content.match(/^- \*\*specialists:([^*]+)\*\*/gm);
346
+ if (!entries || entries.length === 0) {
347
+ checks.specialists = "not configured (AGENTS.md empty)";
348
+ }
349
+ else {
350
+ const lines = [];
351
+ for (const entry of entries) {
352
+ const nameMatch = entry.match(/specialists:([^*]+)/);
353
+ if (!nameMatch)
354
+ continue;
355
+ const name = nameMatch[1];
356
+ const fileExists = existsSync(join(specialistsAgentsDir, `${name}.md`));
357
+ let status = fileExists ? "ok" : "missing file";
358
+ if (name === "personal-assistant" && !chromeUp) {
359
+ status = "degraded — chrome not running";
360
+ }
361
+ lines.push(` ${name}: ${status}`);
362
+ }
363
+ checks.specialists = `${entries.length} registered\n${lines.join("\n")}`;
364
+ }
365
+ }
366
+ }
367
+ catch {
368
+ checks.specialists = "error reading specialist registry";
369
+ }
370
+ // OS hostname — the device's actual network name, used for URL construction
371
+ try {
372
+ checks.hostname = osHostname();
373
+ }
374
+ catch (err) {
375
+ console.error("[system-status] os.hostname() failed, falling back to brand.hostname", err);
376
+ checks.hostname = "unknown (os.hostname failed)";
377
+ }
378
+ // Running port — PLATFORM_PORT is what the parent process passes to MCP subprocesses;
379
+ // PORT is the main-process variable (available in dev); 19200 is the install default.
380
+ checks.port = process.env.PLATFORM_PORT ?? process.env.PORT ?? "19200";
381
+ console.error(`[system-status] port=${checks.port} hostname=${checks.hostname}`);
382
+ // Local URL — pre-computed for agent URL construction (avoids hostname/port assembly errors)
383
+ checks.localUrl = `http://${checks.hostname}.local:${checks.port}`;
384
+ // Brand identity — read build-time manifest stamped by the bundler
385
+ try {
386
+ const brandPath = resolve(PLATFORM_ROOT, "config/brand.json");
387
+ if (!existsSync(brandPath)) {
388
+ checks.brand = "not configured (dev environment)";
389
+ }
390
+ else {
391
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
392
+ checks.brand = `${brand.productName ?? "unknown"} (hostname: ${brand.hostname ?? "unknown"})`;
393
+ }
394
+ }
395
+ catch (err) {
396
+ checks.brand = `error reading brand config: ${err instanceof Error ? err.message : String(err)}`;
397
+ }
398
+ // configDir — the brand-specific config directory name (e.g. ".maxy", ".realagent").
399
+ // Resolved from brand.json at module load. Exposed here so skills can reference
400
+ // the actual value instead of deriving it from the brand name via LLM inference.
401
+ checks.configDir = BRAND_CONFIG.configDir;
402
+ const formatted = Object.entries(checks)
403
+ .map(([service, status]) => `${service}: ${status}`)
404
+ .join("\n");
405
+ return { content: [{ type: "text", text: formatted }] };
406
+ });
407
+ eagerTool(server, "public-hostname", "Resolve this account's canonical public hostname. Reads cloudflared ingress + alias-domains.json — the same files the platform server trusts to route. Returns a single deterministic answer; use this immediately after publish-site to construct the full URL.", {}, async () => {
408
+ const TAG = "[admin:public-hostname]";
409
+ try {
410
+ const result = resolvePublicHostname(CONFIG_DIR);
411
+ if (result.hostname !== null) {
412
+ console.error(`${TAG} resolved accountId=${ACCOUNT_ID} hostname=${result.hostname} source=${result.source}`);
413
+ const body = `hostname: ${result.hostname}\n` +
414
+ `isApex: ${result.isApex}\n` +
415
+ `source: ${result.source}\n` +
416
+ `usage: paste \`https://${result.hostname}<path-slug>\` to the operator — the <path-slug> comes from publish-site.`;
417
+ return { content: [{ type: "text", text: body }] };
418
+ }
419
+ console.error(`${TAG} empty accountId=${ACCOUNT_ID} reason=${result.reason} files-checked=cloudflared-config.yml,alias-domains.json`);
420
+ return {
421
+ content: [{
422
+ type: "text",
423
+ text: `hostname: (none)\nreason: ${result.reason}\n` +
424
+ `No Cloudflare tunnel is configured for this account. Run the cloudflare setup-tunnel skill before publishing externally.`,
425
+ }],
426
+ };
427
+ }
428
+ catch (err) {
429
+ const errMsg = err instanceof Error ? err.message : String(err);
430
+ console.error(`${TAG} error accountId=${ACCOUNT_ID} message="${errMsg.replace(/"/g, "'")}"`);
431
+ return {
432
+ content: [{ type: "text", text: `error resolving public hostname: ${errMsg}` }],
433
+ isError: true,
434
+ };
435
+ }
436
+ });
437
+ eagerTool(server, "remote-auth-status", "Check whether the remote access password is configured. When not configured, emits a device-bound URL affordance (maxy-device-url fenced block) pointing at the password setup page — this URL opens on the device's own screen when the operator clicks it. The agent never constructs the password file path or runs shell commands — this tool is the single authority.", {}, async () => {
438
+ const TAG = "[remote-auth-status]";
439
+ const platformPort = parseInt(PLATFORM_PORT, 10);
440
+ const setupUrl = `http://${osHostname()}.local:${platformPort}/__remote-auth/setup`;
441
+ try {
442
+ const res = await fetch(`http://127.0.0.1:${platformPort}/api/remote-auth/status`, {
443
+ signal: AbortSignal.timeout(5000),
444
+ });
445
+ const body = await res.json();
446
+ const configured = body.configured === true;
447
+ console.error(`${TAG} configured=${configured} port=${platformPort}`);
448
+ // Shape the response for both the agent (deciding what to say) and
449
+ // the user (seeing the device-URL button when they need to act).
450
+ // Configured → plain status, no URL. Unconfigured → include the
451
+ // fenced block so the agent can surface the button verbatim.
452
+ const text = configured
453
+ ? `Remote access password is configured.`
454
+ : `Remote access password is not configured. The admin interface will refuse to expose over the tunnel until one is set.\n\n` +
455
+ `${deviceUrlBlock({ url: setupUrl, intent: "Set remote access password", hostname: osHostname() })}\n\n` +
456
+ `Ask the operator to set a password — either by clicking the button above (opens the setup page on the device) or by telling you one you can pass to \`remote-auth-set-password\`.`;
457
+ return {
458
+ content: [{ type: "text", text }],
459
+ };
460
+ }
461
+ catch (err) {
462
+ const errMsg = err instanceof Error ? err.message : String(err);
463
+ console.error(`${TAG} failed to check remote-auth status: ${errMsg}`);
464
+ return {
465
+ content: [{
466
+ type: "text",
467
+ text: `Cannot verify remote authentication — the web server at http://127.0.0.1:${platformPort} is not reachable: ${errMsg}. Ensure the ${BRAND_NAME} platform is running.`,
468
+ }],
469
+ isError: true,
470
+ };
471
+ }
472
+ });
473
+ eagerTool(server, "brand-settings", "Read the brand/styling configuration (name, tagline, colours, fonts, plugin sets). Reads from config/brand.json (stamped by the bundler at install time).", {}, async () => {
474
+ try {
475
+ const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
476
+ if (!existsSync(brandPath)) {
477
+ return { content: [{ type: "text", text: "No brand.json found — platform not properly installed." }], isError: true };
478
+ }
479
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
480
+ return { content: [{ type: "text", text: JSON.stringify(brand, null, 2) }] };
481
+ }
482
+ catch (err) {
483
+ return {
484
+ content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
485
+ isError: true,
486
+ };
487
+ }
488
+ });
489
+ /* ── Plugin selector options (deterministic) ────────────────────── */
490
+ const PLUGIN_DISPLAY = {
491
+ admin: { value: "admin", label: "admin", description: "Platform management — system status, account settings, logs, session control." },
492
+ memory: { value: "memory", label: "memory", description: "Graph memory — search, write, reindex, and ingest knowledge." },
493
+ docs: { value: "docs", label: "maxy-guide", description: "User guide and platform documentation — loaded on demand." },
494
+ cloudflare: { value: "cloudflare", label: "cloudflare", description: "Cloudflare Tunnel — expose your assistant publicly via a custom domain." },
495
+ anthropic: { value: "anthropic", label: "anthropic", description: "Claude connection — API key acquisition and configuration." },
496
+ scheduling: { value: "scheduling", label: "scheduling", description: "Calendar and scheduling — events, appointments, recurring triggers." },
497
+ email: { value: "email", label: "email", description: "Agent email account — setup, read, send, search." },
498
+ contacts: { value: "contacts", label: "contacts", description: "CRM contact management — create, lookup, update, list." },
499
+ tasks: { value: "tasks", label: "tasks", description: "Graph-backed task lifecycle — create, update, list, relate, complete." },
500
+ workflows: { value: "workflows", label: "workflows", description: "Persistent named workflows — reusable instruction sets." },
501
+ projects: { value: "projects", label: "projects", description: "Structured project execution — phased sprints, investigations, reviews, retrospectives." },
502
+ "business-assistant": { value: "business-assistant", label: "business-assistant", description: "Customer enquiries, scheduling, quoting, invoicing, daily briefings. Enhances: personal-assistant" },
503
+ sales: { value: "sales", label: "sales", description: "Buying signal detection, closing techniques, objection handling. Enhances: personal-assistant" },
504
+ "deep-research": { value: "deep-research", label: "deep-research", description: "Structured multi-source research — query decomposition, source evaluation, citation formatting. Enhances: research-assistant" },
505
+ telegram: { value: "telegram", label: "telegram", description: "Telegram bot setup — BotFather, admin and public channels. Enhances: personal-assistant" },
506
+ whatsapp: { value: "whatsapp", label: "whatsapp", description: "WhatsApp messaging, pairing, and conversation browsing. Enhances: personal-assistant" },
507
+ waitlist: { value: "waitlist", label: "waitlist", description: "Waitlist lifecycle — extract sign-ups, review, schedule automation." },
508
+ replicate: { value: "replicate", label: "replicate", description: "Image generation — photorealistic, design, and fast draft images via Replicate. Enhances: content-producer, research-assistant" },
509
+ };
510
+ server.tool("onboarding-plugin-options", "Return the fully-assembled multi-select options array for onboarding Step 1. Classification (core/recommended/available) is derived from brand.json — the agent renders the result directly via render-component without further transformation.", {}, async () => {
511
+ try {
512
+ const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
513
+ if (!existsSync(brandPath)) {
514
+ return { content: [{ type: "text", text: "No brand.json found at " + brandPath + " — platform not properly installed." }], isError: true };
515
+ }
516
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
517
+ const plugins = brand?.plugins;
518
+ if (!plugins)
519
+ return { content: [{ type: "text", text: "brand.json has no plugins configuration." }], isError: true };
520
+ const coreSet = new Set(plugins.core ?? []);
521
+ const defaultSet = new Set(plugins.defaultEnabled ?? []);
522
+ const availableSet = new Set(plugins.available ?? []);
523
+ const options = [];
524
+ for (const name of plugins.core ?? []) {
525
+ const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
526
+ options.push({ ...meta, badge: "Core", group: "Core — always active", locked: true, defaultSelected: true });
527
+ }
528
+ for (const name of plugins.defaultEnabled ?? []) {
529
+ if (coreSet.has(name))
530
+ continue;
531
+ const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
532
+ options.push({ ...meta, badge: "Recommended", group: "Maxy", defaultSelected: true });
533
+ }
534
+ for (const name of plugins.available ?? []) {
535
+ if (coreSet.has(name) || defaultSet.has(name))
536
+ continue;
537
+ const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
538
+ options.push({ ...meta, group: "Maxy" });
539
+ }
540
+ // Task 976: signal that the onboarding skill will append three Anthropic
541
+ // vertical entries from the claude-for-financial-services and
542
+ // knowledge-work-plugins marketplaces. The count is fixed because the
543
+ // skill prose always appends the same three (kyc-screener,
544
+ // meeting-prep-agent, pdf-viewer). Absence of this line post-install
545
+ // = the group was not surfaced (release blocker per task spec).
546
+ console.error(`[plugin-onboarding] group=anthropic-verticals presented=3`);
547
+ return { content: [{ type: "text", text: JSON.stringify(options, null, 2) }] };
548
+ }
549
+ catch (err) {
550
+ return { content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
551
+ }
552
+ });
553
+ eagerTool(server, "account-manage", "Read the account configuration (tier, domains, settings).", {}, async () => {
554
+ try {
555
+ const config = await readAccountConfig();
556
+ return {
557
+ content: [{ type: "text", text: JSON.stringify(config, null, 2) }],
558
+ };
559
+ }
560
+ catch (err) {
561
+ return {
562
+ content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
563
+ isError: true,
564
+ };
565
+ }
566
+ });
567
+ eagerTool(server, "account-update", "Update a user-configurable setting in account.json. Valid fields: outputStyle (default|explanatory), thinkingView (default|expanded|collapsed), effort (low|medium|high|max|auto), adminModel (any Anthropic model ID), publicModel (any Anthropic model ID), defaultAgent (slug of an existing public agent, or empty string to clear). Changes take effect on the next session.", {
568
+ field: z.enum(["outputStyle", "thinkingView", "effort", "adminModel", "publicModel", "defaultAgent"]),
569
+ value: z.string(),
570
+ }, async ({ field, value }) => {
571
+ const VALID = {
572
+ outputStyle: ["default", "explanatory"],
573
+ thinkingView: ["default", "expanded", "collapsed"],
574
+ effort: ["low", "medium", "high", "max", "auto"],
575
+ };
576
+ // defaultAgent: non-empty slug must reference an existing agent; empty string clears the field
577
+ if (field === "defaultAgent") {
578
+ const slug = value.trim();
579
+ if (slug) {
580
+ const agentConfigPath = join(getAccountDir(), "agents", slug, "config.json");
581
+ if (!existsSync(agentConfigPath)) {
582
+ return {
583
+ content: [{ type: "text", text: `Agent '${slug}' not found — no config.json in agents/${slug}/` }],
584
+ isError: true,
585
+ };
586
+ }
587
+ }
588
+ // empty slug is valid — handled below as a delete operation
589
+ }
590
+ // Model fields accept any non-empty string (model IDs change frequently)
591
+ const FREE_FORM_FIELDS = ["adminModel", "publicModel"];
592
+ if (FREE_FORM_FIELDS.includes(field)) {
593
+ if (!value.trim()) {
594
+ return {
595
+ content: [{ type: "text", text: `Invalid value for ${field}. Must be a non-empty model ID.` }],
596
+ isError: true,
597
+ };
598
+ }
599
+ }
600
+ else if (field !== "defaultAgent" && !VALID[field].includes(value)) {
601
+ console.error(`[admin:account-update] rejected field=${field} value="${value}" valid=[${VALID[field].join(", ")}]`);
602
+ return {
603
+ content: [{ type: "text", text: `Invalid value "${value}" for ${field}. Valid values: ${VALID[field].join(", ")}` }],
604
+ isError: true,
605
+ };
606
+ }
607
+ try {
608
+ const configPath = join(getAccountDir(), "account.json");
609
+ const config = await readAccountConfig();
610
+ const oldDefault = field === "defaultAgent" ? config.defaultAgent ?? "(none)" : undefined;
611
+ if (field === "defaultAgent" && !value.trim()) {
612
+ delete config.defaultAgent;
613
+ }
614
+ else {
615
+ config[field] = value;
616
+ }
617
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
618
+ console.error(`[admin:account-update] wrote field=${field} value="${value}" path=${configPath}`);
619
+ if (field === "defaultAgent") {
620
+ if (!value.trim()) {
621
+ return {
622
+ content: [{ type: "text", text: `Default agent cleared (was "${oldDefault}"). Visitors to the root URL will see the first available agent, or a "no agents" message if none exist. Takes effect on next page load.` }],
623
+ };
624
+ }
625
+ return {
626
+ content: [{ type: "text", text: `Default agent changed from "${oldDefault}" to "${value}". Takes effect on next page load.` }],
627
+ };
628
+ }
629
+ return {
630
+ content: [{ type: "text", text: `Updated ${field} to "${value}". Takes effect on next session.` }],
631
+ };
632
+ }
633
+ catch (err) {
634
+ return {
635
+ content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
636
+ isError: true,
637
+ };
638
+ }
639
+ });
640
+ // Plugin enable/disable: deterministic write to account.json.enabledPlugins.
641
+ // The pre-tool-use hook denies the agent's direct Edit on account.json (Task 831),
642
+ // so the agent can no longer toggle enablement by hand-editing the file. This
643
+ // tool is the legitimate path: validates the plugin name, refuses core plugins,
644
+ // confirms the plugin directory exists, and atomically updates the array.
645
+ // Entitlement-bearing fields (tier, purchasedPlugins) are NOT writable here.
646
+ server.tool("plugin-toggle-enabled", "Enable or disable a plugin in this account by adding/removing its name from account.json's enabledPlugins array. Validates the plugin exists under platform/plugins/, refuses to disable core plugins (admin, memory, docs, cloudflare, anthropic), and writes atomically. Takes effect on next session start. Does NOT change purchasedPlugins or tier — those derive from the signed entitlement payload.", {
647
+ pluginName: z.string().describe("Plugin slug (lowercase a-z0-9-)."),
648
+ action: z.enum(["enable", "disable"]).describe("enable adds to enabledPlugins; disable removes."),
649
+ }, async ({ pluginName, action }) => {
650
+ const TAG = "[admin:plugin-toggle-enabled]";
651
+ if (!VALID_PLUGIN_NAME.test(pluginName)) {
652
+ return { content: [{ type: "text", text: `${TAG} Invalid plugin name "${pluginName}". Must match ${VALID_PLUGIN_NAME}.` }], isError: true };
653
+ }
654
+ if (CORE_PLUGINS.includes(pluginName)) {
655
+ return { content: [{ type: "text", text: `${TAG} "${pluginName}" is a core plugin and cannot be toggled.` }], isError: true };
656
+ }
657
+ const pluginDir = resolve(PLUGINS_DIR, pluginName);
658
+ if (!existsSync(pluginDir) || !existsSync(join(pluginDir, "PLUGIN.md"))) {
659
+ return { content: [{ type: "text", text: `${TAG} Plugin "${pluginName}" not installed at ${pluginDir} (no PLUGIN.md). Install via premium-deliver or platform release.` }], isError: true };
660
+ }
661
+ try {
662
+ const configPath = join(getAccountDir(), "account.json");
663
+ const config = await readAccountConfig();
664
+ const current = Array.isArray(config.enabledPlugins) ? config.enabledPlugins : [];
665
+ let next;
666
+ if (action === "enable") {
667
+ if (current.includes(pluginName)) {
668
+ return { content: [{ type: "text", text: `${TAG} "${pluginName}" is already enabled.` }] };
669
+ }
670
+ next = [...current, pluginName];
671
+ }
672
+ else {
673
+ if (!current.includes(pluginName)) {
674
+ return { content: [{ type: "text", text: `${TAG} "${pluginName}" is not enabled — nothing to disable.` }] };
675
+ }
676
+ next = current.filter((n) => n !== pluginName);
677
+ }
678
+ config.enabledPlugins = next;
679
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
680
+ console.error(`${TAG} ${action}d plugin=${pluginName} path=${configPath}`);
681
+ return { content: [{ type: "text", text: `Plugin "${pluginName}" ${action}d. Takes effect on next session.` }] };
682
+ }
683
+ catch (err) {
684
+ return { content: [{ type: "text", text: `${TAG} Failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
685
+ }
686
+ });
687
+ // ===================================================================
688
+ // Admin user management tools
689
+ // ===================================================================
690
+ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a device-level user entry (users.json) and adds them to this account's admins list (account.json). PIN must be at least 4 digits. If no PIN is provided, a unique 4-digit PIN is generated. Returns the userId and PIN to share with the new admin.\n\nIMPORTANT — retry behaviour: if the user already stated a specific PIN earlier in the conversation and a first admin-add call failed (e.g. tier cap reached, PIN collision), every retry MUST re-pass that PIN as the `pin` parameter. Omitting `pin` on the retry auto-generates a different 4-digit PIN, silently substituting what the user asked for.", {
691
+ name: z.string().describe("Display name for the new admin (stored on the AdminUser node in Neo4j)."),
692
+ pin: z.string().optional().describe("Optional PIN (minimum 4 digits). If omitted, a unique 4-digit PIN is generated."),
693
+ }, async ({ name, pin: rawPin }) => {
694
+ const TAG = "[admin]";
695
+ if (!name.trim()) {
696
+ return { content: [{ type: "text", text: `${TAG} Name is required.` }], isError: true };
697
+ }
698
+ let users;
699
+ try {
700
+ users = readUsersJson();
701
+ }
702
+ catch (err) {
703
+ return { content: [{ type: "text", text: `${TAG} ${err instanceof Error ? err.message : String(err)}` }], isError: true };
704
+ }
705
+ // Enforce per-tier admin limit. Effective tier comes from the signed
706
+ // entitlement payload (Task 831) — editing account.json.tier directly
707
+ // has no effect; the verifier uses the signed value and emits a
708
+ // [entitlement] tampered: line if disk and signed diverge.
709
+ try {
710
+ const config = await readAccountConfig();
711
+ const currentAdmins = (config.admins ?? []);
712
+ const entitlement = await currentEntitlement();
713
+ const tier = entitlement.tier;
714
+ const maxAdmins = MAX_ADMINS_BY_TIER[tier] ?? MAX_ADMINS_DEFAULT;
715
+ if (currentAdmins.length >= maxAdmins) {
716
+ return { content: [{ type: "text", text: `${TAG} Admin limit reached (${maxAdmins} for ${tier || "this"} tier). Remove an existing admin before adding a new one.` }], isError: true };
717
+ }
718
+ }
719
+ catch (err) {
720
+ console.error(`${TAG} failed to check admin limit: ${err instanceof Error ? err.message : String(err)}`);
721
+ // Fail open would be unsafe — fail closed
722
+ return { content: [{ type: "text", text: `${TAG} Unable to verify admin limit — cannot add admin.` }], isError: true };
723
+ }
724
+ // Resolve the calling user's identity from the session environment
725
+ const callerUserId = process.env.USER_ID;
726
+ // PIN: use provided or generate. Constraint: minimum 4 digits, no upper bound.
727
+ let plaintextPin;
728
+ if (rawPin) {
729
+ if (rawPin.length < 4) {
730
+ return { content: [{ type: "text", text: `${TAG} PIN must be at least 4 digits.` }], isError: true };
731
+ }
732
+ const hashed = hashPin(rawPin);
733
+ if (users.some(u => u.pin === hashed)) {
734
+ return { content: [{ type: "text", text: `${TAG} That PIN is already in use. Choose a different PIN.` }], isError: true };
735
+ }
736
+ plaintextPin = rawPin;
737
+ }
738
+ else {
739
+ try {
740
+ plaintextPin = generateUniquePin(users);
741
+ }
742
+ catch (err) {
743
+ return { content: [{ type: "text", text: `${TAG} ${err instanceof Error ? err.message : String(err)}` }], isError: true };
744
+ }
745
+ }
746
+ const pinHash = hashPin(plaintextPin);
747
+ const userId = crypto.randomUUID();
748
+ // Three-store admin auth invariant (Task 850 → Task 904): users.json
749
+ // (device-level PIN auth) and account.json admins[] (account-level role)
750
+ // route through the shared `admins-write` helper so the dual write has a
751
+ // single chokepoint. Per-leg `[admins-write]` and legacy `[admin-auth-store]`
752
+ // log lines fire so existing dashboards continue to grep. Neo4j AdminUser
753
+ // (display + graph identity) writes below remain inline because they have
754
+ // no dual-file invariant.
755
+ const userIdShort = userId.slice(0, 8);
756
+ const authResult = writeAdminEntry({
757
+ userId,
758
+ pin: pinHash,
759
+ role: "admin",
760
+ usersFile: USERS_FILE,
761
+ accountDir: getAccountDir(),
762
+ caller: "admin-add",
763
+ });
764
+ console.error(`[admin-auth-store] action=add userId=${userIdShort} result=${authResult.usersJsonResult} store=users${authResult.usersError ? ` error=${authResult.usersError}` : ""}`);
765
+ if (authResult.usersJsonResult !== "ok") {
766
+ return { content: [{ type: "text", text: `${TAG} Failed to write users.json: ${authResult.usersError ?? "unknown error"}` }], isError: true };
767
+ }
768
+ console.error(`[admin-auth-store] action=add userId=${userIdShort} result=${authResult.accountJsonResult} store=account${authResult.accountError ? ` error=${authResult.accountError}` : ""}`);
769
+ if (authResult.accountJsonResult === "fail") {
770
+ return { content: [{ type: "text", text: `${TAG} users.json updated; account.json write FAILED — manual reconciliation needed: ${authResult.accountError ?? "unknown error"}` }], isError: true };
771
+ }
772
+ // 3. Write to Neo4j (graph-level): AdminUser + Person + OWNS atomically,
773
+ // plus ADMIN_OF edge to the LocalBusiness for this account.
774
+ // Task 830 — deterministic identity creation: never delegate node
775
+ // creation to the LLM. Person reuse rule mirrors writeAdminUserAndPerson
776
+ // in platform/ui/app/lib/neo4j-store.ts (case-insensitive exact match
777
+ // on givenName + familyName; partial-name ambiguity does NOT match —
778
+ // rationalisation is a separate agent-mediated concern).
779
+ // Task 850 — Neo4j-leg failure now returns is_error: true with the
780
+ // [admin-auth-store] line; previously it set a soft warning string and
781
+ // returned success, which is what hid the Adam Mackay incident from
782
+ // the recovery agent. The user is still functional via users.json +
783
+ // account.json, but the graph state is divergent and the operator
784
+ // must know.
785
+ let personReused = false;
786
+ try {
787
+ const session = getSession();
788
+ try {
789
+ const createdAt = new Date().toISOString();
790
+ const trimmedName = name.trim();
791
+ const firstSpace = trimmedName.search(/\s/);
792
+ const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
793
+ const familyName = firstSpace === -1 ? null : (trimmedName.slice(firstSpace + 1).trim() || null);
794
+ // Task 897: stamp `accountId` on every AdminUser at MERGE time, both
795
+ // ON CREATE and ON MATCH (the latter via COALESCE so a pre-existing
796
+ // value isn't overwritten if it differs — which would itself be a
797
+ // graph-invariant violation worth surfacing). Pre-897 the missing
798
+ // accountId fingerprint produced :AdminUser nodes that migration 004
799
+ // pruned silently, costing the admin pin during onboarding.
800
+ const result = await session.run(`MERGE (au:AdminUser {userId: $userId})
801
+ ON CREATE SET au.accountId = $accountId,
802
+ au.name = $name,
803
+ au.createdAt = $createdAt
804
+ ON MATCH SET au.accountId = COALESCE(au.accountId, $accountId),
805
+ au.name = $name,
806
+ au.updatedAt = $createdAt
807
+ WITH au
808
+ MATCH (b:LocalBusiness {accountId: $accountId})
809
+ MERGE (au)-[r:ADMIN_OF]->(b)
810
+ ON CREATE SET r.role = 'admin', r.grantedAt = $createdAt
811
+ WITH au
812
+ OPTIONAL MATCH (existingPerson:Person {accountId: $accountId})
813
+ WHERE toLower(existingPerson.givenName) = toLower($givenName)
814
+ AND coalesce(toLower(existingPerson.familyName), '') = coalesce(toLower($familyName), '')
815
+ WITH au, existingPerson
816
+ CALL {
817
+ WITH au, existingPerson
818
+ WITH au, existingPerson WHERE existingPerson IS NOT NULL
819
+ MERGE (au)-[:OWNS]->(existingPerson)
820
+ RETURN true AS reused
821
+ UNION
822
+ WITH au, existingPerson
823
+ WITH au WHERE existingPerson IS NULL
824
+ CREATE (newPerson:Person {
825
+ accountId: $accountId,
826
+ givenName: $givenName,
827
+ familyName: $familyName,
828
+ role: 'admin-personal',
829
+ scope: 'admin',
830
+ createdAt: $createdAt
831
+ })
832
+ MERGE (au)-[:OWNS]->(newPerson)
833
+ RETURN false AS reused
834
+ }
835
+ RETURN reused`, { userId, name: trimmedName, createdAt, accountId: ACCOUNT_ID, givenName, familyName });
836
+ if (result.records.length > 0) {
837
+ personReused = result.records[0].get("reused");
838
+ }
839
+ // Task 897 — post-write assertion mirroring Task 785 in neo4j-store.ts.
840
+ // MATCH back the AdminUser we just wrote and verify every required
841
+ // field landed. Loud-fail the tool call if accountId or name is null
842
+ // so a Cypher regression that drops the field is grep-detectable.
843
+ const verify = await session.run(`MATCH (au:AdminUser {userId: $userId})
844
+ RETURN coalesce(au.accountId, '') AS accountId,
845
+ coalesce(au.name, '') AS name`, { userId });
846
+ const verifiedAccountId = verify.records[0]?.get("accountId") || "";
847
+ const verifiedName = verify.records[0]?.get("name") || "";
848
+ if (!verifiedAccountId || !verifiedName) {
849
+ throw new Error(`post-write assertion failed: AdminUser userId=${userIdShort} accountId=${verifiedAccountId || "(null)"} name=${verifiedName || "(null)"} — required fields missing after MERGE`);
850
+ }
851
+ }
852
+ finally {
853
+ await session.close();
854
+ }
855
+ console.error(`[admin] admin-add success userId=${userIdShort} accountId=${ACCOUNT_ID} name=${name.trim()} required-fields=ok personReused=${personReused}`);
856
+ }
857
+ catch (err) {
858
+ const errMsg = err instanceof Error ? err.message : String(err);
859
+ console.error(`[admin-auth-store] action=add userId=${userIdShort} result=fail store=neo4j error=${errMsg}`);
860
+ return {
861
+ content: [{
862
+ type: "text",
863
+ text: `${TAG} users.json + account.json updated for userId ${userId} (PIN: ${plaintextPin}); Neo4j sync FAILED — manual reconciliation needed: ${errMsg}`,
864
+ }],
865
+ isError: true,
866
+ };
867
+ }
868
+ console.error(`${TAG} [admin-identity] adminuser-bound userId=${userIdShort} name=${name.trim()} personReused=${personReused}`);
869
+ console.error(`${TAG} admin added: userId=${userId} userName=${name.trim()} accountId=${ACCOUNT_ID} role=admin addedBy=${callerUserId ?? "unknown"}`);
870
+ return {
871
+ content: [{
872
+ type: "text",
873
+ text: `Admin added successfully.\n\n- **Name:** ${name.trim()}\n- **userId:** ${userId}\n- **PIN:** ${plaintextPin}\n- **Role:** admin\n\nShare the PIN with ${name.trim()} so they can log in.`,
874
+ }],
875
+ };
876
+ });
877
+ eagerTool(server, "admin-remove", "Remove an admin from this account. Removes them from the account's admins list (account.json) and deletes the ADMIN_OF relationship in Neo4j. Does NOT remove the device-level user entry (they may admin other accounts). Cannot remove the last admin on the account.", {
878
+ userId: z.string().describe("The userId of the admin to remove (use admin-list to find userIds)"),
879
+ }, async ({ userId }) => {
880
+ const TAG = "[admin]";
881
+ const callerUserId = process.env.USER_ID;
882
+ // Read account.json and validate
883
+ let config;
884
+ let admins;
885
+ try {
886
+ config = await readAccountConfig();
887
+ admins = (config.admins ?? []);
888
+ }
889
+ catch (err) {
890
+ return { content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
891
+ }
892
+ const targetIndex = admins.findIndex(a => a.userId === userId);
893
+ if (targetIndex === -1) {
894
+ return { content: [{ type: "text", text: `${TAG} User ${userId} is not an admin of this account.` }], isError: true };
895
+ }
896
+ if (admins.length <= 1) {
897
+ return { content: [{ type: "text", text: `${TAG} Cannot remove the last admin. At least one admin must remain on the account.` }], isError: true };
898
+ }
899
+ // Resolve the admin's name from Neo4j (canonical) for the confirmation
900
+ // message. Best-effort — fall back to userId if the graph is unreachable.
901
+ let removedName = userId;
902
+ try {
903
+ const session = getSession();
904
+ try {
905
+ const result = await session.run(`MATCH (au:AdminUser {userId: $userId}) RETURN au.name AS name LIMIT 1`, { userId });
906
+ if (result.records.length > 0) {
907
+ const name = result.records[0].get("name");
908
+ if (name && name.trim())
909
+ removedName = name.trim();
910
+ }
911
+ }
912
+ finally {
913
+ await session.close();
914
+ }
915
+ }
916
+ catch { /* name lookup is best-effort */ }
917
+ // 1. Remove from account.json — single-file write through the shared
918
+ // chokepoint helper. users.json is intentionally untouched (the user may
919
+ // admin other accounts on this device); the helper logs `usersJsonResult=skip`.
920
+ const removeResult = removeAdminFromAccount({
921
+ userId,
922
+ accountDir: getAccountDir(),
923
+ caller: "admin-remove",
924
+ });
925
+ if (removeResult.accountJsonResult === "fail") {
926
+ return { content: [{ type: "text", text: `${TAG} Failed to write account.json: ${removeResult.accountError ?? "unknown error"}` }], isError: true };
927
+ }
928
+ // 2. Delete ADMIN_OF relationship in Neo4j — partial failure is a warning
929
+ let neo4jWarning = "";
930
+ try {
931
+ const session = getSession();
932
+ try {
933
+ await session.run(`MATCH (au:AdminUser {userId: $userId})-[r:ADMIN_OF]->(b:LocalBusiness {accountId: $accountId})
934
+ DELETE r`, { userId, accountId: ACCOUNT_ID });
935
+ }
936
+ finally {
937
+ await session.close();
938
+ }
939
+ }
940
+ catch (err) {
941
+ const errMsg = err instanceof Error ? err.message : String(err);
942
+ console.error(`${TAG} Neo4j sync failed during admin-remove: userId=${userId} error=${errMsg}`);
943
+ neo4jWarning = ` Note: Neo4j relationship deletion failed (${errMsg}) — will be reconciled on next seed.`;
944
+ }
945
+ console.error(`${TAG} admin removed: userId=${userId} accountId=${ACCOUNT_ID} removedBy=${callerUserId ?? "unknown"}`);
946
+ return {
947
+ content: [{
948
+ type: "text",
949
+ text: `Removed ${removedName} from this account's admin list. Their PIN still works for any other accounts they administer.${neo4jWarning}`,
950
+ }],
951
+ };
952
+ });
953
+ eagerTool(server, "admin-list", "List all admins for this account with their names and roles.", {}, async () => {
954
+ const TAG = "[admin]";
955
+ let admins;
956
+ try {
957
+ const config = await readAccountConfig();
958
+ admins = (config.admins ?? []);
959
+ }
960
+ catch (err) {
961
+ return { content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
962
+ }
963
+ if (admins.length === 0) {
964
+ return { content: [{ type: "text", text: `${TAG} No admins configured for this account.` }] };
965
+ }
966
+ // Enrich with names from Neo4j AdminUser (canonical, Task 829). Best
967
+ // effort — render "(unknown)" for any userId without a graph entry.
968
+ const userMap = new Map();
969
+ try {
970
+ const session = getSession();
971
+ try {
972
+ const result = await session.run(`UNWIND $userIds AS uid
973
+ MATCH (au:AdminUser {userId: uid})
974
+ RETURN au.userId AS userId, au.name AS name`, { userIds: admins.map(a => a.userId) });
975
+ for (const record of result.records) {
976
+ const uid = record.get("userId");
977
+ const name = record.get("name");
978
+ if (name && name.trim())
979
+ userMap.set(uid, name.trim());
980
+ }
981
+ }
982
+ finally {
983
+ await session.close();
984
+ }
985
+ }
986
+ catch { /* name lookup is best-effort — userIds shown when graph is unreachable */ }
987
+ const lines = admins.map(a => {
988
+ const name = userMap.get(a.userId) ?? "(unknown)";
989
+ return `- **${name}** — role: ${a.role}, userId: ${a.userId}`;
990
+ });
991
+ return {
992
+ content: [{ type: "text", text: `Admins for this account:\n\n${lines.join("\n")}` }],
993
+ };
994
+ });
995
+ eagerTool(server, "admin-update-pin", "Update an existing admin user's PIN. Defaults to the calling admin if no userId is given. PIN must be at least 4 digits and unique across all users on the device. PINs are device-level: updating another admin's PIN does not require shared account membership — any admin on the device can rotate any other admin's PIN, matching the existing trust model used by admin-remove.", {
996
+ userId: z.string().optional().describe("The userId of the admin whose PIN to update. Defaults to the caller (the admin invoking this tool)."),
997
+ newPin: z.string().describe("The new PIN. Minimum 4 digits, no upper bound."),
998
+ }, async ({ userId: targetUserId, newPin }) => {
999
+ const TAG = "[admin-update-pin]";
1000
+ const callerUserId = process.env.USER_ID;
1001
+ const userId = targetUserId ?? callerUserId;
1002
+ const userIdLabel = userId ? userId.slice(0, 8) : "unknown";
1003
+ if (!userId) {
1004
+ console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=no-caller-context`);
1005
+ return { content: [{ type: "text", text: `${TAG} No userId supplied and no caller context — cannot update.` }], isError: true };
1006
+ }
1007
+ if (newPin.length < 4) {
1008
+ console.error(`${TAG} userId=${userIdLabel} result=too-short`);
1009
+ return { content: [{ type: "text", text: `${TAG} PIN must be at least 4 digits.` }], isError: true };
1010
+ }
1011
+ let users;
1012
+ try {
1013
+ users = readUsersJson();
1014
+ }
1015
+ catch (err) {
1016
+ const errMsg = err instanceof Error ? err.message : String(err);
1017
+ console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=users-json-read-failed`);
1018
+ console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=${errMsg}`);
1019
+ return { content: [{ type: "text", text: `${TAG} ${errMsg}` }], isError: true };
1020
+ }
1021
+ const targetIndex = users.findIndex(u => u.userId === userId);
1022
+ if (targetIndex === -1) {
1023
+ console.error(`${TAG} userId=${userIdLabel} result=user-not-found`);
1024
+ console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=user-not-found`);
1025
+ return { content: [{ type: "text", text: `${TAG} User ${userId} not found in users.json.` }], isError: true };
1026
+ }
1027
+ const newHash = hashPin(newPin);
1028
+ const collidesWithOther = users.some((u, i) => i !== targetIndex && u.pin === newHash);
1029
+ if (collidesWithOther) {
1030
+ console.error(`${TAG} userId=${userIdLabel} result=collision`);
1031
+ console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=pin-collision`);
1032
+ return { content: [{ type: "text", text: `${TAG} That PIN is already in use by another user. Choose a different PIN.` }], isError: true };
1033
+ }
1034
+ users[targetIndex].pin = newHash;
1035
+ try {
1036
+ writeUsersJson(users);
1037
+ }
1038
+ catch (err) {
1039
+ const errMsg = err instanceof Error ? err.message : String(err);
1040
+ console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=${errMsg}`);
1041
+ return { content: [{ type: "text", text: `${TAG} Failed to write users.json: ${errMsg}` }], isError: true };
1042
+ }
1043
+ console.error(`${TAG} userId=${userIdLabel} result=ok`);
1044
+ console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=ok store=users`);
1045
+ const self = userId === callerUserId;
1046
+ return {
1047
+ content: [{ type: "text", text: `PIN updated${self ? "" : ` for userId ${userId}`}. The new PIN takes effect on the next login.` }],
1048
+ };
1049
+ });
1050
+ server.tool("agent-image", "Upload, update, or remove an agent's image. action=set: copy the file at filePath to the agent's assets directory and update config.json with image URL and shape. action=remove: delete the image file and clear the image fields from config.json. imageShape: 'circle' for avatars/icons, 'rounded' for logos.", {
1051
+ agentSlug: z.string().describe("Agent slug (e.g. 'coaching', 'sales')"),
1052
+ action: z.enum(["set", "remove"]),
1053
+ filePath: z.string().optional().describe("Absolute path to the image file (required for action=set)"),
1054
+ imageShape: z.enum(["circle", "rounded"]).optional().describe("Image shape: circle for avatars, rounded for logos (required for action=set)"),
1055
+ }, async ({ agentSlug, action, filePath, imageShape }) => {
1056
+ const VALID_IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
1057
+ try {
1058
+ const accountDir = getAccountDir();
1059
+ const agentDir = resolve(accountDir, "agents", agentSlug);
1060
+ const configPath = resolve(agentDir, "config.json");
1061
+ if (!existsSync(configPath)) {
1062
+ return {
1063
+ content: [{ type: "text", text: `Agent '${agentSlug}' not found — no config.json at agents/${agentSlug}/` }],
1064
+ isError: true,
1065
+ };
1066
+ }
1067
+ if (action === "set") {
1068
+ if (!filePath) {
1069
+ return {
1070
+ content: [{ type: "text", text: "filePath is required for action=set" }],
1071
+ isError: true,
1072
+ };
1073
+ }
1074
+ if (!imageShape) {
1075
+ return {
1076
+ content: [{ type: "text", text: "imageShape is required for action=set — use 'circle' for avatars/icons or 'rounded' for logos" }],
1077
+ isError: true,
1078
+ };
1079
+ }
1080
+ if (!existsSync(filePath)) {
1081
+ return {
1082
+ content: [{ type: "text", text: `File not found: ${filePath}` }],
1083
+ isError: true,
1084
+ };
1085
+ }
1086
+ // Validate image extension
1087
+ const ext = filePath.toLowerCase().split(".").pop();
1088
+ if (!ext || !VALID_IMAGE_EXTENSIONS.includes(`.${ext}`)) {
1089
+ return {
1090
+ content: [{ type: "text", text: `Not a valid image file. Supported formats: ${VALID_IMAGE_EXTENSIONS.join(", ")}` }],
1091
+ isError: true,
1092
+ };
1093
+ }
1094
+ // Copy file to agent assets directory
1095
+ const assetsDir = resolve(agentDir, "assets");
1096
+ const { mkdirSync: mkdirSyncFs, copyFileSync, writeFileSync: writeFileSyncFs, renameSync } = await import("node:fs");
1097
+ mkdirSyncFs(assetsDir, { recursive: true });
1098
+ const filename = `agent-image.${ext}`;
1099
+ const destPath = resolve(assetsDir, filename);
1100
+ copyFileSync(filePath, destPath);
1101
+ // Atomic config update: read → modify → write temp → rename
1102
+ const configRaw = await readFile(configPath, "utf-8");
1103
+ let config;
1104
+ try {
1105
+ config = JSON.parse(configRaw);
1106
+ }
1107
+ catch {
1108
+ config = {};
1109
+ }
1110
+ config.image = `/agent-assets/${agentSlug}/${filename}`;
1111
+ config.imageShape = imageShape;
1112
+ const tmpPath = configPath + ".tmp";
1113
+ writeFileSyncFs(tmpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1114
+ renameSync(tmpPath, configPath);
1115
+ console.error(`[agent-config] ${agentSlug}: image=${config.image} imageShape=${imageShape} (set)`);
1116
+ return {
1117
+ content: [{ type: "text", text: `Image set for agent '${agentSlug}':\n File: ${destPath}\n URL: ${config.image}\n Shape: ${imageShape}\n\nThe image is now accessible at ${config.image} and will appear in the public chat header.` }],
1118
+ };
1119
+ }
1120
+ // action === "remove"
1121
+ const configRaw = await readFile(configPath, "utf-8");
1122
+ let config;
1123
+ try {
1124
+ config = JSON.parse(configRaw);
1125
+ }
1126
+ catch {
1127
+ config = {};
1128
+ }
1129
+ // Delete the image file if it exists
1130
+ if (typeof config.image === "string") {
1131
+ const filename = config.image.split("/").pop();
1132
+ if (filename) {
1133
+ const imgPath = resolve(agentDir, "assets", filename);
1134
+ if (existsSync(imgPath)) {
1135
+ const { unlinkSync } = await import("node:fs");
1136
+ unlinkSync(imgPath);
1137
+ }
1138
+ }
1139
+ }
1140
+ // Remove image fields from config
1141
+ delete config.image;
1142
+ delete config.imageShape;
1143
+ const { writeFileSync: writeFileSyncRm, renameSync: renameSyncRm } = await import("node:fs");
1144
+ const tmpPath = configPath + ".tmp";
1145
+ writeFileSyncRm(tmpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1146
+ renameSyncRm(tmpPath, configPath);
1147
+ console.error(`[agent-config] ${agentSlug}: image removed`);
1148
+ return {
1149
+ content: [{ type: "text", text: `Image removed for agent '${agentSlug}'. The public chat header will fall back to the account brand logo.` }],
1150
+ };
1151
+ }
1152
+ catch (err) {
1153
+ console.error(`[agent-config] agent-image failed: ${err instanceof Error ? err.message : String(err)}`);
1154
+ return {
1155
+ content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
1156
+ isError: true,
1157
+ };
1158
+ }
1159
+ });
1160
+ // ---------------------------------------------------------------------------
1161
+ // Agent config tools — deterministic reads for agent configuration
1162
+ // ---------------------------------------------------------------------------
1163
+ server.tool("agent-config-read", "Read the full config.json for a specific public agent by slug. Returns the complete configuration including slug, displayName, model, plugins, status, liveMemory, and any other fields.", {
1164
+ slug: z.string().describe("Agent slug (directory name under agents/)"),
1165
+ }, async ({ slug }) => {
1166
+ const TAG = "[admin:agent-config-read]";
1167
+ // Path traversal guard
1168
+ if (slug.includes("/") || slug.includes("\\") || slug.includes("..")) {
1169
+ console.error(`${TAG} rejected slug="${slug}" — contains path separator or traversal sequence`);
1170
+ return {
1171
+ content: [{ type: "text", text: `Invalid agent slug: "${slug}" — slug must not contain path separators or traversal sequences` }],
1172
+ isError: true,
1173
+ };
1174
+ }
1175
+ try {
1176
+ const accountDir = getAccountDir();
1177
+ const configPath = resolve(accountDir, "agents", slug, "config.json");
1178
+ if (!existsSync(configPath)) {
1179
+ console.error(`${TAG} agent not found: slug="${slug}"`);
1180
+ return {
1181
+ content: [{ type: "text", text: `Agent "${slug}" not found — no config.json at agents/${slug}/` }],
1182
+ isError: true,
1183
+ };
1184
+ }
1185
+ const raw = readFileSync(configPath, "utf-8");
1186
+ JSON.parse(raw); // validate JSON before returning
1187
+ return {
1188
+ content: [{ type: "text", text: raw }],
1189
+ };
1190
+ }
1191
+ catch (err) {
1192
+ console.error(`${TAG} failed for slug="${slug}": ${err instanceof Error ? err.message : String(err)}`);
1193
+ return {
1194
+ content: [{ type: "text", text: `Failed to read config for agent "${slug}": ${err instanceof Error ? err.message : String(err)}` }],
1195
+ isError: true,
1196
+ };
1197
+ }
1198
+ });
1199
+ server.tool("agent-list", "List all public (non-admin) agents with their full configuration: slug, displayName, model, plugins, status, liveMemory, and whether each is the account's default agent.", {}, async () => {
1200
+ const TAG = "[admin:agent-list]";
1201
+ try {
1202
+ const accountDir = getAccountDir();
1203
+ const agentsDir = resolve(accountDir, "agents");
1204
+ if (!existsSync(agentsDir)) {
1205
+ return {
1206
+ content: [{ type: "text", text: JSON.stringify({ agents: [], skipped: [] }, null, 2) }],
1207
+ };
1208
+ }
1209
+ // Best-effort defaultAgent lookup — don't fail the whole list if account.json is unreadable
1210
+ let defaultAgent;
1211
+ try {
1212
+ const accountConfig = await readAccountConfig();
1213
+ defaultAgent = accountConfig.defaultAgent;
1214
+ }
1215
+ catch (err) {
1216
+ console.error(`${TAG} failed to read account config for defaultAgent lookup: ${err instanceof Error ? err.message : String(err)}`);
1217
+ }
1218
+ const agents = [];
1219
+ const skipped = [];
1220
+ const entries = readdirSync(agentsDir, { withFileTypes: true });
1221
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
1222
+ if (!entry.isDirectory())
1223
+ continue;
1224
+ if (entry.name === "admin")
1225
+ continue;
1226
+ const configPath = resolve(agentsDir, entry.name, "config.json");
1227
+ if (!existsSync(configPath))
1228
+ continue;
1229
+ try {
1230
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
1231
+ agents.push({
1232
+ slug: entry.name,
1233
+ displayName: config.displayName ?? entry.name,
1234
+ model: config.model,
1235
+ plugins: config.plugins,
1236
+ status: config.status,
1237
+ liveMemory: config.liveMemory === true || config.liveMemory === "true",
1238
+ isDefault: entry.name === defaultAgent,
1239
+ });
1240
+ }
1241
+ catch (err) {
1242
+ console.error(`${TAG} failed to parse config.json for agent "${entry.name}" — skipping: ${err instanceof Error ? err.message : String(err)}`);
1243
+ skipped.push(entry.name);
1244
+ }
1245
+ }
1246
+ return {
1247
+ content: [{ type: "text", text: JSON.stringify({ agents, skipped }, null, 2) }],
1248
+ };
1249
+ }
1250
+ catch (err) {
1251
+ console.error(`${TAG} failed: ${err instanceof Error ? err.message : String(err)}`);
1252
+ return {
1253
+ content: [{ type: "text", text: `Failed to list agents: ${err instanceof Error ? err.message : String(err)}` }],
1254
+ isError: true,
1255
+ };
1256
+ }
1257
+ });
1258
+ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream/error/session/public) are per-session — pass `sessionKey` (preferred) or the legacy `conversationId` alias to retrieve a single session's log from first [spawn] to final [process-exit]. type=agent-stream: per-session tool-use/tool-result archive (every `[tool-use]` and `[tool-result]` pair with full input + output JSON, plus raw Claude stream-json, agent events, and MCP server stderr via tee). USE THIS when investigating what an agent ACTUALLY did with its tools — server.log only carries `[persist] tool-call persisted` markers, not bodies. (`type=system` is a backwards-compatible alias for the same archive.) type=session: SSE events sent to client. type=error: Claude subprocess stderr (raw — NODE_DEBUG HTTP/NET/UNDICI traces land in agent-stream via the stream tee, not here). type=heartbeat: platform event dispatcher (check-due-events cron). type=public: public agent diagnostic log. type=server: platform server log. type=mcp: MCP server stderr (per-plugin raw). type=vnc: VNC browser viewer lifecycle.", {
1259
+ type: z.enum(["agent-stream", "system", "session", "error", "heartbeat", "public", "server", "mcp", "vnc"]).optional(),
1260
+ lines: z.number().optional(),
1261
+ sessionKey: z.string().optional(),
1262
+ conversationId: z.string().optional(),
1263
+ }, async ({ type, lines = 50, sessionKey, conversationId }) => {
1264
+ try {
1265
+ const LOG_DIR = resolve(getAccountDir(), "logs");
1266
+ // Task 1006 — sessionKey is the single identifier on disk.
1267
+ // `conversationId` is retained as a legacy alias that maps to the
1268
+ // sessionKey-named file; Task 1007 will collapse the alias in the UI.
1269
+ const idForResolve = sessionKey ?? conversationId;
1270
+ if (idForResolve) {
1271
+ if (!existsSync(LOG_DIR)) {
1272
+ return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
1273
+ }
1274
+ const prefixMap = {
1275
+ "agent-stream": "claude-agent-stream",
1276
+ system: "claude-agent-stream", // Task 850 — backwards-compatible alias for agent-stream
1277
+ error: "claude-agent-stderr",
1278
+ session: "sse-events",
1279
+ public: "public-agent-stream",
1280
+ };
1281
+ const resolvedType = type ?? "agent-stream";
1282
+ const prefix = prefixMap[resolvedType];
1283
+ if (!prefix) {
1284
+ return {
1285
+ content: [{ type: "text", text: `type=${resolvedType} is not per-session. Valid per-session types: agent-stream, error, session, public. For platform-scoped types (server, vnc, heartbeat, mcp) omit the id.` }],
1286
+ isError: true,
1287
+ };
1288
+ }
1289
+ const fileName = `${prefix}-${idForResolve}.log`;
1290
+ const filePath = resolve(LOG_DIR, fileName);
1291
+ if (existsSync(filePath)) {
1292
+ const result = execFileSync("tail", ["-n", String(lines), filePath], { timeout: 5000 }).toString();
1293
+ return { content: [{ type: "text", text: `# ${fileName}\n\n${result}` }] };
1294
+ }
1295
+ // Emit missing-on-resolve so the writer-side existence contract is
1296
+ // the authoritative signal. One such line on any device is a P0.
1297
+ try {
1298
+ const ts = new Date().toISOString();
1299
+ appendFileSync(resolve(CONFIG_DIR, "logs", "server.log"), `${ts} [log-tee] missing-on-resolve sessionKey=${idForResolve.slice(0, 8)} surface=mcp-logs-read reason="file-not-found-in-LOG_DIR"\n`);
1300
+ }
1301
+ catch {
1302
+ // best-effort
1303
+ }
1304
+ return {
1305
+ content: [{
1306
+ type: "text",
1307
+ text: `No log file found for id=${idForResolve} type=${resolvedType}. tried=[${filePath}] reason=file-not-found`,
1308
+ }],
1309
+ };
1310
+ }
1311
+ if (!existsSync(LOG_DIR)) {
1312
+ return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
1313
+ }
1314
+ // --- Session-key filtered mode: grep across log files ---
1315
+ if (sessionKey) {
1316
+ const prefixes = {
1317
+ "agent-stream": "claude-agent-stream-",
1318
+ system: "claude-agent-stream-", // Task 850 — backwards-compatible alias
1319
+ error: "claude-agent-stderr-",
1320
+ session: "sse-events-",
1321
+ public: "public-agent-stream-",
1322
+ mcp: "mcp-",
1323
+ };
1324
+ // When a type is explicitly provided, search only that type's files.
1325
+ // When type is omitted, search all prefix-based log types (excludes heartbeat — no session context).
1326
+ // server and vnc are single-file (configDir-scoped), so they have no prefix search;
1327
+ // their matches are produced by dedicated blocks below.
1328
+ const searchPrefixes = type && type !== "heartbeat" && type !== "server" && type !== "vnc" && prefixes[type]
1329
+ ? { [type]: prefixes[type] }
1330
+ : (type === "server" || type === "vnc") ? {} : prefixes;
1331
+ const allFiles = readdirSync(LOG_DIR).filter(f => f.endsWith(".log"));
1332
+ const sections = [];
1333
+ // Search account-scoped prefix-based log files
1334
+ for (const [logType, prefix] of Object.entries(searchPrefixes)) {
1335
+ const typeFiles = allFiles
1336
+ .filter(f => f.startsWith(prefix))
1337
+ .sort(); // chronological by date suffix
1338
+ for (const file of typeFiles) {
1339
+ const filePath = resolve(LOG_DIR, file);
1340
+ try {
1341
+ const result = execFileSync("grep", ["-F", sessionKey, filePath], { timeout: 5000 }).toString().trim();
1342
+ if (result) {
1343
+ sections.push(`## ${file} (${logType})\n${result}`);
1344
+ }
1345
+ }
1346
+ catch (grepErr) {
1347
+ // grep exits 1 when no lines match — that's not an error
1348
+ const exitCode = grepErr.status;
1349
+ if (exitCode !== 1)
1350
+ throw grepErr;
1351
+ }
1352
+ }
1353
+ }
1354
+ // Search server.log (platform-scoped, in configDir — not account-scoped)
1355
+ const includeServer = !type || type === "server";
1356
+ if (includeServer) {
1357
+ const serverLog = resolve(CONFIG_DIR, "logs", "server.log");
1358
+ if (existsSync(serverLog)) {
1359
+ try {
1360
+ const result = execFileSync("grep", ["-F", sessionKey, serverLog], { timeout: 5000 }).toString().trim();
1361
+ if (result) {
1362
+ sections.push(`## server.log (server)\n${result}`);
1363
+ }
1364
+ }
1365
+ catch (grepErr) {
1366
+ const exitCode = grepErr.status;
1367
+ if (exitCode !== 1)
1368
+ throw grepErr;
1369
+ }
1370
+ }
1371
+ }
1372
+ // Search vnc-boot.log (platform-scoped, in configDir) — the
1373
+ // consolidated VNC browser viewer lifecycle log.
1374
+ const includeVnc = !type || type === "vnc";
1375
+ if (includeVnc) {
1376
+ const vncLog = resolve(CONFIG_DIR, "logs", "vnc-boot.log");
1377
+ if (existsSync(vncLog)) {
1378
+ try {
1379
+ const result = execFileSync("grep", ["-F", sessionKey, vncLog], { timeout: 5000 }).toString().trim();
1380
+ if (result) {
1381
+ sections.push(`## vnc-boot.log (vnc)\n${result}`);
1382
+ }
1383
+ }
1384
+ catch (grepErr) {
1385
+ const exitCode = grepErr.status;
1386
+ if (exitCode !== 1)
1387
+ throw grepErr;
1388
+ }
1389
+ }
1390
+ }
1391
+ if (sections.length === 0) {
1392
+ return { content: [{ type: "text", text: `No log lines found for session key "${sessionKey}".` }] };
1393
+ }
1394
+ return { content: [{ type: "text", text: `# Session timeline: ${sessionKey}\n\n${sections.join("\n\n")}` }] };
1395
+ }
1396
+ // --- Standard mode: tail most recent file of the requested type ---
1397
+ const resolvedType = type ?? "agent-stream";
1398
+ // Heartbeat log is a single fixed file, not prefix-based
1399
+ if (resolvedType === "heartbeat") {
1400
+ const logFile = resolve(LOG_DIR, "check-due-events.log");
1401
+ if (!existsSync(logFile)) {
1402
+ return { content: [{ type: "text", text: "No heartbeat log found yet. The platform cron may not have run." }] };
1403
+ }
1404
+ const result = execFileSync("tail", ["-n", String(lines), logFile], {
1405
+ timeout: 5000,
1406
+ }).toString();
1407
+ return { content: [{ type: "text", text: `# check-due-events.log\n\n${result}` }] };
1408
+ }
1409
+ // Server log is a single fixed file in configDir (platform-scoped, not account-scoped)
1410
+ if (resolvedType === "server") {
1411
+ const serverLog = resolve(CONFIG_DIR, "logs", "server.log");
1412
+ if (!existsSync(serverLog)) {
1413
+ return { content: [{ type: "text", text: `No server log found at ${serverLog}` }] };
1414
+ }
1415
+ const result = execFileSync("tail", ["-n", String(lines), serverLog], {
1416
+ timeout: 5000,
1417
+ }).toString();
1418
+ return { content: [{ type: "text", text: `# server.log\n\n${result}` }] };
1419
+ }
1420
+ // VNC log is a single platform-scoped file covering the full
1421
+ // browser viewer lifecycle — see tool description for signals.
1422
+ if (resolvedType === "vnc") {
1423
+ const vncLogFile = resolve(CONFIG_DIR, "logs", "vnc-boot.log");
1424
+ if (!existsSync(vncLogFile)) {
1425
+ return { content: [{ type: "text", text: `No vnc-boot log found at ${vncLogFile}` }] };
1426
+ }
1427
+ const result = execFileSync("tail", ["-n", String(lines), vncLogFile], {
1428
+ timeout: 5000,
1429
+ }).toString();
1430
+ return { content: [{ type: "text", text: `# vnc-boot.log\n\n${result}` }] };
1431
+ }
1432
+ // Task 850 — agent-stream and the legacy system alias both map to
1433
+ // claude-agent-stream-. The fall-through default also covers them.
1434
+ const prefix = resolvedType === "error" ? "claude-agent-stderr-"
1435
+ : resolvedType === "session" ? "sse-events-"
1436
+ : resolvedType === "public" ? "public-agent-stream-"
1437
+ : resolvedType === "mcp" ? "mcp-"
1438
+ : "claude-agent-stream-";
1439
+ // Find the most recently modified file with the matching prefix
1440
+ const match = readdirSync(LOG_DIR)
1441
+ .filter(f => f.startsWith(prefix) && f.endsWith(".log"))
1442
+ .map(f => ({ file: f, mtime: statSync(resolve(LOG_DIR, f)).mtimeMs }))
1443
+ .sort((a, b) => b.mtime - a.mtime)[0];
1444
+ if (!match) {
1445
+ return { content: [{ type: "text", text: `No log file found for type '${resolvedType}' in ${LOG_DIR}` }] };
1446
+ }
1447
+ const logFile = resolve(LOG_DIR, match.file);
1448
+ const result = execFileSync("tail", ["-n", String(lines), logFile], {
1449
+ timeout: 5000,
1450
+ }).toString();
1451
+ return { content: [{ type: "text", text: `# ${match.file}\n\n${result}` }] };
1452
+ }
1453
+ catch (err) {
1454
+ return { content: [{ type: "text", text: `Failed to read logs: ${err instanceof Error ? err.message : String(err)}` }] };
1455
+ }
1456
+ });
1457
+ // Task 918 — operator-authored skills are written to disk as a plugin so the
1458
+ // existing plugin-manifest loader picks them up (parsePluginFrontmatter +
1459
+ // assemblePublicPluginContent + loadEmbeddedPlugins all read from
1460
+ // <PLATFORM_ROOT>/plugins/<dir>/). Canonical = <accountDir>/plugins/<plugin>
1461
+ // survives installer rmSync (data/ excluded); the mirror under
1462
+ // <PLATFORM_ROOT>/plugins/<plugin> is rehydrated at admin session start by
1463
+ // autoDeliverUserPlugins. The agent supplies pluginName/skillName/body —
1464
+ // path is computed by this tool from ACCOUNT_ID. Symmetric write counterpart
1465
+ // to plugin-read (Task 916).
1466
+ eagerTool(server, "store-skill", "Save an operator-authored skill on disk as part of an admin-managed plugin. " +
1467
+ "The skill becomes immediately discoverable by the admin agent (and the public agent if publicEmbed=true). " +
1468
+ "Path is computed internally from the active account; the agent supplies content + names only. " +
1469
+ "Re-running for the same skillName overwrites in place (drops orphan reference files).", {
1470
+ pluginName: z.string().describe("Plugin directory name in lowercase kebab-case (e.g. 'beacons-skills'). Must not collide with a shipped plugin name."),
1471
+ skillName: z.string().describe("Skill directory name in lowercase kebab-case (e.g. 'lead-capture')."),
1472
+ description: z.string().describe("One-sentence skill description used in SKILL.md frontmatter and as the trigger hint for the agent."),
1473
+ publicEmbed: z.boolean().describe("True to embed this skill in the public agent's system prompt; false for admin-only."),
1474
+ body: z.string().describe("SKILL.md body content WITHOUT frontmatter. Frontmatter (name, description, publicEmbed) is composed by the tool."),
1475
+ references: z.array(z.object({
1476
+ filename: z.string().describe("Reference filename matching ^[a-z0-9-]+\\.md$ (no path separators)."),
1477
+ content: z.string().describe("Reference file body content."),
1478
+ })).default([]).describe("Optional reference files written under skills/<skillName>/references/."),
1479
+ }, async ({ pluginName, skillName, description, publicEmbed, body, references }) => {
1480
+ const KEBAB_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
1481
+ const REF_RE = /^[a-z0-9-]+\.md$/;
1482
+ if (!KEBAB_RE.test(pluginName)) {
1483
+ console.error(`[store-skill] ERROR pluginName=${pluginName} reason=invalid-kebab path=-`);
1484
+ return { content: [{ type: "text", text: `pluginName must be lowercase kebab-case (1-64 chars, no leading/trailing hyphen): "${pluginName}"` }], isError: true };
1485
+ }
1486
+ if (!KEBAB_RE.test(skillName)) {
1487
+ console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=invalid-kebab path=-`);
1488
+ return { content: [{ type: "text", text: `skillName must be lowercase kebab-case (1-64 chars, no leading/trailing hyphen): "${skillName}"` }], isError: true };
1489
+ }
1490
+ for (const ref of references) {
1491
+ if (!REF_RE.test(ref.filename)) {
1492
+ console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=invalid-ref-filename ref=${ref.filename} path=-`);
1493
+ return { content: [{ type: "text", text: `reference filename must match ^[a-z0-9-]+\\.md$: "${ref.filename}"` }], isError: true };
1494
+ }
1495
+ }
1496
+ const accountDir = getAccountDir();
1497
+ const canonicalPluginDir = resolve(accountDir, "plugins", pluginName);
1498
+ const mirrorPluginDir = resolve(PLATFORM_ROOT, "plugins", pluginName);
1499
+ const canonicalPluginMd = resolve(canonicalPluginDir, "PLUGIN.md");
1500
+ const mirrorPluginMd = resolve(mirrorPluginDir, "PLUGIN.md");
1501
+ // Collision: mirror dir exists from a shipped plugin (no canonical mirror).
1502
+ // Refuse rather than silently shadow.
1503
+ if (existsSync(mirrorPluginMd) && !existsSync(canonicalPluginMd)) {
1504
+ console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=collision-with-shipped path=${mirrorPluginDir}`);
1505
+ return { content: [{ type: "text", text: `pluginName "${pluginName}" collides with a shipped plugin. Choose a different pluginName.` }], isError: true };
1506
+ }
1507
+ const skillDir = resolve(canonicalPluginDir, "skills", skillName);
1508
+ const action = existsSync(resolve(skillDir, "SKILL.md")) ? "overwrite" : "create";
1509
+ try {
1510
+ mkdirSync(canonicalPluginDir, { recursive: true });
1511
+ // Compose PLUGIN.md only on first write. embed=["admin","public"] +
1512
+ // optional=false matches assemblePublicPluginContent / loadEmbeddedPlugins
1513
+ // expectations. metadata is single-line JSON (parsePluginFrontmatter at
1514
+ // plugin-manifest.ts:67 expects single-line) — multi-line silently falls
1515
+ // back to platform={}.
1516
+ if (!existsSync(canonicalPluginMd)) {
1517
+ const pluginMd = `---
1518
+ name: ${pluginName}
1519
+ description: "Operator-authored plugin"
1520
+ tools: []
1521
+ hidden: []
1522
+ requires: []
1523
+ metadata: {"platform":{"embed":["admin","public"],"optional":false}}
1524
+ ---
1525
+
1526
+ # ${pluginName}
1527
+
1528
+ Operator-authored plugin. Skills under \`skills/\` were created via the admin skill-builder.
1529
+ `;
1530
+ writeFileSync(canonicalPluginMd, pluginMd);
1531
+ }
1532
+ // Reset skill dir before write so removed reference files don't linger.
1533
+ if (existsSync(skillDir)) {
1534
+ rmSync(skillDir, { recursive: true, force: true });
1535
+ }
1536
+ mkdirSync(skillDir, { recursive: true });
1537
+ // Normalise description — strip newlines and tabs to keep YAML
1538
+ // frontmatter on a single line. parsePluginFrontmatter uses a
1539
+ // single-line regex; an embedded newline (or `\n---\n`) would
1540
+ // otherwise corrupt the frontmatter parse and silently drop body
1541
+ // content from the agent prompt.
1542
+ const singleLineDescription = description.replace(/[\r\n\t]+/g, " ").trim();
1543
+ const escapedDescription = singleLineDescription.replace(/"/g, '\\"');
1544
+ const skillMd = `---
1545
+ name: ${skillName}
1546
+ description: "${escapedDescription}"
1547
+ publicEmbed: ${publicEmbed}
1548
+ ---
1549
+
1550
+ ${body}
1551
+ `;
1552
+ writeFileSync(resolve(skillDir, "SKILL.md"), skillMd);
1553
+ let refCount = 0;
1554
+ if (references.length > 0) {
1555
+ const refsDir = resolve(skillDir, "references");
1556
+ mkdirSync(refsDir, { recursive: true });
1557
+ for (const ref of references) {
1558
+ writeFileSync(resolve(refsDir, ref.filename), ref.content);
1559
+ refCount++;
1560
+ }
1561
+ }
1562
+ // Write the `.user-mirror` marker to CANONICAL first — cpSync will
1563
+ // carry it into the platform-side mirror as part of the same recursive
1564
+ // copy. This eliminates the post-cpSync writeFileSync race: if cpSync
1565
+ // succeeds, the marker is already in target; if cpSync fails partway,
1566
+ // canonical's marker is intact and the next autoDeliverUserPlugins
1567
+ // call retries the copy. autoDeliverUserPlugins relies on the marker
1568
+ // arriving with the source tree — never writes its own marker.
1569
+ writeFileSync(resolve(canonicalPluginDir, ".user-mirror"), `pluginName=${pluginName}\nsource=${canonicalPluginDir}\n`);
1570
+ // Mirror to <PLATFORM_ROOT>/plugins/<pluginName>/ so the change is
1571
+ // visible to the loader without a session restart.
1572
+ mkdirSync(mirrorPluginDir, { recursive: true });
1573
+ cpSync(canonicalPluginDir, mirrorPluginDir, { recursive: true, force: true });
1574
+ const bodyBytes = Buffer.byteLength(body, "utf-8");
1575
+ console.log(`[store-skill] write pluginName=${pluginName} skillName=${skillName} publicEmbed=${publicEmbed} path=${canonicalPluginDir} bodyBytes=${bodyBytes} referenceCount=${refCount} action=${action}`);
1576
+ return {
1577
+ content: [{
1578
+ type: "text",
1579
+ text: `Skill "${skillName}" saved to plugin "${pluginName}" (${action}). publicEmbed=${publicEmbed}, references=${refCount}. Active immediately for the admin agent${publicEmbed ? "; surfaces in the public agent on next session start" : ""}.`,
1580
+ }],
1581
+ };
1582
+ }
1583
+ catch (err) {
1584
+ const msg = err instanceof Error ? err.message : String(err);
1585
+ console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=${msg.slice(0, 120)} path=${canonicalPluginDir}`);
1586
+ return { content: [{ type: "text", text: `Failed to write skill: ${msg}` }], isError: true };
1587
+ }
1588
+ });
1589
+ eagerTool(server, "plugin-read", "Read a plugin definition (PLUGIN.md) or one of its reference files.", {
1590
+ pluginName: z.string().describe("Name of the plugin directory (e.g. 'sales', 'business-assistant')"),
1591
+ file: z.string().optional().describe("Specific file to read (e.g. 'references/pricing.md'). Defaults to PLUGIN.md."),
1592
+ }, async ({ pluginName, file }) => {
1593
+ try {
1594
+ const resolvedFile = file ?? "PLUGIN.md";
1595
+ const pluginPath = resolve(PLATFORM_ROOT, "plugins", pluginName, resolvedFile);
1596
+ if (!existsSync(pluginPath)) {
1597
+ // Task 964/1000 — when the agent's pluginName/file combination misses
1598
+ // a skill that DOES live under `plugins/*/skills/<slug>/SKILL.md`,
1599
+ // surface the corrected pluginName AND the canonical file path so
1600
+ // the next call is deterministic. Both wrong-`file` and wrong-`pluginName`
1601
+ // shapes feed through `computePluginReadHint`.
1602
+ const hint = computePluginReadHint(PLATFORM_ROOT, pluginName, resolvedFile);
1603
+ const hintText = hint ? ` Did you mean pluginName="${hint.pluginName}", file="${hint.file}"?` : "";
1604
+ console.log(`[plugin-read] ${pluginName}/${resolvedFile} not-found hint=${hint ? hint.pluginName : "none"}`);
1605
+ return {
1606
+ content: [{ type: "text", text: `Plugin file not found: ${pluginPath}.${hintText}` }],
1607
+ isError: true,
1608
+ };
1609
+ }
1610
+ console.log(`[plugin-read] ${pluginName}/${resolvedFile}`);
1611
+ const rawContent = await readFile(pluginPath, "utf-8");
1612
+ // Brand-substitute every plugin file before it reaches the agent —
1613
+ // PLUGIN.md, skills/<name>/SKILL.md, references/*.md all flow through
1614
+ // here. Missing brand.json hard-throws (Tasks 787/788 doctrine).
1615
+ const content = substituteBrandPlaceholders(rawContent, pluginPath);
1616
+ // Append file inventory on default calls (PLUGIN.md) so agents can discover
1617
+ // skill and reference paths without guessing
1618
+ if (resolvedFile === "PLUGIN.md") {
1619
+ const pluginRoot = resolve(PLATFORM_ROOT, "plugins", pluginName);
1620
+ const skills = [];
1621
+ const references = [];
1622
+ const scanDir = (base, prefix, target) => {
1623
+ const scanPath = resolve(pluginRoot, base);
1624
+ if (!existsSync(scanPath))
1625
+ return;
1626
+ try {
1627
+ const walk = (current, rel) => {
1628
+ for (const entry of readdirSync(current)) {
1629
+ const full = resolve(current, entry);
1630
+ try {
1631
+ const stat = statSync(full);
1632
+ if (stat.isDirectory()) {
1633
+ walk(full, `${rel}${entry}/`);
1634
+ }
1635
+ else if (entry.endsWith(".md")) {
1636
+ target.push(`${prefix}${rel}${entry}`);
1637
+ }
1638
+ }
1639
+ catch { /* skip unreadable entries */ }
1640
+ }
1641
+ };
1642
+ walk(scanPath, "");
1643
+ }
1644
+ catch { /* skip unreadable directories */ }
1645
+ };
1646
+ scanDir("skills", "skills/", skills);
1647
+ scanDir("references", "references/", references);
1648
+ let inventory = "";
1649
+ if (skills.length > 0)
1650
+ inventory += `\nSkills: ${skills.join(", ")}`;
1651
+ if (references.length > 0)
1652
+ inventory += `\nReferences: ${references.join(", ")}`;
1653
+ return { content: [{ type: "text", text: content + inventory }] };
1654
+ }
1655
+ return { content: [{ type: "text", text: content }] };
1656
+ }
1657
+ catch (err) {
1658
+ return {
1659
+ content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
1660
+ isError: true,
1661
+ };
1662
+ }
1663
+ });
1664
+ // Task 964 — agent-side `Glob` fallthrough when "set up cloudflare" maps a
1665
+ // SKILL.md to the wrong plugin: skill-find walks
1666
+ // `${PLATFORM_ROOT}/plugins/*/skills/<skillName>/SKILL.md` once and returns
1667
+ // the owner. With one tool call the agent learns the correct plugin name.
1668
+ //
1669
+ // Task 1015 — `skill-load` (below) is the canonical single-call surface that
1670
+ // most agents want (resolve + read body in one shot). `skill-find` is kept
1671
+ // for callers that need only the path (e.g. tooling that lists owners
1672
+ // without reading the body).
1673
+ server.tool("skill-find", "Find which plugin owns a skill by name. Walks plugins/*/skills/<skillName>/SKILL.md and returns the owning plugin (unique), every candidate (ambiguous), or not-found. For the common resolve-and-read case, prefer `skill-load skillName=<name>` — it returns the SKILL.md body in the same call.", {
1674
+ skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
1675
+ }, async ({ skillName }) => {
1676
+ const start = Date.now();
1677
+ const result = findSkillOwners(PLATFORM_ROOT, skillName);
1678
+ const ms = Date.now() - start;
1679
+ console.log(`[skill-find] skillName=${skillName} result=${result.status} candidates=${result.candidates.length} ms=${ms}`);
1680
+ if (result.status === "unique") {
1681
+ const [{ pluginName, file }] = result.candidates;
1682
+ return {
1683
+ content: [{
1684
+ type: "text",
1685
+ text: `pluginName="${pluginName}" file="${file}"\nTo read the body in one call, use: skill-load skillName="${skillName}"`,
1686
+ }],
1687
+ };
1688
+ }
1689
+ if (result.status === "ambiguous") {
1690
+ const lines = result.candidates.map(c => ` pluginName="${c.pluginName}" file="${c.file}"`).join("\n");
1691
+ return {
1692
+ content: [{
1693
+ type: "text",
1694
+ text: `Ambiguous — skill "${skillName}" exists in ${result.candidates.length} plugins:\n${lines}`,
1695
+ }],
1696
+ };
1697
+ }
1698
+ return {
1699
+ content: [{ type: "text", text: `not_found — no plugin owns a skill named "${skillName}"` }],
1700
+ isError: true,
1701
+ };
1702
+ });
1703
+ // Task 1015 — `skill-load` collapses skill resolve+read into one call.
1704
+ //
1705
+ // Why: 21 prose call sites previously said *"Load `admin/skills/plainly/SKILL.md`
1706
+ // via `plugin-read`"*. The LLM kept passing the path as `pluginName`, tripping
1707
+ // the not-found branch with the corrected-pluginName hint that Task 1000 added.
1708
+ // The recurring failure is recorded in the corrections ledger. One slug-arg
1709
+ // tool removes the path-format ambiguity at the source.
1710
+ //
1711
+ // Shape mirrors `plugin-read` (eagerTool registration, same brand-substitution
1712
+ // call) so SKILL.md bodies are byte-identical regardless of which tool reads
1713
+ // them. `plugin-read` is retained for `references/*` and `PLUGIN.md` reads —
1714
+ // those are plugin-scoped by nature and the path-as-pluginName failure pattern
1715
+ // is specific to skills (addressed by slug in prose).
1716
+ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill name. One call: resolves the owning plugin and reads the body, with brand placeholders substituted. Returns the SKILL.md body when exactly one plugin owns the skill, a candidate list when multiple plugins own it (ambiguous), or an error when no plugin owns it.", {
1717
+ skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
1718
+ }, async ({ skillName }) => {
1719
+ const start = Date.now();
1720
+ try {
1721
+ const outcome = await loadSkill(PLATFORM_ROOT, skillName);
1722
+ if (outcome.status === "ambiguous") {
1723
+ const ms = Date.now() - start;
1724
+ console.log(`[skill-load] skillName=${skillName} result=ambiguous pluginName=none file=none bodyBytes=0 ms=${ms}`);
1725
+ const lines = outcome.candidates.map(c => ` pluginName="${c.pluginName}" file="${c.file}"`).join("\n");
1726
+ return {
1727
+ content: [{
1728
+ type: "text",
1729
+ text: `Ambiguous — skill "${skillName}" exists in ${outcome.candidates.length} plugins:\n${lines}\nUse plugin-read with one of the listed pluginName/file pairs.`,
1730
+ }],
1731
+ isError: true,
1732
+ };
1733
+ }
1734
+ if (outcome.status === "not-found") {
1735
+ const ms = Date.now() - start;
1736
+ console.log(`[skill-load] skillName=${skillName} result=not-found pluginName=none file=none bodyBytes=0 ms=${ms}`);
1737
+ return {
1738
+ content: [{ type: "text", text: `not_found — no plugin owns a skill named "${skillName}"` }],
1739
+ isError: true,
1740
+ };
1741
+ }
1742
+ const content = substituteBrandPlaceholders(outcome.body, outcome.absolutePath);
1743
+ // Task 1021 — prepend a skill-input-contract notice when the skill's
1744
+ // frontmatter declares `requiredInputs:`. The notice tells the agent
1745
+ // to route missing inputs through AskUserQuestion (per IDENTITY.md
1746
+ // "Skill-input resolution"); the embedded `<!-- skill-input-contract
1747
+ // requiredInputs=... -->` marker is the per-turn keying signal that
1748
+ // parseClaudeStream uses to fire [skill-input-resolution] /
1749
+ // [skill-input-missing] observability lines.
1750
+ const requiredInputs = parseRequiredInputs(outcome.body);
1751
+ let bodyOut = content;
1752
+ if (requiredInputs) {
1753
+ const slotLabels = requiredInputs.map(s => s.join(" | ")).join(", ");
1754
+ const marker = JSON.stringify(requiredInputs);
1755
+ const notice = `<!-- skill-input-contract requiredInputs=${marker} -->\nREQUIRED INPUTS: ${slotLabels}\nIf the operator has not supplied each of these inputs, your next tool call MUST be AskUserQuestion. Do not use memory-search, conversation-search, graph-read, or task-get to guess the missing input.\n<!-- /skill-input-contract -->\n\n`;
1756
+ bodyOut = notice + content;
1757
+ }
1758
+ const ms = Date.now() - start;
1759
+ const reqLog = requiredInputs ? ` requiredInputs=${JSON.stringify(requiredInputs)}` : "";
1760
+ console.log(`[skill-load] skillName=${skillName} result=unique pluginName=${outcome.pluginName} file=${outcome.file} bodyBytes=${bodyOut.length}${reqLog} ms=${ms}`);
1761
+ return { content: [{ type: "text", text: bodyOut }] };
1762
+ }
1763
+ catch (err) {
1764
+ const ms = Date.now() - start;
1765
+ console.log(`[skill-load] skillName=${skillName} result=read-error pluginName=unknown file=unknown bodyBytes=0 ms=${ms}`);
1766
+ return {
1767
+ content: [{ type: "text", text: `Failed to load skill "${skillName}": ${err instanceof Error ? err.message : String(err)}` }],
1768
+ isError: true,
1769
+ };
1770
+ }
1771
+ });
1772
+ // Task 940 — `"rendered"` is a sentinel, not a persistence ack. The platform
1773
+ // UI's stream-parser intercepts this tool_use upstream of the SDK reply
1774
+ // (platform/ui/app/lib/claude-agent/stream-parser.ts:362) and emits a typed
1775
+ // `component` event that admin-agent.ts's persistMessage flushes to Neo4j as
1776
+ // a sibling :Component node.
1777
+ //
1778
+ // Task 942 — render-component is also the *server commitment surface* for
1779
+ // PERSISTENT_COMPONENTS (action-list, document-editor, rich-content-editor,
1780
+ // grid-editor). The actual file write + :KnowledgeDocument projection lives
1781
+ // in the admin server process (stream-parser path) because the SDK delivers
1782
+ // `tool_use.input` to the UI in the assistant message but the MCP handler's
1783
+ // return reaches only the agent — there is no path back to persistMessage
1784
+ // from here. This handler emits a one-line observability marker per
1785
+ // persistence-eligible call so operators can grep for the commitment, and
1786
+ // returns richer JSON so the doctrine grep
1787
+ // `render-component.*"rendered"` resolves to a persistence-aware handler
1788
+ // rather than a bare stub.
1789
+ eagerTool(server, "render-component", "Render a pre-built UI component inline in the conversation. " +
1790
+ "Call this instead of describing a UI — the component handles input collection. " +
1791
+ "Wait for the user's response before continuing.", {
1792
+ name: z.string().describe("Component name from the UI suite (e.g. single-select, confirm, info-card, form, progress)"),
1793
+ data: z.record(z.string(), z.unknown()),
1794
+ }, async ({ name, data }) => {
1795
+ if (isPersistentComponent(name)) {
1796
+ // Persistence-eligible: log the commitment so the live + audit grep
1797
+ // (`[render-component-persist]`) can correlate the MCP call against
1798
+ // the downstream :KnowledgeDocument MERGE. Byte counts come from
1799
+ // data.html (preferred) or data.content; both being absent is a
1800
+ // legitimate render of an empty editor and the projection is skipped
1801
+ // server-side.
1802
+ const html = typeof data?.html === "string" ? data.html : "";
1803
+ const content = typeof data?.content === "string" ? data.content : "";
1804
+ const bytes = html.length > 0 ? html.length : content.length;
1805
+ const mimeType = html.length > 0 ? "text/html" : (content.length > 0 ? "text/markdown" : "");
1806
+ console.error(`[render-component-persist] componentName=${name} bytes=${bytes} mimeType=${mimeType || "none"}`);
1807
+ return {
1808
+ content: [{
1809
+ type: "text",
1810
+ text: JSON.stringify({ rendered: true, name, persistent: true, bytes, mimeType }),
1811
+ }],
1812
+ };
1813
+ }
1814
+ return { content: [{ type: "text", text: "rendered" }] };
1815
+ });
1816
+ eagerTool(server, "session-reset", "Reset the current session. Compacts conversation history to memory, clears the visible conversation, " +
1817
+ "and starts a fresh session with a new greeting. Call when the user asks to start a new session, " +
1818
+ "clear the conversation, or start fresh.", {}, async () => ({ content: [{ type: "text", text: "reset" }] }));
1819
+ eagerTool(server, "session-resume", "Resume a previous session. Loads the selected session's message history into the chat timeline " +
1820
+ "and routes new messages to that conversation. Call after the user selects a session from the " +
1821
+ "session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => ({ content: [{ type: "text", text: "resumed" }] }));
1822
+ server.tool("remote-auth-set-password", "Set the remote access password. Hashes with scrypt and writes to the platform's brand-specific config directory with mode 0600. " +
1823
+ "Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
1824
+ "Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself. " +
1825
+ "Set before `setup-tunnel.sh` — the script's post-restart verification curls the admin hostname and fails if remote-auth is not configured.", { password: z.string() }, async ({ password }) => {
1826
+ // Validate strength — same rules as the web server's validatePasswordStrength
1827
+ const checks = [
1828
+ { label: "at least 8 characters", met: password.length >= 8 },
1829
+ { label: "contains a number", met: /\d/.test(password) },
1830
+ { label: "contains a special character", met: /[^A-Za-z0-9]/.test(password) },
1831
+ { label: "no spaces", met: password.length > 0 && !/\s/.test(password) },
1832
+ ];
1833
+ const failed = checks.filter((c) => !c.met);
1834
+ if (failed.length > 0) {
1835
+ return {
1836
+ content: [{ type: "text", text: `Password requirements not met: ${failed.map((c) => c.label).join(", ")}` }],
1837
+ isError: true,
1838
+ };
1839
+ }
1840
+ try {
1841
+ const { scrypt, randomBytes } = await import("node:crypto");
1842
+ const salt = randomBytes(32);
1843
+ const hash = await new Promise((res, rej) => {
1844
+ scrypt(password, salt, 64, { N: 16384, r: 8, p: 1 }, (err, key) => {
1845
+ if (err)
1846
+ rej(err);
1847
+ else
1848
+ res(key);
1849
+ });
1850
+ });
1851
+ const stored = `${salt.toString("hex")}:${hash.toString("hex")}`;
1852
+ const pwPath = REMOTE_PASSWORD_FILE;
1853
+ await writeFile(pwPath, stored, { mode: 0o600 });
1854
+ return { content: [{ type: "text", text: "Remote access password set." }] };
1855
+ }
1856
+ catch (err) {
1857
+ return {
1858
+ content: [{ type: "text", text: `Failed to set password: ${err instanceof Error ? err.message : String(err)}` }],
1859
+ isError: true,
1860
+ };
1861
+ }
1862
+ });
1863
+ eagerTool(server, "api-key-store", "Validate and store an Anthropic API key. Key must start with sk-ant- and be at least 90 characters. " +
1864
+ "Writes to the platform's brand-specific config directory. Takes effect immediately for the public agent.", { apiKey: z.string() }, async ({ apiKey }) => {
1865
+ try {
1866
+ writeKey(apiKey);
1867
+ return { content: [{ type: "text", text: `API key stored at ${keyFilePath()}.` }] };
1868
+ }
1869
+ catch (err) {
1870
+ return {
1871
+ content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
1872
+ isError: true,
1873
+ };
1874
+ }
1875
+ });
1876
+ eagerTool(server, "api-key-verify", "Verify the stored Anthropic API key works by making a minimal probe call. " +
1877
+ "Returns one of: valid (key works, credits available), billing (key valid but credit balance too low — " +
1878
+ "user must add credits at console.anthropic.com/settings/billing), auth_error (key rejected — invalid or revoked), " +
1879
+ "missing (no key file found), error (could not verify — network issue or Anthropic outage). " +
1880
+ "Call this on greeting, during system checks, or after storing a new key.", {}, async () => {
1881
+ const result = await validateKey();
1882
+ return {
1883
+ content: [{
1884
+ type: "text",
1885
+ text: `Status: ${result.status}\n${result.message}`,
1886
+ }],
1887
+ };
1888
+ });
1889
+ // ===================================================================
1890
+ // Anthropic setup — deterministic state machine
1891
+ // ===================================================================
1892
+ /*
1893
+ * anthropic-setup state machine (action-relay pattern)
1894
+ *
1895
+ * Call 1: anthropic-setup({})
1896
+ * ├─ no public endpoint → { status: "not_needed" } ← Phase 0 gate
1897
+ * ├─ key stored + valid? → { status: "complete" }
1898
+ * ├─ key stored + billing → { status: "awaiting_credits" }
1899
+ * ├─ key stored + error → { status: "error" }
1900
+ * └─ no key → { status: "awaiting_signin", action }
1901
+ *
1902
+ * Call 2: anthropic-setup({ consoleResult: "..." })
1903
+ * ├─ no_session → { status: "awaiting_signin", action }
1904
+ * ├─ no_org → { status: "error" }
1905
+ * ├─ no_credits → { status: "awaiting_credits", action }
1906
+ * ├─ success → writeKey → validateKey → { status: "complete" }
1907
+ * └─ *_failed → { status: "error" }
1908
+ */
1909
+ const CONSOLE_BASE = "https://platform.claude.com";
1910
+ /**
1911
+ * JavaScript expression for browser_evaluate that runs the complete
1912
+ * Console API pipeline in one call: check session → find API org →
1913
+ * check credits → create key. Returns a structured JSON result.
1914
+ */
1915
+ function consoleEvaluateExpression(keyName) {
1916
+ return `async () => {
1917
+ try {
1918
+ const orgsRes = await fetch("/api/organizations", { credentials: "include" });
1919
+ if (orgsRes.status === 401 || orgsRes.status === 403) {
1920
+ return JSON.stringify({ status: "no_session" });
1921
+ }
1922
+ if (!orgsRes.ok) {
1923
+ return JSON.stringify({ status: "fetch_error", error: "organizations: " + orgsRes.status });
1924
+ }
1925
+ const orgs = await orgsRes.json();
1926
+ const apiOrg = orgs.find(o => o.capabilities && o.capabilities.includes("api"));
1927
+ if (!apiOrg) {
1928
+ return JSON.stringify({ status: "no_org" });
1929
+ }
1930
+ const creditsRes = await fetch("/api/organizations/" + apiOrg.uuid + "/prepaid/credits", { credentials: "include" });
1931
+ if (!creditsRes.ok) {
1932
+ return JSON.stringify({ status: "fetch_error", error: "credits: " + creditsRes.status });
1933
+ }
1934
+ const credits = await creditsRes.json();
1935
+ if (typeof credits.amount === "number" && credits.amount <= 0) {
1936
+ return JSON.stringify({ status: "no_credits", orgId: apiOrg.uuid, orgName: apiOrg.name, creditUrl: "${CONSOLE_BASE}/settings/billing" });
1937
+ }
1938
+ const createRes = await fetch("/api/console/organizations/" + apiOrg.uuid + "/workspaces/default/api_keys", {
1939
+ method: "POST",
1940
+ credentials: "include",
1941
+ headers: { "content-type": "application/json" },
1942
+ body: JSON.stringify({ name: ${JSON.stringify(keyName)} })
1943
+ });
1944
+ if (!createRes.ok) {
1945
+ return JSON.stringify({ status: "create_failed", error: "key creation: " + createRes.status });
1946
+ }
1947
+ const keyData = await createRes.json();
1948
+ if (!keyData.raw_key) {
1949
+ return JSON.stringify({ status: "create_failed", error: "no raw_key in response" });
1950
+ }
1951
+ return JSON.stringify({
1952
+ status: "success",
1953
+ key: keyData.raw_key,
1954
+ keyId: keyData.id,
1955
+ keyHint: keyData.partial_key_hint,
1956
+ orgName: apiOrg.name,
1957
+ credits: credits.amount
1958
+ });
1959
+ } catch (err) {
1960
+ return JSON.stringify({ status: "fetch_error", error: String(err) });
1961
+ }
1962
+ }`;
1963
+ }
1964
+ function anthropicResult(r) {
1965
+ return {
1966
+ content: [{ type: "text", text: JSON.stringify(r, null, 2) }],
1967
+ ...(r.status === "error" ? { isError: true } : {}),
1968
+ };
1969
+ }
1970
+ /**
1971
+ * Check whether a public-facing hostname is configured. The Anthropic API key
1972
+ * powers the public agent only — admin runs on Claude OAuth — so without a
1973
+ * public endpoint there is nothing for the key to power.
1974
+ *
1975
+ * Mirrors the runtime `isPublicHost(host)` predicate in
1976
+ * `platform/ui/server/index.ts`: a host is public iff it starts with `public.`
1977
+ * OR is listed in `~/{configDir}/alias-domains.json`. Step 7's form submit
1978
+ * handler at `platform/ui/server/routes/admin/cloudflare.ts` writes secondary
1979
+ * hostnames (apex + custom public labels) to `alias-domains.json` and skips
1980
+ * `public.*` because the prefix itself is the signal — so the two predicates
1981
+ * together cover every shape the form can produce.
1982
+ *
1983
+ * Returns true if any configured hostname satisfies either predicate.
1984
+ */
1985
+ function hasPublicEndpointConfigured() {
1986
+ const aliasPath = join(CONFIG_DIR, "alias-domains.json");
1987
+ if (existsSync(aliasPath)) {
1988
+ try {
1989
+ const parsed = JSON.parse(readFileSync(aliasPath, "utf-8"));
1990
+ if (Array.isArray(parsed)) {
1991
+ const entries = parsed.filter((h) => typeof h === "string" && h.length > 0);
1992
+ if (entries.length > 0) {
1993
+ return { has: true, reason: `alias-domains.json: ${entries.join(", ")}` };
1994
+ }
1995
+ }
1996
+ }
1997
+ catch {
1998
+ // Malformed file — treat as empty; matches the server-side reader's tolerance.
1999
+ }
2000
+ }
2001
+ const cfConfigPath = join(CONFIG_DIR, "cloudflared", "config.yml");
2002
+ if (existsSync(cfConfigPath)) {
2003
+ try {
2004
+ const yaml = readFileSync(cfConfigPath, "utf-8");
2005
+ const hostnames = [...yaml.matchAll(/^\s*-\s*hostname:\s*(\S+)/gm)].map((m) => m[1]);
2006
+ const publicPrefixed = hostnames.find((h) => h.startsWith("public."));
2007
+ if (publicPrefixed) {
2008
+ return { has: true, reason: `cloudflared/config.yml ingress: ${publicPrefixed}` };
2009
+ }
2010
+ }
2011
+ catch {
2012
+ // Unreadable — treat as no public host.
2013
+ }
2014
+ }
2015
+ return {
2016
+ has: false,
2017
+ reason: "no entries in alias-domains.json and no public.* hostname in cloudflared/config.yml",
2018
+ };
2019
+ }
2020
+ eagerTool(server, "anthropic-setup", "Deterministic state machine for Anthropic API key acquisition. " +
2021
+ "Checks current state, advances as far as possible, and returns a structured JSON result. " +
2022
+ "On first call (no consoleResult): first verifies a public-facing endpoint is configured " +
2023
+ "(the key only powers the public agent); if not, returns status 'not_needed'. Otherwise " +
2024
+ "checks if a valid key is already stored. If not, returns status 'awaiting_signin' with " +
2025
+ "a browser_evaluate action — the agent must open the browser to platform.claude.com, run " +
2026
+ "the action's function via browser_evaluate, and pass the string result back as " +
2027
+ "consoleResult on the next call. " +
2028
+ "On second call (with consoleResult): parses the Console API result, stores the key, " +
2029
+ "verifies it, and returns status 'complete'. " +
2030
+ "Statuses: complete (key stored and verified), not_needed (no public endpoint configured — " +
2031
+ "skip the step), awaiting_signin (user must sign in to Console), awaiting_credits (signed in " +
2032
+ "but no credits — user must add credits), error (fatal — relay message).", {
2033
+ consoleResult: z.string().optional().describe("JSON string result from running the browser_evaluate action returned by a prior call. " +
2034
+ "Omit on the first call."),
2035
+ }, async ({ consoleResult }) => {
2036
+ const log = (msg) => console.error(`[anthropic-setup] ${msg}`);
2037
+ // ── Phase 0: gate on public endpoint existence ─────────────
2038
+ // The API key only powers the public-facing agent. If no public hostname
2039
+ // is configured (operator skipped step 7, or completed it without a
2040
+ // public/apex hostname) the key has no consumer — short-circuit before
2041
+ // any Console interaction so we never create keys the operator cannot use.
2042
+ if (!consoleResult) {
2043
+ const publicCheck = hasPublicEndpointConfigured();
2044
+ if (!publicCheck.has) {
2045
+ log(`gate: no public endpoint (${publicCheck.reason}) — returning not_needed`);
2046
+ return anthropicResult({
2047
+ status: "not_needed",
2048
+ message: "No public-facing endpoint is configured, so an Anthropic API key is not needed. " +
2049
+ "The key powers the public agent only — admin runs on your Claude OAuth session. " +
2050
+ "When you set up a public hostname via Cloudflare (or apex domain), come back and " +
2051
+ "ask to set up the API key then.",
2052
+ });
2053
+ }
2054
+ log(`gate: public endpoint present (${publicCheck.reason})`);
2055
+ }
2056
+ // ── Phase 1: check stored key ──────────────────────────────
2057
+ if (!consoleResult) {
2058
+ log("checking stored key");
2059
+ if (hasKey()) {
2060
+ const verification = await validateKey();
2061
+ log(`stored key verification: ${verification.status}`);
2062
+ switch (verification.status) {
2063
+ case "valid":
2064
+ log("key valid, returning complete");
2065
+ return anthropicResult({
2066
+ status: "complete",
2067
+ message: `API key is stored and working. Key file: ${keyFilePath()}.`,
2068
+ });
2069
+ case "billing":
2070
+ log("key valid but billing issue");
2071
+ return anthropicResult({
2072
+ status: "awaiting_credits",
2073
+ message: "API key is stored and valid, but your account has insufficient credits. " +
2074
+ `Add credits at ${CONSOLE_BASE}/settings/billing, then call this tool again.`,
2075
+ data: { url: `${CONSOLE_BASE}/settings/billing` },
2076
+ });
2077
+ case "auth_error":
2078
+ // Deterministic transition: the state machine knows the key file
2079
+ // path and the only recovery is to delete and restart sign-in.
2080
+ // Auto-reset here so the agent never has to rediscover the path
2081
+ // via filesystem probes.
2082
+ log(`auth_error → auto-resetting: deleting revoked key at ${keyFilePath()}`);
2083
+ try {
2084
+ deleteKey();
2085
+ }
2086
+ catch (err) {
2087
+ const msg = err instanceof Error ? err.message : String(err);
2088
+ log(`auto-reset failed: ${msg}`);
2089
+ return anthropicResult({
2090
+ status: "error",
2091
+ message: `The stored API key was rejected and auto-reset failed: ${msg}. ` +
2092
+ `Manual intervention needed — remove ${keyFilePath()} and call this tool again.`,
2093
+ });
2094
+ }
2095
+ // Fall through to the awaiting_signin path below.
2096
+ break;
2097
+ case "missing":
2098
+ // hasKey() returned true but readKey() returned null — shouldn't happen
2099
+ log("hasKey true but validate returned missing — proceeding to setup");
2100
+ break;
2101
+ case "error":
2102
+ log(`verification failed: ${verification.message}`);
2103
+ return anthropicResult({
2104
+ status: "error",
2105
+ message: `Could not verify the stored key: ${verification.message}. ` +
2106
+ "This may be a network issue or Anthropic outage. Try again later.",
2107
+ });
2108
+ }
2109
+ }
2110
+ else {
2111
+ log("no key stored");
2112
+ }
2113
+ // No valid key — return action for Console API calls
2114
+ const expression = consoleEvaluateExpression(BRAND_NAME);
2115
+ log("returning awaiting_signin with browser_evaluate action");
2116
+ return anthropicResult({
2117
+ status: "awaiting_signin",
2118
+ message: "No valid API key stored. Open the browser to the Anthropic Console " +
2119
+ `(${CONSOLE_BASE}) and sign in. This is a separate service from Claude — ` +
2120
+ "you may need to sign in again even if you used the same credentials earlier. " +
2121
+ "Once signed in, run the browser_evaluate action below, then call this tool " +
2122
+ "again with the result as consoleResult.",
2123
+ data: {
2124
+ url: CONSOLE_BASE,
2125
+ action: {
2126
+ tool: "browser_evaluate",
2127
+ params: { function: expression },
2128
+ },
2129
+ },
2130
+ });
2131
+ }
2132
+ // ── Phase 2: process Console API result ────────────────────
2133
+ log("processing consoleResult");
2134
+ let parsed;
2135
+ try {
2136
+ parsed = JSON.parse(consoleResult);
2137
+ }
2138
+ catch {
2139
+ log(`consoleResult parse failed: ${consoleResult.slice(0, 200)}`);
2140
+ return anthropicResult({
2141
+ status: "error",
2142
+ message: "Failed to parse the browser result. The Console session may have " +
2143
+ "been lost or the browser encountered an error. Please try again — " +
2144
+ "open the browser to platform.claude.com, sign in, and re-run the action.",
2145
+ });
2146
+ }
2147
+ log(`console result status: ${parsed.status}`);
2148
+ switch (parsed.status) {
2149
+ case "no_session": {
2150
+ log("no Console session — user needs to sign in");
2151
+ const expression = consoleEvaluateExpression(BRAND_NAME);
2152
+ return anthropicResult({
2153
+ status: "awaiting_signin",
2154
+ message: "Not signed in to the Anthropic Console. Please sign in at " +
2155
+ `${CONSOLE_BASE} and then run the browser_evaluate action below.`,
2156
+ data: {
2157
+ url: CONSOLE_BASE,
2158
+ action: {
2159
+ tool: "browser_evaluate",
2160
+ params: { function: expression },
2161
+ },
2162
+ },
2163
+ });
2164
+ }
2165
+ case "no_org":
2166
+ log("no API organization found");
2167
+ return anthropicResult({
2168
+ status: "error",
2169
+ message: "No API organization found in your Anthropic account. " +
2170
+ "You may be signed in to a Claude consumer account instead of the API Console. " +
2171
+ `Sign in at ${CONSOLE_BASE} with an account that has API access.`,
2172
+ });
2173
+ case "no_credits": {
2174
+ log(`no credits — org: ${parsed.orgName ?? "unknown"}`);
2175
+ const expression = consoleEvaluateExpression(BRAND_NAME);
2176
+ return anthropicResult({
2177
+ status: "awaiting_credits",
2178
+ message: `Your API organization (${parsed.orgName ?? "unknown"}) has no credits. ` +
2179
+ `Add credits at ${parsed.creditUrl ?? CONSOLE_BASE + "/settings/billing"}, ` +
2180
+ "then run the browser_evaluate action below.",
2181
+ data: {
2182
+ url: parsed.creditUrl ?? `${CONSOLE_BASE}/settings/billing`,
2183
+ action: {
2184
+ tool: "browser_evaluate",
2185
+ params: { function: expression },
2186
+ },
2187
+ },
2188
+ });
2189
+ }
2190
+ case "create_failed":
2191
+ log(`key creation failed: ${parsed.error ?? "unknown"}`);
2192
+ return anthropicResult({
2193
+ status: "error",
2194
+ message: `Failed to create API key: ${parsed.error ?? "unknown error"}. ` +
2195
+ "This may be a permissions issue or a temporary Console error. Try again.",
2196
+ });
2197
+ case "fetch_error":
2198
+ log(`fetch error: ${parsed.error ?? "unknown"}`);
2199
+ return anthropicResult({
2200
+ status: "error",
2201
+ message: `Console API error: ${parsed.error ?? "unknown"}. ` +
2202
+ "The Console may be temporarily unavailable. Try again.",
2203
+ });
2204
+ case "success": {
2205
+ if (!parsed.key) {
2206
+ log("success status but no key in result");
2207
+ return anthropicResult({
2208
+ status: "error",
2209
+ message: "Key creation succeeded but the key was not returned. " +
2210
+ "This is unexpected. Please try again.",
2211
+ });
2212
+ }
2213
+ log(`key created: ${parsed.keyId ?? "unknown"} (hint: ${parsed.keyHint ?? "n/a"}) — org: ${parsed.orgName ?? "unknown"}`);
2214
+ // Store the key — persist AFTER confirmation (sprint learning: Task 201)
2215
+ try {
2216
+ writeKey(parsed.key);
2217
+ log("key stored");
2218
+ }
2219
+ catch (err) {
2220
+ log(`key storage failed: ${err instanceof Error ? err.message : String(err)}`);
2221
+ return anthropicResult({
2222
+ status: "error",
2223
+ message: `Key was created in the Console but could not be stored locally: ` +
2224
+ `${err instanceof Error ? err.message : String(err)}`,
2225
+ });
2226
+ }
2227
+ // Verify the stored key
2228
+ const verification = await validateKey();
2229
+ log(`post-store verification: ${verification.status}`);
2230
+ switch (verification.status) {
2231
+ case "valid":
2232
+ log("key stored and verified");
2233
+ return anthropicResult({
2234
+ status: "complete",
2235
+ message: `API key created, stored at ${keyFilePath()}, and verified. ` +
2236
+ `Organization: ${parsed.orgName ?? "unknown"}. ` +
2237
+ `Credits: $${((parsed.credits ?? 0) / 100).toFixed(2)}.`,
2238
+ });
2239
+ case "billing":
2240
+ log("key stored but billing issue on verification");
2241
+ return anthropicResult({
2242
+ status: "awaiting_credits",
2243
+ message: "API key created and stored, but your account has insufficient credits. " +
2244
+ `Add credits at ${CONSOLE_BASE}/settings/billing, then verify the key with api-key-verify.`,
2245
+ data: { url: `${CONSOLE_BASE}/settings/billing` },
2246
+ });
2247
+ default:
2248
+ log(`unexpected verification status after store: ${verification.status} — ${verification.message}`);
2249
+ return anthropicResult({
2250
+ status: "error",
2251
+ message: `Key was stored but verification returned: ${verification.status}. ` +
2252
+ `${verification.message}. Try running api-key-verify manually.`,
2253
+ });
2254
+ }
2255
+ }
2256
+ default:
2257
+ log(`unknown console result status: ${parsed.status}`);
2258
+ return anthropicResult({
2259
+ status: "error",
2260
+ message: `Unexpected result from Console: status "${parsed.status}". ` +
2261
+ "Please try again.",
2262
+ });
2263
+ }
2264
+ });
2265
+ // ===================================================================
2266
+ // Onboarding tools
2267
+ // ===================================================================
2268
+ server.tool("onboarding-get", "Read the current onboarding state from the graph. Returns which setup steps " +
2269
+ "have been completed and their timestamps. On first call for an account, " +
2270
+ "migrates any existing onboardingStep from account.json to the graph and " +
2271
+ "removes it from the file. If Neo4j is unreachable, returns an error — " +
2272
+ "the agent should skip onboarding for this session and retry next time.", {}, async () => {
2273
+ try {
2274
+ const state = await getOnboardingState(getSession, ACCOUNT_ID, getAccountDir());
2275
+ return {
2276
+ content: [{
2277
+ type: "text",
2278
+ text: JSON.stringify(state, null, 2),
2279
+ }],
2280
+ };
2281
+ }
2282
+ catch (err) {
2283
+ return {
2284
+ content: [{
2285
+ type: "text",
2286
+ text: `Failed to read onboarding state: ${err instanceof Error ? err.message : String(err)}`,
2287
+ }],
2288
+ isError: true,
2289
+ };
2290
+ }
2291
+ });
2292
+ server.tool("onboarding-complete-step", "Mark an onboarding step as completed. Sets a completion timestamp on the step " +
2293
+ "and updates currentStep to the highest completed step. Idempotent — completing " +
2294
+ "an already-completed step preserves its original timestamp. Step must be 1–9. " +
2295
+ "When step is 1, optionally pass acceptedAnthropicVerticals + declinedAnthropicVerticals " +
2296
+ "(both required together) to emit the paired counter for the [plugin-onboarding] " +
2297
+ "group=anthropic-verticals prefix; the skill derives both arrays from the closed set " +
2298
+ "{kyc-screener, meeting-prep-agent, pdf-viewer} and the user's submission.", {
2299
+ step: z.number().int().min(1).max(9).describe("The onboarding step number (1–9) to mark as completed"),
2300
+ acceptedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user accepted from the Anthropic verticals group. Pass together with declinedAnthropicVerticals."),
2301
+ declinedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user declined from the Anthropic verticals group. Pass together with acceptedAnthropicVerticals."),
2302
+ }, async ({ step, acceptedAnthropicVerticals, declinedAnthropicVerticals }) => {
2303
+ try {
2304
+ const state = await completeOnboardingStep(getSession, ACCOUNT_ID, step);
2305
+ // Task 977: closed set lives in skill prose, not server validation — a fourth vertical updates the skill.
2306
+ if (step === 1 && acceptedAnthropicVerticals !== undefined && declinedAnthropicVerticals !== undefined) {
2307
+ console.error(`[plugin-onboarding] group=anthropic-verticals accepted=${acceptedAnthropicVerticals.length} declined=${declinedAnthropicVerticals.length}`);
2308
+ }
2309
+ return {
2310
+ content: [{
2311
+ type: "text",
2312
+ text: JSON.stringify(state, null, 2),
2313
+ }],
2314
+ };
2315
+ }
2316
+ catch (err) {
2317
+ return {
2318
+ content: [{
2319
+ type: "text",
2320
+ text: `Failed to complete onboarding step: ${err instanceof Error ? err.message : String(err)}`,
2321
+ }],
2322
+ isError: true,
2323
+ };
2324
+ }
2325
+ });
2326
+ // Task 704: pin the operator's persona (personal/business-owner) at step 9
2327
+ // BEFORE any graph write happens. The agent calls this immediately on the
2328
+ // user's single-select submission. The tool's job is twofold:
2329
+ // 1. Emit a deterministic [onboarding-step9-mode] log line for the
2330
+ // diagnostic-grep path on the Pi (matches [onboarding-step-complete]
2331
+ // format — same accountId-prefix slicing, same surrounding code).
2332
+ // 2. Return the next-action prose so the agent's branching is anchored to
2333
+ // the tool surface, not free-text inference.
2334
+ // No persistence — the graph state (LocalBusiness exists vs Person with
2335
+ // role=admin-personal exists) remains the source of truth for gate
2336
+ // satisfaction. Re-calling this tool with a different mode on the same
2337
+ // session is allowed; the most recent log line wins for diagnostic purposes.
2338
+ server.tool("onboarding-step9-mode", "Step 9 onboarding fork: record the operator's persona (personal or " +
2339
+ "business-owner) before any graph write. Logs the choice for diagnostic " +
2340
+ "grep and returns the deterministic next-action prose for the agent.", {
2341
+ mode: z
2342
+ .enum(["personal", "business-owner"])
2343
+ .describe("The operator's persona: 'personal' for an individual using Maxy for personal operations (including someone with an employer that is NOT being registered here), or 'business-owner' for someone setting up Maxy as the operations agent for their own company."),
2344
+ }, async ({ mode }) => {
2345
+ console.log(`[onboarding-step9-mode] accountId=${ACCOUNT_ID.slice(0, 8)}… mode=${mode}`);
2346
+ const nextAction = mode === "business-owner"
2347
+ ? "Invoke the `business-profile` skill to create AdminUser + LocalBusiness and capture the business identity. Mark step 9 complete only after both nodes exist in the graph."
2348
+ : "Ask the user for their email (one sentence). Use their name from `admin-identity` in the system prompt; split into givenName + familyName. Call `memory-write` to create the AdminUser node, then call `memory-write` to create a Person node with `role: \"admin-personal\"`, the givenName, familyName, email, and an OWNS edge from the AdminUser. Then call `onboarding-complete-step` with step 9. Do not invoke the `business-profile` skill — personal mode does not register a LocalBusiness.";
2349
+ return {
2350
+ content: [{
2351
+ type: "text",
2352
+ text: JSON.stringify({ mode, nextAction }, null, 2),
2353
+ }],
2354
+ };
2355
+ });
2356
+ // ===================================================================
2357
+ // Utility tools
2358
+ // ===================================================================
2359
+ eagerTool(server, "qr-generate", "Generate a QR code from text or a URL. Returns the QR code as a data URI (PNG) or saves to a file path.", {
2360
+ data: z.string().describe("The text or URL to encode in the QR code"),
2361
+ outputPath: z
2362
+ .string()
2363
+ .optional()
2364
+ .describe("File path to save the PNG. If omitted, returns a data URI."),
2365
+ width: z.number().optional().describe("Width in pixels (default 300)"),
2366
+ }, async ({ data, outputPath, width }) => {
2367
+ try {
2368
+ const options = { width: width ?? 300, margin: 2 };
2369
+ if (outputPath) {
2370
+ await QRCode.toFile(outputPath, data, options);
2371
+ return {
2372
+ content: [
2373
+ {
2374
+ type: "text",
2375
+ text: `QR code saved to ${outputPath}`,
2376
+ },
2377
+ ],
2378
+ };
2379
+ }
2380
+ const dataUri = await QRCode.toDataURL(data, options);
2381
+ return {
2382
+ content: [
2383
+ {
2384
+ type: "text",
2385
+ text: `QR code generated for: ${data}`,
2386
+ },
2387
+ {
2388
+ type: "image",
2389
+ data: dataUri.replace(/^data:image\/png;base64,/, ""),
2390
+ mimeType: "image/png",
2391
+ },
2392
+ ],
2393
+ };
2394
+ }
2395
+ catch (err) {
2396
+ return {
2397
+ content: [
2398
+ {
2399
+ type: "text",
2400
+ text: `QR generation failed: ${err instanceof Error ? err.message : String(err)}`,
2401
+ },
2402
+ ],
2403
+ isError: true,
2404
+ };
2405
+ }
2406
+ });
2407
+ // ===================================================================
2408
+ // WiFi management (Task 429)
2409
+ // ===================================================================
2410
+ eagerTool(server, "wifi", "Manage WiFi connections on this device. Actions: " +
2411
+ "scan (list visible networks with signal strength and security), " +
2412
+ "connect (join a network by SSID and password — warn user first if signal is below 30%), " +
2413
+ "status (current WiFi connection details), " +
2414
+ "saved (list remembered networks), " +
2415
+ "forget (remove a saved network — if the user is forgetting their currently active WiFi " +
2416
+ "connection, warn them that they may lose connectivity before calling this action).", {
2417
+ action: z.enum(["scan", "connect", "status", "saved", "forget"]).describe("The WiFi operation to perform"),
2418
+ ssid: z.string().optional().describe("Network name — required for connect and forget actions"),
2419
+ password: z.string().optional().describe("Network password — required for connect action (WPA/WPA2/WPA3 PSK)"),
2420
+ }, async ({ action, ssid, password }) => {
2421
+ /** Parse nmcli terse output into rows of colon-separated fields.
2422
+ * nmcli -t escapes literal colons in values as \: — split on
2423
+ * unescaped colons only, then unescape the fields. */
2424
+ const parseNmcliLines = (output) => output.split("\n").filter(Boolean).map((l) => l.split(/(?<!\\):/).map((f) => f.replace(/\\:/g, ":")));
2425
+ const logPrefix = "[wifi]";
2426
+ try {
2427
+ switch (action) {
2428
+ // ── scan ──────────────────────────────────────────────────
2429
+ case "scan": {
2430
+ const raw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL,SECURITY", "device", "wifi", "list", "--rescan", "yes"], { encoding: "utf-8", timeout: 15000 });
2431
+ const lines = parseNmcliLines(raw);
2432
+ // Filter hidden networks (empty SSID) and deduplicate by SSID
2433
+ const seen = new Set();
2434
+ const networks = [];
2435
+ for (const [name, sig, sec] of lines) {
2436
+ if (!name || seen.has(name))
2437
+ continue;
2438
+ seen.add(name);
2439
+ networks.push({ ssid: name, signal: parseInt(sig, 10) || 0, security: sec || "open" });
2440
+ }
2441
+ // Sort by signal strength descending
2442
+ networks.sort((a, b) => b.signal - a.signal);
2443
+ console.error(`${logPrefix} action="scan" result="ok" networks=${networks.length}`);
2444
+ if (networks.length === 0) {
2445
+ return {
2446
+ content: [{ type: "text", text: "No WiFi networks found. Make sure your router is powered on and within range." }],
2447
+ };
2448
+ }
2449
+ const formatted = networks
2450
+ .map((n) => `${n.ssid} (${n.signal}% ${n.security})`)
2451
+ .join("\n");
2452
+ return {
2453
+ content: [{ type: "text", text: `# WiFi Networks\n\n${formatted}` }],
2454
+ };
2455
+ }
2456
+ // ── connect ──────────────────────────────────────────────
2457
+ case "connect": {
2458
+ if (!ssid) {
2459
+ return {
2460
+ content: [{ type: "text", text: "SSID is required for connect action." }],
2461
+ isError: true,
2462
+ };
2463
+ }
2464
+ if (!password) {
2465
+ return {
2466
+ content: [{ type: "text", text: "Password is required for connect action." }],
2467
+ isError: true,
2468
+ };
2469
+ }
2470
+ // Check signal strength before connecting (cherry-pick 1)
2471
+ try {
2472
+ const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 10000 });
2473
+ const scanLines = parseNmcliLines(scanRaw);
2474
+ const match = scanLines.find(([name]) => name === ssid);
2475
+ const signal = match ? parseInt(match[1], 10) || 0 : 0;
2476
+ if (signal > 0 && signal < 30) {
2477
+ // Include warning in result but proceed with connection
2478
+ console.error(`${logPrefix} action="connect" ssid="${ssid}" signal=${signal} warning="weak_signal"`);
2479
+ }
2480
+ }
2481
+ catch {
2482
+ // Scan check is best-effort — proceed with connect regardless
2483
+ }
2484
+ execFileSync("nmcli", ["device", "wifi", "connect", ssid, "password", password, "--wait", "30"], { encoding: "utf-8", timeout: 35000 });
2485
+ console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connecting" password=[REDACTED]`);
2486
+ // Post-connect verification: confirm IP assignment on wlan0 (cherry-pick 2)
2487
+ let ip = "";
2488
+ try {
2489
+ const deviceInfo = execFileSync("nmcli", ["-t", "-f", "IP4.ADDRESS", "device", "show", "wlan0"], { encoding: "utf-8", timeout: 5000 });
2490
+ const ipLine = deviceInfo.split("\n").find((l) => l.startsWith("IP4.ADDRESS"));
2491
+ if (ipLine) {
2492
+ // Format: IP4.ADDRESS[1]:192.168.1.100/24
2493
+ ip = ipLine.split(":").slice(1).join(":").split("/")[0].trim();
2494
+ }
2495
+ }
2496
+ catch {
2497
+ // IP check failed — connection may still be establishing
2498
+ }
2499
+ if (ip) {
2500
+ console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connected" ip="${ip}"`);
2501
+ // Check if signal was weak and include warning
2502
+ let warning = "";
2503
+ try {
2504
+ const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
2505
+ const match = parseNmcliLines(scanRaw).find(([name]) => name === ssid);
2506
+ const signal = match ? parseInt(match[1], 10) || 0 : 0;
2507
+ if (signal > 0 && signal < 30) {
2508
+ warning = `\n\nWarning: Signal strength is weak (${signal}%). Connection may be unreliable.`;
2509
+ }
2510
+ }
2511
+ catch {
2512
+ // Signal check is best-effort
2513
+ }
2514
+ return {
2515
+ content: [{
2516
+ type: "text",
2517
+ text: `Connected to ${ssid}. IP address: ${ip}. You can safely unplug ethernet.${warning}`,
2518
+ }],
2519
+ };
2520
+ }
2521
+ else {
2522
+ console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connected_no_ip"`);
2523
+ return {
2524
+ content: [{
2525
+ type: "text",
2526
+ text: `Connected to ${ssid} but no IP address was assigned. Stay on ethernet. The network may require additional configuration — check with your network administrator.`,
2527
+ }],
2528
+ };
2529
+ }
2530
+ }
2531
+ // ── status ───────────────────────────────────────────────
2532
+ case "status": {
2533
+ // Check if wlan0 exists
2534
+ let deviceStatus;
2535
+ try {
2536
+ deviceStatus = execFileSync("nmcli", ["-t", "-f", "DEVICE,TYPE,STATE", "device", "status"], { encoding: "utf-8", timeout: 5000 });
2537
+ }
2538
+ catch (err) {
2539
+ console.error(`${logPrefix} action="status" result="failed" error="${err instanceof Error ? err.message : String(err)}"`);
2540
+ return {
2541
+ content: [{ type: "text", text: `WiFi status check failed: ${err instanceof Error ? err.message : String(err)}` }],
2542
+ isError: true,
2543
+ };
2544
+ }
2545
+ const wifiDevice = parseNmcliLines(deviceStatus).find(([, type]) => type === "wifi");
2546
+ if (!wifiDevice) {
2547
+ console.error(`${logPrefix} action="status" result="no_hardware"`);
2548
+ return {
2549
+ content: [{ type: "text", text: "No WiFi hardware detected on this device." }],
2550
+ };
2551
+ }
2552
+ if (wifiDevice[2] !== "connected") {
2553
+ console.error(`${logPrefix} action="status" result="not_connected" state="${wifiDevice[2]}"`);
2554
+ return {
2555
+ content: [{ type: "text", text: `WiFi is not connected. Device state: ${wifiDevice[2]}.` }],
2556
+ };
2557
+ }
2558
+ // Get detailed connection info
2559
+ const details = execFileSync("nmcli", ["-t", "-f", "GENERAL.CONNECTION,IP4.ADDRESS,IP4.GATEWAY,GENERAL.HWADDR", "device", "show", "wlan0"], { encoding: "utf-8", timeout: 5000 });
2560
+ const detailLines = parseNmcliLines(details);
2561
+ const conn = detailLines.find(([k]) => k === "GENERAL.CONNECTION")?.[1] ?? "unknown";
2562
+ const ipAddr = detailLines.find(([k]) => k.startsWith("IP4.ADDRESS"))?.[1] ?? "none";
2563
+ const gateway = detailLines.find(([k]) => k.startsWith("IP4.GATEWAY"))?.[1] ?? "none";
2564
+ // Get signal strength
2565
+ let signal = "unknown";
2566
+ try {
2567
+ const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
2568
+ const match = parseNmcliLines(scanRaw).find(([name]) => name === conn);
2569
+ if (match)
2570
+ signal = `${match[1]}%`;
2571
+ }
2572
+ catch {
2573
+ // Signal lookup is best-effort
2574
+ }
2575
+ console.error(`${logPrefix} action="status" result="connected" ssid="${conn}"`);
2576
+ return {
2577
+ content: [{
2578
+ type: "text",
2579
+ text: `WiFi: connected\nSSID: ${conn}\nIP: ${ipAddr}\nGateway: ${gateway}\nSignal: ${signal}`,
2580
+ }],
2581
+ };
2582
+ }
2583
+ // ── saved ────────────────────────────────────────────────
2584
+ case "saved": {
2585
+ const raw = execFileSync("nmcli", ["-t", "-f", "NAME,TYPE", "connection", "show"], { encoding: "utf-8", timeout: 5000 });
2586
+ const wifiConns = parseNmcliLines(raw).filter(([, type]) => type === "802-11-wireless");
2587
+ console.error(`${logPrefix} action="saved" count=${wifiConns.length}`);
2588
+ if (wifiConns.length === 0) {
2589
+ return {
2590
+ content: [{ type: "text", text: "No saved WiFi networks." }],
2591
+ };
2592
+ }
2593
+ const formatted = wifiConns.map(([name]) => name).join("\n");
2594
+ return {
2595
+ content: [{ type: "text", text: `# Saved WiFi Networks\n\n${formatted}` }],
2596
+ };
2597
+ }
2598
+ // ── forget ───────────────────────────────────────────────
2599
+ case "forget": {
2600
+ if (!ssid) {
2601
+ return {
2602
+ content: [{ type: "text", text: "SSID is required for forget action." }],
2603
+ isError: true,
2604
+ };
2605
+ }
2606
+ execFileSync("nmcli", ["connection", "delete", ssid], { encoding: "utf-8", timeout: 10000 });
2607
+ console.error(`${logPrefix} action="forget" ssid="${ssid}" result="ok"`);
2608
+ return {
2609
+ content: [{
2610
+ type: "text",
2611
+ text: `Forgotten network: ${ssid}. It will no longer auto-connect.`,
2612
+ }],
2613
+ };
2614
+ }
2615
+ default:
2616
+ return {
2617
+ content: [{ type: "text", text: `Unknown WiFi action: ${action}` }],
2618
+ isError: true,
2619
+ };
2620
+ }
2621
+ }
2622
+ catch (err) {
2623
+ const errMsg = err instanceof Error ? err.message : String(err);
2624
+ console.error(`${logPrefix} action="${action}" ssid="${ssid ?? ""}" result="failed" error="${errMsg}"`);
2625
+ // Parse common nmcli error patterns into user-friendly messages
2626
+ if (errMsg.includes("NetworkManager is not running")) {
2627
+ return {
2628
+ content: [{ type: "text", text: "NetworkManager is not running. Start it with: sudo systemctl start NetworkManager" }],
2629
+ isError: true,
2630
+ };
2631
+ }
2632
+ if (errMsg.includes("No network with SSID") || errMsg.includes("not found")) {
2633
+ return {
2634
+ content: [{ type: "text", text: `Network '${ssid}' not found — it may be out of range or the name may be incorrect.` }],
2635
+ isError: true,
2636
+ };
2637
+ }
2638
+ if (errMsg.includes("Secrets were required") || errMsg.includes("802-11-wireless-security.psk")) {
2639
+ return {
2640
+ content: [{ type: "text", text: `Authentication failed for '${ssid}' — the password may be incorrect.` }],
2641
+ isError: true,
2642
+ };
2643
+ }
2644
+ if (errMsg.includes("no wifi device found") || errMsg.includes("Wi-Fi device not found")) {
2645
+ return {
2646
+ content: [{ type: "text", text: "No WiFi hardware detected on this device." }],
2647
+ isError: true,
2648
+ };
2649
+ }
2650
+ if (errMsg.includes("ENOENT")) {
2651
+ return {
2652
+ content: [{ type: "text", text: "nmcli not found — NetworkManager may not be installed on this device." }],
2653
+ isError: true,
2654
+ };
2655
+ }
2656
+ if (errMsg.includes("ETIMEDOUT") || errMsg.includes("timed out") || errMsg.includes("Timeout")) {
2657
+ return {
2658
+ content: [{ type: "text", text: `WiFi operation timed out. The adapter may be busy — try again.` }],
2659
+ isError: true,
2660
+ };
2661
+ }
2662
+ return {
2663
+ content: [{ type: "text", text: `WiFi ${action} failed: ${errMsg}` }],
2664
+ isError: true,
2665
+ };
2666
+ }
2667
+ });
2668
+ // ===================================================================
2669
+ // Premium plugin tools
2670
+ // ===================================================================
2671
+ server.tool("premium-list", "List available premium plugins and their delivery status. Returns structured data " +
2672
+ "for each premium plugin in the staging area: name, type (standalone/bundle), description, " +
2673
+ "purchase status, and delivery status (per sub-plugin for bundles).", {}, async () => {
2674
+ try {
2675
+ if (!existsSync(STAGING_ROOT)) {
2676
+ return {
2677
+ content: [{ type: "text", text: "No premium plugins available. The staging directory does not exist — premium plugins become available after the next platform update." }],
2678
+ };
2679
+ }
2680
+ const config = await readAccountConfig();
2681
+ // Effective purchasedPlugins from signed entitlement (Task 831).
2682
+ const entitlement = await currentEntitlement();
2683
+ const purchased = entitlement.purchasedPlugins;
2684
+ const enabled = Array.isArray(config.enabledPlugins) ? config.enabledPlugins : [];
2685
+ const entries = readdirSync(STAGING_ROOT, { withFileTypes: true })
2686
+ .filter(e => e.isDirectory())
2687
+ .sort((a, b) => a.name.localeCompare(b.name));
2688
+ if (entries.length === 0) {
2689
+ return {
2690
+ content: [{ type: "text", text: "No premium plugins found in the staging area." }],
2691
+ };
2692
+ }
2693
+ const plugins = [];
2694
+ for (const entry of entries) {
2695
+ const stagingDir = resolve(STAGING_ROOT, entry.name);
2696
+ const bundlePath = join(stagingDir, "BUNDLE.md");
2697
+ const pluginPath = join(stagingDir, "PLUGIN.md");
2698
+ if (existsSync(bundlePath)) {
2699
+ // Bundle
2700
+ const fm = parseFrontmatter(readFileSync(bundlePath, "utf-8"));
2701
+ const subPlugins = (Array.isArray(fm.plugins) ? fm.plugins : [])
2702
+ .filter(name => VALID_PLUGIN_NAME.test(name));
2703
+ const subStatus = subPlugins.map(name => ({
2704
+ name,
2705
+ delivered: existsSync(join(PLUGINS_DIR, name, "PLUGIN.md")),
2706
+ enabled: enabled.includes(name),
2707
+ }));
2708
+ plugins.push({
2709
+ name: entry.name,
2710
+ type: "bundle",
2711
+ description: typeof fm.description === "string" ? fm.description : "",
2712
+ purchased: purchased.includes(entry.name),
2713
+ subPlugins: subStatus,
2714
+ });
2715
+ }
2716
+ else if (existsSync(pluginPath)) {
2717
+ // Standalone
2718
+ const fm = parseFrontmatter(readFileSync(pluginPath, "utf-8"));
2719
+ plugins.push({
2720
+ name: entry.name,
2721
+ type: "standalone",
2722
+ description: typeof fm.description === "string" ? fm.description : "",
2723
+ purchased: purchased.includes(entry.name),
2724
+ delivered: existsSync(join(PLUGINS_DIR, entry.name, "PLUGIN.md")),
2725
+ enabled: enabled.includes(entry.name),
2726
+ });
2727
+ }
2728
+ // Skip directories that have neither BUNDLE.md nor PLUGIN.md
2729
+ }
2730
+ return {
2731
+ content: [{ type: "text", text: JSON.stringify(plugins, null, 2) }],
2732
+ };
2733
+ }
2734
+ catch (err) {
2735
+ return {
2736
+ content: [{ type: "text", text: `Failed to list premium plugins: ${err instanceof Error ? err.message : String(err)}` }],
2737
+ isError: true,
2738
+ };
2739
+ }
2740
+ });
2741
+ server.tool("premium-deliver", "Deliver a purchased premium plugin. Copies plugin files from the staging area to the active " +
2742
+ "plugin directory, verifies each copy, updates enabledPlugins in account.json, and scans for " +
2743
+ "agent templates. Handles both standalone plugins and bundles (auto-detected). Idempotent — " +
2744
+ "already-delivered sub-plugins are skipped. Returns a structured result with per-sub-plugin " +
2745
+ "status and available templates.", {
2746
+ pluginName: z.string().describe("Name of the premium plugin to deliver (e.g. 'real-agency', 'teaching')"),
2747
+ }, async ({ pluginName }) => {
2748
+ const TAG = "[premium-deliver]";
2749
+ // --- Validate plugin name ---
2750
+ if (!pluginName || !VALID_PLUGIN_NAME.test(pluginName)) {
2751
+ return {
2752
+ content: [{ type: "text", text: `${TAG} Invalid plugin name "${pluginName}". Must match [a-z0-9-].` }],
2753
+ isError: true,
2754
+ };
2755
+ }
2756
+ // --- Guard against clobbering core plugins ---
2757
+ if (CORE_PLUGINS.includes(pluginName)) {
2758
+ return {
2759
+ content: [{ type: "text", text: `${TAG} "${pluginName}" is a core plugin and cannot be delivered as a premium plugin.` }],
2760
+ isError: true,
2761
+ };
2762
+ }
2763
+ // --- Check staging directory ---
2764
+ if (!existsSync(STAGING_ROOT)) {
2765
+ return {
2766
+ content: [{ type: "text", text: `${TAG} Premium plugins staging directory does not exist. Premium plugins become available after the next platform update.` }],
2767
+ isError: true,
2768
+ };
2769
+ }
2770
+ const stagingDir = resolve(STAGING_ROOT, pluginName);
2771
+ if (!existsSync(stagingDir)) {
2772
+ return {
2773
+ content: [{ type: "text", text: `${TAG} No premium plugin named "${pluginName}" found in the staging area.` }],
2774
+ isError: true,
2775
+ };
2776
+ }
2777
+ // --- Read account config ---
2778
+ let config;
2779
+ try {
2780
+ config = await readAccountConfig();
2781
+ }
2782
+ catch (err) {
2783
+ return {
2784
+ content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }],
2785
+ isError: true,
2786
+ };
2787
+ }
2788
+ // Effective purchasedPlugins from signed entitlement (Task 831). The
2789
+ // raw account.json value is ignored on commercial installs; verifier
2790
+ // returns the signed list (or [] on anonymous-fallback).
2791
+ const entitlement = await resolveEntitlement(ENTITLEMENT_BRAND, {
2792
+ accountId: typeof config.accountId === "string" ? config.accountId : "",
2793
+ customerEmail: typeof config.customerEmail === "string" ? config.customerEmail : undefined,
2794
+ tier: typeof config.tier === "string" ? config.tier : undefined,
2795
+ purchasedPlugins: Array.isArray(config.purchasedPlugins) ? config.purchasedPlugins : undefined,
2796
+ });
2797
+ const purchased = entitlement.purchasedPlugins;
2798
+ // --- Check purchase status ---
2799
+ if (!purchased.includes(pluginName)) {
2800
+ return {
2801
+ content: [{ type: "text", text: `${TAG} "${pluginName}" is not in purchasedPlugins. Record the purchase first.` }],
2802
+ isError: true,
2803
+ };
2804
+ }
2805
+ // --- Detect type: bundle vs standalone ---
2806
+ const bundlePath = join(stagingDir, "BUNDLE.md");
2807
+ const pluginMdPath = join(stagingDir, "PLUGIN.md");
2808
+ const isBundle = existsSync(bundlePath);
2809
+ const isStandalone = !isBundle && existsSync(pluginMdPath);
2810
+ if (!isBundle && !isStandalone) {
2811
+ return {
2812
+ content: [{ type: "text", text: `${TAG} Staging directory "${pluginName}" contains neither BUNDLE.md nor PLUGIN.md. Invalid premium plugin.` }],
2813
+ isError: true,
2814
+ };
2815
+ }
2816
+ const items = [];
2817
+ if (isBundle) {
2818
+ const fm = parseFrontmatter(readFileSync(bundlePath, "utf-8"));
2819
+ const subPluginNames = Array.isArray(fm.plugins) ? fm.plugins : [];
2820
+ if (subPluginNames.length === 0) {
2821
+ return {
2822
+ content: [{ type: "text", text: `${TAG} BUNDLE.md for "${pluginName}" has an empty or missing plugins array.` }],
2823
+ isError: true,
2824
+ };
2825
+ }
2826
+ for (const sub of subPluginNames) {
2827
+ if (!VALID_PLUGIN_NAME.test(sub)) {
2828
+ return {
2829
+ content: [{ type: "text", text: `${TAG} Sub-plugin name "${sub}" in BUNDLE.md contains invalid characters. Aborting delivery.` }],
2830
+ isError: true,
2831
+ };
2832
+ }
2833
+ if (CORE_PLUGINS.includes(sub)) {
2834
+ return {
2835
+ content: [{ type: "text", text: `${TAG} Sub-plugin "${sub}" collides with a core plugin name. Aborting delivery.` }],
2836
+ isError: true,
2837
+ };
2838
+ }
2839
+ items.push({ name: sub, sourcePath: resolve(stagingDir, "plugins", sub) });
2840
+ }
2841
+ }
2842
+ else {
2843
+ items.push({ name: pluginName, sourcePath: stagingDir });
2844
+ }
2845
+ const results = [];
2846
+ const newlyEnabled = [];
2847
+ for (const item of items) {
2848
+ const destPath = resolve(PLUGINS_DIR, item.name);
2849
+ // Already delivered — skip (preserves user's enable/disable state)
2850
+ if (existsSync(join(destPath, "PLUGIN.md"))) {
2851
+ console.log(`${TAG} ${item.name} already delivered — skipping`);
2852
+ results.push({ name: item.name, status: "skipped" });
2853
+ continue;
2854
+ }
2855
+ // Validate source
2856
+ if (!existsSync(join(item.sourcePath, "PLUGIN.md"))) {
2857
+ const msg = `Source PLUGIN.md missing at ${item.sourcePath}`;
2858
+ console.error(`${TAG} FAILED ${item.name}: ${msg}`);
2859
+ results.push({ name: item.name, status: "failed", error: msg });
2860
+ continue;
2861
+ }
2862
+ // Copy
2863
+ try {
2864
+ cpSync(item.sourcePath, destPath, { recursive: true });
2865
+ console.log(`${TAG} copied ${item.name} from staging`);
2866
+ }
2867
+ catch (err) {
2868
+ const msg = err instanceof Error ? err.message : String(err);
2869
+ console.error(`${TAG} FAILED to copy ${item.name}: ${msg}`);
2870
+ results.push({ name: item.name, status: "failed", error: msg });
2871
+ continue;
2872
+ }
2873
+ // Verify
2874
+ const verifyPath = join(destPath, "PLUGIN.md");
2875
+ if (!existsSync(verifyPath)) {
2876
+ console.error(`${TAG} FAILED to verify ${item.name}: PLUGIN.md missing after copy`);
2877
+ results.push({ name: item.name, status: "failed", error: "PLUGIN.md missing after copy" });
2878
+ continue;
2879
+ }
2880
+ // Count skills
2881
+ const skillsDir = join(destPath, "skills");
2882
+ let skillCount = 0;
2883
+ if (existsSync(skillsDir)) {
2884
+ skillCount = readdirSync(skillsDir, { withFileTypes: true })
2885
+ .filter(e => e.isDirectory()).length;
2886
+ }
2887
+ console.log(`${TAG} verified ${item.name} (${skillCount} skills)`);
2888
+ results.push({ name: item.name, status: "delivered", skills: skillCount });
2889
+ newlyEnabled.push(item.name);
2890
+ }
2891
+ // --- Update enabledPlugins (only add newly delivered, deduplicate) ---
2892
+ if (newlyEnabled.length > 0) {
2893
+ try {
2894
+ // Re-read config to reduce race window
2895
+ const freshConfig = await readAccountConfig();
2896
+ const freshEnabled = Array.isArray(freshConfig.enabledPlugins)
2897
+ ? freshConfig.enabledPlugins
2898
+ : [];
2899
+ const merged = [...new Set([...freshEnabled, ...newlyEnabled])];
2900
+ freshConfig.enabledPlugins = merged;
2901
+ const configPath = join(getAccountDir(), "account.json");
2902
+ await writeFile(configPath, JSON.stringify(freshConfig, null, 2) + "\n", "utf-8");
2903
+ console.log(`${TAG} updated enabledPlugins: added ${newlyEnabled.join(", ")}`);
2904
+ }
2905
+ catch (err) {
2906
+ const msg = err instanceof Error ? err.message : String(err);
2907
+ console.error(`${TAG} FAILED to update account.json: ${msg}`);
2908
+ return {
2909
+ content: [{
2910
+ type: "text",
2911
+ text: `${TAG} Plugins were copied but account.json update failed: ${msg}\n\nDelivery results:\n${JSON.stringify(results, null, 2)}`,
2912
+ }],
2913
+ isError: true,
2914
+ };
2915
+ }
2916
+ }
2917
+ const templates = [];
2918
+ // For bundles, templates live in the staging agents/ dir; for standalone, in the delivered plugin
2919
+ const agentsSearchDirs = [];
2920
+ if (isBundle) {
2921
+ const bundleAgentsDir = join(stagingDir, "agents");
2922
+ if (existsSync(bundleAgentsDir))
2923
+ agentsSearchDirs.push(bundleAgentsDir);
2924
+ }
2925
+ else {
2926
+ const pluginAgentsDir = join(PLUGINS_DIR, pluginName, "agents");
2927
+ if (existsSync(pluginAgentsDir))
2928
+ agentsSearchDirs.push(pluginAgentsDir);
2929
+ }
2930
+ for (const agentsDir of agentsSearchDirs) {
2931
+ const subdirs = readdirSync(agentsDir, { withFileTypes: true })
2932
+ .filter(e => e.isDirectory());
2933
+ for (const sub of subdirs) {
2934
+ const templateJsonPath = join(agentsDir, sub.name, "template.json");
2935
+ if (!existsSync(templateJsonPath))
2936
+ continue;
2937
+ try {
2938
+ const templateData = JSON.parse(readFileSync(templateJsonPath, "utf-8"));
2939
+ const identityExists = existsSync(join(agentsDir, sub.name, "IDENTITY.md"));
2940
+ const soulExists = existsSync(join(agentsDir, sub.name, "SOUL.md"));
2941
+ if (!identityExists || !soulExists) {
2942
+ console.log(`${TAG} template ${sub.name} incomplete — missing ${!identityExists ? "IDENTITY.md" : "SOUL.md"}, skipping`);
2943
+ continue;
2944
+ }
2945
+ templates.push({
2946
+ directory: join(agentsDir, sub.name),
2947
+ displayName: templateData.displayName ?? sub.name,
2948
+ description: templateData.description ?? "",
2949
+ suggestedSlug: templateData.suggestedSlug,
2950
+ });
2951
+ }
2952
+ catch {
2953
+ console.log(`${TAG} template ${sub.name} has invalid template.json, skipping`);
2954
+ }
2955
+ }
2956
+ }
2957
+ // --- Build structured result ---
2958
+ const delivered = results.filter(r => r.status === "delivered");
2959
+ const skipped = results.filter(r => r.status === "skipped");
2960
+ const failed = results.filter(r => r.status === "failed");
2961
+ const totalSkills = delivered.reduce((sum, r) => sum + (r.skills ?? 0), 0);
2962
+ const output = {
2963
+ pluginName,
2964
+ type: isBundle ? "bundle" : "standalone",
2965
+ delivered: delivered.map(r => ({ name: r.name, skills: r.skills })),
2966
+ skipped: skipped.map(r => r.name),
2967
+ failed: failed.map(r => ({ name: r.name, error: r.error })),
2968
+ totalDelivered: delivered.length,
2969
+ totalSkipped: skipped.length,
2970
+ totalFailed: failed.length,
2971
+ totalSkills,
2972
+ templates: templates.map(t => ({
2973
+ displayName: t.displayName,
2974
+ description: t.description,
2975
+ suggestedSlug: t.suggestedSlug,
2976
+ directory: t.directory,
2977
+ })),
2978
+ };
2979
+ return {
2980
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
2981
+ };
2982
+ });
2983
+ // ---------------------------------------------------------------------------
2984
+ // file-attach — copy a generated file into the attachment store for download
2985
+ // ---------------------------------------------------------------------------
2986
+ eagerTool(server, "file-attach", "Attach a file from the account directory for delivery to the user. " +
2987
+ "Copies the file into the attachment store and returns metadata for rendering " +
2988
+ "a download component. The file must be within the account directory tree. " +
2989
+ "After calling this tool, call render-component with name 'file-attachment' " +
2990
+ "and pass the returned metadata as data.", {
2991
+ filePath: z.string().describe("Absolute path to the file to attach"),
2992
+ }, async ({ filePath }) => {
2993
+ const TAG = "[file-attach]";
2994
+ if (!filePath || !filePath.trim()) {
2995
+ return {
2996
+ content: [{ type: "text", text: `${TAG} filePath is required` }],
2997
+ isError: true,
2998
+ };
2999
+ }
3000
+ const accountDir = getAccountDir();
3001
+ // --- Path validation: must be within the account directory ---
3002
+ let resolvedPath;
3003
+ try {
3004
+ const { realpathSync: rp } = await import("node:fs");
3005
+ resolvedPath = rp(filePath);
3006
+ const accountResolved = rp(accountDir);
3007
+ if (!resolvedPath.startsWith(accountResolved + "/")) {
3008
+ const sanitised = filePath.replace(accountDir, "<account>/");
3009
+ console.error(`${TAG} REJECTED path=${sanitised} reason=outside_account_directory`);
3010
+ return {
3011
+ content: [{ type: "text", text: `${TAG} Access denied: file is outside the account directory.` }],
3012
+ isError: true,
3013
+ };
3014
+ }
3015
+ }
3016
+ catch (err) {
3017
+ const code = err.code;
3018
+ if (code === "ENOENT") {
3019
+ console.error(`${TAG} ENOENT path=${filePath}`);
3020
+ return {
3021
+ content: [{ type: "text", text: `${TAG} File not found: ${filePath}` }],
3022
+ isError: true,
3023
+ };
3024
+ }
3025
+ console.error(`${TAG} path validation error: ${err instanceof Error ? err.message : String(err)}`);
3026
+ return {
3027
+ content: [{ type: "text", text: `${TAG} Failed to validate file path: ${err instanceof Error ? err.message : String(err)}` }],
3028
+ isError: true,
3029
+ };
3030
+ }
3031
+ // --- Store in attachment store ---
3032
+ const targetUrl = `http://127.0.0.1:${PLATFORM_PORT}/api/admin/file-attach`;
3033
+ try {
3034
+ const res = await fetch(targetUrl, {
3035
+ method: "POST",
3036
+ headers: { "Content-Type": "application/json" },
3037
+ body: JSON.stringify({ filePath: resolvedPath, accountId: ACCOUNT_ID }),
3038
+ });
3039
+ const result = await res.json();
3040
+ if (!res.ok || result.error) {
3041
+ const msg = result.error || `HTTP ${res.status}`;
3042
+ console.error(`${TAG} store failed url=${targetUrl} status=${res.status} error=${msg}`);
3043
+ return {
3044
+ content: [{ type: "text", text: `${TAG} Failed to attach file: ${msg} (url=${targetUrl})` }],
3045
+ isError: true,
3046
+ };
3047
+ }
3048
+ console.error(`${TAG} attached path=${resolvedPath} size=${result.sizeBytes} mime=${result.mimeType} id=${result.attachmentId}`);
3049
+ return {
3050
+ content: [{
3051
+ type: "text",
3052
+ text: JSON.stringify({
3053
+ attachmentId: result.attachmentId,
3054
+ filename: result.filename,
3055
+ sizeBytes: result.sizeBytes,
3056
+ mimeType: result.mimeType,
3057
+ }),
3058
+ }],
3059
+ };
3060
+ }
3061
+ catch (err) {
3062
+ const errMsg = err instanceof Error ? err.message : String(err);
3063
+ console.error(`${TAG} fetch failed url=${targetUrl} error=${errMsg}`);
3064
+ return {
3065
+ content: [{ type: "text", text: `${TAG} Failed to attach file: could not reach platform server at ${targetUrl} (${errMsg})` }],
3066
+ isError: true,
3067
+ };
3068
+ }
3069
+ });
3070
+ // ===================================================================
3071
+ // Action approval tools (EU AI Act Article 14 — human oversight)
3072
+ //
3073
+ // Hook queues require-review actions to {accountDir}/pending-actions/.
3074
+ // These tools let the admin list, approve, reject, or edit pending actions.
3075
+ // On approval, the target plugin MCP server is spawned directly (dispatchStep
3076
+ // pattern) to execute the original tool call.
3077
+ // ===================================================================
3078
+ let _pendingDir;
3079
+ function getPendingDir() {
3080
+ if (!_pendingDir)
3081
+ _pendingDir = resolve(getAccountDir(), "pending-actions");
3082
+ return _pendingDir;
3083
+ }
3084
+ function readPendingActions() {
3085
+ const pendingDir = getPendingDir();
3086
+ if (!existsSync(pendingDir))
3087
+ return [];
3088
+ const files = readdirSync(pendingDir)
3089
+ .filter(f => f.endsWith(".json") && !f.startsWith("."));
3090
+ const actions = [];
3091
+ for (const file of files) {
3092
+ try {
3093
+ const raw = readFileSync(resolve(pendingDir, file), "utf-8");
3094
+ const parsed = JSON.parse(raw);
3095
+ if (parsed.state === "pending") {
3096
+ actions.push(parsed);
3097
+ }
3098
+ }
3099
+ catch (err) {
3100
+ console.error(`[approval] Failed to read pending action ${file}: ${err instanceof Error ? err.message : String(err)}`);
3101
+ }
3102
+ }
3103
+ return actions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
3104
+ }
3105
+ function readPendingAction(actionId) {
3106
+ const filePath = resolve(getPendingDir(), `${actionId}.json`);
3107
+ if (!existsSync(filePath))
3108
+ return null;
3109
+ try {
3110
+ return JSON.parse(readFileSync(filePath, "utf-8"));
3111
+ }
3112
+ catch {
3113
+ return null;
3114
+ }
3115
+ }
3116
+ function resolvePendingAction(actionId, state, editedInput) {
3117
+ const filePath = resolve(getPendingDir(), `${actionId}.json`);
3118
+ if (!existsSync(filePath))
3119
+ return false;
3120
+ try {
3121
+ const action = JSON.parse(readFileSync(filePath, "utf-8"));
3122
+ if (action.state !== "pending")
3123
+ return false;
3124
+ action.state = state;
3125
+ action.resolvedAt = new Date().toISOString();
3126
+ if (editedInput)
3127
+ action.editedInput = editedInput;
3128
+ writeFileSync(filePath, JSON.stringify(action, null, 2), "utf-8");
3129
+ return true;
3130
+ }
3131
+ catch {
3132
+ return false;
3133
+ }
3134
+ }
3135
+ function cleanupPendingAction(actionId) {
3136
+ const pendingDir = getPendingDir();
3137
+ const filePath = resolve(pendingDir, `${actionId}.json`);
3138
+ try {
3139
+ if (existsSync(filePath)) {
3140
+ const archiveDir = resolve(pendingDir, "resolved");
3141
+ if (!existsSync(archiveDir)) {
3142
+ mkdirSync(archiveDir, { recursive: true });
3143
+ }
3144
+ renameSync(filePath, resolve(archiveDir, `${actionId}.json`));
3145
+ }
3146
+ }
3147
+ catch (err) {
3148
+ console.error(`[approval] Failed to archive pending action ${actionId}: ${err instanceof Error ? err.message : String(err)}`);
3149
+ }
3150
+ }
3151
+ async function persistApprovalToolCall(opts) {
3152
+ // Write doctrine (Task 673): a ToolCall without its owning Conversation is
3153
+ // noise — every admin tool call belongs to a chat. When conversationId is
3154
+ // missing or its Conversation cannot be matched, we log and skip the
3155
+ // persist rather than create an orphan ToolCall node. The legacy
3156
+ // OPTIONAL-MATCH + FOREACH pattern created orphans silently.
3157
+ if (!opts.conversationId) {
3158
+ console.error(`[approval] ToolCall skipped (no conversationId): tool=${opts.toolName} state=${opts.approvalState}`);
3159
+ return;
3160
+ }
3161
+ const session = getSession();
3162
+ try {
3163
+ const optionalFields = [
3164
+ opts.originalInput != null ? ", originalInput: $originalInput" : "",
3165
+ ].join("");
3166
+ const res = await session.run(`MATCH (c:Conversation {conversationId: $conversationId})
3167
+ CREATE (c)-[:HAS_TOOL_CALL]->(tc:ToolCall {
3168
+ callId: $callId,
3169
+ toolName: $toolName,
3170
+ pluginName: $pluginName,
3171
+ input: $input,
3172
+ output: $output,
3173
+ isError: $isError,
3174
+ agentType: 'admin',
3175
+ accountId: $accountId,
3176
+ approvalState: $approvalState,
3177
+ conversationId: $conversationId,
3178
+ createdBySource: 'admin-approval',
3179
+ startedAt: datetime($startedAt),
3180
+ completedAt: datetime($completedAt)
3181
+ ${optionalFields}
3182
+ })`, {
3183
+ callId: randomUUID(),
3184
+ toolName: opts.toolName,
3185
+ pluginName: opts.pluginName,
3186
+ input: opts.input.slice(0, 300),
3187
+ output: opts.output.slice(0, 300),
3188
+ isError: opts.isError,
3189
+ accountId: ACCOUNT_ID,
3190
+ approvalState: opts.approvalState,
3191
+ conversationId: opts.conversationId,
3192
+ startedAt: new Date().toISOString(),
3193
+ completedAt: new Date().toISOString(),
3194
+ ...(opts.originalInput != null ? { originalInput: opts.originalInput.slice(0, 300) } : {}),
3195
+ });
3196
+ if (res.summary.counters.updates().nodesCreated === 0) {
3197
+ console.error(`[approval] ToolCall skipped (conversation ${opts.conversationId} not found): tool=${opts.toolName}`);
3198
+ return;
3199
+ }
3200
+ console.error(`[approval] ToolCall persisted: ${opts.toolName} state=${opts.approvalState}`);
3201
+ }
3202
+ catch (err) {
3203
+ console.error(`[approval] ToolCall persist failed: ${err instanceof Error ? err.message : String(err)}`);
3204
+ }
3205
+ finally {
3206
+ await session.close();
3207
+ }
3208
+ }
3209
+ /**
3210
+ * Spawn a plugin MCP server and execute a tool call directly.
3211
+ * Same pattern as workflow-execute.ts dispatchStep.
3212
+ */
3213
+ async function dispatchApprovedAction(plugin, tool, args, timeoutMs = 30_000) {
3214
+ const serverPath = resolve(PLATFORM_ROOT, "plugins", plugin, "mcp", "dist", "index.js");
3215
+ // Path traversal check
3216
+ const pluginsRoot = resolve(PLATFORM_ROOT, "plugins");
3217
+ if (!serverPath.startsWith(pluginsRoot + "/")) {
3218
+ throw new Error(`Invalid plugin name: "${plugin}" — path traversal blocked`);
3219
+ }
3220
+ if (!existsSync(serverPath)) {
3221
+ throw new Error(`Plugin MCP server not found: ${serverPath}`);
3222
+ }
3223
+ const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
3224
+ const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
3225
+ const env = {};
3226
+ for (const [k, v] of Object.entries(process.env)) {
3227
+ if (v !== undefined)
3228
+ env[k] = v;
3229
+ }
3230
+ env.PLATFORM_ROOT = PLATFORM_ROOT;
3231
+ env.ACCOUNT_ID = ACCOUNT_ID;
3232
+ const transport = new StdioClientTransport({
3233
+ command: process.execPath,
3234
+ args: [serverPath],
3235
+ env,
3236
+ stderr: "pipe",
3237
+ });
3238
+ let childStderr = "";
3239
+ const stderrStream = transport.stderr;
3240
+ if (stderrStream) {
3241
+ stderrStream.on("data", (chunk) => {
3242
+ if (childStderr.length < 4096) {
3243
+ childStderr += chunk.toString("utf-8").slice(0, 4096 - childStderr.length);
3244
+ }
3245
+ });
3246
+ }
3247
+ const client = new Client({ name: "approval-executor", version: "1.0.0" });
3248
+ let timedOut = false;
3249
+ const timeout = setTimeout(() => {
3250
+ timedOut = true;
3251
+ client.close().catch(() => { });
3252
+ }, timeoutMs);
3253
+ try {
3254
+ await client.connect(transport);
3255
+ const result = await client.callTool({ name: tool, arguments: args });
3256
+ const contentArr = Array.isArray(result.content) ? result.content : [];
3257
+ const text = contentArr
3258
+ .filter((c) => c.type === "text")
3259
+ .map((c) => c.text)
3260
+ .join("\n");
3261
+ return { text, isError: result.isError === true };
3262
+ }
3263
+ catch (err) {
3264
+ const msg = err instanceof Error ? err.message : String(err);
3265
+ const stderrSuffix = childStderr ? `\nChild stderr: ${childStderr.trim()}` : "";
3266
+ if (timedOut) {
3267
+ throw new Error(`Tool "${tool}" on plugin "${plugin}" timed out after ${timeoutMs}ms${stderrSuffix}`);
3268
+ }
3269
+ throw new Error(`Tool "${tool}" on plugin "${plugin}" failed: ${msg}${stderrSuffix}`);
3270
+ }
3271
+ finally {
3272
+ clearTimeout(timeout);
3273
+ await client.close().catch(() => { });
3274
+ }
3275
+ }
3276
+ eagerTool(server, "action-pending", "List actions that are queued for human approval. Returns each pending action's ID, " +
3277
+ "tool name, input summary, and when it was queued. Use this to review what the agent " +
3278
+ "wanted to do before approving or rejecting.", {}, async () => {
3279
+ const actions = readPendingActions();
3280
+ if (actions.length === 0) {
3281
+ return {
3282
+ content: [{ type: "text", text: "No pending actions. All actions are either approved, rejected, or auto-executed." }],
3283
+ };
3284
+ }
3285
+ const lines = actions.map((a) => {
3286
+ const inputSummary = JSON.stringify(a.hookPayload?.tool_input ?? {}).slice(0, 200);
3287
+ return `- **${a.actionId}** | ${a.toolName} | queued ${a.createdAt}\n Input: ${inputSummary}`;
3288
+ });
3289
+ return {
3290
+ content: [{ type: "text", text: `## Pending Actions (${actions.length})\n\n${lines.join("\n\n")}` }],
3291
+ };
3292
+ });
3293
+ eagerTool(server, "action-approve", "Approve a pending action and execute it immediately. The original tool call is " +
3294
+ "executed via the target plugin's MCP server. The result is returned and an audit " +
3295
+ "record is written to Neo4j with approvalState: approved.", {
3296
+ actionId: z.string().describe("The action ID to approve (from action-pending)"),
3297
+ conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
3298
+ }, async ({ actionId, conversationId }) => {
3299
+ const action = readPendingAction(actionId);
3300
+ if (!action) {
3301
+ return {
3302
+ content: [{ type: "text", text: `Action ${actionId} not found. It may have already been resolved or the ID is incorrect.` }],
3303
+ isError: true,
3304
+ };
3305
+ }
3306
+ if (action.state !== "pending") {
3307
+ return {
3308
+ content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot approve a resolved action.` }],
3309
+ isError: true,
3310
+ };
3311
+ }
3312
+ if (!action.pluginName) {
3313
+ return {
3314
+ content: [{ type: "text", text: `Action ${actionId} has no plugin name — cannot determine which MCP server to execute.` }],
3315
+ isError: true,
3316
+ };
3317
+ }
3318
+ // Mark as approved before execution
3319
+ if (!resolvePendingAction(actionId, "approved")) {
3320
+ return {
3321
+ content: [{ type: "text", text: `Failed to update action ${actionId} state. The file may have been modified concurrently.` }],
3322
+ isError: true,
3323
+ };
3324
+ }
3325
+ const toolInput = action.hookPayload?.tool_input ?? {};
3326
+ console.error(`[approval] Executing approved action: ${action.toolName} plugin=${action.pluginName} actionId=${actionId}`);
3327
+ try {
3328
+ const result = await dispatchApprovedAction(action.pluginName, action.toolName, toolInput);
3329
+ // Persist audit record
3330
+ await persistApprovalToolCall({
3331
+ toolName: action.toolName,
3332
+ pluginName: action.pluginName,
3333
+ input: JSON.stringify(toolInput),
3334
+ output: result.text,
3335
+ isError: result.isError,
3336
+ approvalState: "approved",
3337
+ conversationId,
3338
+ });
3339
+ cleanupPendingAction(actionId);
3340
+ return {
3341
+ content: [{ type: "text", text: `Action approved and executed.\n\nTool: ${action.toolName}\nResult:\n${result.text}` }],
3342
+ isError: result.isError,
3343
+ };
3344
+ }
3345
+ catch (err) {
3346
+ const errMsg = err instanceof Error ? err.message : String(err);
3347
+ console.error(`[approval] Execution failed for ${actionId}: ${errMsg}`);
3348
+ await persistApprovalToolCall({
3349
+ toolName: action.toolName,
3350
+ pluginName: action.pluginName,
3351
+ input: JSON.stringify(toolInput),
3352
+ output: errMsg,
3353
+ isError: true,
3354
+ approvalState: "approved",
3355
+ conversationId,
3356
+ });
3357
+ return {
3358
+ content: [{ type: "text", text: `Action approved but execution failed: ${errMsg}` }],
3359
+ isError: true,
3360
+ };
3361
+ }
3362
+ });
3363
+ eagerTool(server, "action-reject", "Reject a pending action. The action is not executed. An audit record is written " +
3364
+ "to Neo4j with approvalState: rejected.", {
3365
+ actionId: z.string().describe("The action ID to reject (from action-pending)"),
3366
+ reason: z.string().optional().describe("Optional reason for rejection"),
3367
+ conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
3368
+ }, async ({ actionId, reason, conversationId }) => {
3369
+ const action = readPendingAction(actionId);
3370
+ if (!action) {
3371
+ return {
3372
+ content: [{ type: "text", text: `Action ${actionId} not found. It may have already been resolved or the ID is incorrect.` }],
3373
+ isError: true,
3374
+ };
3375
+ }
3376
+ if (action.state !== "pending") {
3377
+ return {
3378
+ content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot reject a resolved action.` }],
3379
+ isError: true,
3380
+ };
3381
+ }
3382
+ if (!resolvePendingAction(actionId, "rejected")) {
3383
+ return {
3384
+ content: [{ type: "text", text: `Failed to update action ${actionId} state.` }],
3385
+ isError: true,
3386
+ };
3387
+ }
3388
+ const reasonSuffix = reason ? ` Reason: ${reason}` : "";
3389
+ console.error(`[approval] Action rejected: ${action.toolName} actionId=${actionId}${reasonSuffix}`);
3390
+ await persistApprovalToolCall({
3391
+ toolName: action.toolName,
3392
+ pluginName: action.pluginName ?? "unknown",
3393
+ input: JSON.stringify(action.hookPayload?.tool_input ?? {}),
3394
+ output: `Rejected by admin.${reasonSuffix}`,
3395
+ isError: false,
3396
+ approvalState: "rejected",
3397
+ conversationId,
3398
+ });
3399
+ cleanupPendingAction(actionId);
3400
+ return {
3401
+ content: [{ type: "text", text: `Action rejected. ${action.toolName} will not be executed.${reasonSuffix}` }],
3402
+ };
3403
+ });
3404
+ eagerTool(server, "action-edit", "Edit a pending action's input and then execute the modified version. The original " +
3405
+ "input is preserved in the audit trail. Use this when the admin wants to modify the " +
3406
+ "action (e.g., change the email subject) before approving.", {
3407
+ actionId: z.string().describe("The action ID to edit (from action-pending)"),
3408
+ editedInput: z.string().describe("The modified tool input as a JSON string"),
3409
+ conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
3410
+ }, async ({ actionId, editedInput, conversationId }) => {
3411
+ const action = readPendingAction(actionId);
3412
+ if (!action) {
3413
+ return {
3414
+ content: [{ type: "text", text: `Action ${actionId} not found.` }],
3415
+ isError: true,
3416
+ };
3417
+ }
3418
+ if (action.state !== "pending") {
3419
+ return {
3420
+ content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot edit a resolved action.` }],
3421
+ isError: true,
3422
+ };
3423
+ }
3424
+ if (!action.pluginName) {
3425
+ return {
3426
+ content: [{ type: "text", text: `Action ${actionId} has no plugin name — cannot determine which MCP server to execute.` }],
3427
+ isError: true,
3428
+ };
3429
+ }
3430
+ let parsedInput;
3431
+ try {
3432
+ parsedInput = JSON.parse(editedInput);
3433
+ }
3434
+ catch {
3435
+ return {
3436
+ content: [{ type: "text", text: `Invalid JSON in editedInput. Please provide valid JSON for the tool input.` }],
3437
+ isError: true,
3438
+ };
3439
+ }
3440
+ const originalInput = action.hookPayload?.tool_input ?? {};
3441
+ // Mark as approved with the edited input
3442
+ if (!resolvePendingAction(actionId, "approved", parsedInput)) {
3443
+ return {
3444
+ content: [{ type: "text", text: `Failed to update action ${actionId} state.` }],
3445
+ isError: true,
3446
+ };
3447
+ }
3448
+ console.error(`[approval] Executing edited action: ${action.toolName} plugin=${action.pluginName} actionId=${actionId}`);
3449
+ try {
3450
+ const result = await dispatchApprovedAction(action.pluginName, action.toolName, parsedInput);
3451
+ await persistApprovalToolCall({
3452
+ toolName: action.toolName,
3453
+ pluginName: action.pluginName,
3454
+ input: JSON.stringify(parsedInput),
3455
+ output: result.text,
3456
+ isError: result.isError,
3457
+ approvalState: "approved",
3458
+ originalInput: JSON.stringify(originalInput),
3459
+ conversationId,
3460
+ });
3461
+ cleanupPendingAction(actionId);
3462
+ return {
3463
+ content: [{ type: "text", text: `Action edited, approved, and executed.\n\nTool: ${action.toolName}\nResult:\n${result.text}` }],
3464
+ isError: result.isError,
3465
+ };
3466
+ }
3467
+ catch (err) {
3468
+ const errMsg = err instanceof Error ? err.message : String(err);
3469
+ console.error(`[approval] Edited execution failed for ${actionId}: ${errMsg}`);
3470
+ await persistApprovalToolCall({
3471
+ toolName: action.toolName,
3472
+ pluginName: action.pluginName,
3473
+ input: JSON.stringify(parsedInput),
3474
+ output: errMsg,
3475
+ isError: true,
3476
+ approvalState: "approved",
3477
+ originalInput: JSON.stringify(originalInput),
3478
+ conversationId,
3479
+ });
3480
+ return {
3481
+ content: [{ type: "text", text: `Action edited and approved but execution failed: ${errMsg}` }],
3482
+ isError: true,
3483
+ };
3484
+ }
3485
+ });
3486
+ // Cleanup on exit (SIGTERM for systemd service stops on Pi)
3487
+ const cleanup = async () => {
3488
+ await closeDriver();
3489
+ process.exit(0);
3490
+ };
3491
+ process.on("SIGINT", cleanup);
3492
+ process.on("SIGTERM", cleanup);
3493
+ const transport = new StdioServerTransport();
3494
+ await server.connect(transport);
3495
+ //# sourceMappingURL=index.js.map