@rubytech/create-realagent-code 0.1.254 → 0.1.256

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 (340) hide show
  1. package/dist/__tests__/plugin-install.test.js +58 -40
  2. package/dist/index.js +77 -26
  3. package/dist/lib/plugin-install.js +31 -29
  4. package/package.json +1 -1
  5. package/payload/platform/.docs/search-surface-contract.md +58 -0
  6. package/payload/platform/config/brand-registry.json +8 -0
  7. package/payload/platform/config/brand.json +2 -2
  8. package/payload/platform/lib/embed-client/dist/index.d.ts +4 -0
  9. package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -0
  10. package/payload/platform/lib/embed-client/dist/index.js +53 -0
  11. package/payload/platform/lib/embed-client/dist/index.js.map +1 -0
  12. package/payload/platform/lib/embed-client/src/index.ts +53 -0
  13. package/payload/platform/lib/embed-client/tsconfig.json +8 -0
  14. package/payload/platform/lib/graph-search/dist/index.d.ts +27 -6
  15. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  16. package/payload/platform/lib/graph-search/dist/index.js +19 -1
  17. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  18. package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +12 -0
  19. package/payload/platform/lib/graph-search/src/index.ts +28 -6
  20. package/payload/platform/lib/graph-write/dist/index.d.ts +25 -0
  21. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  22. package/payload/platform/lib/graph-write/dist/index.js +80 -2
  23. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  24. package/payload/platform/lib/graph-write/src/index.ts +98 -1
  25. package/payload/platform/neo4j/schema.cypher +126 -0
  26. package/payload/platform/package.json +2 -2
  27. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  28. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  29. package/payload/platform/plugins/admin/PLUGIN.md +3 -6
  30. package/payload/platform/plugins/admin/mcp/dist/index.js +14 -60
  31. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  32. package/payload/platform/plugins/admin/skills/insight/SKILL.md +24 -0
  33. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +84 -31
  34. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  35. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  36. package/payload/platform/plugins/docs/references/deployment.md +18 -5
  37. package/payload/platform/plugins/docs/references/graph.md +2 -0
  38. package/payload/platform/plugins/docs/references/internals.md +12 -1
  39. package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
  40. package/payload/platform/plugins/docs/references/neo4j.md +2 -2
  41. package/payload/platform/plugins/docs/references/platform.md +1 -1
  42. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  43. package/payload/platform/plugins/docs/references/session-retrospective.md +5 -18
  44. package/payload/platform/plugins/docs/references/slides.md +31 -0
  45. package/payload/platform/plugins/docs/references/voice-mirror-guide.md +1 -1
  46. package/payload/platform/plugins/email/PLUGIN.md +2 -2
  47. package/payload/platform/plugins/email/mcp/dist/index.js +8 -0
  48. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  49. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts +19 -0
  50. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts.map +1 -0
  51. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js +64 -0
  52. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js.map +1 -0
  53. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +4 -0
  54. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
  55. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +1 -0
  56. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
  57. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +1 -0
  58. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
  59. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +6 -1
  60. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
  61. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +1 -0
  62. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
  63. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +7 -1
  64. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
  65. package/payload/platform/plugins/email/references/email-reference.md +4 -0
  66. package/payload/platform/plugins/memory/PLUGIN.md +1 -2
  67. package/payload/platform/plugins/memory/mcp/dist/index.js +6 -44
  68. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts +2 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts.map +1 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js +41 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts +2 -0
  74. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts.map +1 -0
  75. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js +90 -0
  76. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js.map +1 -0
  77. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts +2 -0
  78. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts.map +1 -0
  79. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js +27 -0
  80. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js.map +1 -0
  81. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts +10 -0
  82. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts.map +1 -0
  83. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js +47 -0
  84. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js.map +1 -0
  85. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +1 -2
  86. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -1
  87. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +5 -28
  88. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -1
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts +2 -0
  90. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts.map +1 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js +20 -0
  92. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js.map +1 -0
  93. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts +2 -0
  94. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts.map +1 -0
  95. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js +67 -0
  96. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js.map +1 -0
  97. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts +2 -0
  98. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts.map +1 -0
  99. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js +34 -0
  100. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js.map +1 -0
  101. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts +2 -0
  102. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts.map +1 -0
  103. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js +61 -0
  104. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js.map +1 -0
  105. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +23 -1
  106. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts +2 -0
  108. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts.map +1 -0
  109. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js +87 -0
  110. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js.map +1 -0
  111. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +3 -0
  112. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
  113. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts +2 -0
  114. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts.map +1 -0
  115. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js +34 -0
  116. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js.map +1 -0
  117. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
  118. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +19 -4
  119. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
  120. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +33 -6
  121. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  122. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +280 -10
  123. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  124. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -1
  125. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +76 -37
  126. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -1
  127. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
  128. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +11 -2
  129. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
  130. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +4 -4
  131. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +3 -3
  132. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  133. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +20 -2
  134. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  135. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +6 -3
  136. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  137. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
  138. package/payload/platform/plugins/memory/references/schema-base.md +1 -1
  139. package/payload/platform/plugins/memory/references/schema-construction.md +72 -0
  140. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts.map +1 -1
  141. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js +15 -0
  142. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js.map +1 -1
  143. package/payload/platform/plugins/slides/.claude-plugin/plugin.json +8 -0
  144. package/payload/platform/plugins/slides/LICENSE +21 -0
  145. package/payload/platform/plugins/slides/PLUGIN.md +18 -0
  146. package/payload/platform/plugins/slides/PROVENANCE.md +40 -0
  147. package/payload/platform/plugins/slides/commands/add-slide.md +29 -0
  148. package/payload/platform/plugins/slides/commands/slides-claus.md +39 -0
  149. package/payload/platform/plugins/slides/commands/slides-new-component.md +39 -0
  150. package/payload/platform/plugins/slides/commands/slides-outline.md +43 -0
  151. package/payload/platform/plugins/slides/commands/slides-review.md +52 -0
  152. package/payload/platform/plugins/slides/commands/slides-theme.md +64 -0
  153. package/payload/platform/plugins/slides/commands/slides.md +59 -0
  154. package/payload/platform/plugins/slides/skills/deck-system/REFERENCE.md +581 -0
  155. package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +607 -0
  156. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-board.md +426 -0
  157. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-claus.md +185 -0
  158. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-mbb.md +450 -0
  159. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-product-launch.md +579 -0
  160. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sales.md +464 -0
  161. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sequoia.md +489 -0
  162. package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING.md +273 -0
  163. package/payload/platform/plugins/slides/skills/deck-system/deck-craft.html +1371 -0
  164. package/payload/platform/plugins/slides/skills/deck-system/deck-solid.html +1667 -0
  165. package/payload/platform/plugins/slides/skills/deck-system/deck.html +1359 -0
  166. package/payload/platform/plugins/url-get/.claude-plugin/plugin.json +1 -1
  167. package/payload/platform/plugins/url-get/PLUGIN.md +26 -21
  168. package/payload/platform/plugins/url-get/mcp/dist/index.js +3 -3
  169. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  170. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts +1 -2
  171. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts.map +1 -1
  172. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js +20 -40
  173. package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js.map +1 -1
  174. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +4 -0
  175. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  176. package/payload/platform/scripts/identity-forbidden-token-check.mjs +0 -1
  177. package/payload/platform/scripts/setup-account.sh +0 -15
  178. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +23 -0
  179. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -0
  180. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +29 -0
  181. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -0
  182. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  183. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -2
  184. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  185. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +5 -0
  186. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  187. package/payload/platform/services/claude-session-manager/dist/http-server.js +32 -2
  188. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  189. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  190. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -1
  191. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  192. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +13 -1
  193. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  194. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +26 -2
  195. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  196. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +2 -2
  197. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  198. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +12 -3
  199. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  200. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +3 -2
  201. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  202. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
  203. package/payload/platform/templates/specialists/agents/database-operator.md +3 -7
  204. package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +1 -1
  205. package/payload/server/{chunk-M6A6EZD4.js → chunk-76HRO7NX.js} +16 -2
  206. package/payload/server/maxy-edge.js +1 -1
  207. package/payload/server/public/assets/AdminShell-T-YknnBn.js +1 -0
  208. package/payload/server/public/assets/Checkbox-DmDxpqVv.js +1 -0
  209. package/payload/server/public/assets/admin-COUV-jgt.js +1 -0
  210. package/payload/server/public/assets/{arc-aUiRP9AS.js → arc-B2CweJq3.js} +1 -1
  211. package/payload/server/public/assets/architecture-YZFGNWBL-Dnn6Hc65.js +1 -0
  212. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-c09loTER.js → architectureDiagram-Q4EWVU46-DP2o-MFV.js} +1 -1
  213. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-Cjdeyoq1.js → blockDiagram-DXYQGD6D-DO4mcYDJ.js} +1 -1
  214. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-NY6Wlzo2.js → c4Diagram-AHTNJAMY-Sy1giHbj.js} +1 -1
  215. package/payload/server/public/assets/channel-CEpR_0rE.js +1 -0
  216. package/payload/server/public/assets/{chunk-2KRD3SAO-BK3470lx.js → chunk-2KRD3SAO-CKsCYCsN.js} +1 -1
  217. package/payload/server/public/assets/chunk-336JU56O-C0-P-aUF.js +2 -0
  218. package/payload/server/public/assets/chunk-426QAEUC-DFjEt3Zb.js +1 -0
  219. package/payload/server/public/assets/{chunk-4BX2VUAB-BOvVdJLf.js → chunk-4BX2VUAB-B8bqAmBa.js} +1 -1
  220. package/payload/server/public/assets/{chunk-4TB4RGXK-BXpto3yW.js → chunk-4TB4RGXK-D1k0VSlW.js} +1 -1
  221. package/payload/server/public/assets/{chunk-55IACEB6-BwZyF7vR.js → chunk-55IACEB6-B-p_QNqz.js} +1 -1
  222. package/payload/server/public/assets/{chunk-5FUZZQ4R-C403gCUk.js → chunk-5FUZZQ4R-D6U6tV_j.js} +1 -1
  223. package/payload/server/public/assets/{chunk-5PVQY5BW-CjVzXQEp.js → chunk-5PVQY5BW-CYK76xfs.js} +1 -1
  224. package/payload/server/public/assets/{chunk-67CJDMHE-D5bhMrtY.js → chunk-67CJDMHE-BC9js-lf.js} +1 -1
  225. package/payload/server/public/assets/{chunk-7N4EOEYR-Si7Lgrwc.js → chunk-7N4EOEYR-4j2OqKkv.js} +1 -1
  226. package/payload/server/public/assets/{chunk-AA7GKIK3-DMuHtDqO.js → chunk-AA7GKIK3-Coen-fXN.js} +1 -1
  227. package/payload/server/public/assets/{chunk-BSJP7CBP-L79XKVcb.js → chunk-BSJP7CBP-CAiOBvec.js} +1 -1
  228. package/payload/server/public/assets/{chunk-CIAEETIT-C0O7Upmg.js → chunk-CIAEETIT-AJzzpZVb.js} +1 -1
  229. package/payload/server/public/assets/{chunk-EDXVE4YY-DJcJAsAg.js → chunk-EDXVE4YY-BL4BKozX.js} +1 -1
  230. package/payload/server/public/assets/{chunk-ENJZ2VHE-CFDNvYu1.js → chunk-ENJZ2VHE-mhAFG8UD.js} +1 -1
  231. package/payload/server/public/assets/{chunk-FMBD7UC4-C_E43NFJ.js → chunk-FMBD7UC4-H231gZA_.js} +1 -1
  232. package/payload/server/public/assets/{chunk-FOC6F5B3-D9lWWHAu.js → chunk-FOC6F5B3-Cl3ZZjYG.js} +1 -1
  233. package/payload/server/public/assets/{chunk-ICPOFSXX-ecLOxGhL.js → chunk-ICPOFSXX-DOEzvzJa.js} +2 -2
  234. package/payload/server/public/assets/{chunk-K5T4RW27-DuhsNH4c.js → chunk-K5T4RW27-C_ipbUDD.js} +1 -1
  235. package/payload/server/public/assets/{chunk-KGLVRYIC-B4-A1Abi.js → chunk-KGLVRYIC-CTsDNSCU.js} +1 -1
  236. package/payload/server/public/assets/{chunk-LIHQZDEY-BxqgHRgT.js → chunk-LIHQZDEY-DvSXhkGf.js} +1 -1
  237. package/payload/server/public/assets/{chunk-ORNJ4GCN-DEYQ5WaJ.js → chunk-ORNJ4GCN-p574NOI7.js} +1 -1
  238. package/payload/server/public/assets/{chunk-OYMX7WX6-B7MW66KB.js → chunk-OYMX7WX6-BlEgFM6U.js} +1 -1
  239. package/payload/server/public/assets/chunk-QZHKN3VN-DpF06ZZQ.js +1 -0
  240. package/payload/server/public/assets/{chunk-U2HBQHQK-BMawmsyk.js → chunk-U2HBQHQK-B2bDK0jv.js} +1 -1
  241. package/payload/server/public/assets/{chunk-X2U36JSP-CT6g7pno.js → chunk-X2U36JSP-D69BxKFw.js} +1 -1
  242. package/payload/server/public/assets/{chunk-XPW4576I-CBfZXZDB.js → chunk-XPW4576I-Dm-PcyUi.js} +1 -1
  243. package/payload/server/public/assets/{chunk-YZCP3GAM-xeAluiAH.js → chunk-YZCP3GAM-Be8RnXgx.js} +1 -1
  244. package/payload/server/public/assets/{chunk-ZZ45TVLE-BRN9qUC5.js → chunk-ZZ45TVLE-Ck8PCTa4.js} +1 -1
  245. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CYbXvKLI.js +1 -0
  246. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DEyHzRhq.js +1 -0
  247. package/payload/server/public/assets/clone-y8gexbBy.js +1 -0
  248. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Br2gjtEO.js → cose-bilkent-S5V4N54A-CmkW2Eaj.js} +1 -1
  249. package/payload/server/public/assets/{dagre-DTjePoco.js → dagre-Dqp-ns8F.js} +1 -1
  250. package/payload/server/public/assets/{dagre-KV5264BT-DHBkRke4.js → dagre-KV5264BT-ZgWWXPLc.js} +1 -1
  251. package/payload/server/public/assets/data-gy6QH9c1.js +1 -0
  252. package/payload/server/public/assets/{diagram-5BDNPKRD-BIq1-idL.js → diagram-5BDNPKRD-CTX5-ScM.js} +1 -1
  253. package/payload/server/public/assets/{diagram-G4DWMVQ6-BsIUDzV6.js → diagram-G4DWMVQ6-BovIsO6H.js} +1 -1
  254. package/payload/server/public/assets/{diagram-MMDJMWI5-CgHSri2i.js → diagram-MMDJMWI5-DcETsQy-.js} +1 -1
  255. package/payload/server/public/assets/{diagram-TYMM5635-Ce2Wh9ZX.js → diagram-TYMM5635-yyq6peoZ.js} +1 -1
  256. package/payload/server/public/assets/{erDiagram-SMLLAGMA-BU0Kh6OQ.js → erDiagram-SMLLAGMA-CiNToftB.js} +1 -1
  257. package/payload/server/public/assets/{flatten-Bo6YRmWl.js → flatten-BtFI066E.js} +1 -1
  258. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-B0N06MF7.js → flowDiagram-DWJPFMVM-Xnl3SpIM.js} +1 -1
  259. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BVbx4ARZ.js → ganttDiagram-T4ZO3ILL-C1iyWe0f.js} +1 -1
  260. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CNs-LD5i.js +1 -0
  261. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-C-xRJ94t.js → gitGraphDiagram-UUTBAWPF-D97pbMQb.js} +1 -1
  262. package/payload/server/public/assets/graph-labels-cZu4pK16.js +1 -0
  263. package/payload/server/public/assets/{graph-g48ZcA5M.js → graph-qz5tFKqU.js} +3 -3
  264. package/payload/server/public/assets/{graphlib-YmNcoMjY.js → graphlib-Lq8ijgON.js} +1 -1
  265. package/payload/server/public/assets/info-OMHHGYJF-DsTNigSS.js +1 -0
  266. package/payload/server/public/assets/infoDiagram-42DDH7IO-C_OarRTA.js +2 -0
  267. package/payload/server/public/assets/{isEmpty-D6Kr-M1M.js → isEmpty-D6QovjYR.js} +1 -1
  268. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DTrq54yC.js → ishikawaDiagram-UXIWVN3A-B8XBdjJn.js} +1 -1
  269. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-OZZZMrFX.js → journeyDiagram-VCZTEJTY-CZYbiOaQ.js} +1 -1
  270. package/payload/server/public/assets/{kanban-definition-6JOO6SKY--w-IP9pN.js → kanban-definition-6JOO6SKY-B1PybFoh.js} +1 -1
  271. package/payload/server/public/assets/{line-Ckeulv5T.js → line-D-tw3hHp.js} +1 -1
  272. package/payload/server/public/assets/{linear-DOh_6k2k.js → linear-BHhXD3cd.js} +1 -1
  273. package/payload/server/public/assets/{mermaid-parser.core-CVRAxYRD.js → mermaid-parser.core-C9RAnysF.js} +2 -2
  274. package/payload/server/public/assets/{mermaid.core-B-mE18I1.js → mermaid.core-B532LT1r.js} +3 -3
  275. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Bm8mDicL.js → mindmap-definition-QFDTVHPH-DGlgeeTV.js} +1 -1
  276. package/payload/server/public/assets/{ordinal-BDi6f4xk.js → ordinal-Bl-aM5b9.js} +1 -1
  277. package/payload/server/public/assets/packet-4T2RLAQJ-DGES22b-.js +1 -0
  278. package/payload/server/public/assets/pie-ZZUOXDRM-ChKeDbzt.js +1 -0
  279. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BCmRLgGO.js → pieDiagram-DEJITSTG-DV9FIWko.js} +1 -1
  280. package/payload/server/public/assets/{public-DknO-g9S.js → public-Bu2_Xi0a.js} +5 -5
  281. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CniTIUTm.js → quadrantDiagram-34T5L4WZ-Betwya4l.js} +1 -1
  282. package/payload/server/public/assets/radar-PYXPWWZC-FGG5Fs7N.js +1 -0
  283. package/payload/server/public/assets/{reduce-CGi9ik8i.js → reduce-BD4xUd2c.js} +1 -1
  284. package/payload/server/public/assets/{requirementDiagram-MS252O5E-CoxBSj9M.js → requirementDiagram-MS252O5E-Cq3vODdg.js} +1 -1
  285. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-BjS-4jzq.js → sankeyDiagram-XADWPNL6-x8krXWcS.js} +1 -1
  286. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B9jVOnPR.js → sequenceDiagram-FGHM5R23-i-_uH-Yl.js} +1 -1
  287. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-BvOQPzP8.js → stateDiagram-FHFEXIEX-il4KqSgI.js} +1 -1
  288. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B6zNJ6Tv.js +1 -0
  289. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CdfgWLo1.js → timeline-definition-GMOUNBTQ-DATdZkA5.js} +1 -1
  290. package/payload/server/public/assets/treeView-SZITEDCU-VAQQdbtf.js +1 -0
  291. package/payload/server/public/assets/treemap-W4RFUUIX-DKchO3zI.js +1 -0
  292. package/payload/server/public/assets/useSelectionMode-A5KItZ2T.js +13 -0
  293. package/payload/server/public/assets/{brand-D0gNihp7.css → useSelectionMode-C-Ojh7W9.css} +1 -1
  294. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-JCgpIbj-.js → vennDiagram-DHZGUBPP-BJh9tJTt.js} +1 -1
  295. package/payload/server/public/assets/wardley-RL74JXVD-CBGtx0bS.js +1 -0
  296. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Dei3VqHo.js → wardleyDiagram-NUSXRM2D-EMN1Hdfg.js} +1 -1
  297. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DUtIyoIb.js → xychartDiagram-5P7HB3ND-DbUWXa7T.js} +1 -1
  298. package/payload/server/public/data.html +5 -5
  299. package/payload/server/public/graph.html +6 -6
  300. package/payload/server/public/index.html +5 -6
  301. package/payload/server/public/public.html +4 -5
  302. package/payload/server/server.js +525 -39
  303. package/payload/platform/plugins/admin/hooks/__tests__/session-end-retrospective.test.sh +0 -396
  304. package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +0 -239
  305. package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +0 -214
  306. package/payload/server/public/assets/AdminShell-892Jy_rs.js +0 -1
  307. package/payload/server/public/assets/Checkbox-Bc2QzX9b.js +0 -1
  308. package/payload/server/public/assets/admin-D3K13ndi.js +0 -1
  309. package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +0 -1
  310. package/payload/server/public/assets/brand-CcN3dELF.js +0 -9
  311. package/payload/server/public/assets/channel-B1IT7to2.js +0 -1
  312. package/payload/server/public/assets/chunk-336JU56O-CdKRCIeE.js +0 -2
  313. package/payload/server/public/assets/chunk-426QAEUC-BybuQ3Ve.js +0 -1
  314. package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +0 -1
  315. package/payload/server/public/assets/classDiagram-6PBFFD2Q-rjCize6i.js +0 -1
  316. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BORWOUt0.js +0 -1
  317. package/payload/server/public/assets/clone-Csqv5U6T.js +0 -1
  318. package/payload/server/public/assets/data-Br-pdljK.js +0 -1
  319. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +0 -1
  320. package/payload/server/public/assets/graph-labels-BYH-IPCb.js +0 -1
  321. package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +0 -1
  322. package/payload/server/public/assets/infoDiagram-42DDH7IO-Di6oPQ_-.js +0 -2
  323. package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +0 -1
  324. package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +0 -1
  325. package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +0 -1
  326. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-v4ND10uR.js +0 -1
  327. package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +0 -1
  328. package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +0 -1
  329. package/payload/server/public/assets/useSelectionMode-DwsyptOw.js +0 -5
  330. package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +0 -1
  331. /package/payload/server/public/assets/{_baseFor-Cam2PbSt.js → _baseFor-Cs8Y-rGh.js} +0 -0
  332. /package/payload/server/public/assets/{array-DYRGGQae.js → array-iHZP4KWJ.js} +0 -0
  333. /package/payload/server/public/assets/{cytoscape.esm-nWsJMTNI.js → cytoscape.esm-BR2GOQ8_.js} +0 -0
  334. /package/payload/server/public/assets/{defaultLocale-Du1XY3Dp.js → defaultLocale-B9aLeOTg.js} +0 -0
  335. /package/payload/server/public/assets/{dist-BzAsli7o.js → dist-DB-VPj_8.js} +0 -0
  336. /package/payload/server/public/assets/{init-B5BXBRcm.js → init-BNFRgqHM.js} +0 -0
  337. /package/payload/server/public/assets/{katex-HOUACuRw.js → katex-B-EfS3nw.js} +0 -0
  338. /package/payload/server/public/assets/{path-CNO468J-.js → path-DmWWdwp7.js} +0 -0
  339. /package/payload/server/public/assets/{rough.esm-DRO6hWPh.js → rough.esm-Ci7Kjt46.js} +0 -0
  340. /package/payload/server/public/assets/{src-CWiyyVfn.js → src-C1jfwBq0.js} +0 -0
