@rubytech/create-maxy-code 0.1.157 → 0.1.159
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.
- package/package.json +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.d.ts +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/admin-conversation-purge/dist/index.js +4 -4
- package/payload/platform/lib/admin-conversation-purge/dist/index.js.map +1 -1
- package/payload/platform/lib/admin-conversation-purge/src/index.ts +5 -5
- package/payload/platform/lib/graph-style/dist/index.js +2 -2
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +1 -1
- package/payload/platform/lib/graph-style/src/index.ts +2 -2
- package/payload/platform/lib/graph-trash/dist/index.js +1 -1
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-trash/src/index.ts +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +3 -3
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +5 -5
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +1 -1
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +1 -1
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +8 -8
- package/payload/platform/lib/graph-write/src/index.ts +1 -1
- package/payload/platform/neo4j/schema.cypher +33 -15
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +8 -4
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/per-turn-graph-pass-gate.test.sh +361 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-turn-graph-pass.test.sh +13 -13
- package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +1 -1
- package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +242 -0
- package/payload/platform/plugins/admin/hooks/per-turn-graph-pass-gate.sh +106 -0
- package/payload/platform/plugins/admin/hooks/pre-turn-graph-pass.sh +47 -91
- package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +32 -91
- package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +4 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js +65 -24
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/session-management/SKILL.md +2 -2
- package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +5 -5
- package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +6 -6
- package/payload/platform/plugins/business-assistant/references/profiling.md +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +3 -3
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +4 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +10 -10
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
- package/payload/platform/plugins/docs/references/admin-session.md +6 -6
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +3 -1
- package/payload/platform/plugins/docs/references/internals.md +5 -5
- package/payload/platform/plugins/docs/references/plugins-guide.md +3 -3
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +15 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +53 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js +2 -2
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts +23 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.js +15 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js +49 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts +20 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +86 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +32 -52
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +19 -83
- package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +87 -0
- package/payload/platform/plugins/memory/PLUGIN.md +4 -4
- package/payload/platform/plugins/memory/mcp/dist/index.js +40 -41
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts +3 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js +6 -6
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.js +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts +35 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +80 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +11 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +1 -1
- package/payload/platform/plugins/memory/mcp/scripts/graph/fixture.cypher +1 -1
- package/payload/platform/plugins/memory/references/graph-primitives.md +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +4 -4
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js +6 -6
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js.map +1 -1
- package/payload/platform/plugins/x-import/references/archive-shape.md +2 -2
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +3 -3
- package/payload/platform/scripts/admin-persist-audit.ts +18 -18
- package/payload/platform/scripts/identity-forbidden-token-check.mjs +2 -0
- package/payload/platform/scripts/seed-neo4j.sh +1 -0
- package/payload/platform/scripts/smoke-boot-services.sh +191 -41
- package/payload/platform/services/claude-session-manager/dist/http-server.js +3 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +3 -3
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +4 -4
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-AXOL2PPG.js → chunk-3TRIXQSJ.js} +101 -101
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{admin-DJW5VnQ1.js → admin-Dal5FWaD.js} +24 -24
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Breg9-99.js → architectureDiagram-Q4EWVU46-Do2zYONG.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DXf1_673.js → blockDiagram-DXYQGD6D-DpIX_bRu.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CSZ36W4-.js → c4Diagram-AHTNJAMY-Cz335ZSY.js} +1 -1
- package/payload/server/public/assets/channel-PC4_IjI1.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-DsrLk2eK.js → chunk-336JU56O-Dmknn3Wo.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-JpNfNf81.js → chunk-426QAEUC-ClfGjizN.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-DmClEAD0.js → chunk-4TB4RGXK-BPKctJ5c.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BI9blW3N.js → chunk-5FUZZQ4R-BWDCO9zw.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BbgttEFO.js → chunk-5PVQY5BW-BE34_Db0.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BX_LCFOx.js → chunk-EDXVE4YY-BysA-_6v.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Bb7EoD0w.js → chunk-ENJZ2VHE-BMVyMsqA.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DH8uQk-s.js → chunk-ICPOFSXX-C5KRdLV4.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-FH5kgXsw.js → chunk-OYMX7WX6-DVZD-AVc.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-D_ljlkHr.js → chunk-U2HBQHQK-DYmQdx8x.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-BX3EmZvm.js → chunk-X2U36JSP-DrZenIl3.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Vu7-E93h.js → chunk-YZCP3GAM-CDuGbvMq.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CwjhNPX5.js → chunk-ZZ45TVLE-COztbiu5.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B_pMa-SX.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BdBAgF20.js +1 -0
- package/payload/server/public/assets/clone-CkNt9L9r.js +1 -0
- package/payload/server/public/assets/{dagre-Ce6gsj0C.js → dagre-Cv9Ib2OM.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BaMGpsoy.js → dagre-KV5264BT-CoEd61KZ.js} +1 -1
- package/payload/server/public/assets/{data-BHASh0_Y.js → data-DbSORCl4.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-CHEE9rjq.js → diagram-5BDNPKRD-WXNzw3uW.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-C98sZLW1.js → diagram-G4DWMVQ6-C6IuQ2VV.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DNvMGsTZ.js → diagram-MMDJMWI5-CdJdir5A.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Cdte4IBa.js → diagram-TYMM5635-DxWKQH5T.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-C9VZsWXK.js → erDiagram-SMLLAGMA-C9fvs33K.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-tuP5fzzR.js → flowDiagram-DWJPFMVM-BO7Yrbqv.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-SJgRPUwx.js → ganttDiagram-T4ZO3ILL-CQJyWBZq.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DU_cI_50.js → gitGraphDiagram-UUTBAWPF-DZfQHrYw.js} +1 -1
- package/payload/server/public/assets/{graph-labels-DXlmm1og.js → graph-labels-BBB5jsZk.js} +1 -1
- package/payload/server/public/assets/{graph-BhEI09tF.js → graph-ys_joP2o.js} +1 -1
- package/payload/server/public/assets/{graphlib-D_nAsvmx.js → graphlib-C40H-Q5E.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-Cj2Y19m6.js → infoDiagram-42DDH7IO-CakfV5f9.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DMc1xq1y.js → ishikawaDiagram-UXIWVN3A-CPuBPyfa.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-C4CRAQma.js → journeyDiagram-VCZTEJTY-zpZm2Mmo.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CTr4AldU.js → kanban-definition-6JOO6SKY-DfJduQCo.js} +1 -1
- package/payload/server/public/assets/{line-Btlm_IAl.js → line-BpYQqO5R.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-DXFdj_y1.js → mermaid-parser.core-C5Mwi14D.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-DFBhoCr5.js → mermaid.core-D8qBtvvT.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-r9gVQybo.js → mindmap-definition-QFDTVHPH-Bli9JhIN.js} +1 -1
- package/payload/server/public/assets/{page-CDWu1Aft.js → page--bl5Y7ZI.js} +2 -2
- package/payload/server/public/assets/page-CwmZ76U7.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CHKZjSaH.js → pieDiagram-DEJITSTG-MsLXTSLg.js} +1 -1
- package/payload/server/public/assets/{public-DyGWgfaZ.js → public-DltgEfA9.js} +2 -2
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CSGR1HJr.js → quadrantDiagram-34T5L4WZ-De1sh2K0.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DZ6hX9D6.js → requirementDiagram-MS252O5E-D5Sfg1t_.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DbZBKh0m.js → sankeyDiagram-XADWPNL6-DHA3LIbv.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BHHpb8dt.js → sequenceDiagram-FGHM5R23-BE_QlG1B.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DQ1jiLB4.js → stateDiagram-FHFEXIEX-NIwp3F4u.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-pfk2F6st.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BlGiEU7p.js → timeline-definition-GMOUNBTQ-CPE9jyDF.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-DmEH-hsB.js → vennDiagram-DHZGUBPP-CKfoyYmH.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-C7KJJ40d.js → wardleyDiagram-NUSXRM2D-B9yJek-o.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-JDnusCaa.js → xychartDiagram-5P7HB3ND-DuCcSLqX.js} +1 -1
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/public/public.html +1 -1
- package/payload/server/server.js +133 -133
- package/payload/server/public/assets/channel-BmZnt3PP.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-kmMLFUpV.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BV7Ae8ct.js +0 -1
- package/payload/server/public/assets/clone-B5eqoN9I.js +0 -1
- package/payload/server/public/assets/page-cyvshX4Z.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CByT2RzF.js +0 -1
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
# "additionalContext":"..."}}` whose additionalContext begins with
|
|
27
27
|
# `[system: pre-turn-graph-pass-hook]`, names `database-operator` as
|
|
28
28
|
# the dispatch target, carries a `<prior-writes>` block with the
|
|
29
|
-
# fetched node, interpolates
|
|
29
|
+
# fetched node, interpolates sessionId from SESSION_NODE_ID,
|
|
30
30
|
# and carries NO `<conversation>` block (admin has the turn already).
|
|
31
31
|
# Dispatch log line carries `directive-shape=admin-walks`.
|
|
32
32
|
# - Prior-writes fetch fails (context endpoint 500): exit 0, dispatch
|
|
33
33
|
# still fires, `<prior-writes>` block resolves to `(none)`.
|
|
34
|
-
# -
|
|
35
|
-
# `
|
|
34
|
+
# - SESSION_NODE_ID unset: exit 0, additionalContext names
|
|
35
|
+
# `sessionId: <unset>`, and no GET to /api/admin/post-turn-context
|
|
36
36
|
# is attempted.
|
|
37
37
|
# - UserPromptSubmit stdin-shape regression: a synthetic UserPromptSubmit
|
|
38
38
|
# payload produces the documented stdout envelope; a stale Stop-shaped
|
|
@@ -125,7 +125,7 @@ PY
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
run_hook() {
|
|
128
|
-
# Args: role, specialist, ui_port,
|
|
128
|
+
# Args: role, specialist, ui_port, session_node_id, stdin_json
|
|
129
129
|
local role="$1"; local specialist="$2"; local ui_port="$3"
|
|
130
130
|
local conv="$4"; local stdin_json="$5"
|
|
131
131
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
@@ -137,7 +137,7 @@ run_hook() {
|
|
|
137
137
|
env_cmd+=("MAXY_UI_INTERNAL_PORT=${ui_port}")
|
|
138
138
|
fi
|
|
139
139
|
if [[ -n "$conv" ]]; then
|
|
140
|
-
env_cmd+=("
|
|
140
|
+
env_cmd+=("SESSION_NODE_ID=${conv}")
|
|
141
141
|
fi
|
|
142
142
|
env_cmd+=("ACCOUNT_ID=acct-test-0001")
|
|
143
143
|
printf '%s' "$stdin_json" | "${env_cmd[@]}" bash "$HOOK" >"$stdout_file" 2>"$stderr_file"
|
|
@@ -328,14 +328,14 @@ fi
|
|
|
328
328
|
if ! printf '%s' "$CTX_BODY" | grep -q 'database-operator'; then
|
|
329
329
|
fail "case-5 additionalContext must name the database-operator dispatch target"
|
|
330
330
|
fi
|
|
331
|
-
if ! printf '%s' "$CTX_BODY" | grep -q "
|
|
332
|
-
fail "case-5 additionalContext must interpolate
|
|
331
|
+
if ! printf '%s' "$CTX_BODY" | grep -q "sessionId: ${CONV_ID}"; then
|
|
332
|
+
fail "case-5 additionalContext must interpolate sessionId=${CONV_ID}"
|
|
333
333
|
fi
|
|
334
334
|
if ! printf '%s' "$CTX_BODY" | grep -q 'KnowledgeDocument'; then
|
|
335
335
|
fail "case-5 additionalContext <prior-writes> must include fetched labels"
|
|
336
336
|
fi
|
|
337
|
-
if ingest_lines | grep -qE "^dispatch
|
|
338
|
-
pass "case-5 dispatch path → exit 0 + JSON envelope with prior-writes,
|
|
337
|
+
if ingest_lines | grep -qE "^dispatch claudeSessionId=${OP_ID} sessionId=${CONV_ID} directive-shape=admin-walks "; then
|
|
338
|
+
pass "case-5 dispatch path → exit 0 + JSON envelope with prior-writes, sessionId, admin-walks shape"
|
|
339
339
|
else
|
|
340
340
|
fail "case-5 expected dispatch log line with directive-shape=admin-walks, got: $(ingest_lines)"
|
|
341
341
|
fi
|
|
@@ -356,7 +356,7 @@ else
|
|
|
356
356
|
fi
|
|
357
357
|
|
|
358
358
|
# ---------------------------------------------------------------------------
|
|
359
|
-
# Case 7:
|
|
359
|
+
# Case 7: SESSION_NODE_ID unset → sessionId:<unset>, no context GET.
|
|
360
360
|
# ---------------------------------------------------------------------------
|
|
361
361
|
cat > "$CTX_FILE" <<'JSON'
|
|
362
362
|
{"writes":[]}
|
|
@@ -365,12 +365,12 @@ JSON
|
|
|
365
365
|
run_hook "admin" "" "$LISTENER_PORT" "" "$ENV_BASIC"
|
|
366
366
|
[[ "$HOOK_RC" -eq 0 ]] || fail "case-7 expected rc=0, got $HOOK_RC"
|
|
367
367
|
CTX_BODY=$(additional_context)
|
|
368
|
-
if ! printf '%s' "$CTX_BODY" | grep -q '
|
|
369
|
-
fail "case-7 additionalContext must contain '
|
|
368
|
+
if ! printf '%s' "$CTX_BODY" | grep -q 'sessionId: <unset>'; then
|
|
369
|
+
fail "case-7 additionalContext must contain 'sessionId: <unset>'"
|
|
370
370
|
fi
|
|
371
371
|
GET_COUNT=$(context_get_count)
|
|
372
372
|
if [[ "$GET_COUNT" == "0" ]]; then
|
|
373
|
-
pass "case-7
|
|
373
|
+
pass "case-7 SESSION_NODE_ID unset → no GET /api/admin/post-turn-context attempted"
|
|
374
374
|
else
|
|
375
375
|
fail "case-7 expected zero context GETs, got: $GET_COUNT"
|
|
376
376
|
fi
|
|
@@ -194,7 +194,7 @@ fi
|
|
|
194
194
|
# --- Case 5 (Task 177): ordered-turns happy path -----------------------
|
|
195
195
|
# Transcript with four turns: user-A, asst-A-reply, user-B, asst-B-reply.
|
|
196
196
|
# Envelope `turns` array must carry exactly that order, oldest first.
|
|
197
|
-
# Envelope shape: { turns,
|
|
197
|
+
# Envelope shape: { turns, sessionId, accountId, occurredAt } — no
|
|
198
198
|
# top-level operatorMessage / assistantReply.
|
|
199
199
|
ORDERED_TRANSCRIPT=$(mktemp); TMPFILES+=("$ORDERED_TRANSCRIPT")
|
|
200
200
|
{
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared plumbing for the two admin-side hooks that orchestrate admin-walked
|
|
3
|
+
# graph writes: `pre-turn-graph-pass.sh` (UserPromptSubmit) and
|
|
4
|
+
# `session-end-retrospective.sh` (Stop on operator end-intent). Task 439.
|
|
5
|
+
#
|
|
6
|
+
# Both hooks are the same process — same stdin parse, same role / specialist
|
|
7
|
+
# / transcript guards, same log-ingest envelope, same admin-walks contract
|
|
8
|
+
# dispatching per-write briefs to `database-operator`. Divergence across
|
|
9
|
+
# Tasks 303 → 412 → 425 → 430 → 282 made shared plumbing a recurring-bug
|
|
10
|
+
# surface; this file is the single body. Only the trigger-specific gating
|
|
11
|
+
# and the dispatch directive text live in each hook.
|
|
12
|
+
#
|
|
13
|
+
# Caller contract:
|
|
14
|
+
# 1. Set `AGP_LOG_TAG` to the hook's identifying tag before sourcing.
|
|
15
|
+
# `pre-turn-graph-pass` and `session-retrospective` are the two
|
|
16
|
+
# current values; the helper itself does not log under its own tag.
|
|
17
|
+
# 2. Source this file.
|
|
18
|
+
# 3. Call `agp_setup_logging`, `agp_read_stdin`, `agp_parse_stdin`,
|
|
19
|
+
# `agp_guard_role_specialist`, `agp_guard_transcript` in that order.
|
|
20
|
+
# Each guard exits 0 from inside the helper when its condition fires
|
|
21
|
+
# (sourced `exit` exits the caller).
|
|
22
|
+
# 4. After the guards return, the hook proceeds with its event-specific
|
|
23
|
+
# body (sentinel-grep + intent token for session-end; post-turn-context
|
|
24
|
+
# fetch + directive envelope for pre-turn).
|
|
25
|
+
#
|
|
26
|
+
# Globals set by the helper (read by callers):
|
|
27
|
+
# INPUT — raw stdin (empty when none piped)
|
|
28
|
+
# SESSION_ID — parsed `session_id` field, empty on parse failure
|
|
29
|
+
# TRANSCRIPT_PATH — parsed `transcript_path` field
|
|
30
|
+
# HOOK_EVENT_NAME — parsed `hook_event_name` field
|
|
31
|
+
# SESSION_ID_REPORTED — `$SESSION_ID` or the literal "unknown"
|
|
32
|
+
# UI_BASE — `http://127.0.0.1:${MAXY_UI_INTERNAL_PORT}`
|
|
33
|
+
# LOG_INGEST_URL — `${UI_BASE}/api/admin/log-ingest`
|
|
34
|
+
#
|
|
35
|
+
# Functions:
|
|
36
|
+
# agp_setup_logging — resolves UI port + URLs, or exits 0 with
|
|
37
|
+
# a stderr loud-fail when the port is unset
|
|
38
|
+
# (fail-open so a missing port never bricks
|
|
39
|
+
# the operator).
|
|
40
|
+
# agp_emit_log <line> — POSTs `{tag, level:"info", line}` to
|
|
41
|
+
# log-ingest; silent on network failure.
|
|
42
|
+
# agp_read_stdin — slurps stdin into `INPUT`, leaves it empty
|
|
43
|
+
# when no input is piped.
|
|
44
|
+
# agp_parse_stdin — populates `SESSION_ID`, `TRANSCRIPT_PATH`,
|
|
45
|
+
# `HOOK_EVENT_NAME`, `SESSION_ID_REPORTED`.
|
|
46
|
+
# agp_guard_role_specialist — exits 0 with `trigger-skipped` log on
|
|
47
|
+
# `role-not-admin` or `is-specialist`.
|
|
48
|
+
# agp_guard_transcript — exits 0 with `trigger-skipped` log on
|
|
49
|
+
# `empty-stdin` (when $1="require-stdin") or
|
|
50
|
+
# `missing-transcript`.
|
|
51
|
+
|
|
52
|
+
if [ -z "${AGP_LOG_TAG:-}" ]; then
|
|
53
|
+
echo "[admin-graph-pass-common] AGP_LOG_TAG not set by caller" >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
agp_setup_logging() {
|
|
58
|
+
local ui_port="${MAXY_UI_INTERNAL_PORT:-}"
|
|
59
|
+
if [ -z "$ui_port" ]; then
|
|
60
|
+
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-env env=MAXY_UI_INTERNAL_PORT" >&2
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
UI_BASE="http://127.0.0.1:${ui_port}"
|
|
64
|
+
LOG_INGEST_URL="${UI_BASE}/api/admin/log-ingest"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
agp_emit_log() {
|
|
68
|
+
local line="$1"
|
|
69
|
+
curl -sS -o /dev/null -X POST \
|
|
70
|
+
-H 'Content-Type: application/json' \
|
|
71
|
+
--max-time 2 \
|
|
72
|
+
--data "$(AGP_TAG="$AGP_LOG_TAG" AGP_LINE="$line" python3 -c '
|
|
73
|
+
import os, json
|
|
74
|
+
print(json.dumps({"tag": os.environ["AGP_TAG"], "level": "info", "line": os.environ["AGP_LINE"]}))
|
|
75
|
+
')" \
|
|
76
|
+
"$LOG_INGEST_URL" 2>/dev/null || true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
agp_read_stdin() {
|
|
80
|
+
INPUT=""
|
|
81
|
+
if [ ! -t 0 ]; then
|
|
82
|
+
INPUT=$(cat)
|
|
83
|
+
fi
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
agp_parse_stdin() {
|
|
87
|
+
local parsed
|
|
88
|
+
parsed=$(printf '%s' "$INPUT" | python3 -c '
|
|
89
|
+
import sys, json
|
|
90
|
+
try:
|
|
91
|
+
d = json.load(sys.stdin)
|
|
92
|
+
sid = d.get("session_id", "") or ""
|
|
93
|
+
tpath = d.get("transcript_path", "") or ""
|
|
94
|
+
event = d.get("hook_event_name", "") or ""
|
|
95
|
+
print(f"{sid}\t{tpath}\t{event}")
|
|
96
|
+
except Exception:
|
|
97
|
+
print("\t\t")
|
|
98
|
+
' 2>/dev/null)
|
|
99
|
+
SESSION_ID="${parsed%% *}"
|
|
100
|
+
local rest="${parsed#* }"
|
|
101
|
+
TRANSCRIPT_PATH="${rest%% *}"
|
|
102
|
+
HOOK_EVENT_NAME="${rest#* }"
|
|
103
|
+
SESSION_ID_REPORTED="${SESSION_ID:-unknown}"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
agp_guard_role_specialist() {
|
|
107
|
+
if [ "${MAXY_SESSION_ROLE:-}" != "admin" ]; then
|
|
108
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=role-not-admin"
|
|
109
|
+
exit 0
|
|
110
|
+
fi
|
|
111
|
+
# Recursion guard. A specialist subagent dispatched via the Task tool
|
|
112
|
+
# (e.g. database-operator running the operator's graph writes) carries
|
|
113
|
+
# `MAXY_SPECIALIST=<name>` on the PTY env. Skip — the hooks only fire
|
|
114
|
+
# for the operator's own admin session.
|
|
115
|
+
#
|
|
116
|
+
# Invariant the PTY spawner is expected to honour: `MAXY_SPECIALIST` is
|
|
117
|
+
# either set to a non-empty specialist name OR absent. An explicit empty
|
|
118
|
+
# string from a wrapper would slip the recursion guard here (`-n ""` is
|
|
119
|
+
# false) — that would be a defect at the spawn site, not here.
|
|
120
|
+
if [ -n "${MAXY_SPECIALIST:-}" ]; then
|
|
121
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=is-specialist specialist=${MAXY_SPECIALIST}"
|
|
122
|
+
exit 0
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Usage: agp_guard_transcript [require-stdin]
|
|
127
|
+
# Pass `require-stdin` (session-end-retrospective) to emit `empty-stdin` on a
|
|
128
|
+
# blank `INPUT`. The pre-turn-graph-pass hook calls without the flag because
|
|
129
|
+
# its `wrong-hook-event` guard already runs before this point and parse
|
|
130
|
+
# failure on a non-empty INPUT collapses to `missing-transcript` cleanly.
|
|
131
|
+
agp_guard_transcript() {
|
|
132
|
+
if [ "${1:-}" = "require-stdin" ] && [ -z "$INPUT" ]; then
|
|
133
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=empty-stdin"
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
137
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=missing-transcript"
|
|
138
|
+
exit 0
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Walks $TRANSCRIPT_PATH and reports, in one line on stdout:
|
|
143
|
+
# <real-user-present:yes|no>\t<matched-tool-name-or-empty>
|
|
144
|
+
# A "real user" record matches `session-end-retrospective.sh`'s `is_real_user`
|
|
145
|
+
# — `type=user, message.role=user`, content is a string OR a list with at
|
|
146
|
+
# least one non-`tool_result` block. `tool_result`-only user records are tool
|
|
147
|
+
# replies, never turn starts.
|
|
148
|
+
#
|
|
149
|
+
# Every assistant `tool_use` block that appears AFTER the latest real-user
|
|
150
|
+
# record is matched against the alternation passed as `$1` (an extended-regex
|
|
151
|
+
# alternation of literal `tool_use.name` values). The synthetic name
|
|
152
|
+
# `Task:<subagent_type>` is also matched, so callers can release on a
|
|
153
|
+
# `Task` tool_use with `input.subagent_type=<name>` by including
|
|
154
|
+
# `Task:<name>` in the alternation. Returns the first match encountered after
|
|
155
|
+
# the latest real-user record (oldest-first) so the release log carries the
|
|
156
|
+
# matched tool's actual name. Empty match means none of the release patterns
|
|
157
|
+
# appeared since the operator's last turn.
|
|
158
|
+
agp_scan_post_user_toolnames() {
|
|
159
|
+
local pattern="$1"
|
|
160
|
+
TRANSCRIPT_PATH="$TRANSCRIPT_PATH" AGP_PATTERN="$pattern" python3 - <<'PY'
|
|
161
|
+
import os, json, re
|
|
162
|
+
|
|
163
|
+
pattern = re.compile(os.environ["AGP_PATTERN"])
|
|
164
|
+
path = os.environ["TRANSCRIPT_PATH"]
|
|
165
|
+
|
|
166
|
+
def is_real_user(rec):
|
|
167
|
+
if not isinstance(rec, dict) or rec.get("type") != "user":
|
|
168
|
+
return False
|
|
169
|
+
msg = rec.get("message")
|
|
170
|
+
if not isinstance(msg, dict) or msg.get("role") != "user":
|
|
171
|
+
return False
|
|
172
|
+
content = msg.get("content")
|
|
173
|
+
if isinstance(content, str):
|
|
174
|
+
return True
|
|
175
|
+
if isinstance(content, list):
|
|
176
|
+
for b in content:
|
|
177
|
+
if isinstance(b, dict) and b.get("type") != "tool_result":
|
|
178
|
+
return True
|
|
179
|
+
return False
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
def assistant_tool_use_names(rec):
|
|
183
|
+
if not isinstance(rec, dict) or rec.get("type") != "assistant":
|
|
184
|
+
return []
|
|
185
|
+
msg = rec.get("message", {}) or {}
|
|
186
|
+
if msg.get("role") != "assistant":
|
|
187
|
+
return []
|
|
188
|
+
content = msg.get("content")
|
|
189
|
+
if not isinstance(content, list):
|
|
190
|
+
return []
|
|
191
|
+
names = []
|
|
192
|
+
for b in content:
|
|
193
|
+
if isinstance(b, dict) and b.get("type") == "tool_use":
|
|
194
|
+
n = b.get("name")
|
|
195
|
+
if not isinstance(n, str):
|
|
196
|
+
continue
|
|
197
|
+
names.append(n)
|
|
198
|
+
if n == "Task":
|
|
199
|
+
inp = b.get("input")
|
|
200
|
+
if isinstance(inp, dict):
|
|
201
|
+
st = inp.get("subagent_type")
|
|
202
|
+
if isinstance(st, str) and st:
|
|
203
|
+
names.append(f"Task:{st}")
|
|
204
|
+
return names
|
|
205
|
+
|
|
206
|
+
records = []
|
|
207
|
+
try:
|
|
208
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
209
|
+
for raw in f:
|
|
210
|
+
raw = raw.strip()
|
|
211
|
+
if not raw:
|
|
212
|
+
continue
|
|
213
|
+
try:
|
|
214
|
+
records.append(json.loads(raw))
|
|
215
|
+
except Exception:
|
|
216
|
+
continue
|
|
217
|
+
except Exception:
|
|
218
|
+
print("no\t")
|
|
219
|
+
raise SystemExit(0)
|
|
220
|
+
|
|
221
|
+
latest_user_idx = -1
|
|
222
|
+
for i in range(len(records) - 1, -1, -1):
|
|
223
|
+
if is_real_user(records[i]):
|
|
224
|
+
latest_user_idx = i
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
if latest_user_idx == -1:
|
|
228
|
+
print("no\t")
|
|
229
|
+
raise SystemExit(0)
|
|
230
|
+
|
|
231
|
+
matched = ""
|
|
232
|
+
for r in records[latest_user_idx + 1:]:
|
|
233
|
+
for n in assistant_tool_use_names(r):
|
|
234
|
+
if pattern.fullmatch(n):
|
|
235
|
+
matched = n
|
|
236
|
+
break
|
|
237
|
+
if matched:
|
|
238
|
+
break
|
|
239
|
+
|
|
240
|
+
print(f"yes\t{matched}")
|
|
241
|
+
PY
|
|
242
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop hook — per-turn gate. Task 440.
|
|
3
|
+
#
|
|
4
|
+
# Mirrors `session-end-retrospective.sh`'s structural primitive (exit 2 +
|
|
5
|
+
# stderr instruction + sentinel-grep release) but fires on every admin turn
|
|
6
|
+
# end rather than only on operator end-intent. Closes the gap left when
|
|
7
|
+
# Task 425 moved the per-turn graph pass off Stop onto UserPromptSubmit:
|
|
8
|
+
# the UserPromptSubmit `additionalContext` directive carries the prior-writes
|
|
9
|
+
# skip-list and the instruction, but admin can silently drop the directive
|
|
10
|
+
# because UserPromptSubmit's only blocking shape erases the prompt without
|
|
11
|
+
# feeding stderr to Claude. The structural fix is here — Stop's exit-2
|
|
12
|
+
# stderr-to-Claude contract gives the gate a release path admin can act on.
|
|
13
|
+
#
|
|
14
|
+
# Release conditions, evaluated against the operator's JSONL since the
|
|
15
|
+
# latest real-user message (string content OR a content list with at least
|
|
16
|
+
# one non-`tool_result` block):
|
|
17
|
+
# A. admin dispatched the writes — a `Task` `tool_use` whose
|
|
18
|
+
# `input.subagent_type` is `database-operator` appeared.
|
|
19
|
+
# B. admin declined the writes — a `tool_use` whose `name` is exactly
|
|
20
|
+
# `mcp__admin__pre-turn-graph-pass-skip` appeared. The sentinel takes a
|
|
21
|
+
# `reason` string (free-form, e.g. `no-new-entities`,
|
|
22
|
+
# `turn-is-acknowledgement`); the tool records the call for audit but
|
|
23
|
+
# takes no graph action.
|
|
24
|
+
#
|
|
25
|
+
# Either release path emits `gate-released sessionId=<id> via=<dispatch|skip-sentinel>`
|
|
26
|
+
# and exits 0. Both Stop-hook tags emit independently and compose: the
|
|
27
|
+
# session-end retrospective gates only on `/end` intent and uses its own
|
|
28
|
+
# sentinel; this hook gates every turn and uses the two release patterns
|
|
29
|
+
# above.
|
|
30
|
+
#
|
|
31
|
+
# Recursion guard — the shared `agp_guard_role_specialist` skips when
|
|
32
|
+
# `MAXY_SPECIALIST` is set, so the database-operator subagent's own turn
|
|
33
|
+
# end never re-triggers this gate.
|
|
34
|
+
#
|
|
35
|
+
# Loop-safety — once release path A or B fires, the matching tool_use sits
|
|
36
|
+
# in the JSONL and every subsequent Stop on the same operator-prompt window
|
|
37
|
+
# sees it and exits 0 with `gate-released`. The Task 412 loop case is
|
|
38
|
+
# avoided because the release decision is JSONL-based, never a re-fire of
|
|
39
|
+
# the directive.
|
|
40
|
+
#
|
|
41
|
+
# Input: Claude Code's Stop hook stdin shape
|
|
42
|
+
# { "session_id": "<intrinsic>", "transcript_path": "<jsonl path>", ... }
|
|
43
|
+
|
|
44
|
+
set -uo pipefail
|
|
45
|
+
|
|
46
|
+
AGP_LOG_TAG="per-turn-graph-pass-gate"
|
|
47
|
+
AGP_LIB="$(dirname "$0")/lib/admin-graph-pass-common.sh"
|
|
48
|
+
if [ ! -f "$AGP_LIB" ]; then
|
|
49
|
+
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-helper path=${AGP_LIB}" >&2
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
# shellcheck source=lib/admin-graph-pass-common.sh
|
|
53
|
+
source "$AGP_LIB"
|
|
54
|
+
|
|
55
|
+
agp_setup_logging
|
|
56
|
+
agp_read_stdin
|
|
57
|
+
agp_parse_stdin
|
|
58
|
+
agp_guard_role_specialist
|
|
59
|
+
agp_guard_transcript require-stdin
|
|
60
|
+
|
|
61
|
+
# Release alternation: the MCP-prefixed skip-sentinel name, or a Task
|
|
62
|
+
# tool_use with `input.subagent_type=database-operator` (matched as the
|
|
63
|
+
# synthetic name `Task:database-operator` by the helper).
|
|
64
|
+
RELEASE_PATTERN='mcp__admin__pre-turn-graph-pass-skip|Task:database-operator'
|
|
65
|
+
|
|
66
|
+
INSPECTION=$(agp_scan_post_user_toolnames "$RELEASE_PATTERN")
|
|
67
|
+
USER_PRESENT="${INSPECTION%% *}"
|
|
68
|
+
MATCHED_NAME="${INSPECTION#* }"
|
|
69
|
+
|
|
70
|
+
if [ "$USER_PRESENT" != "yes" ]; then
|
|
71
|
+
# No real-user message in the transcript yet — e.g. the SessionStart
|
|
72
|
+
# bootstrap turn, an empty transcript, or a session that only contains
|
|
73
|
+
# tool_result-shaped user records. Nothing to gate against.
|
|
74
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID} reason=no-real-user"
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [ -n "$MATCHED_NAME" ]; then
|
|
79
|
+
case "$MATCHED_NAME" in
|
|
80
|
+
Task:database-operator)
|
|
81
|
+
VIA="dispatch"
|
|
82
|
+
;;
|
|
83
|
+
mcp__admin__pre-turn-graph-pass-skip)
|
|
84
|
+
VIA="skip-sentinel"
|
|
85
|
+
;;
|
|
86
|
+
*)
|
|
87
|
+
VIA="unknown"
|
|
88
|
+
;;
|
|
89
|
+
esac
|
|
90
|
+
agp_emit_log "gate-released sessionId=${SESSION_ID} via=${VIA}"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
agp_emit_log "gate-blocked sessionId=${SESSION_ID} reason=sentinel-absent"
|
|
95
|
+
|
|
96
|
+
cat >&2 <<'INSTRUCTION'
|
|
97
|
+
Before this turn closes, do the per-turn graph pass. Scan the operator's latest message for any new graph-worthy content not already covered by the prior-writes block in this turn's pre-turn-graph-pass directive — mentions of named entities, ideas or learnings the operator stated, contacts named for the first time, tasks the operator committed to, decisions, anything the schema admits.
|
|
98
|
+
|
|
99
|
+
For each candidate write, dispatch one Task to the `database-operator` specialist with a brief that names the label, the relevant properties, and (when attaching to an existing node) the elementId from the prior-writes list. Skip writes the prior-writes list already covers.
|
|
100
|
+
|
|
101
|
+
If the turn supports no new writes, call the `pre-turn-graph-pass-skip` MCP tool with a short `reason` (e.g. `no-new-entities`, `turn-is-acknowledgement`) so the gate releases. Calling that tool is the deterministic signal that you considered the turn and chose not to write.
|
|
102
|
+
|
|
103
|
+
Either path releases this gate and lets the turn close. Do not surface this dispatch in your reply to the operator.
|
|
104
|
+
INSTRUCTION
|
|
105
|
+
|
|
106
|
+
exit 2
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# UserPromptSubmit hook — pre-turn graph pass. Tasks 425, 430.
|
|
2
|
+
# UserPromptSubmit hook — pre-turn graph pass. Tasks 425, 430, 439.
|
|
3
3
|
#
|
|
4
4
|
# Fires once per admin-agent operator prompt submission. Emits a
|
|
5
5
|
# directive into the admin agent's `additionalContext` that instructs
|
|
@@ -9,32 +9,19 @@
|
|
|
9
9
|
# per candidate write. Admin is the session-walker; the specialist only
|
|
10
10
|
# sees a narrowly-scoped per-write brief, never the conversation.
|
|
11
11
|
#
|
|
12
|
-
# Task 430 corrects the Task 425 orchestration shape
|
|
13
|
-
# packaged the full conversation blob into the specialist's prompt
|
|
14
|
-
# collapsed
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# schema doctrine read as binding the agent (Task 199 / 995). The
|
|
19
|
-
# session-end retrospective never had this defect because admin walked
|
|
20
|
-
# the transcript itself and dispatched per-item briefs to the
|
|
21
|
-
# specialist — Task 430 brings the per-turn pass to the same shape.
|
|
12
|
+
# Task 430 corrects the Task 425 orchestration shape (the Task 425 hook
|
|
13
|
+
# packaged the full conversation blob into the specialist's prompt and
|
|
14
|
+
# collapsed data and instruction channels into one context, so operator
|
|
15
|
+
# prose pattern-matching as a writer-binding instruction was treated as
|
|
16
|
+
# binding and the pass self-denied — same failure class as the writer-POV
|
|
17
|
+
# schema doctrine read as binding the agent, Tasks 199 / 995).
|
|
22
18
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `session-end-retrospective.sh` Stop hook walks the full transcript at
|
|
30
|
-
# `/end` and picks up the final assistant reply.
|
|
31
|
-
#
|
|
32
|
-
# The doctrinal early exits remain:
|
|
33
|
-
# - MAXY_SESSION_ROLE must equal "admin" → reason=role-not-admin
|
|
34
|
-
# - MAXY_SPECIALIST must be empty → reason=is-specialist
|
|
35
|
-
# `missing-transcript` is retained as a safety exit (the prior-writes
|
|
36
|
-
# context fetch still needs a real session). `wrong-hook-event` rejects
|
|
37
|
-
# stale Stop-shaped fires.
|
|
19
|
+
# Task 439 converges the shared plumbing — stdin parse, role / specialist
|
|
20
|
+
# / transcript guards, log-ingest envelope — with `session-end-retrospective.sh`
|
|
21
|
+
# via `lib/admin-graph-pass-common.sh`. The only event-specific code in
|
|
22
|
+
# this hook is the wrong-hook-event check, the post-turn-context fetch,
|
|
23
|
+
# the directive-body composition, and the UserPromptSubmit JSON envelope
|
|
24
|
+
# emission on stdout.
|
|
38
25
|
#
|
|
39
26
|
# Input: Claude Code's UserPromptSubmit hook stdin shape
|
|
40
27
|
# { "session_id": "<intrinsic>", "transcript_path": "<jsonl path>",
|
|
@@ -46,76 +33,45 @@
|
|
|
46
33
|
# Output: stdout JSON of the documented UserPromptSubmit shape:
|
|
47
34
|
# { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit",
|
|
48
35
|
# "additionalContext": "<directive body>" } }
|
|
49
|
-
# exit 0.
|
|
50
|
-
# shape, the skip-list, and the silent-execution rule at fire time, so
|
|
51
|
-
# the admin agent does not need a matching IDENTITY.md section to act
|
|
52
|
-
# on it (Task 437 — IDENTITY is operator-facing prose, not a protocol
|
|
53
|
-
# manual). The agent performs the per-write dispatches as a background
|
|
54
|
-
# pass and then continues with its normal response to the operator.
|
|
36
|
+
# exit 0.
|
|
55
37
|
|
|
56
38
|
set -uo pipefail
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
AGP_LOG_TAG="pre-turn-graph-pass"
|
|
41
|
+
AGP_LIB="$(dirname "$0")/lib/admin-graph-pass-common.sh"
|
|
42
|
+
if [ ! -f "$AGP_LIB" ]; then
|
|
43
|
+
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-helper path=${AGP_LIB}" >&2
|
|
61
44
|
exit 0
|
|
62
45
|
fi
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
CONTEXT_URL="${UI_BASE}/api/admin/post-turn-context"
|
|
46
|
+
# shellcheck source=lib/admin-graph-pass-common.sh
|
|
47
|
+
source "$AGP_LIB"
|
|
66
48
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
curl -sS -o /dev/null -X POST \
|
|
70
|
-
-H 'Content-Type: application/json' \
|
|
71
|
-
--max-time 2 \
|
|
72
|
-
--data "$(python3 -c '
|
|
73
|
-
import sys, json
|
|
74
|
-
print(json.dumps({"tag": "pre-turn-graph-pass", "level": "info", "line": sys.argv[1]}))
|
|
75
|
-
' "$line")" \
|
|
76
|
-
"$LOG_INGEST_URL" 2>/dev/null || true
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
INPUT=""
|
|
80
|
-
if [ ! -t 0 ]; then
|
|
81
|
-
INPUT=$(cat)
|
|
82
|
-
fi
|
|
49
|
+
agp_setup_logging
|
|
50
|
+
CONTEXT_URL="${UI_BASE}/api/admin/post-turn-context"
|
|
83
51
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
try:
|
|
87
|
-
d = json.load(sys.stdin)
|
|
88
|
-
sid = d.get("session_id", "") or ""
|
|
89
|
-
tpath = d.get("transcript_path", "") or ""
|
|
90
|
-
event = d.get("hook_event_name", "") or ""
|
|
91
|
-
print(f"{sid}\t{tpath}\t{event}")
|
|
92
|
-
except Exception:
|
|
93
|
-
print("\t\t")
|
|
94
|
-
' 2>/dev/null)
|
|
95
|
-
SESSION_ID="${PARSED%% *}"
|
|
96
|
-
REST="${PARSED#* }"
|
|
97
|
-
TRANSCRIPT_PATH="${REST%% *}"
|
|
98
|
-
HOOK_EVENT_NAME="${REST#* }"
|
|
99
|
-
SESSION_ID_REPORTED="${SESSION_ID:-unknown}"
|
|
52
|
+
agp_read_stdin
|
|
53
|
+
agp_parse_stdin
|
|
100
54
|
|
|
55
|
+
# Wrong-hook-event guard. Pre-turn-graph-pass is UserPromptSubmit-only;
|
|
56
|
+
# reject stale Stop-shaped payloads before the role / transcript guards
|
|
57
|
+
# run so the operator observes a `wrong-hook-event` skip line, not a
|
|
58
|
+
# misleading `missing-transcript`.
|
|
101
59
|
if [ -n "$HOOK_EVENT_NAME" ] && [ "$HOOK_EVENT_NAME" != "UserPromptSubmit" ]; then
|
|
102
|
-
|
|
103
|
-
exit 0
|
|
104
|
-
fi
|
|
105
|
-
if [ "${MAXY_SESSION_ROLE:-}" != "admin" ]; then
|
|
106
|
-
emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=role-not-admin"
|
|
107
|
-
exit 0
|
|
108
|
-
fi
|
|
109
|
-
if [ -n "${MAXY_SPECIALIST:-}" ]; then
|
|
110
|
-
emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=is-specialist specialist=${MAXY_SPECIALIST}"
|
|
111
|
-
exit 0
|
|
112
|
-
fi
|
|
113
|
-
if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
114
|
-
emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=missing-transcript"
|
|
60
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=wrong-hook-event event=${HOOK_EVENT_NAME}"
|
|
115
61
|
exit 0
|
|
116
62
|
fi
|
|
117
63
|
|
|
118
|
-
|
|
64
|
+
agp_guard_role_specialist
|
|
65
|
+
agp_guard_transcript
|
|
66
|
+
|
|
67
|
+
# Task 429 — the env var carries the graph `:Conversation.sessionId`.
|
|
68
|
+
# Claude Code's own stdin `session_id` field (parsed into `SESSION_ID`
|
|
69
|
+
# by `agp_parse_stdin`) is a distinct identifier (JSONL filename); one
|
|
70
|
+
# graph sessionId can map to many Claude session ids. Shadow the lib's
|
|
71
|
+
# `SESSION_ID` into `CLAUDE_SESSION_ID` so the rest of this hook can
|
|
72
|
+
# refer to the graph identifier as `SESSION_ID` without collision.
|
|
73
|
+
CLAUDE_SESSION_ID="$SESSION_ID"
|
|
74
|
+
SESSION_ID="${SESSION_NODE_ID:-}"
|
|
119
75
|
|
|
120
76
|
# Fetch the actual content of every node already written under this
|
|
121
77
|
# conversation, account-scoped. The route runs loopback-only — the hook
|
|
@@ -125,9 +81,9 @@ CONVERSATION_ID="${CONVERSATION_NODE_ID:-}"
|
|
|
125
81
|
# elementId admin already resolved when attaching to an existing node).
|
|
126
82
|
PRIOR_WRITES="(none)"
|
|
127
83
|
PRIOR_WRITES_COUNT=0
|
|
128
|
-
if [ -n "$
|
|
84
|
+
if [ -n "$SESSION_ID" ]; then
|
|
129
85
|
CONTEXT_JSON=$(curl -sS --max-time 5 -G \
|
|
130
|
-
--data-urlencode "
|
|
86
|
+
--data-urlencode "sessionId=${SESSION_ID}" \
|
|
131
87
|
--data-urlencode "accountId=${ACCOUNT_ID:-}" \
|
|
132
88
|
"$CONTEXT_URL" 2>/dev/null || echo '')
|
|
133
89
|
if [ -n "$CONTEXT_JSON" ]; then
|
|
@@ -171,8 +127,8 @@ fi
|
|
|
171
127
|
# Compose the directive body before logging so contextBytes reflects
|
|
172
128
|
# the actual payload size handed to admin (skip-list + directive prose).
|
|
173
129
|
DIRECTIVE_BODY=$(ACCOUNT_ID_OUT="${ACCOUNT_ID:-<unset>}" \
|
|
174
|
-
|
|
175
|
-
|
|
130
|
+
CLAUDE_SESSION_ID_OUT="${CLAUDE_SESSION_ID}" \
|
|
131
|
+
SESSION_ID_OUT="${SESSION_ID:-<unset>}" \
|
|
176
132
|
PRIOR_WRITES_OUT="${PRIOR_WRITES}" \
|
|
177
133
|
python3 - <<'PY'
|
|
178
134
|
import os
|
|
@@ -186,8 +142,8 @@ body = (
|
|
|
186
142
|
"If the turn supports no new writes, dispatch nothing. "
|
|
187
143
|
"Do not surface these dispatches in your reply to the operator. After the dispatches return, continue with your normal response to the operator's prompt.\n\n"
|
|
188
144
|
f"accountId: {os.environ['ACCOUNT_ID_OUT']}\n"
|
|
189
|
-
f"
|
|
190
|
-
f"
|
|
145
|
+
f"claudeSessionId: {os.environ['CLAUDE_SESSION_ID_OUT']}\n"
|
|
146
|
+
f"sessionId: {os.environ['SESSION_ID_OUT']}\n\n"
|
|
191
147
|
"<prior-writes>\n"
|
|
192
148
|
f"{os.environ['PRIOR_WRITES_OUT']}\n"
|
|
193
149
|
"</prior-writes>\n"
|
|
@@ -197,7 +153,7 @@ PY
|
|
|
197
153
|
)
|
|
198
154
|
CONTEXT_BYTES=${#DIRECTIVE_BODY}
|
|
199
155
|
|
|
200
|
-
|
|
156
|
+
agp_emit_log "dispatch claudeSessionId=${CLAUDE_SESSION_ID} sessionId=${SESSION_ID:-<unset>} directive-shape=admin-walks priorWritesCount=${PRIOR_WRITES_COUNT} contextBytes=${CONTEXT_BYTES} ms=0"
|
|
201
157
|
|
|
202
158
|
# Emit the documented UserPromptSubmit JSON envelope on stdout. Python
|
|
203
159
|
# handles JSON encoding so embedded newlines, quotes, and backslashes
|