@rubytech/create-maxy-code 0.1.419 → 0.1.427
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/neo4j/schema.cypher +13 -0
- package/payload/platform/package-lock.json +5158 -2922
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +5 -1
- package/payload/platform/plugins/admin/hooks/__tests__/datetime-inject.test.sh +119 -0
- package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +22 -0
- package/payload/platform/plugins/admin/hooks/datetime-inject.sh +115 -0
- package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +14 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +62 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +44 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +96 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
- package/payload/platform/plugins/admin/skills/datetime/SKILL.md +9 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -2
- package/payload/platform/plugins/docs/references/cross-account-authority.md +8 -0
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +2 -2
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +73 -4
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/mailbox-resolve.test.js +42 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/mailbox-resolve.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/index.js +1 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +4 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +5 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +25 -3
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +67 -12
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts +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 +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +8 -0
- package/payload/platform/plugins/memory/PLUGIN.md +3 -0
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +30 -1
- package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +50 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +5 -2
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js +50 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js +22 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts +35 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js +36 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts +11 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js +19 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +85 -3
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js +32 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +14 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +94 -4
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +23 -7
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js +30 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts +29 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +47 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +5 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.js +20 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js +15 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js +5 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.d.ts +9 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.js +18 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +17 -14
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +7 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +13 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -0
- package/payload/platform/plugins/work/PLUGIN.md +1 -1
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.js +72 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.js +67 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js +66 -0
- package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.js +3 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts +6 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +41 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
- package/payload/platform/scripts/lib/provision-account-dir.sh +2 -1
- package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts +39 -0
- package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-admin-census.js +122 -0
- package/payload/platform/services/claude-session-manager/dist/account-admin-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-timezone.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/account-timezone.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-timezone.js +126 -0
- package/payload/platform/services/claude-session-manager/dist/account-timezone.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +53 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +52 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts +37 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js +80 -0
- package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts +70 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js +129 -0
- package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +17 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +40 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.js +37 -50
- package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +22 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +22 -1
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +44 -78
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +22 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +22 -1
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +42 -38
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +66 -82
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/account.json +2 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +3 -2
- package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
- package/payload/server/public/assets/{AdminLoginScreens-B1PkOF66.js → AdminLoginScreens-yAIhWcGc.js} +1 -1
- package/payload/server/public/assets/AdminShell-Bk1AFQ4D.js +2 -0
- package/payload/server/public/assets/{Checkbox-Bm9Er726.js → Checkbox-B_lfjsqd.js} +1 -1
- package/payload/server/public/assets/Transcript-D67OZnVM.js +2 -0
- package/payload/server/public/assets/admin-DuOhkvrZ.js +1 -0
- package/payload/server/public/assets/{arc-DyzjCWmw.js → arc-dVLpR-ec.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CgeA3HPD.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-qWfElD16.js → architectureDiagram-Q4EWVU46-C6u_CVzb.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DO2fFHC3.js → blockDiagram-DXYQGD6D-Cy8F5b8O.js} +1 -1
- package/payload/server/public/assets/browser-CCWufeXr.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Ca-q5gDe.js → c4Diagram-AHTNJAMY-DRXrlepj.js} +1 -1
- package/payload/server/public/assets/calendar-CNk1iPrA.js +1 -0
- package/payload/server/public/assets/channel-B04VBWf7.js +1 -0
- package/payload/server/public/assets/chat-7qB-dwBq.js +1 -0
- package/payload/server/public/assets/chevron-left-CpcDbHIz.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-CX2PxuDw.js → chunk-2KRD3SAO-fEyfhRLz.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-D8l6U4Ne.js → chunk-336JU56O-NUWO4y7q.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-DZkHcXRA.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CG32Lkbh.js → chunk-4BX2VUAB-BnfiNR3l.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BnAQbO-v.js → chunk-4TB4RGXK-Bj_aJOz8.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-ejqRH2M5.js → chunk-55IACEB6-aK0DNSKb.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-ClkicQ-T.js → chunk-5FUZZQ4R-DeqAkq5C.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-Be-mE_rx.js → chunk-5PVQY5BW-SShlujAQ.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-DPsemed2.js → chunk-67CJDMHE-CZpzQ0DL.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BQRQJETl.js → chunk-7N4EOEYR-COnjO6jy.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-B1wcAVCK.js → chunk-AA7GKIK3-C9V31L2z.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DIua053h.js → chunk-BSJP7CBP-DSwnSGe3.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C9GHSCS9.js → chunk-CIAEETIT-CnRUTuta.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-C1T3l1xj.js → chunk-EDXVE4YY-BexiL2LH.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-8kPcXEQC.js → chunk-ENJZ2VHE-aTklWdJQ.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Bhoeq9sD.js → chunk-FMBD7UC4-svcEBQzs.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-BIBpJi8I.js → chunk-FOC6F5B3-CG7R7Xri.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DELXp6fj.js → chunk-ICPOFSXX-CGN7Z4x9.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DdckXldP.js → chunk-K5T4RW27-CWBh78jw.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-BJhXejKT.js → chunk-KGLVRYIC-Ctiq4q-5.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BSd8nyM2.js → chunk-LIHQZDEY-BII77Q7K.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-CBi55uxq.js → chunk-ORNJ4GCN-DT7R01H8.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-Dfbzl8xh.js → chunk-OYMX7WX6-y-Jkbd4C.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-D9Mwgzti.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BN1w8_Cq.js → chunk-U2HBQHQK-DzFo6KbQ.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-CrRay1NJ.js → chunk-X2U36JSP-CYSwgtLV.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-C_ZE776O.js → chunk-XPW4576I-DHlM4tOn.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CUSlqWBY.js → chunk-YZCP3GAM-B20rTmLl.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-BdPUfSiD.js → chunk-ZZ45TVLE-C7hP4J8m.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CIo_TzfF.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-SzQYvAUZ.js +1 -0
- package/payload/server/public/assets/clone-BVjcjsBA.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-D4E9htTz.js → cose-bilkent-S5V4N54A-nKjTv9sR.js} +1 -1
- package/payload/server/public/assets/{dagre-D1B5TQL1.js → dagre-Cx8LiiUZ.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-ByuvYV7N.js → dagre-KV5264BT-BNJYTnls.js} +1 -1
- package/payload/server/public/assets/data-BnHcS27X.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-B-0KduTa.js → diagram-5BDNPKRD-CoAN5_f9.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CSQmo3eB.js → diagram-G4DWMVQ6-pRmsN4_x.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DK0kWiB3.js → diagram-MMDJMWI5-CRoylyp4.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CYbK2reZ.js → diagram-TYMM5635-BT3ZtG5b.js} +1 -1
- package/payload/server/public/assets/{dist-CADCHk0t.js → dist-CRpLAnCP.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-C-JQHvJF.js → erDiagram-SMLLAGMA-DSKws1ZV.js} +1 -1
- package/payload/server/public/assets/file-download-Hf_UILaA.js +1 -0
- package/payload/server/public/assets/{flatten-ROVzlo1u.js → flatten-jn8bZgcI.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-OTOd2czg.js → flowDiagram-DWJPFMVM-C2L38ZE5.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BWJ778ou.js → ganttDiagram-T4ZO3ILL-Bm2h-vjm.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DzQ18i0T.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CkNcyISe.js → gitGraphDiagram-UUTBAWPF-BoowbF7u.js} +1 -1
- package/payload/server/public/assets/graph-klLugHEK.js +51 -0
- package/payload/server/public/assets/{graph-labels-BFG4bjXQ.js → graph-labels-BLx-0vyT.js} +1 -1
- package/payload/server/public/assets/{graphlib-8z59Ng6C.js → graphlib-BvT2s_o1.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-CwsoaVzV.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-ieNsH2bV.js +2 -0
- package/payload/server/public/assets/{isEmpty-DXf_sApt.js → isEmpty-Dkk6_L-B.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js → ishikawaDiagram-UXIWVN3A-nxcmj-IX.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CZT_4r_Z.js → journeyDiagram-VCZTEJTY-q5SNIvmS.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Dp1Vd5YM.js → kanban-definition-6JOO6SKY-BugG9Q1o.js} +1 -1
- package/payload/server/public/assets/{line-UPAcwY66.js → line-MZCQLR-q.js} +1 -1
- package/payload/server/public/assets/{linear-BeeahLKw.js → linear-DmQGNk-g.js} +1 -1
- package/payload/server/public/assets/{maximize-2-DkZ40lSl.js → maximize-2-CnNQnj_v.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CLKle8df.js → mermaid-parser.core-DvoeqLxw.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-CwRj5rL5.js → mermaid.core-BLaw7iG4.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CNQUY9UJ.js → mindmap-definition-QFDTVHPH-DMkt10Vc.js} +1 -1
- package/payload/server/public/assets/operator-CRcySYv9.js +1 -0
- package/payload/server/public/assets/{ordinal-B2r5L1v4.js → ordinal-DuJYM7p1.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-DxB-bO4L.js +1 -0
- package/payload/server/public/assets/page-CVo3CWIY.js +1 -0
- package/payload/server/public/assets/page-DPTbZRwl.js +32 -0
- package/payload/server/public/assets/{pdf-render-m08Xm2Nm.js → pdf-render-riHufgrn.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-PSdKvB8-.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-Scnq58FX.js → pieDiagram-DEJITSTG-DRp4lD2B.js} +1 -1
- package/payload/server/public/assets/play-yEkd7ouW.js +1 -0
- package/payload/server/public/assets/public-Fi6ccYaE.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wPfZo_vs.js → quadrantDiagram-34T5L4WZ-DkT63Dn4.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-D8FqRiWq.js +1 -0
- package/payload/server/public/assets/{reduce-CFlDlJf5.js → reduce-COyotL2V.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-yYkH12K2.js → requirementDiagram-MS252O5E-65GGtDkc.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-xHksz9mr.js +1 -0
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-fVWRBzzV.js → sankeyDiagram-XADWPNL6-D1vRwLWd.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-Dbap3X6h.js → sequenceDiagram-FGHM5R23-Bp47ZSN2.js} +1 -1
- package/payload/server/public/assets/square-BZqaxg7D.js +1 -0
- package/payload/server/public/assets/{src-Bi0fLV9t.js → src-xN1oULc8.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DkPLAqIh.js → stateDiagram-FHFEXIEX-BZHHcP_e.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-qVtoGx56.js +1 -0
- package/payload/server/public/assets/tasks-COWmfts8.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DabKBJyd.js → timeline-definition-GMOUNBTQ-DZ1wUyj-.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-i-w4rJmQ.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Dij6AlAj.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DADvGnr1.js +9 -0
- package/payload/server/public/assets/useSubAccountSwitcher-ayajZz8T.css +1 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CaKT7c-c.js → vennDiagram-DHZGUBPP-CttBahsn.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BJhOQVqW.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BZ5TTSOL.js → wardleyDiagram-NUSXRM2D-Y5tWieZg.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CLfeklJ9.js → xychartDiagram-5P7HB3ND-C78A9qbe.js} +1 -1
- package/payload/server/public/browser.html +7 -5
- package/payload/server/public/calendar.html +8 -6
- package/payload/server/public/chat.html +15 -12
- package/payload/server/public/data.html +11 -10
- package/payload/server/public/graph.html +12 -10
- package/payload/server/public/index.html +16 -14
- package/payload/server/public/operator.html +17 -14
- package/payload/server/public/public.html +15 -12
- package/payload/server/public/tasks.html +18 -0
- package/payload/server/server.js +775 -287
- package/payload/server/public/assets/AdminShell-D8wNArGQ.js +0 -2
- package/payload/server/public/assets/Transcript-CBIyQ8T5.js +0 -2
- package/payload/server/public/assets/admin-CWM9G_yA.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +0 -1
- package/payload/server/public/assets/browser-D8QhNffV.js +0 -1
- package/payload/server/public/assets/calendar-qob5K4t_.js +0 -1
- package/payload/server/public/assets/channel-CYBkRz7E.js +0 -1
- package/payload/server/public/assets/chat-BNrxVUF6.js +0 -1
- package/payload/server/public/assets/chevron-left-BQThFVrd.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CPhOHriU.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CDnAAZVJ.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdXUKVrS.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DAq-uJr_.js +0 -1
- package/payload/server/public/assets/clone-DQl28-jK.js +0 -1
- package/payload/server/public/assets/data-C-EqNbRi.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +0 -1
- package/payload/server/public/assets/graph-DZ2HMpIB.js +0 -51
- package/payload/server/public/assets/info-OMHHGYJF-CtZajyAB.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C-_WYlmI.js +0 -2
- package/payload/server/public/assets/operator-FrXrSsBj.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +0 -1
- package/payload/server/public/assets/page-CHWMI9Tm.js +0 -32
- package/payload/server/public/assets/page-CXKV01gT.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +0 -1
- package/payload/server/public/assets/public-2p1pJ4WF.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +0 -1
- package/payload/server/public/assets/rotate-ccw-D3o_bMBA.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DceWO1u6.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3197Rac.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-BPrpx29h.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-8dLvF3Ux.js +0 -9
- package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BVuIxZJh.js → _baseFor-Bjj9It3Q.js} +0 -0
- /package/payload/server/public/assets/{admin-types-hioowVct.js → admin-types-ClHBm0ni.js} +0 -0
- /package/payload/server/public/assets/{array-B9UHiPd-.js → array-BrrxEo12.js} +0 -0
- /package/payload/server/public/assets/{chunk-CAM3fms7.js → chunk-Ck5AOu5v.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-D5_TRwB-.js → cytoscape.esm-CTWJxUxo.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-CFDbRV4k.js} +0 -0
- /package/payload/server/public/assets/{init-DlZdxViB.js → init-dXCzGgLP.js} +0 -0
- /package/payload/server/public/assets/{katex-Xo0SyWvP.js → katex-CHA1JFYJ.js} +0 -0
- /package/payload/server/public/assets/{path-6uRLdFF7.js → path-CCRpdXhK.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CvXuAEud.js → preload-helper-CH6UZRzu.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-Djo4Abte.js → rough.esm-Bud4g4fh.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "admin",
|
|
3
|
-
"description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
|
|
3
|
+
"description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_create requires a managing house admin (its userId) and refuses without one, recording it as the sub-account's managingAdminUserId; account_reassign_admin repoints a client sub-account's managing admin to another live house admin (the repair path for a dangling managingAdminUserId); account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove (refuses to remove a house admin who still manages one or more client sub-accounts, naming them so the operator reassigns each with account_reassign_admin first — never silently orphans them), admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: admin
|
|
3
3
|
surface: platform
|
|
4
|
-
description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
4
|
+
description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_create requires a managing house admin (its userId) and refuses without one, recording it as the sub-account's managingAdminUserId; account_reassign_admin repoints a client sub-account's managing admin to another live house admin (the repair path for a dangling managingAdminUserId); account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove (refuses to remove a house admin who still manages one or more client sub-accounts, naming them so the operator reassigns each with account_reassign_admin first — never silently orphans them), admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
5
5
|
tools:
|
|
6
6
|
- name: system-status
|
|
7
7
|
publicAllowlist: false
|
|
@@ -33,6 +33,9 @@ tools:
|
|
|
33
33
|
- name: account_create
|
|
34
34
|
publicAllowlist: false
|
|
35
35
|
adminAllowlist: true
|
|
36
|
+
- name: account_reassign_admin
|
|
37
|
+
publicAllowlist: false
|
|
38
|
+
adminAllowlist: true
|
|
36
39
|
- name: account_delete
|
|
37
40
|
publicAllowlist: false
|
|
38
41
|
adminAllowlist: true
|
|
@@ -175,6 +178,7 @@ Tools are available via the `admin` MCP server.
|
|
|
175
178
|
- `hooks/post-tool-use-agent.sh` — **PostToolUse hook on `Agent`.** Drains any subagent hook-decision buffers under `~/.maxy-code/logs/hook-decisions/` modified since this parent's previous PostToolUse-Agent fire (cursor file keyed by parent session id), prints one `[hook-propagate]` line per record to stdout — Claude Code attaches the stdout as a `hook_success` attachment on the parent JSONL, making the records grep-queryable from the parent session alone. Rotates consumed buffers to `consumed/`. Emits one `[hook-propagate-census] parentSession=<…> subagentHooksObserved=<N> attachmentsEmitted=<M>` line per fire to stdout and server.log; `N != M` is the propagation regression signal. The companion emitter library `hooks/lib/hook-emit.sh` is sourced by `post-tool-use-agent.sh` and any other hook that records a block decision (4 KB stderr truncation, `truncated=true` set on the record).
|
|
176
179
|
- `hooks/admin-authoring-observer.sh` — **PostToolUse hook on Write and Edit.** Observation only — never blocks; exits 0 on every path. Fires when the admin agent (not a specialist subagent — gated by `MAXY_SPECIALIST` env) writes or edits a file under `<accountDir>/output/`. Walks the session transcript from the latest real-user turn forward to detect any prior `Task` `tool_use` whose `subagent_type` starts with `specialists:`. Emits one stderr line `[admin-authoring] inline-write path=<rel> priorSpecialistSpawnInTurn=<true|false|unknown>`. A `false` value on a long-form prose file is the regression signal this hook was designed to make visible — the BioSymm proposal session (admin authored a customer-facing proposal inline despite content-producer being installed) is the failure mode this surfaces mechanically. Mechanical enforcement (refuse the write, force a re-spawn) is deferred per the task spec.
|
|
177
180
|
- `hooks/prompt-optimiser-directive.sh` — **UserPromptSubmit hook.** Injects the standing prompt-optimiser restatement directive plus the per-turn three-tier routing ladder as `additionalContext`: **(1)** delegate to the specialist that owns the deliverable via the Agent tool — the brief states the outcome plus binding constraints, never lines/anchors/literal text; **(2)** only if none fits, load an admin-usable skill with `skill-load`; **(3)** only if neither fits, author inline — freestyle is the named last resort. Re-emits the full agent roster (`agents/admin/AGENTS.md`) and the full admin-usable skills list (`agents/admin/ADMIN-SKILLS.md`) every turn by reading the two generated files from the account dir (the hook fires with the account dir as cwd); it never walks the plugins tree per turn. Fail-open is **visible**: a missing list logs `[prompt-optimiser] missing=<AGENTS.md\|ADMIN-SKILLS.md> emitting-partial` to stderr and the ladder still injects. The trivial-turn skip (one-word confirmation, slash-command, direct continuation) is unchanged. **Staleness:** `ADMIN-SKILLS.md` is regenerated only by `setup-account.sh`; a plugin add/remove since the last setup leaves the list stale — compare `ADMIN-SKILLS.md` mtime against the newest `SKILL.md` mtime and re-run setup to refresh. The list generator is `platform/scripts/lib/admin-skills-bootstrap.sh`; it logs `[admin-skills] scanned=<N> admin-usable=<M> no-declaration=<K>` (failure signature: `admin-usable=0` while `scanned>0`, or any `missing-declaration` line). The directive also carries a standing CAPABILITY-QUESTIONS-ARE-OWNED-WORK clause (how-to / "do you have instructions for X" / config questions about platform features are answered from the owning specialist or plugin tool/reference, never from training memory), and the hook appends a durable `<ts> [prompt-optimiser-directive] injected len=<n> session=<id>` breadcrumb to `$LOG_DIR/prompt-optimiser-directive.log` so per-turn injection is greppable, not stderr-only. The directive is also **suppressed on native channel turns** — when the parsed `.prompt` starts with the `<channel source=` event marker, the hook logs `[prompt-optimiser-directive] skipped reason=channel-turn session=<id>` to stderr and exits without injecting, because the channel service already reframes the inbound into a select-and-dispatch turn (`composeAdminContent`, see `.docs/whatsapp-inbound-lifeline.md`). Marker-matched at start-of-prompt only, so an admin/Terminal prompt that merely mentions "channel" still gets the directive; fail-open injects if the prompt cannot be parsed.
|
|
181
|
+
- `hooks/datetime-inject.sh` — **UserPromptSubmit hook.** Injects a fresh `<datetime>` block as `additionalContext` on every turn (`refresh=per-turn`), so the agent always has the current instant — date, time, timezone, weekday, and the raw UTC ISO — without hand-computing it and without depending on Claude Code's spawn-time "Today's date" line going stale. The instant is computed fresh each turn; the timezone is **not** computed here — it is the operator's own zone, resolved from their `UserProfile.timezone` on the graph once per admin spawn and stamped into `MAXY_ACCOUNT_TZ` (see `platform/services/claude-session-manager/src/account-timezone.ts`). When `MAXY_ACCOUNT_TZ` is empty (a non-admin turn, an unset profile timezone, or a neo4j hiccup at spawn) the block falls back to the server's own zone and labels the source, so a reader can always tell which clock the agent saw. This is the current-time surface the `datetime` skill points at. **Fail-open** on every path (missing `python3` or `node`, any formatting error → exit 0, empty stdout — the turn is never blocked). The hook appends `<ts> [datetime-inject] injected tz=<zone> source=<graph|server-fallback> refresh=per-turn session=<id>` to `$LOG_DIR/datetime-inject.log` so "did the agent have the right time this turn, and from which clock?" is answerable from logs without reproduction.
|
|
178
182
|
- `hooks/prompt-optimiser-compliance.sh` — **Stop hook.** After each admin turn, reads the just-finished turn from `transcript_path` and appends `<ts> [prompt-optimiser-compliance] directive-fired no-route-taken session=<id8> prompt="<clip>"` to `$LOG_DIR/prompt-optimiser-directive.log` (and stderr) when the routing directive fired, the prompt was non-trivial (not a slash-command, not a one-word confirmation), and the turn took **no route** — no `Agent` dispatch, no `Skill` load, no `ToolSearch`, no `mcp__*` tool call. This is the standing compliance signal that surfaces the session-`da0b12d4` failure class (agent answers a capability question from memory) as a visible event instead of a silent stale answer. Directive-fired is detected by the marker `PROMPT-OPTIMISER DIRECTIVE` in the turn slice, so it is robust to the CC-version difference in how `UserPromptSubmit` `additionalContext` is recorded (`attachment`/`hook_success` vs `hook_additional_context`). **Known limitation:** "direct continuation of the prior turn" is not detectable from the transcript, so a continuation turn that legitimately needs no route can be flagged; treat the log as a review signal, not a gate. **Fail-open:** no python3, no `transcript_path`, or an unreadable transcript → exit 0, no output. Lives in the same log as the directive breadcrumb, so a single `grep` interleaves "fired" and "no-route" into one per-session timeline; cross-check via `platform/scripts/logs-read.sh <sessionKey>`. This is a lightweight transcript read, not a per-turn spawn (contrast the turn recorder below).
|
|
179
183
|
- **Turn recorder — removed entirely.** The `turn-completed-graph-write.sh` Stop hook, the `/api/admin/claude-sessions` loopback bypass it relied on, the `[turn-recorder]` emitters, the envelope walker, and the recorder-auto-archive subscriber are deleted. It had been dormant for a long time (never re-registered in settings.json); the admin now writes to the graph by delegating to `database-operator` via the Task tool inside the live session, and the on-demand `/insight` pass (`skills/insight/SKILL.md`, a registered admin skill) is the per-session review. There is no per-turn spawn.
|
|
180
184
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression test for datetime-inject.sh.
|
|
3
|
+
#
|
|
4
|
+
# The hook injects a fresh <datetime> block as UserPromptSubmit additionalContext
|
|
5
|
+
# every turn, reporting "now" in $MAXY_ACCOUNT_TZ (the operator's graph timezone,
|
|
6
|
+
# stamped at spawn) or the server zone when that env is empty.
|
|
7
|
+
#
|
|
8
|
+
# Covers:
|
|
9
|
+
# 1. MAXY_ACCOUNT_TZ set -> exit 0, valid JSON, additionalContext has
|
|
10
|
+
# the <datetime> block, the named zone, and a UTC: line; source=graph.
|
|
11
|
+
# 2. MAXY_ACCOUNT_TZ empty -> exit 0, block falls back to the server zone
|
|
12
|
+
# and labels source as server-timezone.
|
|
13
|
+
# 3. Breadcrumb -> $LOG_DIR/datetime-inject.log gains a line
|
|
14
|
+
# with source= and refresh=per-turn.
|
|
15
|
+
# 4. python3 absent -> exit 0, empty stdout (fail-open).
|
|
16
|
+
# 5. node absent -> exit 0, empty stdout (fail-open).
|
|
17
|
+
|
|
18
|
+
set -u
|
|
19
|
+
|
|
20
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/datetime-inject.sh"
|
|
21
|
+
if [[ ! -x "$HOOK" ]]; then
|
|
22
|
+
echo "FAIL: $HOOK not executable" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
PASS=0
|
|
27
|
+
FAIL=0
|
|
28
|
+
STDIN='{"hook_event_name":"UserPromptSubmit","prompt":"when is the meeting","session_id":"sess-abc123"}'
|
|
29
|
+
|
|
30
|
+
# Case 1 — MAXY_ACCOUNT_TZ set: block reports that zone, source=graph.
|
|
31
|
+
out=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Europe/London" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex=$?
|
|
32
|
+
if [[ "$ex" -eq 0 ]] && printf '%s' "$out" | python3 -c '
|
|
33
|
+
import json, sys
|
|
34
|
+
d = json.load(sys.stdin)
|
|
35
|
+
h = d["hookSpecificOutput"]
|
|
36
|
+
assert h["hookEventName"] == "UserPromptSubmit", "wrong hookEventName"
|
|
37
|
+
ctx = h["additionalContext"]
|
|
38
|
+
assert "<datetime>" in ctx and "</datetime>" in ctx, "no datetime block"
|
|
39
|
+
assert "Europe/London" in ctx, "zone not named"
|
|
40
|
+
assert "UTC:" in ctx, "no UTC line"
|
|
41
|
+
assert "account-timezone" in ctx, "source label missing for graph tz"
|
|
42
|
+
' 2>/dev/null; then
|
|
43
|
+
echo "PASS: MAXY_ACCOUNT_TZ set -> datetime block in named zone"; PASS=$((PASS+1))
|
|
44
|
+
else
|
|
45
|
+
echo "FAIL: graph-tz injection (exit=$ex stdout=$out)" >&2; FAIL=$((FAIL+1))
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Case 2 — MAXY_ACCOUNT_TZ empty: falls back to the server zone, labels it.
|
|
49
|
+
out2=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex2=$?
|
|
50
|
+
if [[ "$ex2" -eq 0 ]] && printf '%s' "$out2" | python3 -c '
|
|
51
|
+
import json, sys
|
|
52
|
+
ctx = json.load(sys.stdin)["hookSpecificOutput"]["additionalContext"]
|
|
53
|
+
assert "<datetime>" in ctx, "no datetime block"
|
|
54
|
+
assert "server-timezone" in ctx, "server fallback not labelled"
|
|
55
|
+
assert "UTC:" in ctx, "no UTC line"
|
|
56
|
+
' 2>/dev/null; then
|
|
57
|
+
echo "PASS: empty MAXY_ACCOUNT_TZ -> server-zone fallback labelled"; PASS=$((PASS+1))
|
|
58
|
+
else
|
|
59
|
+
echo "FAIL: server fallback (exit=$ex2 stdout=$out2)" >&2; FAIL=$((FAIL+1))
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Case 2b — MAXY_ACCOUNT_TZ set but NOT a real IANA zone: the node validation
|
|
63
|
+
# rejects it, falls back to the server zone, and labels the source neutrally as
|
|
64
|
+
# "unavailable" (never falsely "unset" — the value was present, just unusable).
|
|
65
|
+
out2b=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Not/AZone" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex2b=$?
|
|
66
|
+
if [[ "$ex2b" -eq 0 ]] && printf '%s' "$out2b" | python3 -c '
|
|
67
|
+
import json, sys
|
|
68
|
+
ctx = json.load(sys.stdin)["hookSpecificOutput"]["additionalContext"]
|
|
69
|
+
assert "<datetime>" in ctx, "no datetime block"
|
|
70
|
+
assert "server-timezone (account timezone unavailable)" in ctx, "invalid tz not labelled unavailable"
|
|
71
|
+
assert "Not/AZone" not in ctx, "rejected zone leaked into the block"
|
|
72
|
+
' 2>/dev/null; then
|
|
73
|
+
echo "PASS: set-but-invalid MAXY_ACCOUNT_TZ -> server fallback, labelled unavailable"; PASS=$((PASS+1))
|
|
74
|
+
else
|
|
75
|
+
echo "FAIL: invalid-tz fallback (exit=$ex2b stdout=$out2b)" >&2; FAIL=$((FAIL+1))
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Case 3 — breadcrumb carries source= and refresh=per-turn.
|
|
79
|
+
LOGD=$(mktemp -d)
|
|
80
|
+
printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Europe/London" LOG_DIR="$LOGD" bash "$HOOK" >/dev/null 2>&1
|
|
81
|
+
if [[ -f "$LOGD/datetime-inject.log" ]] \
|
|
82
|
+
&& grep -q "\[datetime-inject\]" "$LOGD/datetime-inject.log" \
|
|
83
|
+
&& grep -q "source=" "$LOGD/datetime-inject.log" \
|
|
84
|
+
&& grep -q "refresh=per-turn" "$LOGD/datetime-inject.log"; then
|
|
85
|
+
echo "PASS: breadcrumb has source= and refresh=per-turn"; PASS=$((PASS+1))
|
|
86
|
+
else
|
|
87
|
+
echo "FAIL: breadcrumb missing (log=$(cat "$LOGD/datetime-inject.log" 2>/dev/null))" >&2; FAIL=$((FAIL+1))
|
|
88
|
+
fi
|
|
89
|
+
rm -rf "$LOGD"
|
|
90
|
+
|
|
91
|
+
# Case 4 — python3 absent -> fail-open (exit 0, empty stdout).
|
|
92
|
+
FAKEBIN=$(mktemp -d)
|
|
93
|
+
for util in cat printf env bash node date; do
|
|
94
|
+
src=$(command -v "$util" 2>/dev/null) && ln -sf "$src" "$FAKEBIN/$util" 2>/dev/null
|
|
95
|
+
done
|
|
96
|
+
nopy_out=$(printf '%s' "$STDIN" | PATH="$FAKEBIN" MAXY_ACCOUNT_TZ="Europe/London" bash "$HOOK" 2>/dev/null); nopy_exit=$?
|
|
97
|
+
if [[ "$nopy_exit" -eq 0 && -z "$nopy_out" ]]; then
|
|
98
|
+
echo "PASS: python3 absent -> exit 0, empty stdout (fail-open)"; PASS=$((PASS+1))
|
|
99
|
+
else
|
|
100
|
+
echo "FAIL: python3 absent (exit=$nopy_exit stdout=$nopy_out)" >&2; FAIL=$((FAIL+1))
|
|
101
|
+
fi
|
|
102
|
+
rm -rf "$FAKEBIN"
|
|
103
|
+
|
|
104
|
+
# Case 5 — node absent -> fail-open (exit 0, empty stdout).
|
|
105
|
+
FAKEBIN2=$(mktemp -d)
|
|
106
|
+
for util in cat printf env bash python3 date; do
|
|
107
|
+
src=$(command -v "$util" 2>/dev/null) && ln -sf "$src" "$FAKEBIN2/$util" 2>/dev/null
|
|
108
|
+
done
|
|
109
|
+
nonode_out=$(printf '%s' "$STDIN" | PATH="$FAKEBIN2" MAXY_ACCOUNT_TZ="Europe/London" bash "$HOOK" 2>/dev/null); nonode_exit=$?
|
|
110
|
+
if [[ "$nonode_exit" -eq 0 && -z "$nonode_out" ]]; then
|
|
111
|
+
echo "PASS: node absent -> exit 0, empty stdout (fail-open)"; PASS=$((PASS+1))
|
|
112
|
+
else
|
|
113
|
+
echo "FAIL: node absent (exit=$nonode_exit stdout=$nonode_out)" >&2; FAIL=$((FAIL+1))
|
|
114
|
+
fi
|
|
115
|
+
rm -rf "$FAKEBIN2"
|
|
116
|
+
|
|
117
|
+
echo "----"
|
|
118
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
119
|
+
[[ "$FAIL" -eq 0 ]]
|
|
@@ -60,6 +60,16 @@ CHANNEL_REFRAME_TRIVIAL='{"type":"user","isMeta":true,"message":{"role":"user","
|
|
|
60
60
|
# registration key `channel`, and the server embeds an inner source tag, so the
|
|
61
61
|
# reframe arrives DOUBLE-wrapped. The strip must iterate to reach ## Context.
|
|
62
62
|
CHANNEL_REFRAME_MULTI='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"channel\">\n<channel source=\"whatsapp\">\n## Context\nplease draft the works order PDF for order 926\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.\n</channel>\n</channel>"}}'
|
|
63
|
+
# Task 1505 — the same reframe carrying the account's active standing-rules block
|
|
64
|
+
# (Task 1486 prepends `## Standing rules\n- …\n\n` above ## Context on admin
|
|
65
|
+
# channel turns). The reframe detection must strip that optional prefix, mirroring
|
|
66
|
+
# the reader anchor, so a rules-present turn is evaluated identically to a
|
|
67
|
+
# rules-absent one; without the strip the turn no longer starts with ## Context
|
|
68
|
+
# and the hook silently stops flagging on exactly the rule-carrying turns.
|
|
69
|
+
CHANNEL_REFRAME_RULES='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules\n- Always greet the client by name\n- Use British spelling\n\n## Context\nplease draft the works order PDF for order 926\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
|
|
70
|
+
# Same rules-prefixed shape, one-word Context payload — after the prefix strip the
|
|
71
|
+
# trivial filter must still read just the payload (`yes`), not the rules block.
|
|
72
|
+
CHANNEL_REFRAME_RULES_TRIVIAL='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules\n- Always greet the client by name\n- Use British spelling\n\n## Context\nyes\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
|
|
63
73
|
|
|
64
74
|
assert_flagged() {
|
|
65
75
|
local name="$1" t="$2" out
|
|
@@ -181,6 +191,18 @@ assert_flagged "multi-target double-wrapped reframe + inline authoring -> flagge
|
|
|
181
191
|
# 18c. Same double-wrapped reframe with an Agent dispatch -> not flagged.
|
|
182
192
|
assert_not_flagged "multi-target double-wrapped reframe + Agent dispatch -> not flagged" \
|
|
183
193
|
"$(mk_transcript "$CHANNEL_REFRAME_MULTI" "$ASSISTANT_REPLY" "$ASSISTANT_AGENT")"
|
|
194
|
+
# 18d. Task 1505 — rules-prefixed reframe, inline authoring, no marker -> flagged.
|
|
195
|
+
# The regression guard: with the standing-rules block above ## Context the pre-fix
|
|
196
|
+
# hook read is_reframe=False and silently stopped flagging this class.
|
|
197
|
+
assert_flagged "rules-prefixed reframe + inline authoring, no marker -> flagged" \
|
|
198
|
+
"$(mk_transcript "$CHANNEL_REFRAME_RULES" "$ASSISTANT_REPLY" "$ASSISTANT_BASH" "$ASSISTANT_WRITE")"
|
|
199
|
+
# 18e. Same rules-prefixed reframe with an Agent dispatch -> not flagged.
|
|
200
|
+
assert_not_flagged "rules-prefixed reframe + Agent dispatch -> not flagged" \
|
|
201
|
+
"$(mk_transcript "$CHANNEL_REFRAME_RULES" "$ASSISTANT_REPLY" "$ASSISTANT_AGENT")"
|
|
202
|
+
# 18f. Rules-prefixed reframe whose Context payload is one word -> trivial, not
|
|
203
|
+
# flagged (the prefix strip must leave just the payload for the trivial filter).
|
|
204
|
+
assert_not_flagged "rules-prefixed reframe with one-word Context payload -> not flagged (trivial)" \
|
|
205
|
+
"$(mk_transcript "$CHANNEL_REFRAME_RULES_TRIVIAL" "$ASSISTANT_TEXT")"
|
|
184
206
|
# 19. Channel-wrapped NON-reframe turn (no ## Instruction section — webchat
|
|
185
207
|
# today, Task 776) with no marker -> not directive-equivalent, not flagged.
|
|
186
208
|
assert_not_flagged "channel-wrapped non-reframe turn, no marker -> not flagged" \
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 1515 — UserPromptSubmit hook: inject a fresh <datetime> block as
|
|
3
|
+
# additionalContext on every turn, so the agent always has the current instant
|
|
4
|
+
# (date, time, timezone, weekday, and the raw UTC ISO) without hand-computing it
|
|
5
|
+
# and without depending on Claude Code's spawn-time "Today's date" line going
|
|
6
|
+
# stale. This is the per-turn current-time surface the datetime skill points at.
|
|
7
|
+
#
|
|
8
|
+
# The instant is computed fresh here every turn (refresh=per-turn). The timezone
|
|
9
|
+
# is NOT computed here — it is the operator's own zone, resolved from their
|
|
10
|
+
# UserProfile on the graph once per admin spawn and stamped into MAXY_ACCOUNT_TZ.
|
|
11
|
+
# When MAXY_ACCOUNT_TZ is empty (a non-admin turn, an unset profile timezone, or
|
|
12
|
+
# a neo4j hiccup at spawn) the block falls back to the server's own zone and
|
|
13
|
+
# labels the source, so a reader can always tell which clock the agent saw.
|
|
14
|
+
#
|
|
15
|
+
# Mirrors prompt-optimiser-directive.sh: emit hookSpecificOutput.additionalContext
|
|
16
|
+
# via python3, fail-open with exit 0 on every path. UserPromptSubmit output is
|
|
17
|
+
# ADDED to the prompt, never substituted — the raw prompt always still reaches
|
|
18
|
+
# the model.
|
|
19
|
+
#
|
|
20
|
+
# Input (stdin, UserPromptSubmit contract): { "session_id": "...", ... } —
|
|
21
|
+
# session_id is used only for the breadcrumb; the prompt text is ignored
|
|
22
|
+
# (the block is standing and prompt-independent).
|
|
23
|
+
# Output (stdout JSON):
|
|
24
|
+
# { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit",
|
|
25
|
+
# "additionalContext": "<datetime>...</datetime>" } }
|
|
26
|
+
|
|
27
|
+
set -uo pipefail
|
|
28
|
+
|
|
29
|
+
# Drain stdin FIRST, before any exit path. Claude Code writes the full
|
|
30
|
+
# UserPromptSubmit payload (which carries the prompt text) to our stdin; if we
|
|
31
|
+
# exit before reading it, a payload larger than the pipe buffer leaves the parent
|
|
32
|
+
# blocked on write and then hit with EPIPE/SIGPIPE. The sibling
|
|
33
|
+
# prompt-optimiser-directive.sh drains first for this exact reason. Only
|
|
34
|
+
# session_id is used here (for the breadcrumb); the block is prompt-independent.
|
|
35
|
+
HOOK_INPUT=$(cat 2>/dev/null || true)
|
|
36
|
+
|
|
37
|
+
# Fail-open if either encoder is unavailable: python3 wraps the JSON envelope,
|
|
38
|
+
# node formats the instant in the target zone (Intl has the full IANA database).
|
|
39
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
40
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
41
|
+
|
|
42
|
+
# The operator's zone, stamped at spawn from the graph. Empty -> server zone.
|
|
43
|
+
ACCOUNT_TZ="${MAXY_ACCOUNT_TZ:-}"
|
|
44
|
+
|
|
45
|
+
# Format the current instant. node prints two tab-separated fields:
|
|
46
|
+
# 1. the resolved IANA zone actually used (so the server-fallback names a real
|
|
47
|
+
# zone, not the string "server")
|
|
48
|
+
# 2. the assembled block body
|
|
49
|
+
# TZ_IN is passed as argv[1]; empty means "use the runtime's own zone".
|
|
50
|
+
BLOCK=$(TZ_IN="$ACCOUNT_TZ" node -e '
|
|
51
|
+
const tzIn = process.env.TZ_IN && process.env.TZ_IN.trim().length ? process.env.TZ_IN.trim() : null;
|
|
52
|
+
const now = new Date();
|
|
53
|
+
// Resolve the zone actually used. A supplied zone that Intl rejects falls back
|
|
54
|
+
// to the runtime zone, so a bad stamp can never break the turn.
|
|
55
|
+
let zone = tzIn;
|
|
56
|
+
try { if (zone) new Intl.DateTimeFormat("en-GB", { timeZone: zone }); }
|
|
57
|
+
catch { zone = null; }
|
|
58
|
+
if (!zone) zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
59
|
+
// zone === tzIn only when a non-empty, valid graph timezone was stamped; every
|
|
60
|
+
// other case (env empty on a non-admin turn, or a stamped value Intl rejected)
|
|
61
|
+
// falls back to the runtime zone. Label that neutrally as "unavailable" rather
|
|
62
|
+
// than asserting "unset" — the [datetime-tz] spawn log distinguishes unset from
|
|
63
|
+
// invalid from unavailable; here all the hook knows is the zone is not the graph one.
|
|
64
|
+
const source = tzIn && zone === tzIn ? "account-timezone (graph)" : "server-timezone (account timezone unavailable)";
|
|
65
|
+
const local = new Intl.DateTimeFormat("en-GB", {
|
|
66
|
+
weekday: "long", day: "numeric", month: "long", year: "numeric",
|
|
67
|
+
hour: "2-digit", minute: "2-digit", timeZone: zone, timeZoneName: "longOffset",
|
|
68
|
+
}).format(now);
|
|
69
|
+
const body = [
|
|
70
|
+
"Current date and time, read this rather than computing it by hand:",
|
|
71
|
+
"Now: " + local + " (" + zone + ")",
|
|
72
|
+
"UTC: " + now.toISOString(),
|
|
73
|
+
"Source: " + source,
|
|
74
|
+
].join("\n");
|
|
75
|
+
process.stdout.write(zone + "\t" + body);
|
|
76
|
+
' 2>/dev/null) || exit 0
|
|
77
|
+
|
|
78
|
+
[ -n "$BLOCK" ] || exit 0
|
|
79
|
+
ZONE_USED="${BLOCK%%$'\t'*}"
|
|
80
|
+
BODY="${BLOCK#*$'\t'}"
|
|
81
|
+
CONTEXT="<datetime>
|
|
82
|
+
$BODY
|
|
83
|
+
</datetime>"
|
|
84
|
+
|
|
85
|
+
OUT=$(printf '%s' "$CONTEXT" | python3 -c '
|
|
86
|
+
import json, sys
|
|
87
|
+
ctx = sys.stdin.read()
|
|
88
|
+
print(json.dumps({
|
|
89
|
+
"hookSpecificOutput": {
|
|
90
|
+
"hookEventName": "UserPromptSubmit",
|
|
91
|
+
"additionalContext": ctx,
|
|
92
|
+
}
|
|
93
|
+
}))
|
|
94
|
+
' 2>/dev/null) || exit 0
|
|
95
|
+
|
|
96
|
+
[ -n "$OUT" ] || exit 0
|
|
97
|
+
printf '%s\n' "$OUT"
|
|
98
|
+
|
|
99
|
+
# Breadcrumb: source (graph vs server-fallback) + refresh=per-turn so a future
|
|
100
|
+
# staleness report can tell which clock was live each turn without reproduction.
|
|
101
|
+
# Fail-open: a missing/unwritable LOG_DIR just skips the write.
|
|
102
|
+
SID=$(printf '%s' "$HOOK_INPUT" | python3 -c 'import json,sys
|
|
103
|
+
try:
|
|
104
|
+
print(json.load(sys.stdin).get("session_id","") or "?")
|
|
105
|
+
except Exception:
|
|
106
|
+
print("?")' 2>/dev/null)
|
|
107
|
+
SID=${SID:-?}
|
|
108
|
+
if [ -n "$ACCOUNT_TZ" ] && [ "$ZONE_USED" = "$ACCOUNT_TZ" ]; then SRC=graph; else SRC=server-fallback; fi
|
|
109
|
+
NOW_ISO=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "?")
|
|
110
|
+
LINE="$NOW_ISO [datetime-inject] injected tz=$ZONE_USED source=$SRC refresh=per-turn session=$SID"
|
|
111
|
+
if [ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ]; then
|
|
112
|
+
printf '%s\n' "$LINE" >> "$LOG_DIR/datetime-inject.log" 2>/dev/null || true
|
|
113
|
+
fi
|
|
114
|
+
echo "$LINE" >&2 || true
|
|
115
|
+
exit 0
|
|
@@ -42,7 +42,7 @@ INPUT=$(cat 2>/dev/null || true)
|
|
|
42
42
|
command -v python3 >/dev/null 2>&1 || exit 0
|
|
43
43
|
|
|
44
44
|
RESULT=$(printf '%s' "$INPUT" | python3 -c '
|
|
45
|
-
import json, sys, os, datetime
|
|
45
|
+
import json, sys, os, datetime, re
|
|
46
46
|
|
|
47
47
|
def fail_open():
|
|
48
48
|
sys.exit(0)
|
|
@@ -144,6 +144,19 @@ while inner.startswith("<channel"):
|
|
|
144
144
|
inner = inner[end + 1:close].strip()
|
|
145
145
|
is_channel = True
|
|
146
146
|
|
|
147
|
+
# Task 1505 — mirror the reader anchor (parse-transcript.ts COMPOSED_ADMIN_FRAME).
|
|
148
|
+
# For admin channel turns Task 1486 prepends the account active standing-rules block
|
|
149
|
+
# (`## Standing rules\n- …\n\n`) above `## Context`, so a rules-present channel turn
|
|
150
|
+
# no longer begins with `## Context` and both the reframe detection and the trivial
|
|
151
|
+
# strip below silently stopped firing on exactly those turns. Strip an optional
|
|
152
|
+
# leading standing-rules block — anchored to the `## Standing rules` header (not any
|
|
153
|
+
# prefix), non-greedy up to the `\n\n` immediately before `## Context` — so both see
|
|
154
|
+
# the `## Context` payload as before rules injection. Channel-only and match-gated on
|
|
155
|
+
# a following `## Context`, so a rules-absent channel turn is byte-identical to before
|
|
156
|
+
# and a typed (non-channel) prompt is untouched.
|
|
157
|
+
if is_channel:
|
|
158
|
+
inner = re.sub(r"^## Standing rules\n[\s\S]*?\n\n(?=## Context\n)", "", inner, count=1)
|
|
159
|
+
|
|
147
160
|
# Task 805 — a channel-wrapped reframe turn (the ## Context / ## Instruction
|
|
148
161
|
# sections of composeAdminContent) is directive-equivalent: the directive hook suppresses
|
|
149
162
|
# itself on channel turns (Task 753), so the reframe is the only routing
|
|
@@ -27,7 +27,7 @@ import { publishSite } from "./tools/publish-site.js";
|
|
|
27
27
|
import { resolveCapabilitiesHere } from "./tools/capabilities-here.js";
|
|
28
28
|
import { runCatalogueCensus } from "./lib/catalogue-census.js";
|
|
29
29
|
import { lifelineTool } from "../../../../lib/mcp-lifeline/dist/index.js";
|
|
30
|
-
import { listAccountsFromDir, isHouseAccount, archiveAccountDir, provisionClientAccount, findHouseAccount, getChannelRoutingAccountId, setChannelRoutingAccountId, seedAccountGraphRoot, ACCOUNT_NODE_COUNT_CYPHER, ACCOUNT_PURGE_CYPHER, } from "./tools/account-lifecycle.js";
|
|
30
|
+
import { listAccountsFromDir, isHouseAccount, archiveAccountDir, provisionClientAccount, findHouseAccount, getChannelRoutingAccountId, setChannelRoutingAccountId, seedAccountGraphRoot, readHouseAdminUserIds, setManagingAdmin, findClientAccountsManagedBy, reassignManagingAdmin, ACCOUNT_NODE_COUNT_CYPHER, ACCOUNT_PURGE_CYPHER, } from "./tools/account-lifecycle.js";
|
|
31
31
|
function refusalGuidance(kind) {
|
|
32
32
|
switch (kind) {
|
|
33
33
|
case "unsafe-slug":
|
|
@@ -1085,6 +1085,17 @@ eagerTool(server, "admin-remove", "Remove an admin from this account. Removes th
|
|
|
1085
1085
|
if (admins.length <= 1) {
|
|
1086
1086
|
return { content: [{ type: "text", text: `${TAG} Cannot remove the last admin. At least one admin must remain on the account.` }], isError: true };
|
|
1087
1087
|
}
|
|
1088
|
+
// Task 1501 — never orphan a managed sub-account. A managing admin is always a
|
|
1089
|
+
// house admin, so this bites only when removing from the house account: if
|
|
1090
|
+
// `userId` is the managing admin of any client sub-account, refuse and name
|
|
1091
|
+
// them so the operator reassigns each (account_reassign_admin) before retrying.
|
|
1092
|
+
if (isHouseAccount(ACCOUNTS_ROOT, ACCOUNT_ID)) {
|
|
1093
|
+
const managed = findClientAccountsManagedBy(ACCOUNTS_ROOT, userId);
|
|
1094
|
+
if (managed.length > 0) {
|
|
1095
|
+
process.stderr.write(`[account-lifecycle] op=admin-remove-reconcile userId=${userId.slice(0, 8)} managedAccounts=${managed.length} action=blocked\n`);
|
|
1096
|
+
return { content: [{ type: "text", text: `${TAG} Cannot remove ${userId}: they are the managing admin of ${managed.length} client sub-account(s): ${managed.join(", ")}. Reassign each to another house admin with account_reassign_admin, then retry.` }], isError: true };
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1088
1099
|
// Resolve the admin's name from Neo4j (canonical) for the confirmation
|
|
1089
1100
|
// message. Best-effort — fall back to userId if the graph is unreachable.
|
|
1090
1101
|
let removedName = userId;
|
|
@@ -3256,9 +3267,23 @@ eagerTool(server, "account_list", "List every sub-account on this install (one h
|
|
|
3256
3267
|
content: [{ type: "text", text: accounts.length ? lines.join("\n") : "No accounts found." }],
|
|
3257
3268
|
};
|
|
3258
3269
|
});
|
|
3259
|
-
eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this install (managed service). Mints a UUID, scaffolds the account directory (agents, specialists, settings, account.json with role:\"client\"), seeds default plugins, and seeds the account's graph root (a :LocalBusiness plus its owner :AdminUser) so you can populate it immediately. Returns the new accountId. Operator-only; never creates the house sub-account (that is the install itself).", {
|
|
3270
|
+
eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this install (managed service). REQUIRES a managing admin: pass `adminUserId`, the userId of the house team member who will manage this client (get the choices from admin-list; ask the operator which one before creating). Mints a UUID, scaffolds the account directory (agents, specialists, settings, account.json with role:\"client\" and managingAdminUserId), seeds default plugins, and seeds the account's graph root (a :LocalBusiness plus its owner :AdminUser) so you can populate it immediately. Refuses if no adminUserId is given or if it is not a current house admin. Returns the new accountId. Operator-only; never creates the house sub-account (that is the install itself).", {
|
|
3271
|
+
adminUserId: z.string().describe("The userId of the house admin who will manage this client sub-account. Must be a current house admin (see admin-list). Required — creation is refused without it."),
|
|
3272
|
+
}, async ({ adminUserId }) => {
|
|
3260
3273
|
if (!ACCOUNT_ID)
|
|
3261
3274
|
return refuseNoAccount("account_create");
|
|
3275
|
+
// Task 1500 — every client sub-account must carry a responsible house admin.
|
|
3276
|
+
// Refuse (tool error, never a silent default) when none is named, or when
|
|
3277
|
+
// the named userId is not a current house admin.
|
|
3278
|
+
if (!adminUserId || !adminUserId.trim()) {
|
|
3279
|
+
process.stderr.write(`[account-lifecycle] op=create-refused reason=no-admin adminUserId=-\n`);
|
|
3280
|
+
return { content: [{ type: "text", text: `account_create refused: name the managing house admin (adminUserId). Call admin-list to see the choices and ask the operator which one manages this client.` }], isError: true };
|
|
3281
|
+
}
|
|
3282
|
+
const houseAdminUserIds = readHouseAdminUserIds(ACCOUNTS_ROOT);
|
|
3283
|
+
if (!houseAdminUserIds.includes(adminUserId)) {
|
|
3284
|
+
process.stderr.write(`[account-lifecycle] op=create-refused reason=unknown-admin adminUserId=${adminUserId.slice(0, 8)}\n`);
|
|
3285
|
+
return { content: [{ type: "text", text: `account_create refused: ${adminUserId} is not a current house admin. Call admin-list for the valid managing-admin choices.` }], isError: true };
|
|
3286
|
+
}
|
|
3262
3287
|
const newId = randomUUID();
|
|
3263
3288
|
try {
|
|
3264
3289
|
const accountDir = provisionClientAccount({
|
|
@@ -3302,11 +3327,26 @@ eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this
|
|
|
3302
3327
|
finally {
|
|
3303
3328
|
await seedSession.close();
|
|
3304
3329
|
}
|
|
3305
|
-
process.stderr.write(`[account-lifecycle] op=create accountId=${newId} dir-exists=${dirExists} root-seeded=${rootSeeded}\n`);
|
|
3330
|
+
process.stderr.write(`[account-lifecycle] op=create accountId=${newId} dir-exists=${dirExists} root-seeded=${rootSeeded} managingAdmin=${adminUserId.slice(0, 8)}\n`);
|
|
3306
3331
|
if (!rootSeeded) {
|
|
3307
3332
|
return { content: [{ type: "text", text: `account_create FAILED: graph root not seeded for ${newId} (business+admin read-back failed). The account directory was scaffolded but is rootless — run account_delete ${newId} and retry.` }], isError: true };
|
|
3308
3333
|
}
|
|
3309
|
-
|
|
3334
|
+
// Task 1500 — persist the validated managing admin on the new sub-account.
|
|
3335
|
+
// Done after the root seed so a rootless failure never leaves a half-created
|
|
3336
|
+
// account claiming a manager. Its own try: at this point the account is
|
|
3337
|
+
// fully created and seeded, so a write failure must not be reported as a
|
|
3338
|
+
// generic create failure (which would push the operator to retry and
|
|
3339
|
+
// orphan a second account). Report the real state — created but unmanaged —
|
|
3340
|
+
// with the recovery action.
|
|
3341
|
+
try {
|
|
3342
|
+
setManagingAdmin(ACCOUNTS_ROOT, newId, adminUserId);
|
|
3343
|
+
}
|
|
3344
|
+
catch (err) {
|
|
3345
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3346
|
+
process.stderr.write(`[account-lifecycle] op=create-admin-write-failed accountId=${newId} error=${msg}\n`);
|
|
3347
|
+
return { content: [{ type: "text", text: `account_create: sub-account ${newId} was created and its graph root seeded, but recording the managing admin failed: ${msg}. The account exists with no managing admin — run account_delete ${newId} and retry.` }], isError: true };
|
|
3348
|
+
}
|
|
3349
|
+
return { content: [{ type: "text", text: `Created client sub-account ${newId}, managed by admin ${adminUserId}. Switch to it from the sub-account picker to work in its data.` }] };
|
|
3310
3350
|
}
|
|
3311
3351
|
catch (err) {
|
|
3312
3352
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3314,6 +3354,24 @@ eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this
|
|
|
3314
3354
|
return { content: [{ type: "text", text: `account_create FAILED: ${msg}` }], isError: true };
|
|
3315
3355
|
}
|
|
3316
3356
|
});
|
|
3357
|
+
eagerTool(server, "account_reassign_admin", "Reassign a CLIENT sub-account's managing house admin. Pass `accountId` (the client sub-account) and `adminUserId` (the new managing admin's userId — must be a current house admin; get the choices from admin-list and ask the operator which team member takes it). Rewrites the sub-account's managingAdminUserId; the new admin is picked up on the next passive intake. Use it to repair a dangling managing admin (one whose house admin was removed) or to hand a client to a different team member. Refuses if the account is not a client sub-account, or if adminUserId is not a current house admin. Operator-only.", {
|
|
3358
|
+
accountId: z.string().describe("The client sub-account (UUID) whose managing admin changes."),
|
|
3359
|
+
adminUserId: z.string().describe("The userId of the house admin to become the managing admin. Must be a current house admin (see admin-list)."),
|
|
3360
|
+
}, async ({ accountId, adminUserId }) => {
|
|
3361
|
+
if (!ACCOUNT_ID)
|
|
3362
|
+
return refuseNoAccount("account_reassign_admin");
|
|
3363
|
+
const res = reassignManagingAdmin(ACCOUNTS_ROOT, accountId, adminUserId);
|
|
3364
|
+
if (!res.ok) {
|
|
3365
|
+
const msg = res.reason === "no-account"
|
|
3366
|
+
? `${accountId} is not a registered account on this install.`
|
|
3367
|
+
: res.reason === "not-client"
|
|
3368
|
+
? `${accountId} is not a client sub-account (the house account has no managing admin).`
|
|
3369
|
+
: `${adminUserId} is not a current house admin. Call admin-list for the valid managing-admin choices.`;
|
|
3370
|
+
return { content: [{ type: "text", text: `account_reassign_admin refused: ${msg}` }], isError: true };
|
|
3371
|
+
}
|
|
3372
|
+
process.stderr.write(`[account-lifecycle] op=reassign accountId=${accountId} from=${res.previousAdminUserId ? res.previousAdminUserId.slice(0, 8) : "-"} to=${adminUserId.slice(0, 8)}\n`);
|
|
3373
|
+
return { content: [{ type: "text", text: `Reassigned client sub-account ${accountId} to managing admin ${adminUserId}. It takes effect on the next passive intake.` }] };
|
|
3374
|
+
});
|
|
3317
3375
|
eagerTool(server, "account_delete", "Archive a CLIENT sub-account: move its directory to data/accounts/.trash/ so it no longer appears in the sub-account list. Graph data is RETAINED — run account_purge with the typed confirmation to irreversibly delete it. Refuses to archive the house sub-account. Pass the accountId to archive.", {
|
|
3318
3376
|
accountId: z.string().describe("The client accountId (UUID) to archive."),
|
|
3319
3377
|
}, async ({ accountId }) => {
|