@@ -4,7 +4,7 @@
4
4
  // permanent verification artifact.
5
5
  import test from "node:test";
6
6
  import assert from "node:assert/strict";
7
- import { parsePluginList, computeInstallActions, computeConfigureActions, parseExternalPlugins, } from "../lib/plugin-install.js";
7
+ import { parsePluginList, computeInstallActions, parseExternalPlugins, } from "../lib/plugin-install.js";
8
8
  test("parsePluginList extracts name@marketplace tuples", () => {
9
9
  const stdout = `
10
10
  ❯ work@maxy-platform
@@ -24,63 +24,81 @@ test("parsePluginList returns empty array for empty stdout", () => {
24
24
  assert.deepEqual(parsePluginList(""), []);
25
25
  assert.deepEqual(parsePluginList("No plugins installed.\n"), []);
26
26
  });
27
+ // `dirSrc` is the directory-source-marketplace discriminator passed as the
28
+ // third argument. maxy-platform is directory-source; claude-plugins-official
29
+ // is a remote (GitHub) marketplace that keeps version-pinned idempotence.
30
+ const dirSrc = new Set(["maxy-platform"]);
27
31
  test("computeInstallActions: every desired plugin needs installing when none present", () => {
28
32
  const desired = [
29
33
  { name: "admin", marketplace: "maxy-platform" },
30
34
  { name: "telegram", marketplace: "claude-plugins-official" },
31
35
  ];
32
- const { toInstall, alreadyInstalled } = computeInstallActions(desired, []);
36
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, [], dirSrc);
33
37
  assert.equal(toInstall.length, 2);
38
+ assert.equal(toResync.length, 0);
34
39
  assert.equal(alreadyInstalled.length, 0);
35
40
  });
36
- test("computeInstallActions: idempotent skip for exact name@marketplace match", () => {
41
+ test("computeInstallActions: directory-source plugin already installed goes to resync, not skip", () => {
37
42
  const desired = [
38
43
  { name: "admin", marketplace: "maxy-platform" },
39
44
  { name: "telegram", marketplace: "claude-plugins-official" },
40
45
  ];
41
46
  const installed = [
42
47
  { name: "admin", marketplace: "maxy-platform" },
48
+ { name: "telegram", marketplace: "claude-plugins-official" },
43
49
  ];
44
- const { toInstall, alreadyInstalled } = computeInstallActions(desired, installed);
45
- assert.deepEqual(toInstall.map(p => p.name), ["telegram"]);
46
- assert.deepEqual(alreadyInstalled.map(p => p.name), ["admin"]);
50
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, dirSrc);
51
+ // admin's directory source changes in place with no version bump, so it must
52
+ // be re-synced even though it is already in `plugin list`. The remote
53
+ // telegram stays idempotent-skipped.
54
+ assert.deepEqual(toInstall.map(p => p.name), []);
55
+ assert.deepEqual(toResync.map(p => p.name), ["admin"]);
56
+ assert.deepEqual(alreadyInstalled.map(p => p.name), ["telegram"]);
47
57
  });
48
- test("computeInstallActions: name-only match skips install (legacy install state)", () => {
49
- const desired = [{ name: "admin", marketplace: "maxy-platform" }];
50
- const installed = [{ name: "admin", marketplace: "" }];
51
- const { toInstall, alreadyInstalled } = computeInstallActions(desired, installed);
58
+ test("computeInstallActions: full directory-marketplace set resyncs when every plugin already installed", () => {
59
+ const desired = [
60
+ { name: "admin", marketplace: "maxy-platform" },
61
+ { name: "work", marketplace: "maxy-platform" },
62
+ ];
63
+ const installed = [
64
+ { name: "admin", marketplace: "maxy-platform" },
65
+ { name: "work", marketplace: "maxy-platform" },
66
+ ];
67
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, dirSrc);
68
+ assert.deepEqual(toResync.map(p => p.name), ["admin", "work"]);
52
69
  assert.equal(toInstall.length, 0);
53
- assert.equal(alreadyInstalled.length, 1);
70
+ assert.equal(alreadyInstalled.length, 0);
54
71
  });
55
- test("computeConfigureActions: skip when no configureSecret declared", () => {
56
- const installed = [{ name: "admin", marketplace: "maxy-platform" }];
57
- const actions = computeConfigureActions(installed, {});
58
- assert.equal(actions.length, 1);
59
- assert.equal(actions[0].kind, "skip-no-secret-name");
72
+ test("computeInstallActions: remote plugin already installed stays idempotent-skipped", () => {
73
+ const desired = [{ name: "telegram", marketplace: "claude-plugins-official" }];
74
+ const installed = [{ name: "telegram", marketplace: "claude-plugins-official" }];
75
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, dirSrc);
76
+ assert.equal(toInstall.length, 0);
77
+ assert.equal(toResync.length, 0);
78
+ assert.deepEqual(alreadyInstalled.map(p => p.name), ["telegram"]);
60
79
  });
61
- test("computeConfigureActions: skip when env var absent", () => {
62
- const installed = [{
63
- name: "telegram",
64
- marketplace: "claude-plugins-official",
65
- configureSecret: "TELEGRAM_BOT_TOKEN",
66
- }];
67
- const actions = computeConfigureActions(installed, {});
68
- assert.equal(actions.length, 1);
69
- assert.equal(actions[0].kind, "skip-no-secret-value");
80
+ test("computeInstallActions: directory-source plugin not yet installed goes to install (fresh build is clean)", () => {
81
+ const desired = [{ name: "admin", marketplace: "maxy-platform" }];
82
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, [], dirSrc);
83
+ assert.deepEqual(toInstall.map(p => p.name), ["admin"]);
84
+ assert.equal(toResync.length, 0);
85
+ assert.equal(alreadyInstalled.length, 0);
70
86
  });
71
- test("computeConfigureActions: configure when env var present", () => {
72
- const installed = [{
73
- name: "telegram",
74
- marketplace: "claude-plugins-official",
75
- configureSecret: "TELEGRAM_BOT_TOKEN",
76
- }];
77
- const actions = computeConfigureActions(installed, { TELEGRAM_BOT_TOKEN: "abc123" });
78
- assert.equal(actions.length, 1);
79
- assert.equal(actions[0].kind, "configure");
80
- if (actions[0].kind === "configure") {
81
- assert.equal(actions[0].secretValue, "abc123");
82
- assert.equal(actions[0].plugin.name, "telegram");
83
- }
87
+ test("computeInstallActions: name-only match on a remote marketplace skips install (legacy install state)", () => {
88
+ const desired = [{ name: "telegram", marketplace: "claude-plugins-official" }];
89
+ const installed = [{ name: "telegram", marketplace: "" }];
90
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, dirSrc);
91
+ assert.equal(toInstall.length, 0);
92
+ assert.equal(toResync.length, 0);
93
+ assert.equal(alreadyInstalled.length, 1);
94
+ });
95
+ test("computeInstallActions: name-only legacy match on a directory marketplace still resyncs", () => {
96
+ const desired = [{ name: "admin", marketplace: "maxy-platform" }];
97
+ const installed = [{ name: "admin", marketplace: "" }];
98
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, dirSrc);
99
+ assert.equal(toInstall.length, 0);
100
+ assert.deepEqual(toResync.map(p => p.name), ["admin"]);
101
+ assert.equal(alreadyInstalled.length, 0);
84
102
  });
85
103
  test("parseExternalPlugins: undefined returns []", () => {
86
104
  assert.deepEqual(parseExternalPlugins(undefined), []);
@@ -88,12 +106,12 @@ test("parseExternalPlugins: undefined returns []", () => {
88
106
  });
89
107
  test("parseExternalPlugins: well-formed array round-trips", () => {
90
108
  const raw = [
91
- { name: "telegram", marketplace: "claude-plugins-official", configureSecret: "TELEGRAM_BOT_TOKEN", channelPlugin: true },
109
+ { name: "telegram", marketplace: "claude-plugins-official", channelPlugin: true },
92
110
  { name: "discord", marketplace: "claude-plugins-official" },
93
111
  ];
94
112
  const out = parseExternalPlugins(raw);
95
113
  assert.deepEqual(out, [
96
- { name: "telegram", marketplace: "claude-plugins-official", configureSecret: "TELEGRAM_BOT_TOKEN", channelPlugin: true },
114
+ { name: "telegram", marketplace: "claude-plugins-official", channelPlugin: true },
97
115
  { name: "discord", marketplace: "claude-plugins-official" },
98
116
  ]);
99
117
  });
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import { installAllBrewPackages } from "./brew-install.js";
16
16
  import { parseSwVers, isSupportedMacosVersion } from "./macos-version.js";
17
17
  import { decideChromiumAction, isSnapConfinedPath } from "./snap-chromium.js";
18
18
  import { classifyPortHolder } from "./preflight-port-classifier.js";
19
- import { parsePluginList, computeInstallActions, computeConfigureActions, parseExternalPlugins, } from "./lib/plugin-install.js";
19
+ import { parsePluginList, computeInstallActions, parseExternalPlugins, } from "./lib/plugin-install.js";
20
20
  import { findPremiumMcpDirs } from "./lib/premium-mcp-discover.js";
21
21
  import { pickLanInterface, mergeSmbConf, formatSambaMarker, } from "./samba-provision.js";
22
22
  import { networkInterfaces, userInfo } from "node:os";
@@ -1997,15 +1997,45 @@ function registerLocalAndExternalPlugins() {
1997
1997
  logFile(`[plugin-install] ERROR brand.externalPlugins parse error=${JSON.stringify(msg.slice(0, 200))}`);
1998
1998
  }
1999
1999
  desired.push(...externals);
2000
+ // The directory-source marketplaces are exactly the local trees discovered
2001
+ // from on-disk marketplace.json above. Remote marketplaces (the Anthropic
2002
+ // ones, GitHub externals) are never in this set, so they keep version-pinned
2003
+ // idempotence. (Task 643.)
2004
+ const directorySourceMarketplaces = new Set(localTrees.map(t => t.name));
2000
2005
  // Snapshot what's installed to compute the install set.
2001
2006
  const pluginList = spawnSync("claude", ["plugin", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
2002
2007
  const installed = parsePluginList(pluginList.stdout ?? "");
2003
- const { toInstall, alreadyInstalled } = computeInstallActions(desired, installed);
2008
+ const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, directorySourceMarketplaces);
2004
2009
  for (const ref of alreadyInstalled) {
2005
2010
  logFile(`[plugin-install] ${ref.name}@${ref.marketplace} idempotent=true`);
2006
2011
  }
2012
+ // Single source of truth for the install invocation, shared by the resync
2013
+ // and fresh-install loops so a future flag/timeout/scope change cannot drift
2014
+ // between them.
2015
+ const pluginInstall = (ref) => spawnSync("claude", ["plugin", "install", `${ref.name}@${ref.marketplace}`, "--scope", "user"], { stdio: "pipe", encoding: "utf-8", timeout: 120_000, env: claudePluginEnv() });
2016
+ // Resync directory-source plugins (Task 643). A directory marketplace
2017
+ // overwrites its tree in place on upgrade with no version bump, so the
2018
+ // runtime cache snapshot the session loads from (installed_plugins.json
2019
+ // `installPath`) freezes at first-install time — `plugin install` and
2020
+ // `plugin update` both short-circuit on the pinned version and never
2021
+ // re-copy. Uninstall then install is the only sequence that rebuilds the
2022
+ // snapshot from the live tree; `install` reads the directory source live.
2023
+ // The uninstall+install pair is not atomic: a failed reinstall leaves the
2024
+ // plugin deregistered, surfaced by the ERROR line below — hardening tracked
2025
+ // in Task 645.
2026
+ for (const ref of toResync) {
2027
+ spawnSync("claude", ["plugin", "uninstall", `${ref.name}@${ref.marketplace}`, "--scope", "user"], { stdio: "pipe", encoding: "utf-8", timeout: 120_000, env: claudePluginEnv() });
2028
+ const install = pluginInstall(ref);
2029
+ if (install.status === 0) {
2030
+ logFile(`[plugin-install] recache ${ref.name}@${ref.marketplace}`);
2031
+ }
2032
+ else {
2033
+ const stderrShort = (install.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
2034
+ logFile(`[plugin-install] ERROR recache ${ref.name}@${ref.marketplace} exit=${install.status} stderr=${JSON.stringify(stderrShort)}`);
2035
+ }
2036
+ }
2007
2037
  for (const ref of toInstall) {
2008
- const install = spawnSync("claude", ["plugin", "install", `${ref.name}@${ref.marketplace}`, "--scope", "user"], { stdio: "pipe", encoding: "utf-8", timeout: 120_000, env: claudePluginEnv() });
2038
+ const install = pluginInstall(ref);
2009
2039
  if (install.status === 0) {
2010
2040
  logFile(`[plugin-install] ${ref.name}@${ref.marketplace} idempotent=false`);
2011
2041
  }
@@ -2014,30 +2044,51 @@ function registerLocalAndExternalPlugins() {
2014
2044
  logFile(`[plugin-install] ERROR ${ref.name}@${ref.marketplace} exit=${install.status} stderr=${JSON.stringify(stderrShort)}`);
2015
2045
  }
2016
2046
  }
2017
- // Configure-token pass only the externals carry configureSecret. For
2018
- // each, pipe `/<name>:configure <secret>` into a one-shot `claude`
2019
- // invocation. Missing env vars log SKIP and continue pairing is a
2020
- // manual per-operator step per the channels-reference docs.
2021
- const configureActions = computeConfigureActions(externals, process.env);
2022
- for (const action of configureActions) {
2023
- if (action.kind === "skip-no-secret-name") {
2024
- // Local plugins and externals without configureSecret — nothing to do.
2025
- continue;
2026
- }
2027
- if (action.kind === "skip-no-secret-value") {
2028
- logFile(`[plugin-configure] SKIP ${action.plugin.name} reason=no-secret-in-env env-var=${action.plugin.configureSecret}`);
2047
+ // Cache-vs-live audit (Task 643). Prove the per-plugin cache snapshot the
2048
+ // runtime loads from now matches the live tree, so drift is a logged
2049
+ // post-condition rather than a "no such skill" surprise in a later session.
2050
+ // Read the authoritative load path from installed_plugins.json
2051
+ // (`installPath`) rather than reconstructing the cache path from a pinned
2052
+ // version string. Warn-only: a count mismatch is surfaced, not fatal — a
2053
+ // benign skill directory without a SKILL.md must not abort the install.
2054
+ const countSkills = (skillsDir) => {
2055
+ if (!existsSync(skillsDir))
2056
+ return 0;
2057
+ let n = 0;
2058
+ for (const entry of readdirSync(skillsDir)) {
2059
+ if (existsSync(join(skillsDir, entry, "SKILL.md")))
2060
+ n++;
2061
+ }
2062
+ return n;
2063
+ };
2064
+ let installedPluginsMap = {};
2065
+ try {
2066
+ const ipPath = join(CLAUDE_CONFIG_DIR, "plugins", "installed_plugins.json");
2067
+ const parsed = JSON.parse(readFileSync(ipPath, "utf-8"));
2068
+ installedPluginsMap = parsed.plugins ?? {};
2069
+ }
2070
+ catch {
2071
+ // No readable installed_plugins.json — audit reports cache-skills=0; any
2072
+ // failed install above already logged a recache/install ERROR line.
2073
+ }
2074
+ // `treeDirByMarketplace` is keyed on the directory-source marketplace names
2075
+ // (= `directorySourceMarketplaces`), so a present tree dir is itself the
2076
+ // directory-source test — no separate Set lookup needed.
2077
+ const treeDirByMarketplace = new Map(localTrees.map(t => [t.name, t.dir]));
2078
+ for (const ref of desired) {
2079
+ const treeDir = treeDirByMarketplace.get(ref.marketplace);
2080
+ if (!treeDir)
2029
2081
  continue;
2030
- }
2031
- // The piped-stdin form: `echo '/<name>:configure <secret>' | claude --print`
2032
- // is the documented one-shot configure path. We do not log the secret value.
2033
- const cmd = `/${action.plugin.name}:configure ${action.secretValue}`;
2034
- const configure = spawnSync("claude", ["--print", "--input-format", "text"], { input: cmd, stdio: ["pipe", "pipe", "pipe"], encoding: "utf-8", timeout: 60_000, env: claudePluginEnv() });
2035
- if (configure.status === 0) {
2036
- logFile(`[plugin-configure] ${action.plugin.name} ok env-var=${action.plugin.configureSecret}`);
2037
- }
2038
- else {
2039
- const stderrShort = (configure.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
2040
- logFile(`[plugin-configure] ERROR ${action.plugin.name} exit=${configure.status} stderr=${JSON.stringify(stderrShort)}`);
2082
+ const liveSkills = countSkills(join(treeDir, ref.name, "skills"));
2083
+ // The resync installs at `--scope user`, so audit that scope's snapshot
2084
+ // not whichever entry happens to be first, since a host may also carry a
2085
+ // stale project-scope entry for the same plugin.
2086
+ const entries = installedPluginsMap[`${ref.name}@${ref.marketplace}`] ?? [];
2087
+ const installPath = (entries.find(e => e.scope === "user") ?? entries[0])?.installPath;
2088
+ const cacheSkills = installPath ? countSkills(join(installPath, "skills")) : 0;
2089
+ logFile(`[plugin-install] audit ${ref.name}@${ref.marketplace} live-skills=${liveSkills} cache-skills=${cacheSkills}`);
2090
+ if (liveSkills !== cacheSkills) {
2091
+ logFile(`[plugin-install] WARN cache-drift ${ref.name}@${ref.marketplace} live-skills=${liveSkills} cache-skills=${cacheSkills}`);
2041
2092
  }
2042
2093
  }
2043
2094
  // post-install assertion. The per-brand CLAUDE_CONFIG_DIR is the
@@ -28,15 +28,30 @@ export function parsePluginList(stdout) {
28
28
  return out;
29
29
  }
30
30
  /**
31
- * Given the desired plugins and what's already installed, return the
32
- * subset that still needs `claude plugin install <name>@<marketplace>`.
33
- * Idempotence rule: a plugin is "already installed" if either its
34
- * exact `<name>@<marketplace>` is in the installed set, OR a row exists
35
- * with the same name and any marketplace (the spawn would re-attach in
36
- * the operator's chosen scope). This mirrors the marketplace-add
37
- * idempotence used in src/index.ts:1012.
31
+ * Given the desired plugins, what's already installed, and which marketplaces
32
+ * are directory sources, classify each desired plugin into one of three
33
+ * buckets:
34
+ *
35
+ * - `toResync` — already installed AND from a directory-source
36
+ * marketplace. Directory marketplaces change content
37
+ * in place with no version bump, so `claude plugin
38
+ * install`/`update` short-circuit on the pinned
39
+ * version and never refresh the runtime cache snapshot
40
+ * (Task 643). These must be uninstalled then installed
41
+ * to rebuild the snapshot from the live tree.
42
+ * - `alreadyInstalled` — already installed AND from a remote (version-pinned)
43
+ * marketplace. Keeps the original idempotent skip.
44
+ * - `toInstall` — not installed yet. A fresh install builds a correct
45
+ * cache snapshot, so directory-source and remote
46
+ * plugins are handled identically here.
47
+ *
48
+ * "Already installed" matches either the exact `<name>@<marketplace>` tuple OR
49
+ * a row with the same name and any marketplace (legacy install state). The
50
+ * directory-source decision is keyed on the marketplace name regardless of
51
+ * which form matched, so a legacy name-only row for a directory plugin still
52
+ * resyncs.
38
53
  */
39
- export function computeInstallActions(desired, installed) {
54
+ export function computeInstallActions(desired, installed, directorySourceMarketplaces) {
40
55
  const byName = new Map();
41
56
  for (const i of installed) {
42
57
  if (!byName.has(i.name))
@@ -44,33 +59,23 @@ export function computeInstallActions(desired, installed) {
44
59
  byName.get(i.name).add(i.marketplace);
45
60
  }
46
61
  const toInstall = [];
62
+ const toResync = [];
47
63
  const alreadyInstalled = [];
48
64
  for (const d of desired) {
49
65
  const marketplaces = byName.get(d.name);
50
- if (marketplaces && (marketplaces.has(d.marketplace) || marketplaces.has(""))) {
66
+ const isInstalled = !!marketplaces && (marketplaces.has(d.marketplace) || marketplaces.has(""));
67
+ const isDirectorySource = directorySourceMarketplaces.has(d.marketplace);
68
+ if (isInstalled && isDirectorySource) {
69
+ toResync.push(d);
70
+ }
71
+ else if (isInstalled) {
51
72
  alreadyInstalled.push(d);
52
73
  }
53
74
  else {
54
75
  toInstall.push(d);
55
76
  }
56
77
  }
57
- return { toInstall, alreadyInstalled };
58
- }
59
- export function computeConfigureActions(installed, env) {
60
- const out = [];
61
- for (const p of installed) {
62
- if (!p.configureSecret) {
63
- out.push({ kind: "skip-no-secret-name", plugin: p });
64
- continue;
65
- }
66
- const value = env[p.configureSecret];
67
- if (typeof value !== "string" || value.length === 0) {
68
- out.push({ kind: "skip-no-secret-value", plugin: p });
69
- continue;
70
- }
71
- out.push({ kind: "configure", plugin: p, secretValue: value });
72
- }
73
- return out;
78
+ return { toInstall, toResync, alreadyInstalled };
74
79
  }
75
80
  /** Brand-config validator. brand.json#externalPlugins is optional; when
76
81
  * present, each entry must declare name + marketplace as non-empty
@@ -96,9 +101,6 @@ export function parseExternalPlugins(raw) {
96
101
  throw new Error(`brand.json#externalPlugins[${e.name}] missing 'marketplace'`);
97
102
  }
98
103
  const ref = { name: e.name, marketplace: e.marketplace };
99
- if (typeof e.configureSecret === "string" && e.configureSecret.length > 0) {
100
- ref.configureSecret = e.configureSecret;
101
- }
102
104
  if (e.channelPlugin === true) {
103
105
  ref.channelPlugin = true;
104
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.254",
3
+ "version": "0.1.256",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -0,0 +1,58 @@
1
+ # Search-surface parity contract
2
+
3
+ Three operator/agent surfaces run the same `hybrid()` search engine
4
+ (`platform/lib/graph-search`). The contract: **the same query yields the same
5
+ node set on every surface** unless a surface declares an explicit, documented
6
+ divergence.
7
+
8
+ | Surface | Entry point | Labels default | Vector floor |
9
+ |---|---|---|---|
10
+ | Chat (agent recall) | `memory-search` MCP tool → `memorySearch()` | none set ⇒ all labels | `DEFAULT_VECTOR_THRESHOLD` (0.82) |
11
+ | `/graph` admin page | `GET /api/admin/graph-search` | no chips ⇒ all labels; chips narrow | `DEFAULT_VECTOR_THRESHOLD` (0.82), `?threshold` overrides |
12
+ | `/data` admin page | `GET /api/admin/graph-search?labels=FileArtifact` | `FileArtifact` only (files) | `DEFAULT_VECTOR_THRESHOLD` (0.82), `?threshold` overrides; show-all sends `?threshold=0` |
13
+
14
+ ## The two parity rules (Task 635)
15
+
16
+ 1. **All labels by default.** A caller that passes no labels (chat) or an empty
17
+ chip set (`/graph`) searches every label — the route forwards `undefined` to
18
+ `hybrid()`, the lib's no-label gate, which queries every vector index. The
19
+ `*` wildcard is a separate explicit opt-in to the same all-labels path (no
20
+ current caller sends it; `/data` sends a real `FileArtifact` label instead).
21
+ `/graph` no longer rejects an empty chip set with a 400; chips, when present,
22
+ narrow post-filter.
23
+
24
+ 2. **One vector floor, defined once.** `DEFAULT_VECTOR_THRESHOLD = 0.82` lives
25
+ in `platform/lib/graph-search/src/index.ts` and is the single source of
26
+ truth. Each *surface* opts into it as its default; `hybrid()` itself keeps
27
+ the `vectorThreshold === undefined ⇒ no floor` contract. Two literals of
28
+ 0.82 in two files would be a drift vector — there is one constant, imported
29
+ by both the route (`src`) and `memory-search` (`dist`, so the lib must be
30
+ rebuilt for the memory plugin to see a change).
31
+
32
+ ## Declared divergences (allowed, by explicit caller or post-filter)
33
+
34
+ - **`/data` is files-only.** It sends `labels=FileArtifact` — a deliberate
35
+ server-side caller filter to file nodes, not a parity break. (It also
36
+ triggers the route's `FileArtifact` index-reconcile branch before searching.)
37
+ - **`graph-subgraph` pivot mask.** `GET /api/admin/graph-subgraph?q=…` runs
38
+ `hybrid()` with `expandHops: 0` and **no** vector floor. Its output is not a
39
+ result list — it is intersected with a clicked node's neighbourhood to
40
+ narrow a pivot. "Same query, same results" does not apply to an intersection
41
+ mask, so it intentionally stays unthresholded.
42
+ - **Post-search projections** — the chat public-twin property rewrite and the
43
+ `/graph` Message→Conversation parent merge run after the engine returns and
44
+ do not change which nodes the engine selected.
45
+
46
+ ## Diagnostics
47
+
48
+ ```
49
+ logs-read.sh --tail server 100 | grep -E '\[graph-search\]'
50
+ ```
51
+
52
+ - `op=labels-default applied=all` — a no-chip `/graph` request took the
53
+ all-labels path. Its absence on a no-labels request is the regression signal.
54
+ - `labels=all` (query line) — no chips (all labels); `labels=*` — explicit
55
+ wildcard opt-in; `labels=FileArtifact` — `/data` files search;
56
+ `labels=Person,…` — narrowed chip search.
57
+ - `threshold=0.82` on a default request; `threshold=off` only when an explicit
58
+ `?threshold=0` was passed.
@@ -15,6 +15,14 @@
15
15
  "rfbPort": 5901,
16
16
  "websockifyPort": 6081,
17
17
  "cdpPort": 9223
18
+ },
19
+ {
20
+ "hostname": "siteoffice-code",
21
+ "configDir": ".siteoffice-code",
22
+ "vncDisplay": 101,
23
+ "rfbPort": 5902,
24
+ "websockifyPort": 6082,
25
+ "cdpPort": 9224
18
26
  }
19
27
  ]
20
28
  }
