@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
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export declare const EMBED_INPUT_MAX_CHARS
|
|
1
|
+
export declare const EMBED_INPUT_MAX_CHARS: number;
|
|
2
2
|
export declare const EMBED_SUBBATCH_SIZE: number;
|
|
3
3
|
export declare const EMBED_SUBBATCH_COOLDOWN_MS: number;
|
|
4
|
+
export declare const EMBED_MS_PER_INPUT: number;
|
|
5
|
+
export declare const EMBED_REQUEST_TIMEOUT_MS: number;
|
|
6
|
+
export declare const EMBED_KEEP_ALIVE: number | string;
|
|
4
7
|
export declare function embed(text: string): Promise<number[]>;
|
|
8
|
+
export declare function effectiveSubBatchSize(): number;
|
|
5
9
|
export declare function embedBatch(texts: string[]): Promise<number[][]>;
|
|
6
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,qBAAqB,QASjC,CAAC;AAaF,eAAO,MAAM,mBAAmB,QAA6D,CAAC;AAC9F,eAAO,MAAM,0BAA0B,QAGtC,CAAC;AAWF,eAAO,MAAM,kBAAkB,QAA8D,CAAC;AAQ9F,eAAO,MAAM,wBAAwB,QAGpC,CAAC;AAcF,eAAO,MAAM,gBAAgB,EAAE,MAAM,GAAG,MAEhB,CAAC;AA8EzB,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG3D;AAKD,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAqBrE"}
|
|
@@ -6,16 +6,32 @@
|
|
|
6
6
|
// and one POST shape across the platform (Task 636). fetch-only — no node:fs —
|
|
7
7
|
// so it is safe to bundle into the ESM payload.
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.EMBED_SUBBATCH_COOLDOWN_MS = exports.EMBED_SUBBATCH_SIZE = exports.EMBED_INPUT_MAX_CHARS = void 0;
|
|
9
|
+
exports.EMBED_KEEP_ALIVE = exports.EMBED_REQUEST_TIMEOUT_MS = exports.EMBED_MS_PER_INPUT = exports.EMBED_SUBBATCH_COOLDOWN_MS = exports.EMBED_SUBBATCH_SIZE = exports.EMBED_INPUT_MAX_CHARS = void 0;
|
|
10
10
|
exports.embed = embed;
|
|
11
|
+
exports.effectiveSubBatchSize = effectiveSubBatchSize;
|
|
11
12
|
exports.embedBatch = embedBatch;
|
|
12
13
|
const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
13
14
|
const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
function envInt(name, def) {
|
|
16
|
+
const raw = process.env[name];
|
|
17
|
+
if (raw === undefined || raw === "")
|
|
18
|
+
return def;
|
|
19
|
+
const n = Number(raw);
|
|
20
|
+
return Number.isFinite(n) ? n : def;
|
|
21
|
+
}
|
|
22
|
+
// nomic-embed-text's usable embed context is 2048 tokens. The old 8000-char cap
|
|
23
|
+
// assumed ~4 chars/token, but dense emoji/markup content runs richer, so a
|
|
24
|
+
// section that passed the char cap still errored with "input length exceeds the
|
|
25
|
+
// context length". Size the default against a conservative 3 chars/token worst
|
|
26
|
+
// case, reserving a small token margin for the tokenizer's specials, so a capped
|
|
27
|
+
// input is provably under context: (2048 - 48) * 3 = 6000. Context is
|
|
28
|
+
// env-overridable so raising it raises the cap; EMBED_INPUT_MAX_CHARS is also
|
|
29
|
+
// directly overridable.
|
|
30
|
+
const EMBED_MODEL_CONTEXT_TOKENS = Math.max(1, Math.floor(envInt("EMBED_MODEL_CONTEXT_TOKENS", 2048)));
|
|
31
|
+
const EMBED_CONTEXT_MARGIN_TOKENS = 48;
|
|
32
|
+
const EMBED_WORST_CASE_CHARS_PER_TOKEN = 3;
|
|
33
|
+
exports.EMBED_INPUT_MAX_CHARS = Math.max(1, Math.floor(envInt("EMBED_INPUT_MAX_CHARS", (EMBED_MODEL_CONTEXT_TOKENS - EMBED_CONTEXT_MARGIN_TOKENS) *
|
|
34
|
+
EMBED_WORST_CASE_CHARS_PER_TOKEN)));
|
|
19
35
|
// Batch pacing (Task 960). On a passively-cooled Pi, embedding a whole
|
|
20
36
|
// document's sections in one `/api/embed` POST pins ollama's llama-server
|
|
21
37
|
// across every core for the entire batch with no idle gaps — the SoC climbs
|
|
@@ -27,15 +43,39 @@ exports.EMBED_INPUT_MAX_CHARS = 8000;
|
|
|
27
43
|
// this pacing is defence-in-depth and faster recovery. Both knobs are
|
|
28
44
|
// env-overridable so an operator can tune them from on-device thermal
|
|
29
45
|
// measurement without a code change.
|
|
30
|
-
function envInt(name, def) {
|
|
31
|
-
const raw = process.env[name];
|
|
32
|
-
if (raw === undefined || raw === "")
|
|
33
|
-
return def;
|
|
34
|
-
const n = Number(raw);
|
|
35
|
-
return Number.isFinite(n) ? n : def;
|
|
36
|
-
}
|
|
37
46
|
exports.EMBED_SUBBATCH_SIZE = Math.max(1, Math.floor(envInt("EMBED_SUBBATCH_SIZE", 32)));
|
|
38
47
|
exports.EMBED_SUBBATCH_COOLDOWN_MS = Math.max(0, envInt("EMBED_SUBBATCH_COOLDOWN_MS", 1000));
|
|
48
|
+
// Throughput-aware sub-batch sizing (Task 1533). On a CPU-only embedder each
|
|
49
|
+
// input takes ~EMBED_MS_PER_INPUT ms; a fixed 32-input POST at the char cap is
|
|
50
|
+
// ~128 s, past the 120 s request timeout, so a full-document batch aborts and
|
|
51
|
+
// the atomic ingest write lands nothing. Bound each POST to 80% of the timeout
|
|
52
|
+
// so it provably completes: derive a duration ceiling and take the smaller of
|
|
53
|
+
// it and the count ceiling (EMBED_SUBBATCH_SIZE, Task 960's thermal cap).
|
|
54
|
+
// EMBED_MS_PER_INPUT is the budgeted worst-case wall-time for one input at the
|
|
55
|
+
// EMBED_INPUT_MAX_CHARS cap on the measured device; env-tunable for other
|
|
56
|
+
// hardware.
|
|
57
|
+
exports.EMBED_MS_PER_INPUT = Math.max(1, Math.floor(envInt("EMBED_MS_PER_INPUT", 6000)));
|
|
58
|
+
const EMBED_TIMEOUT_UTILISATION = 0.8;
|
|
59
|
+
// Explicit per-request timeout, well under undici's 300000ms headers default.
|
|
60
|
+
// Without it a slow or wedged ollama returns no headers and the caller hangs a
|
|
61
|
+
// silent 5 minutes before a bare `fetch failed`. With it the request aborts in
|
|
62
|
+
// seconds and throws a cause-naming error instead. Env-overridable for
|
|
63
|
+
// on-device tuning.
|
|
64
|
+
exports.EMBED_REQUEST_TIMEOUT_MS = Math.max(1, Math.floor(envInt("EMBED_REQUEST_TIMEOUT_MS", 120000)));
|
|
65
|
+
// Model warmth (Task 1533). Keep the embed model resident so an interactive
|
|
66
|
+
// graph query-embed never cold-loads (~10 s) and blows its 5 s timeout, and so
|
|
67
|
+
// a query issued while an ingest runs embeds warm instead of degrading to BM25.
|
|
68
|
+
// Ollama's keep_alive accepts a number (seconds; -1 = keep forever) or a
|
|
69
|
+
// duration string ("30m"). Pass a numeric value through as a number so "-1" is
|
|
70
|
+
// the keep-forever sentinel rather than an unparseable duration string.
|
|
71
|
+
// Treat an empty-string env as unset (matches envInt above), so
|
|
72
|
+
// `EMBED_KEEP_ALIVE=` does not send an empty keep_alive that Ollama rejects.
|
|
73
|
+
const EMBED_KEEP_ALIVE_RAW = process.env.EMBED_KEEP_ALIVE === undefined || process.env.EMBED_KEEP_ALIVE === ""
|
|
74
|
+
? "-1"
|
|
75
|
+
: process.env.EMBED_KEEP_ALIVE;
|
|
76
|
+
exports.EMBED_KEEP_ALIVE = /^-?\d+$/.test(EMBED_KEEP_ALIVE_RAW)
|
|
77
|
+
? Number(EMBED_KEEP_ALIVE_RAW)
|
|
78
|
+
: EMBED_KEEP_ALIVE_RAW;
|
|
39
79
|
function capInput(text) {
|
|
40
80
|
if (text.length <= exports.EMBED_INPUT_MAX_CHARS)
|
|
41
81
|
return text;
|
|
@@ -43,49 +83,87 @@ function capInput(text) {
|
|
|
43
83
|
return text.slice(0, exports.EMBED_INPUT_MAX_CHARS);
|
|
44
84
|
}
|
|
45
85
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
-
// POST
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
86
|
+
// One POST to the embed endpoint for an already-capped input (a single string
|
|
87
|
+
// for embed(), an array for a sub-batch). Both paths route through here so the
|
|
88
|
+
// timeout and the cause-naming error apply on every embed call. The wire shape
|
|
89
|
+
// of `input` is preserved (string vs array) so the ollama request is unchanged.
|
|
90
|
+
// On timeout or failure the error's first line names the cause (timeout vs HTTP
|
|
91
|
+
// status), the input count, and the largest input size, so a slow or failing
|
|
92
|
+
// ollama is legible immediately rather than after a 5-minute silent hang.
|
|
93
|
+
async function embedRequest(input) {
|
|
94
|
+
const inputs = Array.isArray(input) ? input : [input];
|
|
95
|
+
const maxChars = inputs.reduce((m, s) => Math.max(m, s.length), 0);
|
|
96
|
+
const target = `${OLLAMA_URL}/api/embed`;
|
|
97
|
+
const startedAt = Date.now();
|
|
98
|
+
let res;
|
|
99
|
+
try {
|
|
100
|
+
res = await fetch(target, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: { "Content-Type": "application/json" },
|
|
103
|
+
body: JSON.stringify({ model: EMBED_MODEL, input, keep_alive: exports.EMBED_KEEP_ALIVE }),
|
|
104
|
+
signal: AbortSignal.timeout(exports.EMBED_REQUEST_TIMEOUT_MS),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const name = err instanceof Error ? err.name : "";
|
|
109
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
110
|
+
process.stderr.write(`[embed] op=timeout ms=${exports.EMBED_REQUEST_TIMEOUT_MS} inputs=${inputs.length} maxChars=${maxChars}\n`);
|
|
111
|
+
throw new Error(`Ollama embed request timed out after ${exports.EMBED_REQUEST_TIMEOUT_MS}ms (inputs=${inputs.length}, largestInputChars=${maxChars}) at ${target}; ollama did not respond in time (slow, cold-loading, or CPU-saturated). Raise EMBED_REQUEST_TIMEOUT_MS or check ollama.`);
|
|
112
|
+
}
|
|
113
|
+
// undici throws TypeError("fetch failed", { cause }) on a connection error
|
|
114
|
+
// (ollama down, refused, DNS). The legible cause is on err.cause, not the
|
|
115
|
+
// "fetch failed" message, so name it so "network is down" reads distinctly
|
|
116
|
+
// from "ollama is slow".
|
|
117
|
+
let detail = err instanceof Error ? err.message : String(err);
|
|
118
|
+
const cause = err instanceof Error ? err.cause : undefined;
|
|
119
|
+
if (cause !== undefined) {
|
|
120
|
+
detail += `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`Ollama embed request failed (inputs=${inputs.length}, largestInputChars=${maxChars}) at ${target}: ${detail}`);
|
|
123
|
+
}
|
|
53
124
|
if (!res.ok) {
|
|
54
125
|
const body = await res.text();
|
|
55
|
-
throw new Error(`Ollama
|
|
126
|
+
throw new Error(`Ollama embed request failed (HTTP ${res.status}, inputs=${inputs.length}, largestInputChars=${maxChars}): ${body}`);
|
|
56
127
|
}
|
|
57
128
|
const data = (await res.json());
|
|
129
|
+
// Per-POST breadcrumb (Task 1533). One line per successful POST so an
|
|
130
|
+
// oversized or slow sub-batch is visible before it times out and the
|
|
131
|
+
// throughput-aware sizing is verifiable from the log. inputs is the POST's
|
|
132
|
+
// sub-batch size; maxChars its largest input.
|
|
133
|
+
process.stderr.write(`[embed] op=post inputs=${inputs.length} maxChars=${maxChars} ms=${Date.now() - startedAt}\n`);
|
|
58
134
|
return data.embeddings;
|
|
59
135
|
}
|
|
136
|
+
// POST one already-capped sub-batch of inputs to the embed endpoint.
|
|
137
|
+
async function postBatch(inputs) {
|
|
138
|
+
return embedRequest(inputs);
|
|
139
|
+
}
|
|
60
140
|
async function embed(text) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
const data = (await res.json());
|
|
72
|
-
return data.embeddings[0];
|
|
141
|
+
const data = await embedRequest(capInput(text));
|
|
142
|
+
return data[0];
|
|
143
|
+
}
|
|
144
|
+
// The POST size actually used by embedBatch: the smaller of the count ceiling
|
|
145
|
+
// (thermal, Task 960) and the duration ceiling (throughput, Task 1533). At
|
|
146
|
+
// defaults: min(32, floor(120000*0.8/6000)) = min(32, 16) = 16.
|
|
147
|
+
function effectiveSubBatchSize() {
|
|
148
|
+
const durationCap = Math.max(1, Math.floor((exports.EMBED_REQUEST_TIMEOUT_MS * EMBED_TIMEOUT_UTILISATION) / exports.EMBED_MS_PER_INPUT));
|
|
149
|
+
return Math.min(exports.EMBED_SUBBATCH_SIZE, durationCap);
|
|
73
150
|
}
|
|
74
151
|
async function embedBatch(texts) {
|
|
75
152
|
const inputs = texts.map(capInput);
|
|
153
|
+
const subBatch = effectiveSubBatchSize();
|
|
76
154
|
// Common case — an array within the sub-batch size is one POST, unchanged.
|
|
77
|
-
if (inputs.length <=
|
|
155
|
+
if (inputs.length <= subBatch) {
|
|
78
156
|
return postBatch(inputs);
|
|
79
157
|
}
|
|
80
158
|
// Oversized: split into sub-batches, cooling between each (not after the
|
|
81
159
|
// last). Vectors are concatenated in input order so callers can keep their
|
|
82
160
|
// index-aligned `embeddings[i]` ↔ `texts[i]` contract.
|
|
83
161
|
const out = [];
|
|
84
|
-
for (let i = 0; i < inputs.length; i +=
|
|
162
|
+
for (let i = 0; i < inputs.length; i += subBatch) {
|
|
85
163
|
if (i > 0 && exports.EMBED_SUBBATCH_COOLDOWN_MS > 0) {
|
|
86
164
|
await sleep(exports.EMBED_SUBBATCH_COOLDOWN_MS);
|
|
87
165
|
}
|
|
88
|
-
const chunk = inputs.slice(i, i +
|
|
166
|
+
const chunk = inputs.slice(i, i + subBatch);
|
|
89
167
|
out.push(...(await postBatch(chunk)));
|
|
90
168
|
}
|
|
91
169
|
return out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,gDAAgD;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,11 +8,34 @@
|
|
|
8
8
|
const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
9
9
|
const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
function envInt(name: string, def: number): number {
|
|
12
|
+
const raw = process.env[name];
|
|
13
|
+
if (raw === undefined || raw === "") return def;
|
|
14
|
+
const n = Number(raw);
|
|
15
|
+
return Number.isFinite(n) ? n : def;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// nomic-embed-text's usable embed context is 2048 tokens. The old 8000-char cap
|
|
19
|
+
// assumed ~4 chars/token, but dense emoji/markup content runs richer, so a
|
|
20
|
+
// section that passed the char cap still errored with "input length exceeds the
|
|
21
|
+
// context length". Size the default against a conservative 3 chars/token worst
|
|
22
|
+
// case, reserving a small token margin for the tokenizer's specials, so a capped
|
|
23
|
+
// input is provably under context: (2048 - 48) * 3 = 6000. Context is
|
|
24
|
+
// env-overridable so raising it raises the cap; EMBED_INPUT_MAX_CHARS is also
|
|
25
|
+
// directly overridable.
|
|
26
|
+
const EMBED_MODEL_CONTEXT_TOKENS = Math.max(1, Math.floor(envInt("EMBED_MODEL_CONTEXT_TOKENS", 2048)));
|
|
27
|
+
const EMBED_CONTEXT_MARGIN_TOKENS = 48;
|
|
28
|
+
const EMBED_WORST_CASE_CHARS_PER_TOKEN = 3;
|
|
29
|
+
export const EMBED_INPUT_MAX_CHARS = Math.max(
|
|
30
|
+
1,
|
|
31
|
+
Math.floor(
|
|
32
|
+
envInt(
|
|
33
|
+
"EMBED_INPUT_MAX_CHARS",
|
|
34
|
+
(EMBED_MODEL_CONTEXT_TOKENS - EMBED_CONTEXT_MARGIN_TOKENS) *
|
|
35
|
+
EMBED_WORST_CASE_CHARS_PER_TOKEN,
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
);
|
|
16
39
|
|
|
17
40
|
// Batch pacing (Task 960). On a passively-cooled Pi, embedding a whole
|
|
18
41
|
// document's sections in one `/api/embed` POST pins ollama's llama-server
|
|
@@ -25,18 +48,50 @@ export const EMBED_INPUT_MAX_CHARS = 8000;
|
|
|
25
48
|
// this pacing is defence-in-depth and faster recovery. Both knobs are
|
|
26
49
|
// env-overridable so an operator can tune them from on-device thermal
|
|
27
50
|
// measurement without a code change.
|
|
28
|
-
function envInt(name: string, def: number): number {
|
|
29
|
-
const raw = process.env[name];
|
|
30
|
-
if (raw === undefined || raw === "") return def;
|
|
31
|
-
const n = Number(raw);
|
|
32
|
-
return Number.isFinite(n) ? n : def;
|
|
33
|
-
}
|
|
34
51
|
export const EMBED_SUBBATCH_SIZE = Math.max(1, Math.floor(envInt("EMBED_SUBBATCH_SIZE", 32)));
|
|
35
52
|
export const EMBED_SUBBATCH_COOLDOWN_MS = Math.max(
|
|
36
53
|
0,
|
|
37
54
|
envInt("EMBED_SUBBATCH_COOLDOWN_MS", 1000),
|
|
38
55
|
);
|
|
39
56
|
|
|
57
|
+
// Throughput-aware sub-batch sizing (Task 1533). On a CPU-only embedder each
|
|
58
|
+
// input takes ~EMBED_MS_PER_INPUT ms; a fixed 32-input POST at the char cap is
|
|
59
|
+
// ~128 s, past the 120 s request timeout, so a full-document batch aborts and
|
|
60
|
+
// the atomic ingest write lands nothing. Bound each POST to 80% of the timeout
|
|
61
|
+
// so it provably completes: derive a duration ceiling and take the smaller of
|
|
62
|
+
// it and the count ceiling (EMBED_SUBBATCH_SIZE, Task 960's thermal cap).
|
|
63
|
+
// EMBED_MS_PER_INPUT is the budgeted worst-case wall-time for one input at the
|
|
64
|
+
// EMBED_INPUT_MAX_CHARS cap on the measured device; env-tunable for other
|
|
65
|
+
// hardware.
|
|
66
|
+
export const EMBED_MS_PER_INPUT = Math.max(1, Math.floor(envInt("EMBED_MS_PER_INPUT", 6000)));
|
|
67
|
+
const EMBED_TIMEOUT_UTILISATION = 0.8;
|
|
68
|
+
|
|
69
|
+
// Explicit per-request timeout, well under undici's 300000ms headers default.
|
|
70
|
+
// Without it a slow or wedged ollama returns no headers and the caller hangs a
|
|
71
|
+
// silent 5 minutes before a bare `fetch failed`. With it the request aborts in
|
|
72
|
+
// seconds and throws a cause-naming error instead. Env-overridable for
|
|
73
|
+
// on-device tuning.
|
|
74
|
+
export const EMBED_REQUEST_TIMEOUT_MS = Math.max(
|
|
75
|
+
1,
|
|
76
|
+
Math.floor(envInt("EMBED_REQUEST_TIMEOUT_MS", 120000)),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Model warmth (Task 1533). Keep the embed model resident so an interactive
|
|
80
|
+
// graph query-embed never cold-loads (~10 s) and blows its 5 s timeout, and so
|
|
81
|
+
// a query issued while an ingest runs embeds warm instead of degrading to BM25.
|
|
82
|
+
// Ollama's keep_alive accepts a number (seconds; -1 = keep forever) or a
|
|
83
|
+
// duration string ("30m"). Pass a numeric value through as a number so "-1" is
|
|
84
|
+
// the keep-forever sentinel rather than an unparseable duration string.
|
|
85
|
+
// Treat an empty-string env as unset (matches envInt above), so
|
|
86
|
+
// `EMBED_KEEP_ALIVE=` does not send an empty keep_alive that Ollama rejects.
|
|
87
|
+
const EMBED_KEEP_ALIVE_RAW =
|
|
88
|
+
process.env.EMBED_KEEP_ALIVE === undefined || process.env.EMBED_KEEP_ALIVE === ""
|
|
89
|
+
? "-1"
|
|
90
|
+
: process.env.EMBED_KEEP_ALIVE;
|
|
91
|
+
export const EMBED_KEEP_ALIVE: number | string = /^-?\d+$/.test(EMBED_KEEP_ALIVE_RAW)
|
|
92
|
+
? Number(EMBED_KEEP_ALIVE_RAW)
|
|
93
|
+
: EMBED_KEEP_ALIVE_RAW;
|
|
94
|
+
|
|
40
95
|
function capInput(text: string): string {
|
|
41
96
|
if (text.length <= EMBED_INPUT_MAX_CHARS) return text;
|
|
42
97
|
process.stderr.write(
|
|
@@ -48,41 +103,93 @@ function capInput(text: string): string {
|
|
|
48
103
|
const sleep = (ms: number): Promise<void> =>
|
|
49
104
|
new Promise((resolve) => setTimeout(resolve, ms));
|
|
50
105
|
|
|
51
|
-
// POST
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
106
|
+
// One POST to the embed endpoint for an already-capped input (a single string
|
|
107
|
+
// for embed(), an array for a sub-batch). Both paths route through here so the
|
|
108
|
+
// timeout and the cause-naming error apply on every embed call. The wire shape
|
|
109
|
+
// of `input` is preserved (string vs array) so the ollama request is unchanged.
|
|
110
|
+
// On timeout or failure the error's first line names the cause (timeout vs HTTP
|
|
111
|
+
// status), the input count, and the largest input size, so a slow or failing
|
|
112
|
+
// ollama is legible immediately rather than after a 5-minute silent hang.
|
|
113
|
+
async function embedRequest(input: string | string[]): Promise<number[][]> {
|
|
114
|
+
const inputs = Array.isArray(input) ? input : [input];
|
|
115
|
+
const maxChars = inputs.reduce((m, s) => Math.max(m, s.length), 0);
|
|
116
|
+
const target = `${OLLAMA_URL}/api/embed`;
|
|
117
|
+
const startedAt = Date.now();
|
|
118
|
+
let res: Response;
|
|
119
|
+
try {
|
|
120
|
+
res = await fetch(target, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: { "Content-Type": "application/json" },
|
|
123
|
+
body: JSON.stringify({ model: EMBED_MODEL, input, keep_alive: EMBED_KEEP_ALIVE }),
|
|
124
|
+
signal: AbortSignal.timeout(EMBED_REQUEST_TIMEOUT_MS),
|
|
125
|
+
});
|
|
126
|
+
} catch (err) {
|
|
127
|
+
const name = err instanceof Error ? err.name : "";
|
|
128
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
129
|
+
process.stderr.write(
|
|
130
|
+
`[embed] op=timeout ms=${EMBED_REQUEST_TIMEOUT_MS} inputs=${inputs.length} maxChars=${maxChars}\n`,
|
|
131
|
+
);
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Ollama embed request timed out after ${EMBED_REQUEST_TIMEOUT_MS}ms (inputs=${inputs.length}, largestInputChars=${maxChars}) at ${target}; ollama did not respond in time (slow, cold-loading, or CPU-saturated). Raise EMBED_REQUEST_TIMEOUT_MS or check ollama.`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
// undici throws TypeError("fetch failed", { cause }) on a connection error
|
|
137
|
+
// (ollama down, refused, DNS). The legible cause is on err.cause, not the
|
|
138
|
+
// "fetch failed" message, so name it so "network is down" reads distinctly
|
|
139
|
+
// from "ollama is slow".
|
|
140
|
+
let detail = err instanceof Error ? err.message : String(err);
|
|
141
|
+
const cause = err instanceof Error ? (err as { cause?: unknown }).cause : undefined;
|
|
142
|
+
if (cause !== undefined) {
|
|
143
|
+
detail += `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
144
|
+
}
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Ollama embed request failed (inputs=${inputs.length}, largestInputChars=${maxChars}) at ${target}: ${detail}`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
58
149
|
if (!res.ok) {
|
|
59
150
|
const body = await res.text();
|
|
60
|
-
throw new Error(
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Ollama embed request failed (HTTP ${res.status}, inputs=${inputs.length}, largestInputChars=${maxChars}): ${body}`,
|
|
153
|
+
);
|
|
61
154
|
}
|
|
62
155
|
const data = (await res.json()) as { embeddings: number[][] };
|
|
156
|
+
// Per-POST breadcrumb (Task 1533). One line per successful POST so an
|
|
157
|
+
// oversized or slow sub-batch is visible before it times out and the
|
|
158
|
+
// throughput-aware sizing is verifiable from the log. inputs is the POST's
|
|
159
|
+
// sub-batch size; maxChars its largest input.
|
|
160
|
+
process.stderr.write(
|
|
161
|
+
`[embed] op=post inputs=${inputs.length} maxChars=${maxChars} ms=${Date.now() - startedAt}\n`,
|
|
162
|
+
);
|
|
63
163
|
return data.embeddings;
|
|
64
164
|
}
|
|
65
165
|
|
|
166
|
+
// POST one already-capped sub-batch of inputs to the embed endpoint.
|
|
167
|
+
async function postBatch(inputs: string[]): Promise<number[][]> {
|
|
168
|
+
return embedRequest(inputs);
|
|
169
|
+
}
|
|
170
|
+
|
|
66
171
|
export async function embed(text: string): Promise<number[]> {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
172
|
+
const data = await embedRequest(capInput(text));
|
|
173
|
+
return data[0];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// The POST size actually used by embedBatch: the smaller of the count ceiling
|
|
177
|
+
// (thermal, Task 960) and the duration ceiling (throughput, Task 1533). At
|
|
178
|
+
// defaults: min(32, floor(120000*0.8/6000)) = min(32, 16) = 16.
|
|
179
|
+
export function effectiveSubBatchSize(): number {
|
|
180
|
+
const durationCap = Math.max(
|
|
181
|
+
1,
|
|
182
|
+
Math.floor((EMBED_REQUEST_TIMEOUT_MS * EMBED_TIMEOUT_UTILISATION) / EMBED_MS_PER_INPUT),
|
|
183
|
+
);
|
|
184
|
+
return Math.min(EMBED_SUBBATCH_SIZE, durationCap);
|
|
79
185
|
}
|
|
80
186
|
|
|
81
187
|
export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
82
188
|
const inputs = texts.map(capInput);
|
|
189
|
+
const subBatch = effectiveSubBatchSize();
|
|
83
190
|
|
|
84
191
|
// Common case — an array within the sub-batch size is one POST, unchanged.
|
|
85
|
-
if (inputs.length <=
|
|
192
|
+
if (inputs.length <= subBatch) {
|
|
86
193
|
return postBatch(inputs);
|
|
87
194
|
}
|
|
88
195
|
|
|
@@ -90,11 +197,11 @@ export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
|
90
197
|
// last). Vectors are concatenated in input order so callers can keep their
|
|
91
198
|
// index-aligned `embeddings[i]` ↔ `texts[i]` contract.
|
|
92
199
|
const out: number[][] = [];
|
|
93
|
-
for (let i = 0; i < inputs.length; i +=
|
|
200
|
+
for (let i = 0; i < inputs.length; i += subBatch) {
|
|
94
201
|
if (i > 0 && EMBED_SUBBATCH_COOLDOWN_MS > 0) {
|
|
95
202
|
await sleep(EMBED_SUBBATCH_COOLDOWN_MS);
|
|
96
203
|
}
|
|
97
|
-
const chunk = inputs.slice(i, i +
|
|
204
|
+
const chunk = inputs.slice(i, i + subBatch);
|
|
98
205
|
out.push(...(await postBatch(chunk)));
|
|
99
206
|
}
|
|
100
207
|
return out;
|
|
@@ -190,6 +190,19 @@ OPTIONS {
|
|
|
190
190
|
}
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
+
// ----------------------------------------------------------
|
|
194
|
+
// TimeEntry node — per-task billable time recording (Task 1507)
|
|
195
|
+
// A running timer is the single :TimeEntry for an account with endedAt=null.
|
|
196
|
+
// On stop, endedAt + seconds are set and the task's denormalized
|
|
197
|
+
// secondsLogged sum is bumped. (:TimeEntry)-[:LOGGED_AGAINST]->(:Task).
|
|
198
|
+
// ----------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
CREATE CONSTRAINT time_entry_id_unique IF NOT EXISTS
|
|
201
|
+
FOR (te:TimeEntry) REQUIRE te.entryId IS UNIQUE;
|
|
202
|
+
|
|
203
|
+
CREATE INDEX time_entry_task IF NOT EXISTS
|
|
204
|
+
FOR (te:TimeEntry) ON (te.taskId);
|
|
205
|
+
|
|
193
206
|
// ----------------------------------------------------------
|
|
194
207
|
// CreativeWork node — agent-authored documents (session summaries etc.)
|
|
195
208
|
// Schema.org: schema.org/CreativeWork
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "admin",
|
|
3
|
-
"description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
|
|
3
|
+
"description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_create requires a managing house admin (its userId) and refuses without one, recording it as the sub-account's managingAdminUserId; account_reassign_admin repoints a client sub-account's managing admin to another live house admin (the repair path for a dangling managingAdminUserId); account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove (refuses to remove a house admin who still manages one or more client sub-accounts, naming them so the operator reassigns each with account_reassign_admin first — never silently orphans them), admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: admin
|
|
3
3
|
surface: platform
|
|
4
|
-
description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
4
|
+
description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), admin-hostname (the admin-host twin of public-hostname — resolves the host that serves /calendar and the rest of the admin shell behind sign-in from the same ingress + alias + operator files, so agents point the operator at the admin /calendar URL instead of the public host that 404s it), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge, account_channel_routing, account_channel_routing_set — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation; account_create requires a managing house admin (its userId) and refuses without one, recording it as the sub-account's managingAdminUserId; account_reassign_admin repoints a client sub-account's managing admin to another live house admin (the repair path for a dangling managingAdminUserId); account_channel_routing_set repoints the install's single WhatsApp/Telegram inbound stream to a different sub-account), admin-add, admin-remove (refuses to remove a house admin who still manages one or more client sub-accounts, naming them so the operator reassigns each with account_reassign_admin first — never silently orphans them), admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
5
5
|
tools:
|
|
6
6
|
- name: system-status
|
|
7
7
|
publicAllowlist: false
|
|
@@ -33,6 +33,9 @@ tools:
|
|
|
33
33
|
- name: account_create
|
|
34
34
|
publicAllowlist: false
|
|
35
35
|
adminAllowlist: true
|
|
36
|
+
- name: account_reassign_admin
|
|
37
|
+
publicAllowlist: false
|
|
38
|
+
adminAllowlist: true
|
|
36
39
|
- name: account_delete
|
|
37
40
|
publicAllowlist: false
|
|
38
41
|
adminAllowlist: true
|
|
@@ -175,6 +178,7 @@ Tools are available via the `admin` MCP server.
|
|
|
175
178
|
- `hooks/post-tool-use-agent.sh` — **PostToolUse hook on `Agent`.** Drains any subagent hook-decision buffers under `~/.maxy-code/logs/hook-decisions/` modified since this parent's previous PostToolUse-Agent fire (cursor file keyed by parent session id), prints one `[hook-propagate]` line per record to stdout — Claude Code attaches the stdout as a `hook_success` attachment on the parent JSONL, making the records grep-queryable from the parent session alone. Rotates consumed buffers to `consumed/`. Emits one `[hook-propagate-census] parentSession=<…> subagentHooksObserved=<N> attachmentsEmitted=<M>` line per fire to stdout and server.log; `N != M` is the propagation regression signal. The companion emitter library `hooks/lib/hook-emit.sh` is sourced by `post-tool-use-agent.sh` and any other hook that records a block decision (4 KB stderr truncation, `truncated=true` set on the record).
|
|
176
179
|
- `hooks/admin-authoring-observer.sh` — **PostToolUse hook on Write and Edit.** Observation only — never blocks; exits 0 on every path. Fires when the admin agent (not a specialist subagent — gated by `MAXY_SPECIALIST` env) writes or edits a file under `<accountDir>/output/`. Walks the session transcript from the latest real-user turn forward to detect any prior `Task` `tool_use` whose `subagent_type` starts with `specialists:`. Emits one stderr line `[admin-authoring] inline-write path=<rel> priorSpecialistSpawnInTurn=<true|false|unknown>`. A `false` value on a long-form prose file is the regression signal this hook was designed to make visible — the BioSymm proposal session (admin authored a customer-facing proposal inline despite content-producer being installed) is the failure mode this surfaces mechanically. Mechanical enforcement (refuse the write, force a re-spawn) is deferred per the task spec.
|
|
177
180
|
- `hooks/prompt-optimiser-directive.sh` — **UserPromptSubmit hook.** Injects the standing prompt-optimiser restatement directive plus the per-turn three-tier routing ladder as `additionalContext`: **(1)** delegate to the specialist that owns the deliverable via the Agent tool — the brief states the outcome plus binding constraints, never lines/anchors/literal text; **(2)** only if none fits, load an admin-usable skill with `skill-load`; **(3)** only if neither fits, author inline — freestyle is the named last resort. Re-emits the full agent roster (`agents/admin/AGENTS.md`) and the full admin-usable skills list (`agents/admin/ADMIN-SKILLS.md`) every turn by reading the two generated files from the account dir (the hook fires with the account dir as cwd); it never walks the plugins tree per turn. Fail-open is **visible**: a missing list logs `[prompt-optimiser] missing=<AGENTS.md\|ADMIN-SKILLS.md> emitting-partial` to stderr and the ladder still injects. The trivial-turn skip (one-word confirmation, slash-command, direct continuation) is unchanged. **Staleness:** `ADMIN-SKILLS.md` is regenerated only by `setup-account.sh`; a plugin add/remove since the last setup leaves the list stale — compare `ADMIN-SKILLS.md` mtime against the newest `SKILL.md` mtime and re-run setup to refresh. The list generator is `platform/scripts/lib/admin-skills-bootstrap.sh`; it logs `[admin-skills] scanned=<N> admin-usable=<M> no-declaration=<K>` (failure signature: `admin-usable=0` while `scanned>0`, or any `missing-declaration` line). The directive also carries a standing CAPABILITY-QUESTIONS-ARE-OWNED-WORK clause (how-to / "do you have instructions for X" / config questions about platform features are answered from the owning specialist or plugin tool/reference, never from training memory), and the hook appends a durable `<ts> [prompt-optimiser-directive] injected len=<n> session=<id>` breadcrumb to `$LOG_DIR/prompt-optimiser-directive.log` so per-turn injection is greppable, not stderr-only. The directive is also **suppressed on native channel turns** — when the parsed `.prompt` starts with the `<channel source=` event marker, the hook logs `[prompt-optimiser-directive] skipped reason=channel-turn session=<id>` to stderr and exits without injecting, because the channel service already reframes the inbound into a select-and-dispatch turn (`composeAdminContent`, see `.docs/whatsapp-inbound-lifeline.md`). Marker-matched at start-of-prompt only, so an admin/Terminal prompt that merely mentions "channel" still gets the directive; fail-open injects if the prompt cannot be parsed.
|
|
181
|
+
- `hooks/datetime-inject.sh` — **UserPromptSubmit hook.** Injects a fresh `<datetime>` block as `additionalContext` on every turn (`refresh=per-turn`), so the agent always has the current instant — date, time, timezone, weekday, and the raw UTC ISO — without hand-computing it and without depending on Claude Code's spawn-time "Today's date" line going stale. The instant is computed fresh each turn; the timezone is **not** computed here — it is the operator's own zone, resolved from their `UserProfile.timezone` on the graph once per admin spawn and stamped into `MAXY_ACCOUNT_TZ` (see `platform/services/claude-session-manager/src/account-timezone.ts`). When `MAXY_ACCOUNT_TZ` is empty (a non-admin turn, an unset profile timezone, or a neo4j hiccup at spawn) the block falls back to the server's own zone and labels the source, so a reader can always tell which clock the agent saw. This is the current-time surface the `datetime` skill points at. **Fail-open** on every path (missing `python3` or `node`, any formatting error → exit 0, empty stdout — the turn is never blocked). The hook appends `<ts> [datetime-inject] injected tz=<zone> source=<graph|server-fallback> refresh=per-turn session=<id>` to `$LOG_DIR/datetime-inject.log` so "did the agent have the right time this turn, and from which clock?" is answerable from logs without reproduction.
|
|
178
182
|
- `hooks/prompt-optimiser-compliance.sh` — **Stop hook.** After each admin turn, reads the just-finished turn from `transcript_path` and appends `<ts> [prompt-optimiser-compliance] directive-fired no-route-taken session=<id8> prompt="<clip>"` to `$LOG_DIR/prompt-optimiser-directive.log` (and stderr) when the routing directive fired, the prompt was non-trivial (not a slash-command, not a one-word confirmation), and the turn took **no route** — no `Agent` dispatch, no `Skill` load, no `ToolSearch`, no `mcp__*` tool call. This is the standing compliance signal that surfaces the session-`da0b12d4` failure class (agent answers a capability question from memory) as a visible event instead of a silent stale answer. Directive-fired is detected by the marker `PROMPT-OPTIMISER DIRECTIVE` in the turn slice, so it is robust to the CC-version difference in how `UserPromptSubmit` `additionalContext` is recorded (`attachment`/`hook_success` vs `hook_additional_context`). **Known limitation:** "direct continuation of the prior turn" is not detectable from the transcript, so a continuation turn that legitimately needs no route can be flagged; treat the log as a review signal, not a gate. **Fail-open:** no python3, no `transcript_path`, or an unreadable transcript → exit 0, no output. Lives in the same log as the directive breadcrumb, so a single `grep` interleaves "fired" and "no-route" into one per-session timeline; cross-check via `platform/scripts/logs-read.sh <sessionKey>`. This is a lightweight transcript read, not a per-turn spawn (contrast the turn recorder below).
|
|
179
183
|
- **Turn recorder — removed entirely.** The `turn-completed-graph-write.sh` Stop hook, the `/api/admin/claude-sessions` loopback bypass it relied on, the `[turn-recorder]` emitters, the envelope walker, and the recorder-auto-archive subscriber are deleted. It had been dormant for a long time (never re-registered in settings.json); the admin now writes to the graph by delegating to `database-operator` via the Task tool inside the live session, and the on-demand `/insight` pass (`skills/insight/SKILL.md`, a registered admin skill) is the per-session review. There is no per-turn spawn.
|
|
180
184
|
|