@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
@@ -1,396 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Task 282 — session-end retrospective Stop hook test suite.
3
- #
4
- # Listener-mock pattern: a local Python HTTP server stands in for
5
- # /api/admin/log-ingest and records every POST so the assertions can read
6
- # every log line the hook emitted.
7
- #
8
- # Contract under test:
9
- # - All log emissions go through POST /api/admin/log-ingest. Hook stderr
10
- # stays silent on every path EXCEPT the gate-blocked path, which writes
11
- # the three-pass retrospective instruction block to stderr (Stop-hook
12
- # contract — that is how the agent receives the instruction).
13
- # - Every gated-off path emits one `trigger-skipped reason=<r>` line and
14
- # exits 0:
15
- # role-not-admin | is-specialist | empty-stdin | missing-transcript |
16
- # no-intent-match
17
- # - End-intent triggers: `/end`, `/archive`, `end session`,
18
- # `archive this session`, applied case-insensitively to the latest
19
- # real-user message. `tool_result`-only user records are NOT the
20
- # latest user message. Embedded prose ("I'll end session by 5pm")
21
- # does NOT trigger.
22
- # - When the intent matches and the sentinel `tool_use` of
23
- # `session-retrospective-mark-complete` is absent from the JSONL:
24
- # exit 2, stderr carries the instruction block,
25
- # `gate-blocked sessionId=<id> reason=sentinel-absent` emitted.
26
- # - When the intent matches and the sentinel `tool_use` is present:
27
- # exit 0, `gate-released sessionId=<id>` emitted.
28
- # - Re-entry: a second Stop fired after the sentinel call still releases
29
- # (the sentinel-grep is the re-entry guard).
30
- # - Per-session scoping: a sentinel in a DIFFERENT session's JSONL does
31
- # NOT release the active gate.
32
-
33
- set -u
34
-
35
- HOOK="$(cd "$(dirname "$0")/.." && pwd)/session-end-retrospective.sh"
36
- if [[ ! -x "$HOOK" ]]; then
37
- echo "FAIL: $HOOK not executable" >&2
38
- exit 1
39
- fi
40
-
41
- OP_ID='aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb'
42
- OTHER_ID='99999999-7777-6666-5555-444444444444'
43
-
44
- TMPFILES=()
45
- LISTENER_PIDS=()
46
- cleanup_test_state() {
47
- for pid in "${LISTENER_PIDS[@]:-}"; do
48
- if [[ -n "$pid" ]]; then
49
- kill "$pid" 2>/dev/null || true
50
- wait "$pid" 2>/dev/null || true
51
- fi
52
- done
53
- for f in "${TMPFILES[@]:-}"; do
54
- [[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
55
- done
56
- }
57
- trap cleanup_test_state EXIT
58
-
59
- PASS=0
60
- FAIL=0
61
- pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
62
- fail() { echo "FAIL: $1" >&2; FAIL=$((FAIL + 1)); }
63
-
64
- start_listener() {
65
- REQ_LOG=$(mktemp); TMPFILES+=("$REQ_LOG")
66
- LISTENER_PORT=$((39500 + RANDOM % 100))
67
- python3 - "$LISTENER_PORT" "$REQ_LOG" <<'PY' &
68
- import sys, http.server, json
69
- port = int(sys.argv[1])
70
- log_path = sys.argv[2]
71
- class H(http.server.BaseHTTPRequestHandler):
72
- def log_message(self, *a, **k): pass
73
- def do_POST(self):
74
- n = int(self.headers.get('Content-Length','0') or 0)
75
- body = self.rfile.read(n).decode('utf-8','replace')
76
- with open(log_path, 'a', encoding='utf-8') as f:
77
- f.write(self.path + '\t' + body + '\n')
78
- self.send_response(200)
79
- self.send_header('Content-Type','application/json')
80
- self.end_headers()
81
- self.wfile.write(json.dumps({"ok": True}).encode('utf-8'))
82
- http.server.HTTPServer(('127.0.0.1', port), H).serve_forever()
83
- PY
84
- LISTENER_PIDS+=("$!")
85
- for _ in $(seq 1 20); do
86
- if curl -sS --max-time 1 -X POST "http://127.0.0.1:${LISTENER_PORT}/ping" -d '{}' >/dev/null 2>&1; then
87
- break
88
- fi
89
- sleep 0.1
90
- done
91
- : > "$REQ_LOG"
92
- }
93
-
94
- run_hook() {
95
- local role="$1"; local specialist="$2"; local stdin_json="$3"
96
- local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
97
- local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
98
- printf '%s' "$stdin_json" | \
99
- MAXY_SESSION_ROLE="$role" \
100
- MAXY_SPECIALIST="$specialist" \
101
- MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
102
- bash "$HOOK" >"$stdout_file" 2>"$stderr_file"
103
- HOOK_RC=$?
104
- HOOK_STDERR=$(cat "$stderr_file")
105
- HOOK_STDOUT=$(cat "$stdout_file")
106
- sleep 0.1
107
- }
108
-
109
- ingest_lines() {
110
- grep -E '^/api/admin/log-ingest ' "$REQ_LOG" 2>/dev/null | python3 -c '
111
- import sys, json
112
- for raw in sys.stdin:
113
- try:
114
- _, body = raw.rstrip("\n").split("\t", 1)
115
- d = json.loads(body)
116
- if isinstance(d, dict) and isinstance(d.get("line"), str):
117
- print(d["line"])
118
- except Exception:
119
- pass
120
- ' || true
121
- }
122
-
123
- # Build a JSONL transcript from positional jq-style triples. Each triple is
124
- # role|text|extra where extra is one of:
125
- # "" → plain user/assistant text record
126
- # "tool_use:<NAME>" → assistant record carrying a
127
- # tool_use block of that name
128
- # "tool_result" → user record carrying only a
129
- # tool_result block (not a real
130
- # user turn)
131
- write_transcript() {
132
- local out="$1"; shift
133
- : > "$out"
134
- python3 - "$out" "$@" <<'PY'
135
- import sys, json
136
- out = sys.argv[1]
137
- spec = sys.argv[2:]
138
- with open(out, "w", encoding="utf-8") as f:
139
- for entry in spec:
140
- role, text, extra = entry.split("|", 2)
141
- rec = {"type": role, "timestamp": "2026-05-24T10:00:00.000Z"}
142
- if role == "user":
143
- if extra == "tool_result":
144
- rec["message"] = {
145
- "role": "user",
146
- "content": [
147
- {"type": "tool_result",
148
- "tool_use_id": "tu_x",
149
- "content": text or "ok"},
150
- ],
151
- }
152
- else:
153
- rec["message"] = {"role": "user", "content": text}
154
- else:
155
- content_blocks = []
156
- if text:
157
- content_blocks.append({"type": "text", "text": text})
158
- if extra.startswith("tool_use:"):
159
- tool_name = extra.split(":", 1)[1]
160
- content_blocks.append({
161
- "type": "tool_use",
162
- "id": f"toolu_{tool_name}",
163
- "name": tool_name,
164
- "input": {},
165
- })
166
- rec["message"] = {
167
- "id": f"msg_{role}_{hash(text) & 0xffff:04x}",
168
- "role": "assistant",
169
- "content": content_blocks,
170
- }
171
- f.write(json.dumps(rec) + "\n")
172
- PY
173
- }
174
-
175
- envelope_for() {
176
- python3 -c '
177
- import json, sys
178
- print(json.dumps({"session_id": sys.argv[1], "transcript_path": sys.argv[2]}))
179
- ' "$1" "$2"
180
- }
181
-
182
- # ---------------------------------------------------------------------------
183
- # Case 1: role != admin → trigger-skipped reason=role-not-admin
184
- # ---------------------------------------------------------------------------
185
- start_listener
186
- TRANSCRIPT_PUBLIC=$(mktemp); TMPFILES+=("$TRANSCRIPT_PUBLIC")
187
- write_transcript "$TRANSCRIPT_PUBLIC" "user|/end|"
188
- ENV_PUBLIC=$(envelope_for "$OP_ID" "$TRANSCRIPT_PUBLIC")
189
- : > "$REQ_LOG"
190
- run_hook "public" "" "$ENV_PUBLIC"
191
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-1 rc=$HOOK_RC"
192
- [[ -z "$HOOK_STDERR" ]] || fail "case-1 stderr must be empty, got: $HOOK_STDERR"
193
- if ingest_lines | grep -qE '^trigger-skipped sessionId=.* reason=role-not-admin$'; then
194
- pass "case-1 role=public → trigger-skipped reason=role-not-admin"
195
- else
196
- fail "case-1 expected role-not-admin, got: $(ingest_lines)"
197
- fi
198
-
199
- # ---------------------------------------------------------------------------
200
- # Case 2: MAXY_SPECIALIST set → trigger-skipped is-specialist
201
- # ---------------------------------------------------------------------------
202
- : > "$REQ_LOG"
203
- run_hook "admin" "database-operator" "$ENV_PUBLIC"
204
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-2 rc=$HOOK_RC"
205
- [[ -z "$HOOK_STDERR" ]] || fail "case-2 stderr must be empty, got: $HOOK_STDERR"
206
- if ingest_lines | grep -qE '^trigger-skipped sessionId=.* reason=is-specialist specialist=database-operator$'; then
207
- pass "case-2 specialist=database-operator → trigger-skipped is-specialist"
208
- else
209
- fail "case-2 expected is-specialist, got: $(ingest_lines)"
210
- fi
211
-
212
- # ---------------------------------------------------------------------------
213
- # Case 3: empty stdin → trigger-skipped reason=empty-stdin
214
- # ---------------------------------------------------------------------------
215
- : > "$REQ_LOG"
216
- run_hook "admin" "" ""
217
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-3 rc=$HOOK_RC"
218
- if ingest_lines | grep -qE '^trigger-skipped sessionId=.* reason=empty-stdin$'; then
219
- pass "case-3 empty stdin → trigger-skipped reason=empty-stdin"
220
- else
221
- fail "case-3 expected empty-stdin, got: $(ingest_lines)"
222
- fi
223
-
224
- # ---------------------------------------------------------------------------
225
- # Case 4: missing transcript_path → trigger-skipped missing-transcript
226
- # ---------------------------------------------------------------------------
227
- : > "$REQ_LOG"
228
- BAD_ENV=$(python3 -c 'import json,sys; print(json.dumps({"session_id": sys.argv[1]}))' "$OP_ID")
229
- run_hook "admin" "" "$BAD_ENV"
230
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-4 rc=$HOOK_RC"
231
- if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=missing-transcript$"; then
232
- pass "case-4 missing transcript → trigger-skipped reason=missing-transcript"
233
- else
234
- fail "case-4 expected missing-transcript, got: $(ingest_lines)"
235
- fi
236
-
237
- # ---------------------------------------------------------------------------
238
- # Case 5: no end-intent token → trigger-skipped reason=no-intent-match
239
- # ---------------------------------------------------------------------------
240
- TRANSCRIPT_BORING=$(mktemp); TMPFILES+=("$TRANSCRIPT_BORING")
241
- write_transcript "$TRANSCRIPT_BORING" \
242
- "user|hello|" \
243
- "assistant|hi|" \
244
- "user|what's the weather|"
245
- ENV_BORING=$(envelope_for "$OP_ID" "$TRANSCRIPT_BORING")
246
- : > "$REQ_LOG"
247
- run_hook "admin" "" "$ENV_BORING"
248
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-5 rc=$HOOK_RC"
249
- [[ -z "$HOOK_STDERR" ]] || fail "case-5 stderr must be empty"
250
- if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=no-intent-match$"; then
251
- pass "case-5 no intent token → trigger-skipped reason=no-intent-match"
252
- else
253
- fail "case-5 expected no-intent-match, got: $(ingest_lines)"
254
- fi
255
-
256
- # ---------------------------------------------------------------------------
257
- # Case 6: each intent token triggers gate-blocked (sentinel absent)
258
- # ---------------------------------------------------------------------------
259
- for TOKEN in "/end" "/archive" "end session" "archive this session"; do
260
- T_FILE=$(mktemp); TMPFILES+=("$T_FILE")
261
- write_transcript "$T_FILE" "user|hi|" "assistant|hello|" "user|${TOKEN}|"
262
- ENV_BLOCK=$(envelope_for "$OP_ID" "$T_FILE")
263
- : > "$REQ_LOG"
264
- run_hook "admin" "" "$ENV_BLOCK"
265
- if [[ "$HOOK_RC" -ne 2 ]]; then
266
- fail "case-6:${TOKEN} expected rc=2, got $HOOK_RC"
267
- continue
268
- fi
269
- if ! ingest_lines | grep -qE "^trigger sessionId=${OP_ID} token=${TOKEN}$"; then
270
- fail "case-6:${TOKEN} missing trigger line, got: $(ingest_lines)"
271
- continue
272
- fi
273
- if ! ingest_lines | grep -qE "^gate-blocked sessionId=${OP_ID} reason=sentinel-absent$"; then
274
- fail "case-6:${TOKEN} missing gate-blocked line, got: $(ingest_lines)"
275
- continue
276
- fi
277
- if ! printf '%s' "$HOOK_STDERR" | grep -q "session-retrospective-mark-complete"; then
278
- fail "case-6:${TOKEN} instruction block missing sentinel tool name on stderr"
279
- continue
280
- fi
281
- if ! printf '%s' "$HOOK_STDERR" | grep -q "database-operator"; then
282
- fail "case-6:${TOKEN} instruction block missing database-operator reference"
283
- continue
284
- fi
285
- pass "case-6:${TOKEN} triggers gate-blocked exit 2 + instruction stderr"
286
- done
287
-
288
- # ---------------------------------------------------------------------------
289
- # Case 7: intent token + sentinel present → gate-released exit 0
290
- # ---------------------------------------------------------------------------
291
- T_RELEASE=$(mktemp); TMPFILES+=("$T_RELEASE")
292
- write_transcript "$T_RELEASE" \
293
- "user|hi|" \
294
- "assistant|hello|" \
295
- "user|/end|" \
296
- "assistant|running retrospective|" \
297
- "assistant||tool_use:mcp__admin__session-retrospective-mark-complete"
298
- ENV_REL=$(envelope_for "$OP_ID" "$T_RELEASE")
299
- : > "$REQ_LOG"
300
- run_hook "admin" "" "$ENV_REL"
301
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-7 expected rc=0, got $HOOK_RC"
302
- [[ -z "$HOOK_STDERR" ]] || fail "case-7 stderr must be empty, got: $HOOK_STDERR"
303
- if ingest_lines | grep -qE "^gate-released sessionId=${OP_ID}$"; then
304
- pass "case-7 sentinel present → gate-released exit 0"
305
- else
306
- fail "case-7 expected gate-released, got: $(ingest_lines)"
307
- fi
308
-
309
- # ---------------------------------------------------------------------------
310
- # Case 8: re-entry — Stop fires twice after sentinel call, both release
311
- # ---------------------------------------------------------------------------
312
- : > "$REQ_LOG"
313
- run_hook "admin" "" "$ENV_REL"
314
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-8 second-stop rc=$HOOK_RC"
315
- if ingest_lines | grep -qE "^gate-released sessionId=${OP_ID}$"; then
316
- pass "case-8 re-entry: second Stop after sentinel still releases"
317
- else
318
- fail "case-8 second Stop did not emit gate-released, got: $(ingest_lines)"
319
- fi
320
-
321
- # ---------------------------------------------------------------------------
322
- # Case 9: per-session scoping — sentinel lives in a different session's
323
- # JSONL; active gate must NOT release
324
- # ---------------------------------------------------------------------------
325
- T_OTHER=$(mktemp); TMPFILES+=("$T_OTHER")
326
- write_transcript "$T_OTHER" \
327
- "user|/end|" \
328
- "assistant||tool_use:mcp__admin__session-retrospective-mark-complete"
329
- # Active gate inspects only T_ACTIVE (no sentinel inside).
330
- T_ACTIVE=$(mktemp); TMPFILES+=("$T_ACTIVE")
331
- write_transcript "$T_ACTIVE" "user|/end|"
332
- ENV_ACTIVE=$(envelope_for "$OP_ID" "$T_ACTIVE")
333
- : > "$REQ_LOG"
334
- run_hook "admin" "" "$ENV_ACTIVE"
335
- [[ "$HOOK_RC" -eq 2 ]] || fail "case-9 expected rc=2 (other session's sentinel must not release), got $HOOK_RC"
336
- if ingest_lines | grep -qE "^gate-blocked sessionId=${OP_ID} reason=sentinel-absent$"; then
337
- pass "case-9 per-session scoping: sentinel in another transcript does not release"
338
- else
339
- fail "case-9 expected gate-blocked, got: $(ingest_lines)"
340
- fi
341
-
342
- # ---------------------------------------------------------------------------
343
- # Case 10: `tool_result`-only user record is NOT the latest user message;
344
- # the prior real user message wins.
345
- # ---------------------------------------------------------------------------
346
- T_TR_ONLY=$(mktemp); TMPFILES+=("$T_TR_ONLY")
347
- write_transcript "$T_TR_ONLY" \
348
- "user|/end|" \
349
- "assistant||tool_use:Read" \
350
- "user|noise|tool_result"
351
- ENV_TR=$(envelope_for "$OP_ID" "$T_TR_ONLY")
352
- : > "$REQ_LOG"
353
- run_hook "admin" "" "$ENV_TR"
354
- [[ "$HOOK_RC" -eq 2 ]] || fail "case-10 expected rc=2 (tool_result must not mask /end), got $HOOK_RC"
355
- if ingest_lines | grep -qE "^gate-blocked sessionId=${OP_ID} reason=sentinel-absent$"; then
356
- pass "case-10 tool_result-only user record does not mask latest real user /end"
357
- else
358
- fail "case-10 expected gate-blocked, got: $(ingest_lines)"
359
- fi
360
-
361
- # ---------------------------------------------------------------------------
362
- # Case 11: embedded prose does NOT trigger ("I'll end session by 5pm")
363
- # ---------------------------------------------------------------------------
364
- T_PROSE=$(mktemp); TMPFILES+=("$T_PROSE")
365
- write_transcript "$T_PROSE" "user|I'll end session by 5pm|"
366
- ENV_PROSE=$(envelope_for "$OP_ID" "$T_PROSE")
367
- : > "$REQ_LOG"
368
- run_hook "admin" "" "$ENV_PROSE"
369
- [[ "$HOOK_RC" -eq 0 ]] || fail "case-11 expected rc=0 (prose must not trigger), got $HOOK_RC"
370
- if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=no-intent-match$"; then
371
- pass "case-11 embedded prose 'end session' does not trigger gate"
372
- else
373
- fail "case-11 expected no-intent-match, got: $(ingest_lines)"
374
- fi
375
-
376
- # ---------------------------------------------------------------------------
377
- # Case 12: case-insensitive match — `/END` triggers like `/end`
378
- # ---------------------------------------------------------------------------
379
- T_CASE=$(mktemp); TMPFILES+=("$T_CASE")
380
- write_transcript "$T_CASE" "user|/END|"
381
- ENV_CASE=$(envelope_for "$OP_ID" "$T_CASE")
382
- : > "$REQ_LOG"
383
- run_hook "admin" "" "$ENV_CASE"
384
- [[ "$HOOK_RC" -eq 2 ]] || fail "case-12 expected rc=2 for /END, got $HOOK_RC"
385
- if ingest_lines | grep -qE "^trigger sessionId=${OP_ID} token=/end$"; then
386
- pass "case-12 case-insensitive: /END normalises to /end"
387
- else
388
- fail "case-12 expected token=/end, got: $(ingest_lines)"
389
- fi
390
-
391
- # ---------------------------------------------------------------------------
392
- # Summary
393
- # ---------------------------------------------------------------------------
394
- echo
395
- echo "session-end-retrospective tests: $PASS passed, $FAIL failed"
396
- [[ "$FAIL" -eq 0 ]]
@@ -1,239 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Shared plumbing for the admin-side hook that orchestrates admin-walked
3
- # graph writes: `session-end-retrospective.sh` (Stop on operator end-intent).
4
- # Originally introduced (Task 439) as shared plumbing across two hooks; the
5
- # per-turn sibling was removed in Task 447 because session-end Pass 3 already
6
- # performs the graph-completeness sweep. The library is retained — its
7
- # stdin parse, role / specialist / transcript guards, log-ingest envelope,
8
- # and post-user tool-name walker are still the single body for the
9
- # remaining hook.
10
- #
11
- # Caller contract:
12
- # 1. Set `AGP_LOG_TAG` to the hook's identifying tag before sourcing.
13
- # `session-retrospective` is the current value; the helper itself
14
- # does not log under its own tag.
15
- # 2. Source this file.
16
- # 3. Call `agp_setup_logging`, `agp_read_stdin`, `agp_parse_stdin`,
17
- # `agp_guard_role_specialist`, `agp_guard_transcript` in that order.
18
- # Each guard exits 0 from inside the helper when its condition fires
19
- # (sourced `exit` exits the caller).
20
- # 4. After the guards return, the hook proceeds with its event-specific
21
- # body (sentinel-grep + intent token for session-end; post-turn-context
22
- # fetch + directive envelope for pre-turn).
23
- #
24
- # Globals set by the helper (read by callers):
25
- # INPUT — raw stdin (empty when none piped)
26
- # SESSION_ID — parsed `session_id` field, empty on parse failure
27
- # TRANSCRIPT_PATH — parsed `transcript_path` field
28
- # HOOK_EVENT_NAME — parsed `hook_event_name` field
29
- # SESSION_ID_REPORTED — `$SESSION_ID` or the literal "unknown"
30
- # UI_BASE — `http://127.0.0.1:${MAXY_UI_INTERNAL_PORT}`
31
- # LOG_INGEST_URL — `${UI_BASE}/api/admin/log-ingest`
32
- #
33
- # Functions:
34
- # agp_setup_logging — resolves UI port + URLs, or exits 0 with
35
- # a stderr loud-fail when the port is unset
36
- # (fail-open so a missing port never bricks
37
- # the operator).
38
- # agp_emit_log <line> — POSTs `{tag, level:"info", line}` to
39
- # log-ingest; silent on network failure.
40
- # agp_read_stdin — slurps stdin into `INPUT`, leaves it empty
41
- # when no input is piped.
42
- # agp_parse_stdin — populates `SESSION_ID`, `TRANSCRIPT_PATH`,
43
- # `HOOK_EVENT_NAME`, `SESSION_ID_REPORTED`.
44
- # agp_guard_role_specialist — exits 0 with `trigger-skipped` log on
45
- # `role-not-admin` or `is-specialist`.
46
- # agp_guard_transcript — exits 0 with `trigger-skipped` log on
47
- # `empty-stdin` (when $1="require-stdin") or
48
- # `missing-transcript`.
49
-
50
- if [ -z "${AGP_LOG_TAG:-}" ]; then
51
- echo "[admin-graph-pass-common] AGP_LOG_TAG not set by caller" >&2
52
- exit 1
53
- fi
54
-
55
- agp_setup_logging() {
56
- local ui_port="${MAXY_UI_INTERNAL_PORT:-}"
57
- if [ -z "$ui_port" ]; then
58
- echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-env env=MAXY_UI_INTERNAL_PORT" >&2
59
- exit 0
60
- fi
61
- UI_BASE="http://127.0.0.1:${ui_port}"
62
- LOG_INGEST_URL="${UI_BASE}/api/admin/log-ingest"
63
- }
64
-
65
- agp_emit_log() {
66
- local line="$1"
67
- curl -sS -o /dev/null -X POST \
68
- -H 'Content-Type: application/json' \
69
- --max-time 2 \
70
- --data "$(AGP_TAG="$AGP_LOG_TAG" AGP_LINE="$line" python3 -c '
71
- import os, json
72
- print(json.dumps({"tag": os.environ["AGP_TAG"], "level": "info", "line": os.environ["AGP_LINE"]}))
73
- ')" \
74
- "$LOG_INGEST_URL" 2>/dev/null || true
75
- }
76
-
77
- agp_read_stdin() {
78
- INPUT=""
79
- if [ ! -t 0 ]; then
80
- INPUT=$(cat)
81
- fi
82
- }
83
-
84
- agp_parse_stdin() {
85
- local parsed
86
- parsed=$(printf '%s' "$INPUT" | python3 -c '
87
- import sys, json
88
- try:
89
- d = json.load(sys.stdin)
90
- sid = d.get("session_id", "") or ""
91
- tpath = d.get("transcript_path", "") or ""
92
- event = d.get("hook_event_name", "") or ""
93
- print(f"{sid}\t{tpath}\t{event}")
94
- except Exception:
95
- print("\t\t")
96
- ' 2>/dev/null)
97
- SESSION_ID="${parsed%% *}"
98
- local rest="${parsed#* }"
99
- TRANSCRIPT_PATH="${rest%% *}"
100
- HOOK_EVENT_NAME="${rest#* }"
101
- SESSION_ID_REPORTED="${SESSION_ID:-unknown}"
102
- }
103
-
104
- agp_guard_role_specialist() {
105
- if [ "${MAXY_SESSION_ROLE:-}" != "admin" ]; then
106
- agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=role-not-admin"
107
- exit 0
108
- fi
109
- # Recursion guard. A specialist subagent dispatched via the Task tool
110
- # (e.g. database-operator running the operator's graph writes) carries
111
- # `MAXY_SPECIALIST=<name>` on the PTY env. Skip — the hooks only fire
112
- # for the operator's own admin session.
113
- #
114
- # Invariant the PTY spawner is expected to honour: `MAXY_SPECIALIST` is
115
- # either set to a non-empty specialist name OR absent. An explicit empty
116
- # string from a wrapper would slip the recursion guard here (`-n ""` is
117
- # false) — that would be a defect at the spawn site, not here.
118
- if [ -n "${MAXY_SPECIALIST:-}" ]; then
119
- agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=is-specialist specialist=${MAXY_SPECIALIST}"
120
- exit 0
121
- fi
122
- }
123
-
124
- # Usage: agp_guard_transcript [require-stdin]
125
- # Pass `require-stdin` (session-end-retrospective) to emit `empty-stdin` on a
126
- # blank `INPUT`. Without the flag, parse failure on a non-empty INPUT
127
- # collapses to `missing-transcript` cleanly.
128
- agp_guard_transcript() {
129
- if [ "${1:-}" = "require-stdin" ] && [ -z "$INPUT" ]; then
130
- agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=empty-stdin"
131
- exit 0
132
- fi
133
- if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
134
- agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=missing-transcript"
135
- exit 0
136
- fi
137
- }
138
-
139
- # Walks $TRANSCRIPT_PATH and reports, in one line on stdout:
140
- # <real-user-present:yes|no>\t<matched-tool-name-or-empty>
141
- # A "real user" record matches `session-end-retrospective.sh`'s `is_real_user`
142
- # — `type=user, message.role=user`, content is a string OR a list with at
143
- # least one non-`tool_result` block. `tool_result`-only user records are tool
144
- # replies, never turn starts.
145
- #
146
- # Every assistant `tool_use` block that appears AFTER the latest real-user
147
- # record is matched against the alternation passed as `$1` (an extended-regex
148
- # alternation of literal `tool_use.name` values). The synthetic name
149
- # `Task:<subagent_type>` is also matched, so callers can release on a
150
- # `Task` tool_use with `input.subagent_type=<name>` by including
151
- # `Task:<name>` in the alternation. Returns the first match encountered after
152
- # the latest real-user record (oldest-first) so the release log carries the
153
- # matched tool's actual name. Empty match means none of the release patterns
154
- # appeared since the operator's last turn.
155
- agp_scan_post_user_toolnames() {
156
- local pattern="$1"
157
- TRANSCRIPT_PATH="$TRANSCRIPT_PATH" AGP_PATTERN="$pattern" python3 - <<'PY'
158
- import os, json, re
159
-
160
- pattern = re.compile(os.environ["AGP_PATTERN"])
161
- path = os.environ["TRANSCRIPT_PATH"]
162
-
163
- def is_real_user(rec):
164
- if not isinstance(rec, dict) or rec.get("type") != "user":
165
- return False
166
- msg = rec.get("message")
167
- if not isinstance(msg, dict) or msg.get("role") != "user":
168
- return False
169
- content = msg.get("content")
170
- if isinstance(content, str):
171
- return True
172
- if isinstance(content, list):
173
- for b in content:
174
- if isinstance(b, dict) and b.get("type") != "tool_result":
175
- return True
176
- return False
177
- return True
178
-
179
- def assistant_tool_use_names(rec):
180
- if not isinstance(rec, dict) or rec.get("type") != "assistant":
181
- return []
182
- msg = rec.get("message", {}) or {}
183
- if msg.get("role") != "assistant":
184
- return []
185
- content = msg.get("content")
186
- if not isinstance(content, list):
187
- return []
188
- names = []
189
- for b in content:
190
- if isinstance(b, dict) and b.get("type") == "tool_use":
191
- n = b.get("name")
192
- if not isinstance(n, str):
193
- continue
194
- names.append(n)
195
- if n == "Task":
196
- inp = b.get("input")
197
- if isinstance(inp, dict):
198
- st = inp.get("subagent_type")
199
- if isinstance(st, str) and st:
200
- names.append(f"Task:{st}")
201
- return names
202
-
203
- records = []
204
- try:
205
- with open(path, "r", encoding="utf-8") as f:
206
- for raw in f:
207
- raw = raw.strip()
208
- if not raw:
209
- continue
210
- try:
211
- records.append(json.loads(raw))
212
- except Exception:
213
- continue
214
- except Exception:
215
- print("no\t")
216
- raise SystemExit(0)
217
-
218
- latest_user_idx = -1
219
- for i in range(len(records) - 1, -1, -1):
220
- if is_real_user(records[i]):
221
- latest_user_idx = i
222
- break
223
-
224
- if latest_user_idx == -1:
225
- print("no\t")
226
- raise SystemExit(0)
227
-
228
- matched = ""
229
- for r in records[latest_user_idx + 1:]:
230
- for n in assistant_tool_use_names(r):
231
- if pattern.fullmatch(n):
232
- matched = n
233
- break
234
- if matched:
235
- break
236
-
237
- print(f"yes\t{matched}")
238
- PY
239
- }