@@ -68,12 +68,12 @@
68
68
  "plugins": {
69
69
  "core": ["admin", "memory", "docs", "cloudflare", "anthropic", "workflows", "work", "business-assistant", "contacts", "scheduling", "email", "prompt-optimiser", "browser"],
70
70
  "defaultEnabled": ["sales"],
71
- "available": ["deep-research", "projects", "whatsapp", "replicate", "linkedin-import", "notion-import", "obsidian-import", "x-import"],
71
+ "available": ["deep-research", "projects", "whatsapp", "replicate", "linkedin-import", "notion-import", "obsidian-import", "x-import", "slides"],
72
72
  "excluded": ["telegram"]
73
73
  },
74
74
 
75
75
  "externalPlugins": [
76
- { "name": "discord", "marketplace": "claude-plugins-official", "configureSecret": "DISCORD_BOT_TOKEN", "channelPlugin": true },
76
+ { "name": "discord", "marketplace": "claude-plugins-official", "channelPlugin": true },
77
77
  { "name": "imessage", "marketplace": "claude-plugins-official", "channelPlugin": true },
78
78
  { "name": "superpowers", "marketplace": "claude-plugins-official" },
79
79
  { "name": "code-review", "marketplace": "claude-plugins-official" }
@@ -0,0 +1,4 @@
1
+ export declare const EMBED_INPUT_MAX_CHARS = 8000;
2
+ export declare function embed(text: string): Promise<number[]>;
3
+ export declare function embedBatch(texts: string[]): Promise<number[][]>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAU1C,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa3D;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAarE"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ // Shared Ollama embed client. Single capped implementation used by every
3
+ // process that writes a vector-indexed node outside the memory plugin (admin
4
+ // MCP PIN setup, scheduling ICS ingest, UI lazy Person create). The memory
5
+ // plugin's `lib/embeddings.ts` re-exports these so there is one cap constant
6
+ // and one POST shape across the platform (Task 636). fetch-only — no node:fs —
7
+ // so it is safe to bundle into the ESM payload.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.EMBED_INPUT_MAX_CHARS = void 0;
10
+ exports.embed = embed;
11
+ exports.embedBatch = embedBatch;
12
+ const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
13
+ const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
14
+ // nomic-embed-text has a 2048-token context (~8k chars at ~4 chars/token).
15
+ // An over-limit input makes the Ollama embed endpoint error; cap defensively so
16
+ // an oversized body embeds (truncated) instead of throwing and aborting the
17
+ // caller.
18
+ exports.EMBED_INPUT_MAX_CHARS = 8000;
19
+ function capInput(text) {
20
+ if (text.length <= exports.EMBED_INPUT_MAX_CHARS)
21
+ return text;
22
+ process.stderr.write(`[embed] op=truncate origBytes=${text.length} cappedBytes=${exports.EMBED_INPUT_MAX_CHARS}\n`);
23
+ return text.slice(0, exports.EMBED_INPUT_MAX_CHARS);
24
+ }
25
+ async function embed(text) {
26
+ const input = capInput(text);
27
+ const res = await fetch(`${OLLAMA_URL}/api/embed`, {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify({ model: EMBED_MODEL, input }),
31
+ });
32
+ if (!res.ok) {
33
+ const body = await res.text();
34
+ throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
35
+ }
36
+ const data = (await res.json());
37
+ return data.embeddings[0];
38
+ }
39
+ async function embedBatch(texts) {
40
+ const input = texts.map(capInput);
41
+ const res = await fetch(`${OLLAMA_URL}/api/embed`, {
42
+ method: "POST",
43
+ headers: { "Content-Type": "application/json" },
44
+ body: JSON.stringify({ model: EMBED_MODEL, input }),
45
+ });
46
+ if (!res.ok) {
47
+ const body = await res.text();
48
+ throw new Error(`Ollama batch embedding failed (${res.status}): ${body}`);
49
+ }
50
+ const data = (await res.json());
51
+ return data.embeddings;
52
+ }
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,gDAAgD;;;AAmBhD,sBAaC;AAED,gCAaC;AA7CD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,wBAAwB,CAAC;AACtE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,kBAAkB,CAAC;AAElE,2EAA2E;AAC3E,gFAAgF;AAChF,4EAA4E;AAC5E,UAAU;AACG,QAAA,qBAAqB,GAAG,IAAI,CAAC;AAE1C,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,IAAI,CAAC,MAAM,IAAI,6BAAqB;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iCAAiC,IAAI,CAAC,MAAM,gBAAgB,6BAAqB,IAAI,CACtF,CAAC;IACF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,6BAAqB,CAAC,CAAC;AAC9C,CAAC;AAEM,KAAK,UAAU,KAAK,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,YAAY,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;KACpD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;IAC9D,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAEM,KAAK,UAAU,UAAU,CAAC,KAAe;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,YAAY,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;KACpD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;IAC9D,OAAO,IAAI,CAAC,UAAU,CAAC;AACzB,CAAC"}
@@ -0,0 +1,53 @@
1
+ // Shared Ollama embed client. Single capped implementation used by every
2
+ // process that writes a vector-indexed node outside the memory plugin (admin
3
+ // MCP PIN setup, scheduling ICS ingest, UI lazy Person create). The memory
4
+ // plugin's `lib/embeddings.ts` re-exports these so there is one cap constant
5
+ // and one POST shape across the platform (Task 636). fetch-only — no node:fs —
6
+ // so it is safe to bundle into the ESM payload.
7
+
8
+ const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
9
+ const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
10
+
11
+ // nomic-embed-text has a 2048-token context (~8k chars at ~4 chars/token).
12
+ // An over-limit input makes the Ollama embed endpoint error; cap defensively so
13
+ // an oversized body embeds (truncated) instead of throwing and aborting the
14
+ // caller.
15
+ export const EMBED_INPUT_MAX_CHARS = 8000;
16
+
17
+ function capInput(text: string): string {
18
+ if (text.length <= EMBED_INPUT_MAX_CHARS) return text;
19
+ process.stderr.write(
20
+ `[embed] op=truncate origBytes=${text.length} cappedBytes=${EMBED_INPUT_MAX_CHARS}\n`,
21
+ );
22
+ return text.slice(0, EMBED_INPUT_MAX_CHARS);
23
+ }
24
+
25
+ export async function embed(text: string): Promise<number[]> {
26
+ const input = capInput(text);
27
+ const res = await fetch(`${OLLAMA_URL}/api/embed`, {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify({ model: EMBED_MODEL, input }),
31
+ });
32
+ if (!res.ok) {
33
+ const body = await res.text();
34
+ throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
35
+ }
36
+ const data = (await res.json()) as { embeddings: number[][] };
37
+ return data.embeddings[0];
38
+ }
39
+
40
+ export async function embedBatch(texts: string[]): Promise<number[][]> {
41
+ const input = texts.map(capInput);
42
+ const res = await fetch(`${OLLAMA_URL}/api/embed`, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify({ model: EMBED_MODEL, input }),
46
+ });
47
+ if (!res.ok) {
48
+ const body = await res.text();
49
+ throw new Error(`Ollama batch embedding failed (${res.status}): ${body}`);
50
+ }
51
+ const data = (await res.json()) as { embeddings: number[][] };
52
+ return data.embeddings;
53
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"]
8
+ }