@rubytech/create-maxy-code 0.1.423 → 0.1.429
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/embed-client/dist/index.d.ts +5 -1
- package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/embed-client/dist/index.js +114 -36
- package/payload/platform/lib/embed-client/dist/index.js.map +1 -1
- package/payload/platform/lib/embed-client/src/index.ts +141 -34
- package/payload/platform/neo4j/schema.cypher +13 -0
- 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/datetime-inject.sh +115 -0
- 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 +17 -3
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
- package/payload/platform/plugins/docs/references/cross-account-authority.md +8 -0
- package/payload/platform/plugins/docs/references/internals.md +5 -1
- 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/mcp/dist/tools/__tests__/embed-throughput.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-throughput.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-throughput.test.js +92 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-throughput.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-timeout.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-timeout.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-timeout.test.js +56 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embed-timeout.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js +56 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js.map +1 -1
- 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 +2 -0
- 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/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 +13 -11
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- 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/index.js +3 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts +9 -3
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js +19 -15
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.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 +34 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.js +7 -18
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.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 +18 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +13 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +11 -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 +28 -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 +8 -1
- 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 +8 -1
- 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 +28 -2
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +32 -3
- 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 +1 -1
- package/payload/server/{chunk-3Z5WE53X.js → chunk-UUG6ZSO5.js} +65 -9
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/AdminLoginScreens-C4AZT7cQ.js +1 -0
- package/payload/server/public/assets/AdminShell-BfLQQ27Z.js +2 -0
- package/payload/server/public/assets/{Checkbox-CgCqOslz.js → Checkbox-BC0c_gbn.js} +1 -1
- package/payload/server/public/assets/admin-SaF1F4e-.js +1 -0
- package/payload/server/public/assets/{arc-DyzjCWmw.js → arc-qKfXIt8v.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-qWfElD16.js → architectureDiagram-Q4EWVU46-DkVrGn7G.js} +1 -1
- package/payload/server/public/assets/audio-attachment-mime-BBW07eyG.js +2 -0
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DO2fFHC3.js → blockDiagram-DXYQGD6D-DqavswGH.js} +1 -1
- package/payload/server/public/assets/browser-CvZxNdKw.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Ca-q5gDe.js → c4Diagram-AHTNJAMY-B7fyKiXe.js} +1 -1
- package/payload/server/public/assets/calendar-DXxLMF2J.js +1 -0
- package/payload/server/public/assets/channel-j70eMvcL.js +1 -0
- package/payload/server/public/assets/chat-B9Qjt7C9.js +1 -0
- package/payload/server/public/assets/chevron-left-CLPxfjDV.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-CX2PxuDw.js → chunk-2KRD3SAO-DsgMtGkv.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-D8l6U4Ne.js → chunk-336JU56O-CkdISfNt.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-D-NcAlTW.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CG32Lkbh.js → chunk-4BX2VUAB-CYpiF2bl.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BnAQbO-v.js → chunk-4TB4RGXK-CWyc_F7v.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-ejqRH2M5.js → chunk-55IACEB6-BbzKMw2Z.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-ClkicQ-T.js → chunk-5FUZZQ4R-u19Nai9e.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-Be-mE_rx.js → chunk-5PVQY5BW-Ba8by6Uy.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-DPsemed2.js → chunk-67CJDMHE-BwUuxtyb.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BQRQJETl.js → chunk-7N4EOEYR-BRH9iM9L.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-B1wcAVCK.js → chunk-AA7GKIK3-BGoztgLL.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DIua053h.js → chunk-BSJP7CBP-C5GDAZTl.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C9GHSCS9.js → chunk-CIAEETIT-ChY32VPS.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-C1T3l1xj.js → chunk-EDXVE4YY-CPSD3p6p.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-8kPcXEQC.js → chunk-ENJZ2VHE-D1oL-iUr.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Bhoeq9sD.js → chunk-FMBD7UC4-CPvYEr54.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-BIBpJi8I.js → chunk-FOC6F5B3-9Yyj7Vbz.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DELXp6fj.js → chunk-ICPOFSXX-BaYgh8FR.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DdckXldP.js → chunk-K5T4RW27-5ucpeRvm.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-BJhXejKT.js → chunk-KGLVRYIC-DW-FHk7T.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BSd8nyM2.js → chunk-LIHQZDEY-CGGVfAyG.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-CBi55uxq.js → chunk-ORNJ4GCN-Bv8EUa5d.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-Dfbzl8xh.js → chunk-OYMX7WX6-C1mXJnyw.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CAwbQBtf.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BN1w8_Cq.js → chunk-U2HBQHQK-PwSQz6EP.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-CrRay1NJ.js → chunk-X2U36JSP--NPHTBlU.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-C_ZE776O.js → chunk-XPW4576I-Bbc7Da32.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CUSlqWBY.js → chunk-YZCP3GAM-D-9v8e--.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-BdPUfSiD.js → chunk-ZZ45TVLE-CMPg670Q.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ct6Rn0vt.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_2XCIFv.js +1 -0
- package/payload/server/public/assets/clone-DjPWA_NH.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-D4E9htTz.js → cose-bilkent-S5V4N54A-NVSb1Bhy.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-ByuvYV7N.js → dagre-KV5264BT-V2Ai1DFl.js} +1 -1
- package/payload/server/public/assets/{dagre-D1B5TQL1.js → dagre-QfLcO-1j.js} +1 -1
- package/payload/server/public/assets/data-BKE6Sa-g.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-B-0KduTa.js → diagram-5BDNPKRD-Caw21693.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CSQmo3eB.js → diagram-G4DWMVQ6-CgwVkT2q.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DK0kWiB3.js → diagram-MMDJMWI5-DFUrLZhs.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CYbK2reZ.js → diagram-TYMM5635-CEEiEuZJ.js} +1 -1
- package/payload/server/public/assets/{dist-CADCHk0t.js → dist-Cvgxm7qv.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-C-JQHvJF.js → erDiagram-SMLLAGMA-CG10NrT3.js} +1 -1
- package/payload/server/public/assets/file-download-B2B2DTar.js +1 -0
- package/payload/server/public/assets/{flatten-ROVzlo1u.js → flatten-DAO0sfCM.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-OTOd2czg.js → flowDiagram-DWJPFMVM-BqkV9Okv.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BWJ778ou.js → ganttDiagram-T4ZO3ILL-DRiCDQF6.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-B8Hx7f-D.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CkNcyISe.js → gitGraphDiagram-UUTBAWPF-BZV2voyy.js} +1 -1
- package/payload/server/public/assets/graph-DRNzDBTV.js +51 -0
- package/payload/server/public/assets/{graph-labels-BfG6FO25.js → graph-labels-DxqDdO7G.js} +1 -1
- package/payload/server/public/assets/{graphlib-8z59Ng6C.js → graphlib-DGPThX8M.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-BicCJTqU.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +2 -0
- package/payload/server/public/assets/{isEmpty-DXf_sApt.js → isEmpty-Cb_U81C6.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js → ishikawaDiagram-UXIWVN3A-BKsAxSvp.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CZT_4r_Z.js → journeyDiagram-VCZTEJTY-CmliuOd-.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Dp1Vd5YM.js → kanban-definition-6JOO6SKY-DAlElMb-.js} +1 -1
- package/payload/server/public/assets/{line-UPAcwY66.js → line-DKZqLKAC.js} +1 -1
- package/payload/server/public/assets/{linear-BeeahLKw.js → linear-CbSGdh1x.js} +1 -1
- package/payload/server/public/assets/{maximize-2-DrBaKD3z.js → maximize-2-DKx6OBKv.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CLKle8df.js → mermaid-parser.core-DFvbao9C.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-CwRj5rL5.js → mermaid.core-C0vEBxDm.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CNQUY9UJ.js → mindmap-definition-QFDTVHPH-CShASUKd.js} +1 -1
- package/payload/server/public/assets/operator-CTJjI8te.js +1 -0
- package/payload/server/public/assets/{ordinal-B2r5L1v4.js → ordinal-H0tVmBDP.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Ckn_5hq0.js +1 -0
- package/payload/server/public/assets/page-CbI1wtef.js +1 -0
- package/payload/server/public/assets/page-JzxyFGle.js +32 -0
- package/payload/server/public/assets/{pdf-render-m08Xm2Nm.js → pdf-render-jAQU0nXt.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-DzlGQW3J.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-Scnq58FX.js → pieDiagram-DEJITSTG-CBZHpDAJ.js} +1 -1
- package/payload/server/public/assets/play-DHttF4fl.js +1 -0
- package/payload/server/public/assets/public-DzeW3_eM.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wPfZo_vs.js → quadrantDiagram-34T5L4WZ-n3bB3K91.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.js +1 -0
- package/payload/server/public/assets/{reduce-CFlDlJf5.js → reduce-DAvwat-a.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-yYkH12K2.js → requirementDiagram-MS252O5E-DiDvQK3c.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-fVWRBzzV.js → sankeyDiagram-XADWPNL6-DWDQQZmd.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-Dbap3X6h.js → sequenceDiagram-FGHM5R23-DmAqRlBT.js} +1 -1
- package/payload/server/public/assets/square-CHcQHtK8.js +1 -0
- package/payload/server/public/assets/{src-Bi0fLV9t.js → src-DV4LKv2E.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DkPLAqIh.js → stateDiagram-FHFEXIEX-D4R-n6bZ.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BcFRbGRu.js +1 -0
- package/payload/server/public/assets/tasks-ClfJU1CK.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DabKBJyd.js → timeline-definition-GMOUNBTQ-CjiHGP4l.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-BtRr5vXp.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +1 -0
- package/payload/server/public/assets/useCopyFeedback-CPEOUSGU.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CdS1CorH.css +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DyjTbzQR.js +9 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CaKT7c-c.js → vennDiagram-DHZGUBPP-CBdDQ7c-.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DgMn-9ZJ.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BZ5TTSOL.js → wardleyDiagram-NUSXRM2D-BlIQPKLb.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CLfeklJ9.js → xychartDiagram-5P7HB3ND-CslKgh9B.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 +14 -12
- package/payload/server/public/data.html +11 -10
- package/payload/server/public/graph.html +12 -10
- package/payload/server/public/index.html +15 -14
- package/payload/server/public/operator.html +16 -14
- package/payload/server/public/public.html +14 -12
- package/payload/server/public/tasks.html +18 -0
- package/payload/server/server.js +832 -233
- package/payload/server/public/assets/AdminLoginScreens-B1P6PrZ3.js +0 -1
- package/payload/server/public/assets/AdminShell-Y1fHQApK.js +0 -2
- package/payload/server/public/assets/Transcript-DZzFjhoq.js +0 -2
- package/payload/server/public/assets/admin-BRBjJEci.js +0 -1
- package/payload/server/public/assets/admin-types-hioowVct.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +0 -1
- package/payload/server/public/assets/browser-ID7iqIi4.js +0 -1
- package/payload/server/public/assets/calendar-BEQPMTtd.js +0 -1
- package/payload/server/public/assets/channel-CYBkRz7E.js +0 -1
- package/payload/server/public/assets/chat-D0GDSWM-.js +0 -1
- package/payload/server/public/assets/chevron-left-A0HYKwGY.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-CV38dClT.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +0 -1
- package/payload/server/public/assets/graph-BK0MjKRy.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-CnpNyIJ8.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +0 -1
- package/payload/server/public/assets/page-DC_L-IS1.js +0 -32
- package/payload/server/public/assets/page-DKHLflh7.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +0 -1
- package/payload/server/public/assets/public-ByUJr48E.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +0 -1
- package/payload/server/public/assets/rotate-ccw-D45TbeAI.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-CCn22f98.css +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CprHkBnZ.js +0 -9
- package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BVuIxZJh.js → _baseFor-D3BqAOBr.js} +0 -0
- /package/payload/server/public/assets/{array-B9UHiPd-.js → array-iYP9fzJp.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-BQSBzPsB.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-BrdA1gDi.js} +0 -0
- /package/payload/server/public/assets/{init-DlZdxViB.js → init-Bx0S6pkp.js} +0 -0
- /package/payload/server/public/assets/{katex-Xo0SyWvP.js → katex-BsiH_a6d.js} +0 -0
- /package/payload/server/public/assets/{path-6uRLdFF7.js → path-BLSgTy3w.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CvXuAEud.js → preload-helper-ClBMtM1g.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-Djo4Abte.js → rough.esm-BMOtDaaS.js} +0 -0
|
@@ -27,6 +27,50 @@ export declare function getChannelRoutingAccountId(accountsDir: string): string
|
|
|
27
27
|
* correctly-provisioned install) or if the house account.json cannot be
|
|
28
28
|
* read/parsed. Preserves every other field on the house account.json. */
|
|
29
29
|
export declare function setChannelRoutingAccountId(accountsDir: string, targetAccountId: string): void;
|
|
30
|
+
/** Task 1500 — the house account's admin userIds, the set an operator may
|
|
31
|
+
* choose from when naming a new client sub-account's managing admin. Reads the
|
|
32
|
+
* house account.json `admins[]` (each entry keyed on `userId`). Returns [] when
|
|
33
|
+
* no house account exists or its `admins[]` is empty/absent. Client `admins[]`
|
|
34
|
+
* are never house admins, so a client-only registry yields []. */
|
|
35
|
+
export declare function readHouseAdminUserIds(accountsDir: string): string[];
|
|
36
|
+
/** Task 1500 — record the managing house admin on a client sub-account by
|
|
37
|
+
* writing `managingAdminUserId` onto its account.json, preserving every other
|
|
38
|
+
* field. Throws if the sub-account.json cannot be read/parsed (the caller turns
|
|
39
|
+
* it into a tool error). The caller validates `adminUserId` against
|
|
40
|
+
* readHouseAdminUserIds first — this writer does not re-validate. */
|
|
41
|
+
export declare function setManagingAdmin(accountsDir: string, accountId: string, adminUserId: string): void;
|
|
42
|
+
/** Task 1501 — every client sub-account whose `managingAdminUserId` equals
|
|
43
|
+
* `adminUserId`, sorted. Only role:"client" accounts count: a managing admin is
|
|
44
|
+
* always a house admin, so the house account is never a managed sub-account even
|
|
45
|
+
* if its account.json happens to carry the field. Filesystem-only; a dir with no
|
|
46
|
+
* account.json, or an unparseable one, is excluded (corruption discipline).
|
|
47
|
+
*
|
|
48
|
+
* Enumeration matches account-admin-census's `readAccounts` exactly — no UUID_RE
|
|
49
|
+
* prefilter, membership decided solely by a parseable account.json. That census
|
|
50
|
+
* is the acceptance oracle (`dangling=0` after a removal/reassignment), so the
|
|
51
|
+
* guard this drives must see the identical account set; a UUID prefilter here
|
|
52
|
+
* could let the guard miss an account the census still flags as dangling. Drives
|
|
53
|
+
* the admin-remove reconciliation guard — removing an admin this returns
|
|
54
|
+
* non-empty for would orphan those accounts, so the removal is refused. */
|
|
55
|
+
export declare function findClientAccountsManagedBy(accountsDir: string, adminUserId: string): string[];
|
|
56
|
+
export type ReassignResult = {
|
|
57
|
+
ok: true;
|
|
58
|
+
previousAdminUserId: string;
|
|
59
|
+
} | {
|
|
60
|
+
ok: false;
|
|
61
|
+
reason: "no-account" | "not-client" | "unknown-admin";
|
|
62
|
+
};
|
|
63
|
+
/** Task 1501 — repoint a client sub-account's managing admin to another live
|
|
64
|
+
* house admin. Refuses when the account is not registered (`no-account`), is not
|
|
65
|
+
* a client sub-account (`not-client` — the house has no managing admin), or when
|
|
66
|
+
* `adminUserId` is not a current house admin (`unknown-admin`). On success writes
|
|
67
|
+
* the new pointer (preserving every other field) and returns the previous
|
|
68
|
+
* `managingAdminUserId` (`""` when the account had none — the absent-pointer
|
|
69
|
+
* repair path). Reassigning to the same admin is allowed and idempotent. The
|
|
70
|
+
* caller (account_reassign_admin) turns the result into a tool response and the
|
|
71
|
+
* op=reassign log line; this function is pure of logging so its branches are
|
|
72
|
+
* unit-testable. */
|
|
73
|
+
export declare function reassignManagingAdmin(accountsDir: string, accountId: string, adminUserId: string): ReassignResult;
|
|
30
74
|
/** Move a live account dir into `data/accounts/.trash/<id>-<ts>/`. Graph rows are
|
|
31
75
|
* retained (purge is a separate, typed-confirmation step). Returns the trash
|
|
32
76
|
* path and whether the live dir is now absent (the verified post-condition). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAI9E,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;oCAEoC;AACpC,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBzE;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED;;+DAE+D;AAC/D,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAE3E;AAED;;;;;+DAK+D;AAC/D,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED;;;;0EAI0E;AAC1E,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAO7F;AAED;;iFAEiF;AACjF,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAO3C;AAED;;;yBAGyB;AACzB,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAoBT;AAED;iEACiE;AACjE,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClH;AAED;;;;;;;0BAO0B;AAC1B,eAAO,MAAM,WAAW,EAAE,SAAoD,CAAC;AAE/E;;;;;;;;;;;;;;;;;;+BAkB+B;AAC/B,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB7D;AAED;;mEAEmE;AACnE,eAAO,MAAM,yBAAyB,2DAA2D,CAAC;AAClG,eAAO,MAAM,oBAAoB,sDAAsD,CAAC"}
|
|
1
|
+
{"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,6 +77,102 @@ export function setChannelRoutingAccountId(accountsDir, targetAccountId) {
|
|
|
77
77
|
cfg.channelRoutingAccountId = targetAccountId;
|
|
78
78
|
writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
79
79
|
}
|
|
80
|
+
/** Task 1500 — the house account's admin userIds, the set an operator may
|
|
81
|
+
* choose from when naming a new client sub-account's managing admin. Reads the
|
|
82
|
+
* house account.json `admins[]` (each entry keyed on `userId`). Returns [] when
|
|
83
|
+
* no house account exists or its `admins[]` is empty/absent. Client `admins[]`
|
|
84
|
+
* are never house admins, so a client-only registry yields []. */
|
|
85
|
+
export function readHouseAdminUserIds(accountsDir) {
|
|
86
|
+
const house = findHouseAccount(accountsDir);
|
|
87
|
+
if (!house)
|
|
88
|
+
return [];
|
|
89
|
+
const configPath = resolve(accountsDir, house.accountId, "account.json");
|
|
90
|
+
try {
|
|
91
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
92
|
+
if (!Array.isArray(cfg.admins))
|
|
93
|
+
return [];
|
|
94
|
+
return cfg.admins
|
|
95
|
+
.map((a) => (a && typeof a === "object" && typeof a.userId === "string" ? a.userId : null))
|
|
96
|
+
.filter((u) => u !== null && u.length > 0);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Task 1500 — record the managing house admin on a client sub-account by
|
|
103
|
+
* writing `managingAdminUserId` onto its account.json, preserving every other
|
|
104
|
+
* field. Throws if the sub-account.json cannot be read/parsed (the caller turns
|
|
105
|
+
* it into a tool error). The caller validates `adminUserId` against
|
|
106
|
+
* readHouseAdminUserIds first — this writer does not re-validate. */
|
|
107
|
+
export function setManagingAdmin(accountsDir, accountId, adminUserId) {
|
|
108
|
+
const configPath = resolve(accountsDir, accountId, "account.json");
|
|
109
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
110
|
+
cfg.managingAdminUserId = adminUserId;
|
|
111
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
112
|
+
}
|
|
113
|
+
/** Task 1501 — every client sub-account whose `managingAdminUserId` equals
|
|
114
|
+
* `adminUserId`, sorted. Only role:"client" accounts count: a managing admin is
|
|
115
|
+
* always a house admin, so the house account is never a managed sub-account even
|
|
116
|
+
* if its account.json happens to carry the field. Filesystem-only; a dir with no
|
|
117
|
+
* account.json, or an unparseable one, is excluded (corruption discipline).
|
|
118
|
+
*
|
|
119
|
+
* Enumeration matches account-admin-census's `readAccounts` exactly — no UUID_RE
|
|
120
|
+
* prefilter, membership decided solely by a parseable account.json. That census
|
|
121
|
+
* is the acceptance oracle (`dangling=0` after a removal/reassignment), so the
|
|
122
|
+
* guard this drives must see the identical account set; a UUID prefilter here
|
|
123
|
+
* could let the guard miss an account the census still flags as dangling. Drives
|
|
124
|
+
* the admin-remove reconciliation guard — removing an admin this returns
|
|
125
|
+
* non-empty for would orphan those accounts, so the removal is refused. */
|
|
126
|
+
export function findClientAccountsManagedBy(accountsDir, adminUserId) {
|
|
127
|
+
let names;
|
|
128
|
+
try {
|
|
129
|
+
names = readdirSync(accountsDir);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const out = [];
|
|
135
|
+
for (const name of names) {
|
|
136
|
+
const configPath = resolve(accountsDir, name, "account.json");
|
|
137
|
+
try {
|
|
138
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
139
|
+
if (cfg.role === "client" && cfg.managingAdminUserId === adminUserId)
|
|
140
|
+
out.push(name);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// missing/unparseable account.json — not a registered account
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out.sort();
|
|
147
|
+
}
|
|
148
|
+
/** Task 1501 — repoint a client sub-account's managing admin to another live
|
|
149
|
+
* house admin. Refuses when the account is not registered (`no-account`), is not
|
|
150
|
+
* a client sub-account (`not-client` — the house has no managing admin), or when
|
|
151
|
+
* `adminUserId` is not a current house admin (`unknown-admin`). On success writes
|
|
152
|
+
* the new pointer (preserving every other field) and returns the previous
|
|
153
|
+
* `managingAdminUserId` (`""` when the account had none — the absent-pointer
|
|
154
|
+
* repair path). Reassigning to the same admin is allowed and idempotent. The
|
|
155
|
+
* caller (account_reassign_admin) turns the result into a tool response and the
|
|
156
|
+
* op=reassign log line; this function is pure of logging so its branches are
|
|
157
|
+
* unit-testable. */
|
|
158
|
+
export function reassignManagingAdmin(accountsDir, accountId, adminUserId) {
|
|
159
|
+
const configPath = resolve(accountsDir, accountId, "account.json");
|
|
160
|
+
let cfg;
|
|
161
|
+
try {
|
|
162
|
+
cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return { ok: false, reason: "no-account" };
|
|
166
|
+
}
|
|
167
|
+
if (cfg.role !== "client")
|
|
168
|
+
return { ok: false, reason: "not-client" };
|
|
169
|
+
if (!readHouseAdminUserIds(accountsDir).includes(adminUserId))
|
|
170
|
+
return { ok: false, reason: "unknown-admin" };
|
|
171
|
+
const previousAdminUserId = typeof cfg.managingAdminUserId === "string" ? cfg.managingAdminUserId : "";
|
|
172
|
+
cfg.managingAdminUserId = adminUserId;
|
|
173
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
174
|
+
return { ok: true, previousAdminUserId };
|
|
175
|
+
}
|
|
80
176
|
/** Move a live account dir into `data/accounts/.trash/<id>-<ts>/`. Graph rows are
|
|
81
177
|
* retained (purge is a separate, typed-confirmation step). Returns the trash
|
|
82
178
|
* path and whether the live dir is now absent (the verified post-condition). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,7 +22,14 @@ Answer timezone-related questions and relative-date arithmetic using Node.js —
|
|
|
22
22
|
- User asks "what time is it?" with a location qualifier
|
|
23
23
|
- User uses a relative date: "last Tuesday", "two weeks ago", "yesterday", "this Christmas", "Q3 last year", "Q1 2025"
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
## Which surface for which question
|
|
26
|
+
|
|
27
|
+
One rule, no exceptions:
|
|
28
|
+
|
|
29
|
+
- **"What time is it?" / today / the current instant / today's weekday** → read the `<datetime>` block. It is injected into every turn (a `UserPromptSubmit` hook) and reports now in the operator's own timezone with the weekday and the raw UTC time; it is never stale. No tool call — the answer is already in front of you.
|
|
30
|
+
- **The current time in another named city or timezone** → the Node one-liner below (it formats now in any IANA zone).
|
|
31
|
+
- **The weekday or locale view of a *specific* datetime you already know** — a future meeting time, a stored timestamp — → the `time-resolve` tool (scheduling plugin): pass it the ISO and it returns the operator's locale view plus the weekday. Never eyeball a weekday; that is the failure this rule exists to prevent.
|
|
32
|
+
- **A relative date phrase** — "last Tuesday", "two weeks ago", "Q3 2024" → the deterministic Node one-liner in the "Relative date arithmetic" section below.
|
|
26
33
|
|
|
27
34
|
## Behaviour
|
|
28
35
|
|
|
@@ -146,6 +153,6 @@ The algorithm mirrors `platform/plugins/memory/mcp/src/lib/relative-date.ts` —
|
|
|
146
153
|
## Boundaries
|
|
147
154
|
|
|
148
155
|
- Answers timezone questions and relative-date arithmetic only — not scheduling, reminders, or alarms.
|
|
149
|
-
-
|
|
156
|
+
- The `<datetime>` block reports the current instant in the operator's own timezone (resolved from their profile on the graph), falling back to the server zone only when no timezone is configured. The Node one-liners here read the server's system clock. Either way, if the server clock is wrong the times will be wrong.
|
|
150
157
|
- Historical timezone data (e.g., "what time was it in Tokyo on 15 March 1990?") depends on the Node.js ICU dataset and may be inaccurate for dates before 1970.
|
|
151
158
|
- Relative-date parser handles single-date phrases only; multi-date ranges ("from March through May") are out of scope.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:1dcb3b43e4fbb1e5689cd8087e9f58e4a0e5e330fd7bd02df320f59a9bbc035b
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -345,7 +345,7 @@ These are part of Maxy's foundation and cannot be disabled:
|
|
|
345
345
|
| `browser` | Headless browser rendering — `browser-render` runs a JavaScript-heavy page in the device's Chromium and returns its rendered DOM. The JS-rendering leg of retrieval: WebFetch (summary) / `url-get` (verbatim, server-rendered) / `browser-render` (JS-rendered). |
|
|
346
346
|
| `maxy-guide` | User guide and platform documentation (this plugin) |
|
|
347
347
|
| `cloudflare` | Cloudflare Tunnel — remote access via your custom domain |
|
|
348
|
-
| `scheduling` | Calendar and scheduling — events, appointments, recurring triggers.
|
|
348
|
+
| `scheduling` | Calendar and scheduling — events, appointments, recurring triggers. The current instant — now, today, today's weekday — is in the injected `<datetime>` block; read it there rather than computing it by hand. To interpret a *specific* datetime (a stored timestamp, a future meeting time, its day of week), pass its ISO to the `time-resolve` tool, which returns the operator's locale view; `time-resolve` requires an ISO input and cannot read the clock. |
|
|
349
349
|
| `email` | Agent email account — setup, read, send, reply, search, auto-respond |
|
|
350
350
|
| `tasks` | Task lifecycle — create, update, list, relate, complete |
|
|
351
351
|
| `workflows` | Persistent named workflows — reusable instruction sets |
|
|
@@ -528,6 +528,14 @@ By default the admin assistant is scoped the same way — it acts on the house a
|
|
|
528
528
|
|
|
529
529
|
The operator wants this for two surfaces: the account graph and the account filesystem. Each is a separate adoption; a coverage ledger tracks them and a background check confirms neither silently regresses.
|
|
530
530
|
|
|
531
|
+
## Creating a sub-account names its managing admin
|
|
532
|
+
|
|
533
|
+
Every client sub-account must have a responsible house admin. `account_create` requires the `userId` of a current house admin as the managing admin and refuses to create the account without one — the admin agent asks the operator which house admin manages the client (the choices come from `admin-list`) before creating. The chosen admin is recorded as `managingAdminUserId` on the sub-account. A WhatsApp requirement that comes in for that sub-account is filed as a task assigned to its managing admin, so it lands with a named owner rather than unassigned. A standing check reports any client account whose managing admin is missing or no longer a live house admin.
|
|
534
|
+
|
|
535
|
+
## Reassigning and removing a managing admin
|
|
536
|
+
|
|
537
|
+
To hand a client to a different team member — or to repair a client whose managing admin was removed — use `account_reassign_admin` with the client's `accountId` and the new admin's `userId`. It refuses a target that is not a client sub-account or a `userId` that is not a current house admin. Removing a house admin is guarded so it cannot silently orphan anyone: `admin-remove` refuses to remove an admin who is still the managing admin of one or more client sub-accounts and names those accounts. Reassign each with `account_reassign_admin`, then remove the admin. After either action the standing check returns to reporting no dangling managing admins.
|
|
538
|
+
|
|
531
539
|
## Memory (graph)
|
|
532
540
|
|
|
533
541
|
The `memory` plugin gives the admin session access to a sub-account's knowledge graph. Its tools whose read or write is scoped solely by the account — search, lookup, write, update, edit, delete, edges, attachments, reports, conversation history, ingest, and graph maintenance — take an optional target. Omit it and the tool acts on the session's own account, exactly as before; pass a sub-account and a house-scoped admin session reads or writes that account's subgraph instead; any other session that names a target is refused.
|
|
@@ -2682,6 +2690,8 @@ either is a regression.
|
|
|
2682
2690
|
|
|
2683
2691
|
**`/chat` Claude-desktop transcript presentation.** The admin webchat keeps the shared `Transcript` shell (stream, follow-tail) but injects a /chat-only item renderer via the component's optional `renderItems` prop: `renderChatTimeline` in `app/chat/transcript-render.tsx`. Presentation: operator turns render as a right-aligned grey bubble showing the message text and, beneath it, a subtle always-visible time-of-day stamp (no label); delivered agent replies render as plain prose with the same time-of-day stamp beneath (the reply-document filename line stays, as prose); the stamp is HH:MM from the turn's `ts` (locale-formatted, right-aligned inside the operator bubble, left-aligned under agent prose), and a turn whose `ts` is null/unparseable shows no stamp, never an empty line — so only delivered operator and agent-reply turns are stamped, while tool runs, the collapsed "Thinking" block, and the agent-error banner stay time-free; a maximal consecutive run of `tool-call`/`tool-result` turns renders as one collapsed grey one-liner ("Used N tools ›") whose expansion shows every call and result payload — a lone call still gets the one-liner, and prose or a directive row ends a run. The WhatsApp/operator reader (`/whatsapp`, `OperatorConversations`) omits the prop and keeps the default `renderTimeline`, so its bubble/DM chrome is unchanged — except that since the attachment-render extraction the default renderer draws inbound `operator-inbound` uploads and `agent-file` deliveries inline (image thumbnail / pdf.js preview / audio player / download chip) via the attachment renderer shared with `/chat`, extracted to `app/chat/attachment-render.tsx` to break the cycle with `transcript-render.tsx`; `renderChatTimeline` is untouched. Both presentations are test-pinned (`app/whatsapp/__tests__/Transcript-*.test.tsx`, `app/chat/__tests__/transcript-render.test.tsx`, `app/chat/__tests__/pdf-preview.test.tsx`). **Day-divider:** both renderers insert a centered `.day-divider` row between two consecutive timeline items on different local calendar days (label `Today`/`Yesterday`, else `Sat 14 Jun 2026`), so a thread spanning midnight is never an ambiguous run of HH:MM; the first dated item gets a leading divider, a null/unparseable `ts` marks no boundary, dividers are computed over the filtered `visibleItems`, and in `/chat` a day crossover flushes any open tool/think run first so a collapsed run never spans it. Per-bubble HH:MM stamps are unchanged. Shared helpers `dayKey`/`dayLabel`/`itemTs`/`DayDivider` live in `app/whatsapp/Transcript.tsx`.
|
|
2684
2692
|
|
|
2693
|
+
**WhatsApp reader is read/write (WhatsApp-Web surface).** A WhatsApp store conversation in the operator Conversations reader (`OperatorConversations`) and the admin root channel viewer (`app/page.tsx`) no longer opens the plain read-only DM chrome — it opens `app/whatsapp/WhatsappWebConversation.tsx`, a WhatsApp-Web-styled surface: a conversation header, the delivered-only store transcript reskinned green/white under a `.wa-web` root class, and a reply composer (`app/whatsapp/ReplyComposer.tsx`). The operator can type text and attach files (the webchat allowlist: `SUPPORTED_MIME_SET` / 50 MB / 5 files) and send them into that WhatsApp conversation. **Send is reply-only and DM-only:** `POST /api/whatsapp-reader/reply` (`requireAdminSession`) refuses any target with no existing stored conversation (`reason=no-existing-conversation`, HTTP 403) — a cold first-contact send bans the account, so the composer is never a cold-contact path, enforced server-side — and refuses `@g.us` groups (`reason=group-send-blocked`), matching the agent's observe-only group invariant; a group thread shows a read-only note in place of the composer. The endpoint is the sole store writer for a manual send: right after each send resolves it appends the record itself, stamped `origin:'operator-manual'` (attributed "You", distinct from an assistant reply), because the Baileys echo that normally persists the store is gated on message text (`manager.ts:906`) and would leave an uncaptioned media send delivered-but-invisible. The record's `messageId` matches the echo's derivation so the async text echo's own persist dedups to a no-op; the send is confirmed by re-read (`op=recorded present=<bool>`). A sent send does not spawn an assistant turn (echo dispatch stays suppressed). Attachments deliver as WhatsApp media/documents and appear in the reader as an attributed text line (`[sent <file>]`) — inline media rendering in the store reader is a follow-up. The `.wa-web` skin is scoped to WhatsApp store threads only, so `/chat`, the admin agent session view, and the public visitor webchat are unchanged; the visitor webchat gains no send capability. The header call/video/search icons and the in-bubble delivered-tick are cosmetic (no presence or read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable. Non-WhatsApp reader rows (Telegram/webchat session JSONLs) keep the plain read-only `Transcript`. Observability rides `[wa-manual-send]` lines correlated by a per-request `sendId`.
|
|
2694
|
+
|
|
2685
2695
|
**`/chat` live activity line.** While a turn is in flight the transcript tail shows one ephemeral `ChatActivity` line (mounted only while `busy`, admin/operator only). It tracks real activity, not a timer: while a `Task` subagent runs it shows that subagent's headline (`agentType · description`, prefixed `N agents ·` when ≥2 are concurrent), sourced from `agent-<hex>.meta.json` via named `activity` SSE events the admin reader pushes from the session's `subagents/` dir; with no subagent active it shows a neutral word that advances only on a real turn arrival. The line carries a turn-elapsed clock and flips to a `stalled` state once nothing has been written for 5 minutes (`now − lastEmitAt`), so the operator can tell a wedge from progress without SSH. It is never added to the persisted timeline. Detail and the `[webchat-activity]` observability live in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
|
|
2686
2696
|
|
|
2687
2697
|
**maxy title for public sessions.** A `role=public` webchat spawn never produces a useful Claude Code `ai-title` (an anonymous one-line visitor turn), so every public row would otherwise read identically. The webchat route (`chat.ts`) composes a deterministic title — `Web · <senderId[:8]>[ · <personId>] · <UTC YYYY-MM-DD HH:mm>` (personId present only for gated visitors) — and threads it through the native webchat gateway's public spawn (`handleInbound` → `buildPublicWebchatSpawnRequest` → `managerSpawn`) to the manager `POST /public-spawn` body as `name`. `/spawn` validates it with `validateUserTitle` and, for `role=public` only, writes it into `UserTitleStore` so it occupies the operator-rename tier and wins over `ai-title`. Admin (`/rc-spawn`) and WhatsApp titling are unchanged. Observability: every public spawn logs `[spawn] role=public … title="…"` (or `title=missing`); the manager's row builder emits `[public-title] sessionId=… unexpected titleSource=<ai|null>` on the next list read for any public row that did not resolve from the user tier.
|
|
@@ -3490,7 +3500,7 @@ If Ollama is unavailable at write time, the node is stored without an embedding
|
|
|
3490
3500
|
|
|
3491
3501
|
The `memory-reindex` tool backfills missing embeddings by iterating nodes where `embedding IS NULL`. It uses the bounded representation (the `summary` for document/archive labels, never the raw body), embeds in batches with a per-node fallback on batch error, and **skips and continues** past a failing node — keeping skipped nodes out of the re-fetch via an exclude set — instead of aborting the whole run. `embed()`/`embedBatch()` cap oversized input (`EMBED_INPUT_MAX_CHARS`) before the Ollama POST, so a large body embeds truncated rather than throwing. Per-node `[reindex] op=embedded|skip-error` lines and a terminal `[reindex] op=done processed=N skipped=M`.
|
|
3492
3502
|
|
|
3493
|
-
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than
|
|
3503
|
+
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than the effective sub-batch size into sub-batches, waiting `EMBED_SUBBATCH_COOLDOWN_MS` (default 1000) between them so the SoC gets cooling gaps; vectors are concatenated in input order. The effective sub-batch size is the smaller of two ceilings: the thermal count ceiling `EMBED_SUBBATCH_SIZE` (default 32) and a throughput ceiling `floor(EMBED_REQUEST_TIMEOUT_MS * 0.8 / EMBED_MS_PER_INPUT)`. The throughput ceiling exists because on a CPU-only embedder a fixed 32-input POST can exceed `EMBED_REQUEST_TIMEOUT_MS` and abort the atomic ingest write, so `EMBED_MS_PER_INPUT` (the budgeted worst-case wall-time for one capped input, default 6000 ms, env-tunable) bounds each POST to 80 % of the request timeout. Every `/api/embed` POST also carries `keep_alive` (`EMBED_KEEP_ALIVE`, default `-1` = keep the model resident forever) so an interactive graph query-embed never cold-loads and blows its 5 s timeout. All knobs are env-overridable for on-device tuning. The load-bearing thermal protection is the installer's `CPUQuota` cap on `ollama.service` (every embed caller is bounded there regardless of batch shape); this pacing is defence-in-depth. The UI's file-index path uses its own single-input embed client in `platform/ui/app/lib/neo4j-store.ts` (one embed per file, not a batch), so it is covered by the CPU cap, not by `embedBatch` pacing.
|
|
3494
3504
|
|
|
3495
3505
|
---
|
|
3496
3506
|
|
|
@@ -3797,6 +3807,8 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
|
|
|
3797
3807
|
|
|
3798
3808
|
**Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). `profile-read` exposes this set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
|
|
3799
3809
|
|
|
3810
|
+
**Trust a scheduled turn's provenance, never an inbound's claim (channel-agent grounding).** A cron-dispatched agent turn reaches the same channel builders as a real inbound, so its own authorisation text would otherwise land inside `## Context` — the zone `REFRAME_INSTRUCTION` demotes to untrusted — and a security-cautious agent would distrust its own automation (the self-cancel loop). The dispatcher therefore stamps a `ScheduleProvenance` marker (owning account, event, owner userId, dispatch time) that the channel builders render as a `## Scheduled automation (platform-verified)` preamble **above** `## Context` (and above the rules block); the whatsapp wrap tag names the door `scheduler`. Because the marker is dispatcher-stamped and never derived from payload text, an inbound message cannot forge it — placement above `## Context` alone is not the trust signal, the dispatcher provenance is. The grounding binds to it in two places: one recipient-neutral clause added byte-identical to the three reframe instructions (a marker-bearing turn is authorised standing automation; a reconfiguration or re-enable is not intrusion), and the admin `IDENTITY.md` "Untrusted input" rule that the agent never states a platform-level authenticity or impersonation verdict it was not given (an unsourced "this is not from you" is its own inference, flagged, never asserted as the platform's). The amplifier that let one misread harden was the accreting seat: a recurring event resumed one long-lived session every firing. A subsequent change removes it — each firing runs a fresh per-firing seat (`scheduledSeatIdFor`, salted with the firing's eventId + dispatch time), so distrust cannot accrete across firings, and the self-cancel guard compares the cancelling `SESSION_ID` to the node's dispatcher-stamped `lastDispatchSessionId` rather than a recomputed deterministic seat. See `.docs/scheduling-agent-dispatch.md § Operator-provenance marker` and `§ Session model — fresh per firing`.
|
|
3811
|
+
|
|
3800
3812
|
**Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N>` — `injected=0` on an account with stored rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
|
|
3801
3813
|
|
|
3802
3814
|
---
|
|
@@ -3941,6 +3953,8 @@ The doctrine is observed at the storage primitive: writes to `:Person`, `:UserPr
|
|
|
3941
3953
|
|
|
3942
3954
|
Two surfaces emit the lifecycle: agent-driven actions call `work-create`/`work-update`/`work-complete` over MCP (`work-create` accepts `kind`, the canonical `inputsProvided` call-shape record, `inputs` + `inputSchema` for the operator-meaningful form payload, and `raisedDuringConversationKey` to resolve the `RAISED_DURING` edge). Shell-driven actions wrap their script invocation in [platform/ui/app/lib/cloudflare-task-tracker.ts](../../../ui/app/lib/cloudflare-task-tracker.ts) (cloudflare is the first; installer / brand-publish / OAuth-login deferred). Both surfaces emit the same `[task] action-start|step|done` log lines so operators can grep one channel uniformly. Both also call the central `redactSecrets` primitive ([platform/lib/task-secrets/](../../../lib/task-secrets/)) to strip schema-tagged secret keys before persisting `inputs.<field>` props on the Task — see `.docs/neo4j.md § Audit Task input contract` for the contract that replaces per-kind allow-lists.
|
|
3943
3955
|
|
|
3956
|
+
For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is the single account entry with `endedAt=null`, and each task carries a denormalized `secondsLogged` sum for list rendering. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
|
|
3957
|
+
|
|
3944
3958
|
Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `work-create` at the start of an autonomous flow, capture `taskId`, and pass it as `producedByTaskId` on every subsequent `memory-write` for a gated label. The gate verifies Task and write share the same `accountId`; mismatch is rejected loud. (2) **Direct-ask path:** the admin server resolves the active `:AdminConversation`'s `sessionId` UUID and stamps it as `SESSION_NODE_ID` in the spawn env at PTY-spawn time. The same stamp propagates onto specialist subagent spawns the admin dispatches so listing-curator, content-producer, database-operator etc. inherit the same conversation anchor. The `contact-create` and `memory-write` wrappers call `injectConversationProvenance` (exported from [`@maxy/graph-write`](../../../lib/graph-write/src/conversation-provenance.ts)) which MATCHes `(c:Conversation {sessionId, accountId})` — account isolation is part of the natural key, not a separate gate — and prepends the synthetic `:PRODUCED` edge (composed by Neo4j elementId, which the helper reads off the MATCH). No agent-visible schema field changes. `memory-write` uses the env-stamp only as a fallback when `producedByTaskId` is unset; `contact-create` has no `producedByTaskId` parameter today and relies on the env-stamp alone. Autonomous (cron-driven) specialists with no parent conversation legitimately have no env-stamp; those must thread `producedByTaskId`.
|
|
3945
3959
|
|
|
3946
3960
|
Operator audit cyphers:
|
|
@@ -75,6 +75,8 @@ either is a regression.
|
|
|
75
75
|
|
|
76
76
|
**`/chat` Claude-desktop transcript presentation.** The admin webchat keeps the shared `Transcript` shell (stream, follow-tail) but injects a /chat-only item renderer via the component's optional `renderItems` prop: `renderChatTimeline` in `app/chat/transcript-render.tsx`. Presentation: operator turns render as a right-aligned grey bubble showing the message text and, beneath it, a subtle always-visible time-of-day stamp (no label); delivered agent replies render as plain prose with the same time-of-day stamp beneath (the reply-document filename line stays, as prose); the stamp is HH:MM from the turn's `ts` (locale-formatted, right-aligned inside the operator bubble, left-aligned under agent prose), and a turn whose `ts` is null/unparseable shows no stamp, never an empty line — so only delivered operator and agent-reply turns are stamped, while tool runs, the collapsed "Thinking" block, and the agent-error banner stay time-free; a maximal consecutive run of `tool-call`/`tool-result` turns renders as one collapsed grey one-liner ("Used N tools ›") whose expansion shows every call and result payload — a lone call still gets the one-liner, and prose or a directive row ends a run. The WhatsApp/operator reader (`/whatsapp`, `OperatorConversations`) omits the prop and keeps the default `renderTimeline`, so its bubble/DM chrome is unchanged — except that since the attachment-render extraction the default renderer draws inbound `operator-inbound` uploads and `agent-file` deliveries inline (image thumbnail / pdf.js preview / audio player / download chip) via the attachment renderer shared with `/chat`, extracted to `app/chat/attachment-render.tsx` to break the cycle with `transcript-render.tsx`; `renderChatTimeline` is untouched. Both presentations are test-pinned (`app/whatsapp/__tests__/Transcript-*.test.tsx`, `app/chat/__tests__/transcript-render.test.tsx`, `app/chat/__tests__/pdf-preview.test.tsx`). **Day-divider:** both renderers insert a centered `.day-divider` row between two consecutive timeline items on different local calendar days (label `Today`/`Yesterday`, else `Sat 14 Jun 2026`), so a thread spanning midnight is never an ambiguous run of HH:MM; the first dated item gets a leading divider, a null/unparseable `ts` marks no boundary, dividers are computed over the filtered `visibleItems`, and in `/chat` a day crossover flushes any open tool/think run first so a collapsed run never spans it. Per-bubble HH:MM stamps are unchanged. Shared helpers `dayKey`/`dayLabel`/`itemTs`/`DayDivider` live in `app/whatsapp/Transcript.tsx`.
|
|
77
77
|
|
|
78
|
+
**WhatsApp reader is read/write (WhatsApp-Web surface).** A WhatsApp store conversation in the operator Conversations reader (`OperatorConversations`) and the admin root channel viewer (`app/page.tsx`) no longer opens the plain read-only DM chrome — it opens `app/whatsapp/WhatsappWebConversation.tsx`, a WhatsApp-Web-styled surface: a conversation header, the delivered-only store transcript reskinned green/white under a `.wa-web` root class, and a reply composer (`app/whatsapp/ReplyComposer.tsx`). The operator can type text and attach files (the webchat allowlist: `SUPPORTED_MIME_SET` / 50 MB / 5 files) and send them into that WhatsApp conversation. **Send is reply-only and DM-only:** `POST /api/whatsapp-reader/reply` (`requireAdminSession`) refuses any target with no existing stored conversation (`reason=no-existing-conversation`, HTTP 403) — a cold first-contact send bans the account, so the composer is never a cold-contact path, enforced server-side — and refuses `@g.us` groups (`reason=group-send-blocked`), matching the agent's observe-only group invariant; a group thread shows a read-only note in place of the composer. The endpoint is the sole store writer for a manual send: right after each send resolves it appends the record itself, stamped `origin:'operator-manual'` (attributed "You", distinct from an assistant reply), because the Baileys echo that normally persists the store is gated on message text (`manager.ts:906`) and would leave an uncaptioned media send delivered-but-invisible. The record's `messageId` matches the echo's derivation so the async text echo's own persist dedups to a no-op; the send is confirmed by re-read (`op=recorded present=<bool>`). A sent send does not spawn an assistant turn (echo dispatch stays suppressed). Attachments deliver as WhatsApp media/documents and appear in the reader as an attributed text line (`[sent <file>]`) — inline media rendering in the store reader is a follow-up. The `.wa-web` skin is scoped to WhatsApp store threads only, so `/chat`, the admin agent session view, and the public visitor webchat are unchanged; the visitor webchat gains no send capability. The header call/video/search icons and the in-bubble delivered-tick are cosmetic (no presence or read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable. Non-WhatsApp reader rows (Telegram/webchat session JSONLs) keep the plain read-only `Transcript`. Observability rides `[wa-manual-send]` lines correlated by a per-request `sendId`.
|
|
79
|
+
|
|
78
80
|
**`/chat` live activity line.** While a turn is in flight the transcript tail shows one ephemeral `ChatActivity` line (mounted only while `busy`, admin/operator only). It tracks real activity, not a timer: while a `Task` subagent runs it shows that subagent's headline (`agentType · description`, prefixed `N agents ·` when ≥2 are concurrent), sourced from `agent-<hex>.meta.json` via named `activity` SSE events the admin reader pushes from the session's `subagents/` dir; with no subagent active it shows a neutral word that advances only on a real turn arrival. The line carries a turn-elapsed clock and flips to a `stalled` state once nothing has been written for 5 minutes (`now − lastEmitAt`), so the operator can tell a wedge from progress without SSH. It is never added to the persisted timeline. Detail and the `[webchat-activity]` observability live in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
|
|
79
81
|
|
|
80
82
|
**maxy title for public sessions.** A `role=public` webchat spawn never produces a useful Claude Code `ai-title` (an anonymous one-line visitor turn), so every public row would otherwise read identically. The webchat route (`chat.ts`) composes a deterministic title — `Web · <senderId[:8]>[ · <personId>] · <UTC YYYY-MM-DD HH:mm>` (personId present only for gated visitors) — and threads it through the native webchat gateway's public spawn (`handleInbound` → `buildPublicWebchatSpawnRequest` → `managerSpawn`) to the manager `POST /public-spawn` body as `name`. `/spawn` validates it with `validateUserTitle` and, for `role=public` only, writes it into `UserTitleStore` so it occupies the operator-rename tier and wins over `ai-title`. Admin (`/rc-spawn`) and WhatsApp titling are unchanged. Observability: every public spawn logs `[spawn] role=public … title="…"` (or `title=missing`); the manager's row builder emits `[public-title] sessionId=… unexpected titleSource=<ai|null>` on the next list read for any public row that did not resolve from the user tier.
|
|
@@ -6,6 +6,14 @@ By default the admin assistant is scoped the same way — it acts on the house a
|
|
|
6
6
|
|
|
7
7
|
The operator wants this for two surfaces: the account graph and the account filesystem. Each is a separate adoption; a coverage ledger tracks them and a background check confirms neither silently regresses.
|
|
8
8
|
|
|
9
|
+
## Creating a sub-account names its managing admin
|
|
10
|
+
|
|
11
|
+
Every client sub-account must have a responsible house admin. `account_create` requires the `userId` of a current house admin as the managing admin and refuses to create the account without one — the admin agent asks the operator which house admin manages the client (the choices come from `admin-list`) before creating. The chosen admin is recorded as `managingAdminUserId` on the sub-account. A WhatsApp requirement that comes in for that sub-account is filed as a task assigned to its managing admin, so it lands with a named owner rather than unassigned. A standing check reports any client account whose managing admin is missing or no longer a live house admin.
|
|
12
|
+
|
|
13
|
+
## Reassigning and removing a managing admin
|
|
14
|
+
|
|
15
|
+
To hand a client to a different team member — or to repair a client whose managing admin was removed — use `account_reassign_admin` with the client's `accountId` and the new admin's `userId`. It refuses a target that is not a client sub-account or a `userId` that is not a current house admin. Removing a house admin is guarded so it cannot silently orphan anyone: `admin-remove` refuses to remove an admin who is still the managing admin of one or more client sub-accounts and names those accounts. Reassign each with `account_reassign_admin`, then remove the admin. After either action the standing check returns to reporting no dangling managing admins.
|
|
16
|
+
|
|
9
17
|
## Memory (graph)
|
|
10
18
|
|
|
11
19
|
The `memory` plugin gives the admin session access to a sub-account's knowledge graph. Its tools whose read or write is scoped solely by the account — search, lookup, write, update, edit, delete, edges, attachments, reports, conversation history, ingest, and graph maintenance — take an optional target. Omit it and the tool acts on the session's own account, exactly as before; pass a sub-account and a house-scoped admin session reads or writes that account's subgraph instead; any other session that names a target is refused.
|
|
@@ -95,7 +95,7 @@ If Ollama is unavailable at write time, the node is stored without an embedding
|
|
|
95
95
|
|
|
96
96
|
The `memory-reindex` tool backfills missing embeddings by iterating nodes where `embedding IS NULL`. It uses the bounded representation (the `summary` for document/archive labels, never the raw body), embeds in batches with a per-node fallback on batch error, and **skips and continues** past a failing node — keeping skipped nodes out of the re-fetch via an exclude set — instead of aborting the whole run. `embed()`/`embedBatch()` cap oversized input (`EMBED_INPUT_MAX_CHARS`) before the Ollama POST, so a large body embeds truncated rather than throwing. Per-node `[reindex] op=embedded|skip-error` lines and a terminal `[reindex] op=done processed=N skipped=M`.
|
|
97
97
|
|
|
98
|
-
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than
|
|
98
|
+
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than the effective sub-batch size into sub-batches, waiting `EMBED_SUBBATCH_COOLDOWN_MS` (default 1000) between them so the SoC gets cooling gaps; vectors are concatenated in input order. The effective sub-batch size is the smaller of two ceilings: the thermal count ceiling `EMBED_SUBBATCH_SIZE` (default 32) and a throughput ceiling `floor(EMBED_REQUEST_TIMEOUT_MS * 0.8 / EMBED_MS_PER_INPUT)`. The throughput ceiling exists because on a CPU-only embedder a fixed 32-input POST can exceed `EMBED_REQUEST_TIMEOUT_MS` and abort the atomic ingest write, so `EMBED_MS_PER_INPUT` (the budgeted worst-case wall-time for one capped input, default 6000 ms, env-tunable) bounds each POST to 80 % of the request timeout. Every `/api/embed` POST also carries `keep_alive` (`EMBED_KEEP_ALIVE`, default `-1` = keep the model resident forever) so an interactive graph query-embed never cold-loads and blows its 5 s timeout. All knobs are env-overridable for on-device tuning. The load-bearing thermal protection is the installer's `CPUQuota` cap on `ollama.service` (every embed caller is bounded there regardless of batch shape); this pacing is defence-in-depth. The UI's file-index path uses its own single-input embed client in `platform/ui/app/lib/neo4j-store.ts` (one embed per file, not a batch), so it is covered by the CPU cap, not by `embedBatch` pacing.
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
@@ -402,6 +402,8 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
|
|
|
402
402
|
|
|
403
403
|
**Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). `profile-read` exposes this set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
|
|
404
404
|
|
|
405
|
+
**Trust a scheduled turn's provenance, never an inbound's claim (channel-agent grounding).** A cron-dispatched agent turn reaches the same channel builders as a real inbound, so its own authorisation text would otherwise land inside `## Context` — the zone `REFRAME_INSTRUCTION` demotes to untrusted — and a security-cautious agent would distrust its own automation (the self-cancel loop). The dispatcher therefore stamps a `ScheduleProvenance` marker (owning account, event, owner userId, dispatch time) that the channel builders render as a `## Scheduled automation (platform-verified)` preamble **above** `## Context` (and above the rules block); the whatsapp wrap tag names the door `scheduler`. Because the marker is dispatcher-stamped and never derived from payload text, an inbound message cannot forge it — placement above `## Context` alone is not the trust signal, the dispatcher provenance is. The grounding binds to it in two places: one recipient-neutral clause added byte-identical to the three reframe instructions (a marker-bearing turn is authorised standing automation; a reconfiguration or re-enable is not intrusion), and the admin `IDENTITY.md` "Untrusted input" rule that the agent never states a platform-level authenticity or impersonation verdict it was not given (an unsourced "this is not from you" is its own inference, flagged, never asserted as the platform's). The amplifier that let one misread harden was the accreting seat: a recurring event resumed one long-lived session every firing. A subsequent change removes it — each firing runs a fresh per-firing seat (`scheduledSeatIdFor`, salted with the firing's eventId + dispatch time), so distrust cannot accrete across firings, and the self-cancel guard compares the cancelling `SESSION_ID` to the node's dispatcher-stamped `lastDispatchSessionId` rather than a recomputed deterministic seat. See `.docs/scheduling-agent-dispatch.md § Operator-provenance marker` and `§ Session model — fresh per firing`.
|
|
406
|
+
|
|
405
407
|
**Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N>` — `injected=0` on an account with stored rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
|
|
406
408
|
|
|
407
409
|
---
|
|
@@ -546,6 +548,8 @@ The doctrine is observed at the storage primitive: writes to `:Person`, `:UserPr
|
|
|
546
548
|
|
|
547
549
|
Two surfaces emit the lifecycle: agent-driven actions call `work-create`/`work-update`/`work-complete` over MCP (`work-create` accepts `kind`, the canonical `inputsProvided` call-shape record, `inputs` + `inputSchema` for the operator-meaningful form payload, and `raisedDuringConversationKey` to resolve the `RAISED_DURING` edge). Shell-driven actions wrap their script invocation in [platform/ui/app/lib/cloudflare-task-tracker.ts](../../../ui/app/lib/cloudflare-task-tracker.ts) (cloudflare is the first; installer / brand-publish / OAuth-login deferred). Both surfaces emit the same `[task] action-start|step|done` log lines so operators can grep one channel uniformly. Both also call the central `redactSecrets` primitive ([platform/lib/task-secrets/](../../../lib/task-secrets/)) to strip schema-tagged secret keys before persisting `inputs.<field>` props on the Task — see `.docs/neo4j.md § Audit Task input contract` for the contract that replaces per-kind allow-lists.
|
|
548
550
|
|
|
551
|
+
For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is the single account entry with `endedAt=null`, and each task carries a denormalized `secondsLogged` sum for list rendering. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
|
|
552
|
+
|
|
549
553
|
Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `work-create` at the start of an autonomous flow, capture `taskId`, and pass it as `producedByTaskId` on every subsequent `memory-write` for a gated label. The gate verifies Task and write share the same `accountId`; mismatch is rejected loud. (2) **Direct-ask path:** the admin server resolves the active `:AdminConversation`'s `sessionId` UUID and stamps it as `SESSION_NODE_ID` in the spawn env at PTY-spawn time. The same stamp propagates onto specialist subagent spawns the admin dispatches so listing-curator, content-producer, database-operator etc. inherit the same conversation anchor. The `contact-create` and `memory-write` wrappers call `injectConversationProvenance` (exported from [`@maxy/graph-write`](../../../lib/graph-write/src/conversation-provenance.ts)) which MATCHes `(c:Conversation {sessionId, accountId})` — account isolation is part of the natural key, not a separate gate — and prepends the synthetic `:PRODUCED` edge (composed by Neo4j elementId, which the helper reads off the MATCH). No agent-visible schema field changes. `memory-write` uses the env-stamp only as a fallback when `producedByTaskId` is unset; `contact-create` has no `producedByTaskId` parameter today and relies on the env-stamp alone. Autonomous (cron-driven) specialists with no parent conversation legitimately have no env-stamp; those must thread `producedByTaskId`.
|
|
550
554
|
|
|
551
555
|
Operator audit cyphers:
|
|
@@ -21,7 +21,7 @@ These are part of {{productName}}'s foundation and cannot be disabled:
|
|
|
21
21
|
| `browser` | Headless browser rendering — `browser-render` runs a JavaScript-heavy page in the device's Chromium and returns its rendered DOM. The JS-rendering leg of retrieval: WebFetch (summary) / `url-get` (verbatim, server-rendered) / `browser-render` (JS-rendered). |
|
|
22
22
|
| `maxy-guide` | User guide and platform documentation (this plugin) |
|
|
23
23
|
| `cloudflare` | Cloudflare Tunnel — remote access via your custom domain |
|
|
24
|
-
| `scheduling` | Calendar and scheduling — events, appointments, recurring triggers.
|
|
24
|
+
| `scheduling` | Calendar and scheduling — events, appointments, recurring triggers. The current instant — now, today, today's weekday — is in the injected `<datetime>` block; read it there rather than computing it by hand. To interpret a *specific* datetime (a stored timestamp, a future meeting time, its day of week), pass its ISO to the `time-resolve` tool, which returns the operator's locale view; `time-resolve` requires an ISO input and cannot read the clock. |
|
|
25
25
|
| `email` | Agent email account — setup, read, send, reply, search, auto-respond |
|
|
26
26
|
| `tasks` | Task lifecycle — create, update, list, relate, complete |
|
|
27
27
|
| `workflows` | Persistent named workflows — reusable instruction sets |
|
|
@@ -80,14 +80,14 @@ Manages the agent's own dedicated email account — IMAP for reading, SMTP for s
|
|
|
80
80
|
## Capabilities
|
|
81
81
|
|
|
82
82
|
- **Provider lookup:** `email-provider-info` — given just an email address, returns the inferred IMAP/SMTP hosts and the provider's app-password / trusted-application / IMAP-enable requirement, deterministically from the provider map. Call this BEFORE soliciting a password so the requirement is relayed first; most providers reject the normal login password. Returns "unknown" (operator supplies hosts) or "unsupported" (provider blocks IMAP/SMTP, e.g. Tuta).
|
|
83
|
-
- **Setup:** `email-setup` — collect credentials in plain chat and connect IMAP/SMTP. Hosts are auto-inferred for known providers; supports alias addresses (`agentAddress`).
|
|
83
|
+
- **Setup:** `email-setup` — collect credentials in plain chat and connect IMAP/SMTP. Hosts are auto-inferred for known providers; supports alias addresses (`agentAddress`). For hosts where the client-visible Drafts folder is not the one flagged `\Drafts`, pass `draftsFolder` to name it explicitly (e.g. `INBOX.Drafts`).
|
|
84
84
|
- **Read inbox:** `email-read` — metadata only (UID, sender, subject, date), plus a best-effort `Archive`/`Archive-Section` hint when the IMAP message's `receivedAt` falls inside a stored `:ConversationArchive {source:'email'}` section, plus an `Attachments (N): filename (mime, size); …` footer when the message carries enclosures. Supports pagination (`before_uid`), folders (`inbox`/`sent`), filtering by sender/date/subject. Metadata is derived from IMAP `bodyStructure` — no bytes downloaded.
|
|
85
85
|
- **Search inbox:** `email-search` — live IMAP search by sender, subject, body keyword, date range. Same per-archive hint and attachment footer as `email-read`.
|
|
86
86
|
- **Delete (move to Trash):** `email-delete` — move the messages named by `uids` (from a prior `email-read`/`email-search`, scoped to a source `folder` of `inbox` or `sent`) into the mailbox's Trash folder. **Recoverable, never an expunge** — the message leaves the inbox and permanent removal is left to the operator's own mail client or the provider's Trash auto-purge; this tool never permanently deletes. Trash is resolved per provider: the `\Trash` special-use folder if advertised, else the first existing of `Trash`, `[Gmail]/Trash`, `Deleted Items`, `Deleted Messages`. `Junk` is never treated as Trash. A mailbox with no resolvable Trash yields a typed error and moves nothing — there is no silent expunge fallback. A `uid` that is already gone is counted not-moved without failing the rest; the result reports `Moved N of M message(s) to <Trash>`. It deletes only the UIDs you name, never by search criteria.
|
|
87
87
|
- **Read one whole message:** `email-fetch-body` — the COMPLETE decoded body of a single message addressed by `uid` (with optional `folder`) or `message_id` (resolved across inbox and sent). No 500-char preview cap. Downloads the full RFC822 source and decodes it with `mailparser`, so every MIME shape — flat text, single-part HTML, `multipart/alternative`, and nested `multipart/related`/`multipart/mixed` (Outlook's inline-image house style) — yields a body; the preview tools above intentionally stay preview-only. Use this when the operator wants to read or act on a message's actual content.
|
|
88
88
|
- **Send:** `email-send` — new outbound email. Pass `cc`/`bcc` (each an array of email addresses) to copy or blind-copy recipients — CC appears in the message header, BCC recipients receive the message without appearing in any header. Pass `attachments` (absolute file paths under the account directory, ≤25 MB each) to attach files as SMTP attachments; an invalid path fails the send with no email dispatched. The success message names the CC recipients and reports the BCC count without naming them.
|
|
89
89
|
- **Reply:** `email-reply` — threaded reply to an existing email by `messageId`. Resolves the original envelope via IMAP `SEARCH HEADER Message-ID` (INBOX then `\Sent`), so threading works for any message still present on the server even if the graph never ingested it. Supports `replyAll`, `cc`/`bcc`, and `attachments` (same path rules as `email-send`). Explicit `cc`/`bcc` are added in addition to whatever `replyAll` places in `To`, passed through as given (no overlap de-duplication against `To`).
|
|
90
|
-
- **Compose to Drafts:** `email-draft` — builds the same MIME message as `email-send` (same `cc`/`bcc`/`attachments` rules; unlike a dispatched send, the stored draft keeps its `Bcc:` header, because the draft bytes are the only recipient carrier) but APPENDs it to the mailbox's `\Drafts` special-use folder with the `\Draft` flag instead of dispatching it. Nothing is sent. Pass `messageId` to compose a threaded draft-reply: the parent is resolved via IMAP and the draft carries `In-Reply-To`/`References` plus a `Re:` subject from the parent, exactly like `email-reply` (the `subject` argument is then optional and ignored). The success message names the Drafts folder, the APPEND UID, and the draft's own `Message-ID` (`draft id`) so a saved draft is never indistinguishable from a silent no-op and a later edit has a stable handle. Fails naming the mailbox when the server advertises no `\Drafts` special-use folder — no fallback folder is picked. Use when the operator wants to review and send by hand.
|
|
90
|
+
- **Compose to Drafts:** `email-draft` — builds the same MIME message as `email-send` (same `cc`/`bcc`/`attachments` rules; unlike a dispatched send, the stored draft keeps its `Bcc:` header, because the draft bytes are the only recipient carrier) but APPENDs it to the mailbox's `\Drafts` special-use folder with the `\Draft` flag instead of dispatching it. Nothing is sent. Pass `messageId` to compose a threaded draft-reply: the parent is resolved via IMAP and the draft carries `In-Reply-To`/`References` plus a `Re:` subject from the parent, exactly like `email-reply` (the `subject` argument is then optional and ignored). The success message names the Drafts folder, the APPEND UID, and the draft's own `Message-ID` (`draft id`) so a saved draft is never indistinguishable from a silent no-op and a later edit has a stable handle. Fails naming the mailbox when the server advertises no `\Drafts` special-use folder — no fallback folder is picked, unless a per-account `draftsFolder` override is configured (via `email-setup`), which is honoured before special-use discovery for hosts whose client-visible Drafts is a differently-named mailbox. Use when the operator wants to review and send by hand.
|
|
91
91
|
- **Revise a draft:** `email-draft-edit` — replace a stored draft. The `targetUid` is a fast-path hint; the draft's own `Message-ID` (`draftMessageId`) is the stable identity, because Gmail renumbers a draft's UID when it is touched. The tool tries the UID first, then relocates by a `Message-ID` header search, then composes the replacement from the parameters (same shape as `email-draft`, plus `targetUid`, optional `draftMessageId`, and an optional `messageId` for a threaded revision), APPENDs it with `\Draft`, and deletes the resolved old copy — the replacement is always appended before the old copy is expunged, so a mid-cycle failure never leaves zero copies. When neither identity resolves a live draft, the prior draft is gone: the replacement is saved as a fresh draft (`created`) rather than failing. A single transient transport drop is retried on a fresh connection; auth and other permanent errors surface immediately. Nothing is sent. The confirmation names the folder, the new UID, and the new draft id.
|
|
92
92
|
- **Send a draft:** `email-draft-send` — dispatch a stored draft addressed by its Drafts UID via SMTP, then remove it from Drafts. Recipients come from the draft's own To/Cc/Bcc headers (the `\Drafts` copy keeps its `Bcc:`), deduped, sent from the agent alias with an explicit SMTP envelope. The draft is deleted only after SMTP accepts; an SMTP failure leaves it in place and surfaces verbatim. A draft with no recipients is refused before sending. Operator-approved sends only.
|
|
93
93
|
- **Ingestion — fetch:** `email-fetch` — list new IMAP messages since the stored high-water mark. Metadata + body preview only — including attachment names, mimetypes, and sizes so the operator sees what's attached before approving. No bytes downloaded, no graph writes. Stages the batch in-memory for `email-ingest`.
|
|
@@ -27,14 +27,15 @@ vi.mock("imapflow", () => ({
|
|
|
27
27
|
return mockClient;
|
|
28
28
|
},
|
|
29
29
|
}));
|
|
30
|
-
import { appendDraft,
|
|
30
|
+
import { appendDraft, resolveDraftsFolder, resetDraftsCensusForTest, replaceDraft, fetchDraftSource, deleteDraft, } from "../lib/imap.js";
|
|
31
31
|
const config = {
|
|
32
32
|
email: "agent@example.com",
|
|
33
33
|
imapHost: "imap.example.com",
|
|
34
34
|
imapPort: 993,
|
|
35
35
|
imapSecurity: "tls",
|
|
36
36
|
};
|
|
37
|
-
describe("Task 815 —
|
|
37
|
+
describe("Task 815 — resolveDraftsFolder special-use branch", () => {
|
|
38
|
+
beforeEach(() => { resetDraftsCensusForTest(); });
|
|
38
39
|
it("returns the \\Drafts special-use path when advertised", async () => {
|
|
39
40
|
const client = {
|
|
40
41
|
list: async () => [
|
|
@@ -43,11 +44,79 @@ describe("Task 815 — resolveFolderName drafts branch", () => {
|
|
|
43
44
|
{ path: "[Gmail]/Drafts", specialUse: "\\Drafts" },
|
|
44
45
|
],
|
|
45
46
|
};
|
|
46
|
-
await expect(
|
|
47
|
+
await expect(resolveDraftsFolder(client, "m@x")).resolves.toBe("[Gmail]/Drafts");
|
|
47
48
|
});
|
|
48
49
|
it("throws a named error when no \\Drafts special-use folder is advertised", async () => {
|
|
49
50
|
const client = { list: async () => [{ path: "INBOX" }] };
|
|
50
|
-
await expect(
|
|
51
|
+
await expect(resolveDraftsFolder(client, "m@x")).rejects.toThrow(/\\Drafts special-use folder/);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("Task 1514 — drafts override + census", () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
resetDraftsCensusForTest();
|
|
57
|
+
vi.clearAllMocks();
|
|
58
|
+
});
|
|
59
|
+
const boxes = [
|
|
60
|
+
{ path: "INBOX" },
|
|
61
|
+
{ path: "Drafts", specialUse: "\\Drafts" },
|
|
62
|
+
{ path: "INBOX.Drafts" },
|
|
63
|
+
];
|
|
64
|
+
it("returns the override path when it exists in the LIST", async () => {
|
|
65
|
+
const client = { list: async () => boxes };
|
|
66
|
+
await expect(resolveDraftsFolder(client, "m@x", "INBOX.Drafts")).resolves.toBe("INBOX.Drafts");
|
|
67
|
+
});
|
|
68
|
+
it("throws a named error listing advertised folders when the override is absent", async () => {
|
|
69
|
+
const client = { list: async () => boxes };
|
|
70
|
+
await expect(resolveDraftsFolder(client, "m@x", "Nope")).rejects.toThrow(/Configured Drafts folder "Nope" not found/);
|
|
71
|
+
await expect(resolveDraftsFolder(client, "m@x", "Nope")).rejects.toThrow(/INBOX\.Drafts/);
|
|
72
|
+
});
|
|
73
|
+
it("falls back to the \\Drafts special-use folder when no override is set", async () => {
|
|
74
|
+
const client = { list: async () => boxes };
|
|
75
|
+
await expect(resolveDraftsFolder(client, "m@x")).resolves.toBe("Drafts");
|
|
76
|
+
});
|
|
77
|
+
it("logs the mailbox census once per mailbox across two resolutions of the same mailbox", async () => {
|
|
78
|
+
const writes = [];
|
|
79
|
+
vi.spyOn(console, "error").mockImplementation((...args) => {
|
|
80
|
+
writes.push(args.join(" "));
|
|
81
|
+
});
|
|
82
|
+
const client = { list: async () => boxes };
|
|
83
|
+
await resolveDraftsFolder(client, "m@x");
|
|
84
|
+
await resolveDraftsFolder(client, "m@x");
|
|
85
|
+
const census = writes.filter((w) => w.includes("drafts-resolve: census"));
|
|
86
|
+
expect(census).toHaveLength(1);
|
|
87
|
+
expect(census[0]).toContain("mailbox=m@x");
|
|
88
|
+
expect(census[0]).toContain("Drafts:\\Drafts");
|
|
89
|
+
});
|
|
90
|
+
it("logs a separate census per mailbox in one process (regression: two-mailbox account)", async () => {
|
|
91
|
+
const writes = [];
|
|
92
|
+
vi.spyOn(console, "error").mockImplementation((...args) => {
|
|
93
|
+
writes.push(args.join(" "));
|
|
94
|
+
});
|
|
95
|
+
const client = { list: async () => boxes };
|
|
96
|
+
await resolveDraftsFolder(client, "healthy@x");
|
|
97
|
+
await resolveDraftsFolder(client, "problem@x");
|
|
98
|
+
const census = writes.filter((w) => w.includes("drafts-resolve: census"));
|
|
99
|
+
expect(census).toHaveLength(2);
|
|
100
|
+
expect(census.some((c) => c.includes("mailbox=problem@x"))).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it("emits a via=override marker when the override resolves", async () => {
|
|
103
|
+
const writes = [];
|
|
104
|
+
vi.spyOn(console, "error").mockImplementation((...args) => {
|
|
105
|
+
writes.push(args.join(" "));
|
|
106
|
+
});
|
|
107
|
+
const client = { list: async () => boxes };
|
|
108
|
+
await resolveDraftsFolder(client, "m@x", "INBOX.Drafts");
|
|
109
|
+
expect(writes.join("")).toMatch(/drafts-resolve: resolved.*via=override folder=INBOX\.Drafts/);
|
|
110
|
+
});
|
|
111
|
+
it("appendDraft appends to the override folder when config.draftsFolder is set", async () => {
|
|
112
|
+
mockClient.list.mockResolvedValueOnce([
|
|
113
|
+
{ path: "Drafts", specialUse: "\\Drafts" },
|
|
114
|
+
{ path: "INBOX.Drafts" },
|
|
115
|
+
]);
|
|
116
|
+
const cfg = { ...config, draftsFolder: "INBOX.Drafts" };
|
|
117
|
+
const r = await appendDraft(cfg, "pw", Buffer.from("x"));
|
|
118
|
+
expect(mockClient.append).toHaveBeenCalledWith("INBOX.Drafts", expect.any(Buffer), ["\\Draft"]);
|
|
119
|
+
expect(r.folder).toBe("INBOX.Drafts");
|
|
51
120
|
});
|
|
52
121
|
});
|
|
53
122
|
describe("Task 815 — appendDraft", () => {
|