@rubytech/create-maxy-code 0.1.580 → 0.1.582
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/plugins/admin/skills/platform-architecture/SKILL.md +52 -2
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +16 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +3 -1
- package/payload/platform/plugins/docs/references/calendar-booking.md +2 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +46 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js +64 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js +28 -6
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-knowledge-work.md +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +11 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.js +102 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +43 -9
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +50 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +47 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +10 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +20 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +61 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-trash-filter.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-trash-filter.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-trash-filter.test.js +67 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-trash-filter.test.js.map +1 -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 +8 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +6 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +7 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +17 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/telegram/PLUGIN.md +11 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-business-connection.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-business-connection.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-business-connection.test.js +140 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-business-connection.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-tool.test.js +6 -2
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/ask-tool.test.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-ratio.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-ratio.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-ratio.test.js +75 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-ratio.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-store.test.js +13 -4
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-store.test.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-tool.test.js +34 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-tool.test.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-store.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-store.test.js +93 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-store.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-tool.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-tool.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-tool.test.js +76 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/live-location-tool.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js +69 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js +10 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/index.js +19 -1
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.d.ts +4 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.js +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/business-connection-store.d.ts +38 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/business-connection-store.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/business-connection-store.js +56 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/business-connection-store.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-ratio.d.ts +15 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-ratio.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-ratio.js +78 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-ratio.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-store.d.ts +21 -2
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-store.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-store.js +24 -3
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-store.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/live-location-store.d.ts +44 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/live-location-store.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/live-location-store.js +88 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/live-location-store.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +25 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +42 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/ask.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/ask.js +48 -9
- package/payload/platform/plugins/telegram/mcp/dist/tools/ask.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.js +7 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/live-location.d.ts +23 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/live-location.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/live-location.js +43 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/live-location.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/schemas.d.ts +2 -2
- package/payload/platform/plugins/telegram/mcp/dist/tools/schemas.js +2 -2
- package/payload/platform/plugins/telegram/mcp/dist/tools/schemas.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js +14 -2
- package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js.map +1 -1
- package/payload/platform/plugins/telegram/skills/build/SKILL.md +85 -7
- 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/telegram-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.js +15 -1
- package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +27 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +28 -2
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{adminuser-self-heal-Y4U3UNKF.js → adminuser-self-heal-ZHFNDKJK.js} +2 -10
- package/payload/server/{chunk-637C2FGE.js → chunk-BFRB2U75.js} +3278 -3381
- package/payload/server/{chunk-6DQCFP2E.js → chunk-BTXRBXMU.js} +3785 -4090
- package/payload/server/{chunk-KVR2FT6O.js → chunk-HYQNUVGO.js} +1 -12
- package/payload/server/{chunk-PGGGWUBM.js → chunk-SKYZG6OT.js} +224 -372
- package/payload/server/{manager-R3XH3BNM.js → manager-CTVTF4YB.js} +3 -3
- package/payload/server/maxy-edge.js +4 -27
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/agents.html +5 -5
- package/payload/server/public/assets/{AdminLoginScreens-Dxv5t1YY.js → AdminLoginScreens-AnJZfgFz.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-BMslfZxN.js → AdminShell-CEVqyQqg.js} +1 -1
- package/payload/server/public/assets/AdminShell-CEVqyQqg.js.br +0 -0
- package/payload/server/public/assets/AdminShell-CEVqyQqg.js.gz +0 -0
- package/payload/server/public/assets/{activity-kQ3w70LJ.js → activity-czUaUl8I.js} +1 -1
- package/payload/server/public/assets/activity-czUaUl8I.js.br +0 -0
- package/payload/server/public/assets/activity-czUaUl8I.js.gz +0 -0
- package/payload/server/public/assets/{admin-DapcMKCG.js → admin-B0iRcfuk.js} +1 -1
- package/payload/server/public/assets/admin-B0iRcfuk.js.br +0 -0
- package/payload/server/public/assets/admin-B0iRcfuk.js.gz +0 -0
- package/payload/server/public/assets/{agents-Cn_bZgnD.js → agents-BocONCeQ.js} +1 -1
- package/payload/server/public/assets/agents-BocONCeQ.js.br +0 -0
- package/payload/server/public/assets/agents-BocONCeQ.js.gz +0 -0
- package/payload/server/public/assets/{browser-BE-GDk5-.js → browser-Cy-rJm3t.js} +1 -1
- package/payload/server/public/assets/browser-Cy-rJm3t.js.br +0 -0
- package/payload/server/public/assets/browser-Cy-rJm3t.js.gz +0 -0
- package/payload/server/public/assets/{calendar-Di7BEHHN.js → calendar-DUTKxWtd.js} +1 -1
- package/payload/server/public/assets/calendar-DUTKxWtd.js.br +0 -0
- package/payload/server/public/assets/calendar-DUTKxWtd.js.gz +0 -0
- package/payload/server/public/assets/chat-Cis7sHsZ.js +1 -0
- package/payload/server/public/assets/chat-Cis7sHsZ.js.br +0 -0
- package/payload/server/public/assets/chat-Cis7sHsZ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-Bhhx8j2r.js +1 -0
- package/payload/server/public/assets/chevron-left-Bhhx8j2r.js.br +0 -0
- package/payload/server/public/assets/clock-Ca3nx7Cc.js +1 -0
- package/payload/server/public/assets/clock-Ca3nx7Cc.js.br +0 -0
- package/payload/server/public/assets/clock-Ca3nx7Cc.js.gz +0 -0
- package/payload/server/public/assets/{copy-nkxspu2J.js → copy-DE9qmAs5.js} +1 -1
- package/payload/server/public/assets/copy-DE9qmAs5.js.br +0 -0
- package/payload/server/public/assets/copy-DE9qmAs5.js.gz +0 -0
- package/payload/server/public/assets/data-CkaAEeP1.js +1 -0
- package/payload/server/public/assets/data-CkaAEeP1.js.br +0 -0
- package/payload/server/public/assets/data-CkaAEeP1.js.gz +0 -0
- package/payload/server/public/assets/{field-2rIXxGMt.js → field-DE2tEfTo.js} +1 -1
- package/payload/server/public/assets/field-DE2tEfTo.js.br +0 -0
- package/payload/server/public/assets/field-DE2tEfTo.js.gz +0 -0
- package/payload/server/public/assets/{file-text-CdSqrscg.js → file-text-BHfL2gqo.js} +1 -1
- package/payload/server/public/assets/file-text-BHfL2gqo.js.br +0 -0
- package/payload/server/public/assets/file-text-BHfL2gqo.js.gz +0 -0
- package/payload/server/public/assets/{graph-BfBHHXaC.js → graph-08S_BofN.js} +1 -1
- package/payload/server/public/assets/graph-08S_BofN.js.br +0 -0
- package/payload/server/public/assets/graph-08S_BofN.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-CHW2aBke.js → graph-labels-C640Er5R.js} +1 -1
- package/payload/server/public/assets/graph-labels-C640Er5R.js.br +0 -0
- package/payload/server/public/assets/graph-labels-C640Er5R.js.gz +0 -0
- package/payload/server/public/assets/{jsx-runtime-D43jgaWP.css → jsx-runtime-FxjwOUbA.css} +1 -1
- package/payload/server/public/assets/jsx-runtime-FxjwOUbA.css.br +0 -0
- package/payload/server/public/assets/{jsx-runtime-D43jgaWP.css.gz → jsx-runtime-FxjwOUbA.css.gz} +0 -0
- package/payload/server/public/assets/{operator-BOiL8sxx.js → operator-DH5Kw3fL.js} +1 -1
- package/payload/server/public/assets/operator-DH5Kw3fL.js.br +0 -0
- package/payload/server/public/assets/operator-DH5Kw3fL.js.gz +0 -0
- package/payload/server/public/assets/{page-Cd36Y0h6.js → page-BDpa4HWT.js} +1 -1
- package/payload/server/public/assets/page-BDpa4HWT.js.br +0 -0
- package/payload/server/public/assets/page-BDpa4HWT.js.gz +0 -0
- package/payload/server/public/assets/{page-CZ6tyH8t.js → page-yn7ivq8O.js} +1 -1
- package/payload/server/public/assets/page-yn7ivq8O.js.br +0 -0
- package/payload/server/public/assets/page-yn7ivq8O.js.gz +0 -0
- package/payload/server/public/assets/play-CMN7ht-S.js +1 -0
- package/payload/server/public/assets/play-CMN7ht-S.js.br +0 -0
- package/payload/server/public/assets/play-CMN7ht-S.js.gz +0 -0
- package/payload/server/public/assets/{public-CVBav4FQ.js → public-D_qjqet7.js} +1 -1
- package/payload/server/public/assets/public-D_qjqet7.js.br +0 -0
- package/payload/server/public/assets/public-D_qjqet7.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-J9f-GWjm.js → rotate-ccw-Ixkq1iUA.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.gz +0 -0
- package/payload/server/public/assets/{routines-C1AB-3FR.js → routines-Cxjlmcp7.js} +1 -1
- package/payload/server/public/assets/routines-Cxjlmcp7.js.br +0 -0
- package/payload/server/public/assets/routines-Cxjlmcp7.js.gz +0 -0
- package/payload/server/public/assets/{skills-C4EfNKEx.js → skills-DEo9hSVX.js} +1 -1
- package/payload/server/public/assets/skills-DEo9hSVX.js.br +0 -0
- package/payload/server/public/assets/skills-DEo9hSVX.js.gz +0 -0
- package/payload/server/public/assets/{tasks-DszD3aZl.js → tasks-BxApOIXI.js} +1 -1
- package/payload/server/public/assets/tasks-BxApOIXI.js.br +0 -0
- package/payload/server/public/assets/tasks-BxApOIXI.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-B5WAyAkC.js → triangle-alert-BYzMcXv8.js} +1 -1
- package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-B2nuNNJw.js → useCopyFeedback-Bk2HVcjy.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.gz +0 -0
- package/payload/server/public/assets/{useMediaQuery-DUPJPHQt.js → useMediaQuery-DEti-vAx.js} +1 -1
- package/payload/server/public/assets/useMediaQuery-DEti-vAx.js.br +0 -0
- package/payload/server/public/assets/useMediaQuery-DEti-vAx.js.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js +2 -0
- package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.gz +0 -0
- package/payload/server/public/assets/{wrench-DZJZm17j.js → wrench-BhLkHffv.js} +1 -1
- package/payload/server/public/assets/wrench-BhLkHffv.js.br +0 -0
- package/payload/server/public/assets/wrench-BhLkHffv.js.gz +0 -0
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +8 -8
- package/payload/server/public/chat.html +14 -14
- package/payload/server/public/data.html +12 -12
- package/payload/server/public/field.html +3 -3
- package/payload/server/public/graph.html +10 -10
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +16 -16
- package/payload/server/public/public.html +14 -14
- package/payload/server/public/routines.html +8 -8
- package/payload/server/public/skills.html +6 -6
- package/payload/server/public/tasks.html +7 -7
- package/payload/server/server.js +15311 -14753
- package/payload/server/{src-IJCLNFYU.js → src-MM64CYYZ.js} +1 -1
- package/payload/server/{userprofile-reconcile-PUA657YX.js → userprofile-reconcile-TL4JKBPG.js} +1 -8
- package/payload/server/public/assets/AdminLoginScreens-Dxv5t1YY.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-Dxv5t1YY.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-BMslfZxN.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BMslfZxN.js.gz +0 -0
- package/payload/server/public/assets/activity-kQ3w70LJ.js.br +0 -0
- package/payload/server/public/assets/activity-kQ3w70LJ.js.gz +0 -0
- package/payload/server/public/assets/admin-DapcMKCG.js.br +0 -0
- package/payload/server/public/assets/admin-DapcMKCG.js.gz +0 -0
- package/payload/server/public/assets/agents-Cn_bZgnD.js.br +0 -0
- package/payload/server/public/assets/agents-Cn_bZgnD.js.gz +0 -0
- package/payload/server/public/assets/browser-BE-GDk5-.js.br +0 -0
- package/payload/server/public/assets/browser-BE-GDk5-.js.gz +0 -0
- package/payload/server/public/assets/calendar-Di7BEHHN.js.br +0 -0
- package/payload/server/public/assets/calendar-Di7BEHHN.js.gz +0 -0
- package/payload/server/public/assets/chat-DLmikhie.js +0 -1
- package/payload/server/public/assets/chat-DLmikhie.js.br +0 -1
- package/payload/server/public/assets/chat-DLmikhie.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-Id9VAZAz.js +0 -1
- package/payload/server/public/assets/chevron-left-Id9VAZAz.js.br +0 -0
- package/payload/server/public/assets/clock-DKpWWJbJ.js +0 -1
- package/payload/server/public/assets/clock-DKpWWJbJ.js.br +0 -0
- package/payload/server/public/assets/copy-nkxspu2J.js.br +0 -0
- package/payload/server/public/assets/copy-nkxspu2J.js.gz +0 -0
- package/payload/server/public/assets/data-C1_m9Xyf.js +0 -1
- package/payload/server/public/assets/data-C1_m9Xyf.js.br +0 -0
- package/payload/server/public/assets/data-C1_m9Xyf.js.gz +0 -0
- package/payload/server/public/assets/field-2rIXxGMt.js.br +0 -0
- package/payload/server/public/assets/field-2rIXxGMt.js.gz +0 -0
- package/payload/server/public/assets/file-text-CdSqrscg.js.br +0 -0
- package/payload/server/public/assets/file-text-CdSqrscg.js.gz +0 -0
- package/payload/server/public/assets/graph-BfBHHXaC.js.br +0 -0
- package/payload/server/public/assets/graph-BfBHHXaC.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-CHW2aBke.js.br +0 -0
- package/payload/server/public/assets/graph-labels-CHW2aBke.js.gz +0 -0
- package/payload/server/public/assets/jsx-runtime-D43jgaWP.css.br +0 -0
- package/payload/server/public/assets/operator-BOiL8sxx.js.br +0 -0
- package/payload/server/public/assets/operator-BOiL8sxx.js.gz +0 -0
- package/payload/server/public/assets/page-CZ6tyH8t.js.br +0 -0
- package/payload/server/public/assets/page-CZ6tyH8t.js.gz +0 -0
- package/payload/server/public/assets/page-Cd36Y0h6.js.br +0 -0
- package/payload/server/public/assets/page-Cd36Y0h6.js.gz +0 -0
- package/payload/server/public/assets/play-7LTxCuLX.js +0 -1
- package/payload/server/public/assets/play-7LTxCuLX.js.br +0 -0
- package/payload/server/public/assets/play-7LTxCuLX.js.gz +0 -0
- package/payload/server/public/assets/public-CVBav4FQ.js.br +0 -0
- package/payload/server/public/assets/public-CVBav4FQ.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-J9f-GWjm.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-J9f-GWjm.js.gz +0 -0
- package/payload/server/public/assets/routines-C1AB-3FR.js.br +0 -0
- package/payload/server/public/assets/routines-C1AB-3FR.js.gz +0 -0
- package/payload/server/public/assets/skills-C4EfNKEx.js.br +0 -0
- package/payload/server/public/assets/skills-C4EfNKEx.js.gz +0 -0
- package/payload/server/public/assets/tasks-DszD3aZl.js.br +0 -0
- package/payload/server/public/assets/tasks-DszD3aZl.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-B5WAyAkC.js.br +0 -4
- package/payload/server/public/assets/triangle-alert-B5WAyAkC.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-B2nuNNJw.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-B2nuNNJw.js.gz +0 -0
- package/payload/server/public/assets/useMediaQuery-DUPJPHQt.js.br +0 -0
- package/payload/server/public/assets/useMediaQuery-DUPJPHQt.js.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BEgr0nHN.js +0 -2
- package/payload/server/public/assets/useVoiceRecorder-BEgr0nHN.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BEgr0nHN.js.gz +0 -0
- package/payload/server/public/assets/wrench-DZJZm17j.js.br +0 -0
- package/payload/server/public/assets/wrench-DZJZm17j.js.gz +0 -0
- /package/payload/server/public/assets/{jsx-runtime-Cjug_Cyb.js → jsx-runtime-BL3pljPe.js} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-Cjug_Cyb.js.br → jsx-runtime-BL3pljPe.js.br} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-Cjug_Cyb.js.gz → jsx-runtime-BL3pljPe.js.gz} +0 -0
package/package.json
CHANGED
|
@@ -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:f3b452d33afd6a7c74195c06354f261ebac82ab557a00b9f80b5daa36b0a30a7
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -2896,6 +2896,52 @@ Just ask for what you want. "Send the team a message with Yes and No buttons",
|
|
|
2896
2896
|
Maxy loads its Telegram build skill, which lists every available
|
|
2897
2897
|
method and how to call it.
|
|
2898
2898
|
|
|
2899
|
+
When somebody presses one of those buttons or votes in one of those polls,
|
|
2900
|
+
Maxy hears it and can answer. Before, a press was recorded but reached
|
|
2901
|
+
nobody, and a vote was dropped without a trace. A press on a job-sheet card is the
|
|
2902
|
+
one exception: the dispatch plugin applies the change and tells the presser
|
|
2903
|
+
itself, so Maxy does not answer a second time.
|
|
2904
|
+
|
|
2905
|
+
The same is now true of most of what Telegram sends. Reply to one of
|
|
2906
|
+
Maxy's messages and it sees the message you replied to, so "yes, do
|
|
2907
|
+
that one" means something. React to a message with an emoji and it hears the
|
|
2908
|
+
reaction and what it was on. When a poll closes, it hears the final counts. When
|
|
2909
|
+
somebody blocks the bot, that appears in the logs instead of sends quietly
|
|
2910
|
+
failing.
|
|
2911
|
+
|
|
2912
|
+
Two things worth knowing about reactions. Telegram sends them only to a bot that
|
|
2913
|
+
is an administrator of the chat, and a one-to-one chat has no administrator, so a
|
|
2914
|
+
reaction in a private chat may never arrive at all. And a reaction on a message
|
|
2915
|
+
Maxy itself sent will say the original is not held: it knows the
|
|
2916
|
+
reaction happened, not what it was on.
|
|
2917
|
+
|
|
2918
|
+
Payments now complete. Telegram gives a bot ten seconds to approve a checkout
|
|
2919
|
+
before cancelling it, which is faster than anything that has to think, so
|
|
2920
|
+
Maxy approves it automatically and then tells you the sale happened,
|
|
2921
|
+
with your own reference on it. If you sell a subscription, a customer cancelling,
|
|
2922
|
+
re-subscribing or failing to pay all reach Maxy as well.
|
|
2923
|
+
|
|
2924
|
+
One limit worth knowing, and it is Telegram's rather than ours. Telegram only
|
|
2925
|
+
reports who voted in a poll that is **not** anonymous, and polls are anonymous
|
|
2926
|
+
unless you say otherwise. Ask for a poll where you can see who chose what, and
|
|
2927
|
+
Maxy will send it that way; ask for a plain poll and Telegram will tell
|
|
2928
|
+
nobody how anyone voted, so no reply is possible.
|
|
2929
|
+
|
|
2930
|
+
## Checklists and Telegram Business
|
|
2931
|
+
|
|
2932
|
+
Maxy can send a Telegram checklist, the kind with tick-boxes that anyone in the chat can
|
|
2933
|
+
mark off. Telegram only allows this on behalf of a **business account**, never from a bot on its own,
|
|
2934
|
+
so there is one thing to do first: in Telegram, open Settings, then Business, then Chatbots, and
|
|
2935
|
+
connect your bot there.
|
|
2936
|
+
|
|
2937
|
+
That is the whole setup. Maxy notices the connection the moment you make it and remembers
|
|
2938
|
+
it, so from then on you can just ask for a checklist. Disconnect the bot and it stops, and
|
|
2939
|
+
Maxy will tell you that is why rather than failing silently.
|
|
2940
|
+
|
|
2941
|
+
One limit, and it is Telegram's. A message sent on behalf of a business account cannot carry a button
|
|
2942
|
+
that opens a small web page, so a checklist and a job-sheet button arrive as two messages rather than
|
|
2943
|
+
one.
|
|
2944
|
+
|
|
2899
2945
|
Five of the 185 are deliberately refused, because they would break your own
|
|
2900
2946
|
inbound messages: setting or deleting the webhook by hand, polling Telegram for
|
|
2901
2947
|
updates instead, and the two that move the bot off Telegram's cloud servers.
|
|
@@ -3326,6 +3372,8 @@ Click any entry to open its details, where you can also edit it: the full title,
|
|
|
3326
3372
|
|
|
3327
3373
|
Everything on the calendar comes from records the rest of Maxy already uses, scoped to your account.
|
|
3328
3374
|
|
|
3375
|
+
Two things about what Maxy itself can see and change. A deleted entry no longer comes back when Maxy lists your diary, so if you or Maxy removed a duplicate, it stays removed. And every entry Maxy can see, Maxy can now also change or cancel. Before, some entries could be read and not touched, and Maxy would say so rather than act. If you ask Maxy to record something *about* a meeting rather than the meeting itself, it writes that as a note attached to the meeting.
|
|
3376
|
+
|
|
3329
3377
|
## The public booking page
|
|
3330
3378
|
|
|
3331
3379
|
The booking page is a separate, public site on its own web address (for example `book.yourdomain.com`). A visitor sees only open time slots, never the titles or details of your existing meetings. They pick a slot, enter their name, email, and an optional note, and submit. That is the whole flow, like a simple Calendly.
|
|
@@ -3911,7 +3959,9 @@ either is a regression.
|
|
|
3911
3959
|
|
|
3912
3960
|
**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, insert emoji (a codepoint-data picker — no OS input, so the `check-no-emoji-in-jsx` gate stays green), record a voice note (MediaRecorder → the existing audio send path), attach a file **from device storage** (a modal browsing the DATA_ROOT tree via `GET /api/admin/files`, sent by reference — the OS file dialog is no longer the reader's picker), reply to a specific message (a per-bubble affordance that quotes the original in the composer and threads natively on WhatsApp), and search the loaded transcript (a header control that filters to matching turns and highlights the match). The header carries only that Search control — the former Video / Phone / menu icons are gone, because no call or presence data exists behind them. Sent files honour the webchat allowlist (`SUPPORTED_MIME_SET` / 50 MB / 5 files); a device-file reference is authorised server-side (`authorizeReplyFileRef`) against the send's own account (the conversation owner) **or** the session's pinned account (`getAccountIdForSession` — the exact scope the DeviceFilePicker used to offer it), so a house/managing-admin session can attach a managed-client file the picker listed. Because a reply sends bytes OUT to an external WhatsApp recipient, any other account, a `public` partition, and any install-wide / non-account path are refused fail-closed; the cross-account admission's authority is the session's pinned account, never the caller-supplied body `accountId` (the own-account admission matches the body `accountId`, which the route already trusts via `isValidAccountId` and the existing-conversation guard). A scope refusal returns a distinct HTTP 403 reason (`file-ref-cross-account` / `file-ref-public` / `file-ref-foreign`) and copy, never "device file not found" (reserved for a genuine resolve/stat miss). Per-ref `[wa-manual-send] op=reply-file-ref … via=own|session|deny reason=<…> ok=<bool>` logs the decision before any send; `via=deny ok=true` is the exfiltration signature that must never appear. **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 render **inline** in the store reader (image thumbnail / pdf preview / audio player / download chip), because the manual-send path records the message with the servable `attachmentId`. 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 in-bubble delivered-tick is cosmetic (no read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable, and both directions carry a WhatsApp callout tail (`.wa-web`-scoped only). 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`. **Composer sizing matches the webchat composer:** the reply textarea auto-grows to fit multi-line content (measured from `scrollHeight`, clamped to a one-row minimum and a `viewport/3` maximum, scrollable past the cap with the scrollbar hidden), and a top-border grab handle drags (pointer) or nudges (ArrowUp/ArrowDown, keyboard-focusable) to an explicit height in the same clamp; a manual drag pins the height and overrides auto-grow until the field is cleared, which releases it back to auto-grow. A completed manual resize logs `[operator-ui] op=wa-composer-resize mode=drag|key h=<px>`; a bare click on the handle (no drag) logs nothing.
|
|
3913
3961
|
|
|
3914
|
-
**Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves.
|
|
3962
|
+
**Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves. A location shared as a place name with no coordinates shows the name only, because there is no point to put a pin on. The map images are fetched by your browser from OpenStreetMap, so the shared coordinates reach that service when a location bubble is on screen.
|
|
3963
|
+
|
|
3964
|
+
**Selecting a map opens one you can move around.** The small map opens a larger one over the conversation: drag to move it, scroll or pinch to zoom in and out, double-click to zoom in, and press Escape or select the backdrop to close it. On a phone it fills the screen. A link at the bottom still opens the same spot on OpenStreetMap in a new tab if you want it there. Someone sharing their live location keeps arriving as they move, and the open map keeps centring on where they are now until you move it yourself; from then on your own view stays put while the trail keeps growing, and a **Follow** button brings it back to following them.
|
|
3915
3965
|
|
|
3916
3966
|
**`/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).
|
|
3917
3967
|
|
|
@@ -9,6 +9,22 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-08-09 (0.1.582)
|
|
13
|
+
|
|
14
|
+
- Telegram now understands the full range of what arrives, not a quarter of it. Only four of the twenty-six kinds of update were handled, so a reply reached the assistant without the message it was replying to, a reaction never arrived at all, and a Stars payment could not complete.
|
|
15
|
+
- A Telegram Business connection is now kept rather than announced and thrown away, which is what a native checklist needs in order to be sent.
|
|
16
|
+
- The typing indicator keeps going while a background task runs, instead of stopping the moment the work moved off the main turn.
|
|
17
|
+
- A shared location can be panned and zoomed where it sits. Opening a new browser tab was the only way to move the map.
|
|
18
|
+
|
|
19
|
+
## 2026-08-09 (0.1.581)
|
|
20
|
+
|
|
21
|
+
- Pressing a button or voting in a poll on Telegram now reaches the assistant. Only plain text messages ever did, so every other kind of interaction was accepted and then went nowhere.
|
|
22
|
+
- Sharing your live location no longer starts a fresh conversation on every update. Each ping was raising a full turn, and the position it carried was stored nowhere you could read it; a share is now one turn, and the location is kept where it can be used.
|
|
23
|
+
- The assistant knows what it can do on Telegram. Its instructions never mentioned the channel's own abilities, so it did not offer buttons or media even when they were the natural reply.
|
|
24
|
+
- The typing indicator behaves like one. It was throttled rather than repeated, so it stopped showing during the long pauses it exists for.
|
|
25
|
+
- The conversation pane shows message times and attachments, both of which were missing.
|
|
26
|
+
- A Telegram message from you no longer lands in whatever webchat conversation happens to be open.
|
|
27
|
+
|
|
12
28
|
## 2026-08-08 (0.1.580)
|
|
13
29
|
|
|
14
30
|
- Telegram is now a first-class channel rather than a thin pass-through. Answers arrive formatted and stream as they are written instead of landing as one late block; the assistant can ask a question with buttons, send media, and issue an invoice; and the conversation pane has its own look instead of wearing WhatsApp's green.
|
|
@@ -85,7 +85,9 @@ either is a regression.
|
|
|
85
85
|
|
|
86
86
|
**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, insert emoji (a codepoint-data picker — no OS input, so the `check-no-emoji-in-jsx` gate stays green), record a voice note (MediaRecorder → the existing audio send path), attach a file **from device storage** (a modal browsing the DATA_ROOT tree via `GET /api/admin/files`, sent by reference — the OS file dialog is no longer the reader's picker), reply to a specific message (a per-bubble affordance that quotes the original in the composer and threads natively on WhatsApp), and search the loaded transcript (a header control that filters to matching turns and highlights the match). The header carries only that Search control — the former Video / Phone / menu icons are gone, because no call or presence data exists behind them. Sent files honour the webchat allowlist (`SUPPORTED_MIME_SET` / 50 MB / 5 files); a device-file reference is authorised server-side (`authorizeReplyFileRef`) against the send's own account (the conversation owner) **or** the session's pinned account (`getAccountIdForSession` — the exact scope the DeviceFilePicker used to offer it), so a house/managing-admin session can attach a managed-client file the picker listed. Because a reply sends bytes OUT to an external WhatsApp recipient, any other account, a `public` partition, and any install-wide / non-account path are refused fail-closed; the cross-account admission's authority is the session's pinned account, never the caller-supplied body `accountId` (the own-account admission matches the body `accountId`, which the route already trusts via `isValidAccountId` and the existing-conversation guard). A scope refusal returns a distinct HTTP 403 reason (`file-ref-cross-account` / `file-ref-public` / `file-ref-foreign`) and copy, never "device file not found" (reserved for a genuine resolve/stat miss). Per-ref `[wa-manual-send] op=reply-file-ref … via=own|session|deny reason=<…> ok=<bool>` logs the decision before any send; `via=deny ok=true` is the exfiltration signature that must never appear. **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 render **inline** in the store reader (image thumbnail / pdf preview / audio player / download chip), because the manual-send path records the message with the servable `attachmentId`. 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 in-bubble delivered-tick is cosmetic (no read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable, and both directions carry a WhatsApp callout tail (`.wa-web`-scoped only). 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`. **Composer sizing matches the webchat composer:** the reply textarea auto-grows to fit multi-line content (measured from `scrollHeight`, clamped to a one-row minimum and a `viewport/3` maximum, scrollable past the cap with the scrollbar hidden), and a top-border grab handle drags (pointer) or nudges (ArrowUp/ArrowDown, keyboard-focusable) to an explicit height in the same clamp; a manual drag pins the height and overrides auto-grow until the field is cleared, which releases it back to auto-grow. A completed manual resize logs `[operator-ui] op=wa-composer-resize mode=drag|key h=<px>`; a bare click on the handle (no drag) logs nothing.
|
|
87
87
|
|
|
88
|
-
**Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves.
|
|
88
|
+
**Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves. A location shared as a place name with no coordinates shows the name only, because there is no point to put a pin on. The map images are fetched by your browser from OpenStreetMap, so the shared coordinates reach that service when a location bubble is on screen.
|
|
89
|
+
|
|
90
|
+
**Selecting a map opens one you can move around.** The small map opens a larger one over the conversation: drag to move it, scroll or pinch to zoom in and out, double-click to zoom in, and press Escape or select the backdrop to close it. On a phone it fills the screen. A link at the bottom still opens the same spot on OpenStreetMap in a new tab if you want it there. Someone sharing their live location keeps arriving as they move, and the open map keeps centring on where they are now until you move it yourself; from then on your own view stays put while the trail keeps growing, and a **Follow** button brings it back to following them.
|
|
89
91
|
|
|
90
92
|
**`/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).
|
|
91
93
|
|
|
@@ -18,6 +18,8 @@ Click any entry to open its details, where you can also edit it: the full title,
|
|
|
18
18
|
|
|
19
19
|
Everything on the calendar comes from records the rest of Maxy already uses, scoped to your account.
|
|
20
20
|
|
|
21
|
+
Two things about what Maxy itself can see and change. A deleted entry no longer comes back when Maxy lists your diary, so if you or Maxy removed a duplicate, it stays removed. And every entry Maxy can see, Maxy can now also change or cancel. Before, some entries could be read and not touched, and Maxy would say so rather than act. If you ask Maxy to record something *about* a meeting rather than the meeting itself, it writes that as a note attached to the meeting.
|
|
22
|
+
|
|
21
23
|
## The public booking page
|
|
22
24
|
|
|
23
25
|
The booking page is a separate, public site on its own web address (for example `book.yourdomain.com`). A visitor sees only open time slots, never the titles or details of your existing meetings. They pick a slot, enter their name, email, and an optional note, and submit. That is the whole flow, like a simple Calendly.
|
|
@@ -90,6 +90,52 @@ Just ask for what you want. "Send the team a message with Yes and No buttons",
|
|
|
90
90
|
{{productName}} loads its Telegram build skill, which lists every available
|
|
91
91
|
method and how to call it.
|
|
92
92
|
|
|
93
|
+
When somebody presses one of those buttons or votes in one of those polls,
|
|
94
|
+
{{productName}} hears it and can answer. Before, a press was recorded but reached
|
|
95
|
+
nobody, and a vote was dropped without a trace. A press on a job-sheet card is the
|
|
96
|
+
one exception: the dispatch plugin applies the change and tells the presser
|
|
97
|
+
itself, so {{productName}} does not answer a second time.
|
|
98
|
+
|
|
99
|
+
The same is now true of most of what Telegram sends. Reply to one of
|
|
100
|
+
{{productName}}'s messages and it sees the message you replied to, so "yes, do
|
|
101
|
+
that one" means something. React to a message with an emoji and it hears the
|
|
102
|
+
reaction and what it was on. When a poll closes, it hears the final counts. When
|
|
103
|
+
somebody blocks the bot, that appears in the logs instead of sends quietly
|
|
104
|
+
failing.
|
|
105
|
+
|
|
106
|
+
Two things worth knowing about reactions. Telegram sends them only to a bot that
|
|
107
|
+
is an administrator of the chat, and a one-to-one chat has no administrator, so a
|
|
108
|
+
reaction in a private chat may never arrive at all. And a reaction on a message
|
|
109
|
+
{{productName}} itself sent will say the original is not held: it knows the
|
|
110
|
+
reaction happened, not what it was on.
|
|
111
|
+
|
|
112
|
+
Payments now complete. Telegram gives a bot ten seconds to approve a checkout
|
|
113
|
+
before cancelling it, which is faster than anything that has to think, so
|
|
114
|
+
{{productName}} approves it automatically and then tells you the sale happened,
|
|
115
|
+
with your own reference on it. If you sell a subscription, a customer cancelling,
|
|
116
|
+
re-subscribing or failing to pay all reach {{productName}} as well.
|
|
117
|
+
|
|
118
|
+
One limit worth knowing, and it is Telegram's rather than ours. Telegram only
|
|
119
|
+
reports who voted in a poll that is **not** anonymous, and polls are anonymous
|
|
120
|
+
unless you say otherwise. Ask for a poll where you can see who chose what, and
|
|
121
|
+
{{productName}} will send it that way; ask for a plain poll and Telegram will tell
|
|
122
|
+
nobody how anyone voted, so no reply is possible.
|
|
123
|
+
|
|
124
|
+
## Checklists and Telegram Business
|
|
125
|
+
|
|
126
|
+
{{productName}} can send a Telegram checklist, the kind with tick-boxes that anyone in the chat can
|
|
127
|
+
mark off. Telegram only allows this on behalf of a **business account**, never from a bot on its own,
|
|
128
|
+
so there is one thing to do first: in Telegram, open Settings, then Business, then Chatbots, and
|
|
129
|
+
connect your bot there.
|
|
130
|
+
|
|
131
|
+
That is the whole setup. {{productName}} notices the connection the moment you make it and remembers
|
|
132
|
+
it, so from then on you can just ask for a checklist. Disconnect the bot and it stops, and
|
|
133
|
+
{{productName}} will tell you that is why rather than failing silently.
|
|
134
|
+
|
|
135
|
+
One limit, and it is Telegram's. A message sent on behalf of a business account cannot carry a button
|
|
136
|
+
that opens a small web page, so a checklist and a job-sheet button arrive as two messages rather than
|
|
137
|
+
one.
|
|
138
|
+
|
|
93
139
|
Five of the 185 are deliberately refused, because they would break your own
|
|
94
140
|
inbound messages: setting or deleting the webhook by hand, polling Telegram for
|
|
95
141
|
updates instead, and the two that move the bot off Telegram's cloud servers.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Task 350 — tests for the per-account-root reservation gate.
|
|
2
|
+
// Task 2613 — plus the calendar-label reservation, below.
|
|
2
3
|
import { describe, it, expect } from "vitest";
|
|
3
|
-
import { checkLabelOriginGate, RESERVED_LABEL_REFUSAL } from "../label-origin-gate.js";
|
|
4
|
+
import { checkLabelOriginGate, RESERVED_LABEL_REFUSAL, CALENDAR_LABEL_REFUSAL, } from "../label-origin-gate.js";
|
|
4
5
|
describe("checkLabelOriginGate", () => {
|
|
5
6
|
it("(a) system origin writing LocalBusiness → ok", () => {
|
|
6
7
|
const r = checkLabelOriginGate({
|
|
@@ -47,4 +48,66 @@ describe("checkLabelOriginGate", () => {
|
|
|
47
48
|
expect(r.ok).toBe(false);
|
|
48
49
|
});
|
|
49
50
|
});
|
|
51
|
+
// Task 2613 — memory-write created :Meeting rows carrying no uid. A meeting's
|
|
52
|
+
// only handle IS its uid, so schedule-list returned eventId:null for them and
|
|
53
|
+
// schedule-get/update/cancel, which all resolve MATCH (m:Meeting {uid: $id}),
|
|
54
|
+
// could never match. 37 untrashed rows on one install were unreachable, and
|
|
55
|
+
// with no key there was no MERGE collision either, so the same appointment was
|
|
56
|
+
// written twice three weeks apart.
|
|
57
|
+
describe("checkLabelOriginGate — :Meeting is scheduling-owned", () => {
|
|
58
|
+
it("refuses a Meeting create and names schedule-event", () => {
|
|
59
|
+
const r = checkLabelOriginGate({
|
|
60
|
+
labels: ["Meeting"],
|
|
61
|
+
createdBy: { agent: "admin" },
|
|
62
|
+
accountId: "acct-12345678",
|
|
63
|
+
tool: "memory-write",
|
|
64
|
+
});
|
|
65
|
+
expect(r.ok).toBe(false);
|
|
66
|
+
if (r.ok)
|
|
67
|
+
return;
|
|
68
|
+
expect(r.reason).toBe("calendar-label");
|
|
69
|
+
expect(r.message).toBe(CALENDAR_LABEL_REFUSAL);
|
|
70
|
+
expect(r.message).toContain("schedule-event");
|
|
71
|
+
expect(r.message).toContain("uid");
|
|
72
|
+
});
|
|
73
|
+
it("refuses Meeting even from system origin", () => {
|
|
74
|
+
// No exemption: meeting-write.ts, reconcile-bookings.ts and
|
|
75
|
+
// ics-graph-ingest.ts each run their own Cypher and none routes through
|
|
76
|
+
// memoryWrite, so nothing legitimate needs a bypass here.
|
|
77
|
+
const r = checkLabelOriginGate({
|
|
78
|
+
labels: ["Meeting"],
|
|
79
|
+
createdBy: { agent: "system" },
|
|
80
|
+
accountId: "acct-12345678",
|
|
81
|
+
tool: "memory-write",
|
|
82
|
+
});
|
|
83
|
+
expect(r.ok).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
it("refuses Meeting when it rides along with another label", () => {
|
|
86
|
+
const r = checkLabelOriginGate({
|
|
87
|
+
labels: ["KnowledgeDocument", "Meeting"],
|
|
88
|
+
createdBy: { agent: "admin" },
|
|
89
|
+
accountId: "acct-12345678",
|
|
90
|
+
tool: "memory-write",
|
|
91
|
+
});
|
|
92
|
+
expect(r.ok).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
it("still allows Note, the redirect target", () => {
|
|
95
|
+
const r = checkLabelOriginGate({
|
|
96
|
+
labels: ["Note"],
|
|
97
|
+
createdBy: { agent: "admin" },
|
|
98
|
+
accountId: "acct-12345678",
|
|
99
|
+
tool: "memory-write",
|
|
100
|
+
});
|
|
101
|
+
expect(r.ok).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
it("leaves the LocalBusiness rule exactly as it was", () => {
|
|
104
|
+
const system = checkLabelOriginGate({
|
|
105
|
+
labels: ["LocalBusiness"],
|
|
106
|
+
createdBy: { agent: "system" },
|
|
107
|
+
accountId: "acct-12345678",
|
|
108
|
+
tool: "memory-write",
|
|
109
|
+
});
|
|
110
|
+
expect(system.ok).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
50
113
|
//# sourceMappingURL=label-origin-gate.test.js.map
|
package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-origin-gate.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/label-origin-gate.test.ts"],"names":[],"mappings":"AAAA,8DAA8D;
|
|
1
|
+
{"version":3,"file":"label-origin-gate.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/label-origin-gate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CreatedBy } from "../../../../../lib/graph-write/dist/index.js";
|
|
2
2
|
export declare const RESERVED_LABEL_REFUSAL: string;
|
|
3
|
+
export declare const CALENDAR_LABEL_REFUSAL: string;
|
|
3
4
|
interface OriginGateArgs {
|
|
4
5
|
labels: string[];
|
|
5
6
|
createdBy: CreatedBy;
|
|
@@ -12,6 +13,10 @@ type OriginGateResult = {
|
|
|
12
13
|
ok: false;
|
|
13
14
|
reason: "reserved-label";
|
|
14
15
|
message: string;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
reason: "calendar-label";
|
|
19
|
+
message: string;
|
|
15
20
|
};
|
|
16
21
|
export declare function checkLabelOriginGate(args: OriginGateArgs): OriginGateResult;
|
|
17
22
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-origin-gate.d.ts","sourceRoot":"","sources":["../../src/lib/label-origin-gate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"label-origin-gate.d.ts","sourceRoot":"","sources":["../../src/lib/label-origin-gate.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAM9E,eAAO,MAAM,sBAAsB,QAIoC,CAAC;AAExE,eAAO,MAAM,sBAAsB,QAMJ,CAAC;AAEhC,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,KAAK,gBAAgB,GACjB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAgC3E"}
|
|
@@ -1,22 +1,44 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// Two labels this module reserves against agent-origin *creates*.
|
|
2
|
+
//
|
|
3
|
+
// Task 350 + Task 448 — `:LocalBusiness`. The installer's seed-neo4j.sh writes
|
|
4
|
+
// the single root node via raw cypher (createdBy.agent === 'system' by
|
|
5
|
+
// convention); third-party companies must go to `:Organization` per
|
|
6
|
+
// schema-base.md. Without this gate an agent mislabel either silently corrupts
|
|
7
|
+
// the operator's root via MERGE or trips the UNIQUE constraint on
|
|
8
|
+
// LocalBusiness.accountId. System origin is exempt.
|
|
9
|
+
//
|
|
10
|
+
// Task 2613 — `:Meeting`, with no exemption. A meeting's only handle is its
|
|
11
|
+
// `uid`, a content hash schedule-event computes and MERGEs on; memory-write
|
|
12
|
+
// writes none, so every row it created could be listed and never edited or
|
|
13
|
+
// cancelled, and with no key the same appointment duplicated freely.
|
|
7
14
|
//
|
|
8
15
|
// Scope is creates only (`memory-write`). Property updates against the
|
|
9
16
|
// existing root (`memory-update`, elementId-resolved + accountId-scoped)
|
|
10
17
|
// cannot create a new node, so the gate does not apply there — they are
|
|
11
18
|
// guarded by checkLabelWriteGate + checkGraphWriteGate + property restriction.
|
|
12
19
|
const RESERVED_LABEL = "LocalBusiness";
|
|
20
|
+
const CALENDAR_LABEL = "Meeting";
|
|
13
21
|
const SYSTEM_AGENT = "system";
|
|
14
22
|
export const RESERVED_LABEL_REFUSAL = `Creating a second ${RESERVED_LABEL} is refused — it is the per-account root ` +
|
|
15
23
|
`seeded by the installer. Use Organization for third-party companies — see ` +
|
|
16
24
|
`platform/plugins/memory/references/schema-base.md. ` +
|
|
17
25
|
`To edit the existing root, use memory-update against its elementId.`;
|
|
26
|
+
export const CALENDAR_LABEL_REFUSAL = `Creating a ${CALENDAR_LABEL} through memory-write is refused. A calendar ` +
|
|
27
|
+
`entry's handle is its uid, which only schedule-event computes, and a row ` +
|
|
28
|
+
`written without one can never be read back, edited or cancelled by any ` +
|
|
29
|
+
`scheduling tool. Use schedule-event (scheduling plugin) with startDate and ` +
|
|
30
|
+
`endDate. To record notes ABOUT a meeting, write a Note or KnowledgeDocument ` +
|
|
31
|
+
`and link it to the meeting.`;
|
|
18
32
|
export function checkLabelOriginGate(args) {
|
|
19
33
|
const { labels, createdBy, accountId, tool } = args;
|
|
34
|
+
// Checked before LocalBusiness so a write carrying both is refused for the
|
|
35
|
+
// calendar reason, which is the one that names the tool to use instead.
|
|
36
|
+
if (labels.includes(CALENDAR_LABEL)) {
|
|
37
|
+
const acctPrefix = accountId ? accountId.slice(0, 8) : "<none>";
|
|
38
|
+
const agent = createdBy.agent ?? "<unset>";
|
|
39
|
+
console.error(`[label-origin-gate] reject accountId=${acctPrefix} label=${CALENDAR_LABEL} agent=${agent} tool=${tool}`);
|
|
40
|
+
return { ok: false, reason: "calendar-label", message: CALENDAR_LABEL_REFUSAL };
|
|
41
|
+
}
|
|
20
42
|
if (!labels.includes(RESERVED_LABEL)) {
|
|
21
43
|
return { ok: true };
|
|
22
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-origin-gate.js","sourceRoot":"","sources":["../../src/lib/label-origin-gate.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,
|
|
1
|
+
{"version":3,"file":"label-origin-gate.js","sourceRoot":"","sources":["../../src/lib/label-origin-gate.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,+EAA+E;AAC/E,uEAAuE;AACvE,oEAAoE;AACpE,+EAA+E;AAC/E,kEAAkE;AAClE,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,qEAAqE;AACrE,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,wEAAwE;AACxE,+EAA+E;AAI/E,MAAM,cAAc,GAAG,eAAe,CAAC;AACvC,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAE9B,MAAM,CAAC,MAAM,sBAAsB,GACjC,qBAAqB,cAAc,2CAA2C;IAC9E,4EAA4E;IAC5E,qDAAqD;IACrD,qEAAqE,CAAC;AAExE,MAAM,CAAC,MAAM,sBAAsB,GACjC,cAAc,cAAc,+CAA+C;IAC3E,2EAA2E;IAC3E,yEAAyE;IACzE,6EAA6E;IAC7E,8EAA8E;IAC9E,6BAA6B,CAAC;AAchC,MAAM,UAAU,oBAAoB,CAAC,IAAoB;IACvD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAEpD,2EAA2E;IAC3E,wEAAwE;IACxE,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC;QAC3C,OAAO,CAAC,KAAK,CACX,wCAAwC,UAAU,UAAU,cAAc,UAAU,KAAK,SAAS,IAAI,EAAE,CACzG,CAAC;QACF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClF,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,YAAY,EAAE,CAAC;QAC7C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC;IAC3C,OAAO,CAAC,KAAK,CACX,wCAAwC,UAAU,UAAU,cAAc,UAAU,KAAK,SAAS,IAAI,EAAE,CACzG,CAAC;IAEF,OAAO;QACL,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE,sBAAsB;KAChC,CAAC;AACJ,CAAC"}
|
|
@@ -13,7 +13,7 @@ When loading this reference, confirm: "Using schema-base + schema-knowledge-work
|
|
|
13
13
|
Most of knowledge work is already in the base schema. Use the base labels, do not duplicate them:
|
|
14
14
|
|
|
15
15
|
- People and orgs: `Person`, `Organization`, `Position`, `Credential`. Reporting lines are the `REPORTS_TO` edge below, not a node.
|
|
16
|
-
- Work: `Task` (a `schema:Action`), `Project`, `Meeting`, `Event`, `Workflow`.
|
|
16
|
+
- Work: `Task` (a `schema:Action`), `Project`, `Meeting`, `Event`, `Workflow`. `Meeting` is scheduling-owned: `memory-write` refuses to create one, because a meeting's handle is the `uid` only `schedule-event` computes, and a row written without one can never be read back, edited or cancelled. Book it with `schedule-event`; to record notes *about* a meeting, write a `Note` or `KnowledgeDocument` and link it.
|
|
17
17
|
- Concepts and content: `DefinedTerm` (concepts, skills, glossary, learnings), `Concept`, `Idea`, `CreativeWork`, `KnowledgeDocument` and `Section`.
|
|
18
18
|
|
|
19
19
|
The gaps this vertical fills are: goals and OKRs, decision records, risk, and the research / citation / evidence layer. It adds those seven labels and nothing else.
|
|
@@ -134,6 +134,8 @@ To book a meeting with someone, resolve the counterparty to a `:Person` first (v
|
|
|
134
134
|
|
|
135
135
|
The chosen bucket is logged on every write: `[schedule-event] op=write label=Meeting|Event reason=<discriminator>`.
|
|
136
136
|
|
|
137
|
+
`:Meeting` has exactly three writers: `schedule-event` (`meeting-write.ts`), public-booking reconcile (`reconcile-bookings.ts`) and `.ics` import (`ics-graph-ingest.ts`). `memory-write` refuses the label outright and redirects to `schedule-event`, because it computes no `uid`, and a meeting without one can never be read back, edited or cancelled. To record notes *about* a meeting, write a `:Note` or `:KnowledgeDocument` and link it.
|
|
138
|
+
|
|
137
139
|
Backfilling already-written `:Event` appointments into `:Meeting` is a separate divergence-driven migration, not part of this write path.
|
|
138
140
|
|
|
139
141
|
### Reading meetings back
|
|
@@ -142,7 +144,11 @@ Both labels are readable. `schedule-list` returns meetings and events together a
|
|
|
142
144
|
|
|
143
145
|
A meeting's handle is its `uid`. `schedule-event` returns it on creation, and `schedule-get`, `schedule-update` and `schedule-cancel` accept it wherever they accept an event id — they resolve an event first, then a meeting, so a caller never has to say which kind it holds. Updating a meeting sets `title`, `startsAt`, `endsAt` and `location` and never rewrites the `uid`; `eventStatus`, `recurrence`, `note`, `action` and `agentDispatch` have no meeting equivalent and do not cross over. A meeting DOES carry a `description`: `schedule-event` writes one, `schedule-get` returns it, and the admin Calendar edits it. `schedule-update` does not write it, so this tool's field set is narrower than the admin Calendar edit's. Cancelling a meeting **deletes** it and its attendee edges, the same as deleting it on the Calendar page — a `:Meeting` has no status to set to cancelled. A booking-sourced meeting that is deleted is rewritten by the next reconcile run.
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
A trashed meeting or event is excluded from `schedule-list`, `schedule-get`, `schedule-update` and `schedule-cancel`, on the reads and the writes alike. Before this, all four served deleted rows: on 2026-08-09 an agent was shown three copies of one appointment, two of which had been trashed on 28 July and 6 August with reasons naming the duplication, reported a de-duplication problem that had already been resolved twice, and wrote its correction to the copy trashed twelve days earlier.
|
|
148
|
+
|
|
149
|
+
Until this landed, `schedule-list` matched `:Event` alone while `schedule-event` wrote `:Meeting`, so an agent could not see its own writes: a calendar-sync turn read back an empty list, re-applied the same three meetings, and told the operator over WhatsApp that they had not saved. Observability is therefore two-part. Every list call logs `[schedule-list] op=result kind=<kind|any> meetings=<n> events=<n>`. The standing check is `[schedule-audit] account=<id> meetings=<n> listable=<n> invisible=<n> unaddressable=<n>`, emitted per account on every `reconcile-bookings` run (every 120 s) **before** that script's Cloudflare gates, so it also reports on installs with no booking site. `listable` is what the real `scheduleList` returns, not a re-implementation of its query — that is the whole point, since a copy would agree with a broken original. `invisible=0` is the success signal; `invisible>0` means a calendar entry exists that the agent cannot see.
|
|
150
|
+
|
|
151
|
+
`unaddressable` is the other half, and `invisible` is structurally blind to it: a row can be listed and still be untouchable, because a meeting's handle is its `uid` and `memory-write` wrote rows without one. Those came back with `eventId: null`, and `schedule-get`, `schedule-update` and `schedule-cancel` all resolve `MATCH (m:Meeting {uid: $id})`, which can never match a null. The install that raised this read `meetings=97 listable=97 invisible=0` throughout, with 37 rows unreachable. `unaddressable=0` is the success signal; above zero means a live calendar entry the agent can see and cannot act on. The count is taken from the graph rather than from the list result, because the list is precisely what cannot tell the two apart.
|
|
146
152
|
|
|
147
153
|
### Routine run records
|
|
148
154
|
|
|
@@ -344,6 +350,10 @@ Env: `PLATFORM_ROOT`, `NEO4J_URI` (and `NEO4J_PASSWORD` or `config/.neo4j-passwo
|
|
|
344
350
|
|
|
345
351
|
Observability: one `[calendar-booking]` line per step keyed by `bookingId` (`reconcile-claim`, `meeting-write`, `notify`, `booked`), and one `[calendar-reconcile] account=… eligibleExamined=… pendingUnreconciled=… pendingOldestAgeMin=… orphanMeetings=…` audit line per account per run. `eligibleExamined` separates "nothing to do" from "verified clean". A `pendingUnreconciled` that persists across runs means those rows are failing to become meetings rather than merely lagging — the `attempt=` count on the claim and failure lines says how many times each has been tried. `orphanMeetings` means a booking meeting has no backing eligible D1 row. `pendingOldestAgeMin` ages that backlog: a count cannot separate a short lag from a nine-hour one. The companion `[calendar-reconcile] op=liveness lastRunAgeMin=… lastAuditAgeMin=… lastSkipReason=… stale=<bool>` line is emitted every ten minutes by the platform/ui server rather than by this script, because the failure it detects is this script not reporting at all. `op=liveness-write-failed` is the one case where that record could not be written; it carries the error body and never stops the run, because a run that worked must not be reported as failed for losing its own bookkeeping.
|
|
346
352
|
|
|
353
|
+
`[calendar-availability] op=meeting-uid-stamp account=… scanned=… stamped=… skipped=… unhashable=… collided=… failed=…` is the handle repair, in the same loop and on the same terms: it matches untrashed `:Meeting` rows with no `uid` and stamps one, `mem-<hash of account, normalised title and start>`. It stamps only — it never merges and never deletes, because the uid a row would have had is hashed from the values passed at creation and `schedule-update` never rewrites it, so a recomputed uid cannot identify a row's original twin. A stamped row stops matching, so a steady install reads `scanned=0 stamped=0` and this too is a standing repair rather than a migration. The prefix is `mem-`, deliberately not `meetingUid`'s `sched-`, because `sched-` is `schedule-event`'s MERGE key and stamping it would let a later `schedule-event` write adopt one of these rows as its own.
|
|
354
|
+
|
|
355
|
+
The four non-stamped counts are separate because each names a different thing to do. `stamped` is asserted from the uid the write returns, never from the call resolving: the apply carries `uid IS NULL`, so a row stamped by a concurrent tick matches nothing and still resolves, and counting that as stamped would report a write that never happened — it is `skipped`, and it is benign. `unhashable` is a row with no title or no start, which this repair can never fix; it is deliberately in the select rather than filtered out, because the audit counts it as `unaddressable` and a permanently red line with nothing naming its cause is how a warning stops being read. `collided` is the `meeting_account_uid_unique` constraint on `(accountId, uid)` rejecting the write, meaning two rows share an account, a title and a start and a human must decide which survives. `failed` is the write not landing and retries next tick. The select and the audit's `unaddressable` query scan byte-for-byte the same population, so the repair and the audit can never disagree about the total.
|
|
356
|
+
|
|
347
357
|
Two further lines print per account on every tick whether or not anything is wrong. `[calendar-availability] op=meeting-retype account=… scanned=… repaired=… unparseable=… failed=…` is the timestamp type census: it matches `:Meeting` rows whose `startsAt` or `endsAt` is stored as a `STRING` and rebuilds each with `datetime()`. It answers "is anything in this calendar stored with the wrong type right now?" without reproducing the failure and without waiting for a customer to hit a bad slot. A repaired row stops matching, so a steady install reads `scanned=0 repaired=0` and this stays a standing repair rather than a migration to remove. A non-zero `repaired` on a steady install means something is still writing strings and names the account. The two failure counts are deliberately separate: `unparseable` is a value Neo4j's own parser rejects and needs a human, while `failed` is the write not landing (a timeout, a disconnect) and retries on the next tick. Neo4j's error code decides which, so a transient fault is never reported as a corrupt value. No JS pre-check stands in for that parser — `Date.parse` rejects the zone-id form `2026-07-29T13:30:00[Europe/London]` that `datetime()` accepts, and would report a repairable row as needing a human.
|
|
348
358
|
|
|
349
359
|
`[calendar-availability] op=cal-drift account=… outcome=… published=… busyMeetings=… busyConnector=… busyUnusable=… conflicts=…` re-reads what was published and reconciles it against both labels the public free/busy page subtracts, `:Meeting` and `:ConnectorBusy`. It read `:ConnectorBusy` alone before this, so any meeting conflict was outside what it could measure, and it reported `outcome=verified conflicts=0` throughout an incident where five meetings were invisible. The census is split into named counts because one total cannot distinguish "3 connector intervals, no meetings" from "3 meetings, no connector" — the exact ambiguity that made `busy=3 conflicts=0` read as healthy. A meeting with no `endsAt` (nullable by schema) is given `start + durationMins`, the same substitution the free/busy route makes, rather than dropped; `busyUnusable` counts the rows that had no resolvable bound even so, because an interval excluded without a count is the silent drop this whole signal exists to remove. Read together the two lines discriminate three states: a conflict with `repaired=0` is a slot-math or publish fault, a conflict alongside `repaired>0` is corruption caught mid-repair, and `conflicts=0` with `busyMeetings>0` is evidence the meeting path was checked at all.
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meeting-uid-stamp.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/meeting-uid-stamp.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Task 2613. memory-write created :Meeting rows with no uid. A meeting's only
|
|
2
|
+
// handle is its uid, so schedule-list returned eventId:null for them and
|
|
3
|
+
// schedule-get/update/cancel, which all resolve MATCH (m:Meeting {uid: $id}),
|
|
4
|
+
// could never match. 37 untrashed rows on one install were unreachable.
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { MEETING_UID_SELECT, MEETING_UID_APPLY, stampedUid, classifyStampError, } from "../booking-reconcile.js";
|
|
8
|
+
import { meetingUid } from "../meeting-write.js";
|
|
9
|
+
const ACC = "20028fb5-db26-4de7-bff6-c5ec2d4fc766";
|
|
10
|
+
const TITLE = "Mind Circles Sip N' Learn";
|
|
11
|
+
const START = "2026-08-11T19:00:00+01:00";
|
|
12
|
+
describe("stampedUid", () => {
|
|
13
|
+
it("is deterministic", () => {
|
|
14
|
+
expect(stampedUid(ACC, TITLE, START)).toBe(stampedUid(ACC, TITLE, START));
|
|
15
|
+
});
|
|
16
|
+
it("normalises the title the same way meetingUid does", () => {
|
|
17
|
+
expect(stampedUid(ACC, " mind circles sip n' learn ", START)).toBe(stampedUid(ACC, TITLE, START));
|
|
18
|
+
});
|
|
19
|
+
it("carries the mem- prefix, not sched-", () => {
|
|
20
|
+
// sched- is schedule-event's MERGE key. Stamping that form would let a
|
|
21
|
+
// later schedule-event write adopt a memory-write row as its own.
|
|
22
|
+
const uid = stampedUid(ACC, TITLE, START);
|
|
23
|
+
expect(uid.startsWith("mem-")).toBe(true);
|
|
24
|
+
expect(uid).not.toBe(meetingUid(ACC, TITLE, START));
|
|
25
|
+
});
|
|
26
|
+
it("is a 32-char hash body", () => {
|
|
27
|
+
const expected = "mem-" +
|
|
28
|
+
createHash("sha256")
|
|
29
|
+
.update(`${ACC}|${TITLE.trim().toLowerCase()}|${START}`)
|
|
30
|
+
.digest("hex")
|
|
31
|
+
.slice(0, 32);
|
|
32
|
+
expect(stampedUid(ACC, TITLE, START)).toBe(expected);
|
|
33
|
+
expect(stampedUid(ACC, TITLE, START)).toHaveLength(36);
|
|
34
|
+
});
|
|
35
|
+
it("separates rows that differ only by start", () => {
|
|
36
|
+
expect(stampedUid(ACC, TITLE, "2026-08-11T20:00:00+01:00")).not.toBe(stampedUid(ACC, TITLE, START));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe("the stamp Cypher", () => {
|
|
40
|
+
it("selects only untrashed meetings that have no uid", () => {
|
|
41
|
+
expect(MEETING_UID_SELECT).toContain("MATCH (m:Meeting {accountId: $accountId})");
|
|
42
|
+
expect(MEETING_UID_SELECT).toContain("m.uid IS NULL");
|
|
43
|
+
expect(MEETING_UID_SELECT).toContain(":Trashed");
|
|
44
|
+
expect(MEETING_UID_SELECT).toContain("deletedAt IS NULL");
|
|
45
|
+
});
|
|
46
|
+
it("selects the same population the audit counts, so the two cannot disagree", () => {
|
|
47
|
+
// A row with no title or no start cannot be hashed, but it IS unaddressable
|
|
48
|
+
// and the audit counts it. Filtering it out here would leave `unaddressable`
|
|
49
|
+
// permanently above zero with nothing naming the cause. The select takes the
|
|
50
|
+
// whole population and the caller counts what it cannot hash.
|
|
51
|
+
expect(MEETING_UID_SELECT).not.toContain("m.title IS NOT NULL");
|
|
52
|
+
expect(MEETING_UID_SELECT).not.toContain("m.startsAt IS NOT NULL");
|
|
53
|
+
});
|
|
54
|
+
it("returns the two hash inputs alongside the id", () => {
|
|
55
|
+
expect(MEETING_UID_SELECT).toContain("elementId(m) AS meetingId");
|
|
56
|
+
expect(MEETING_UID_SELECT).toContain("m.title AS title");
|
|
57
|
+
expect(MEETING_UID_SELECT).toContain("toString(m.startsAt) AS startsAt");
|
|
58
|
+
});
|
|
59
|
+
it("writes the uid and nothing else, and never deletes", () => {
|
|
60
|
+
expect(MEETING_UID_APPLY).toContain("SET m.uid = $uid");
|
|
61
|
+
expect(MEETING_UID_APPLY).not.toContain("DELETE");
|
|
62
|
+
expect(MEETING_UID_APPLY).not.toContain("REMOVE");
|
|
63
|
+
});
|
|
64
|
+
it("re-asserts the label, the account and the null-uid precondition on the write", () => {
|
|
65
|
+
// Account scope is a property; a write carrying none cannot enforce it. The
|
|
66
|
+
// null-uid guard makes the apply idempotent against a concurrent stamp.
|
|
67
|
+
expect(MEETING_UID_APPLY).toContain("MATCH (m:Meeting {accountId: $accountId})");
|
|
68
|
+
expect(MEETING_UID_APPLY).toContain("elementId(m) = $meetingId");
|
|
69
|
+
expect(MEETING_UID_APPLY).toContain("m.uid IS NULL");
|
|
70
|
+
});
|
|
71
|
+
it("returns the uid it wrote, so the caller can tell a write from a no-op", () => {
|
|
72
|
+
// The null-uid guard means a row stamped by a concurrent tick matches
|
|
73
|
+
// nothing and the run still resolves. Without this RETURN the caller counts
|
|
74
|
+
// it as stamped and the log reports work that never happened.
|
|
75
|
+
expect(MEETING_UID_APPLY).toContain("RETURN m.uid AS uid");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe("classifyStampError", () => {
|
|
79
|
+
it("calls a uniqueness violation a collision, which needs a human", () => {
|
|
80
|
+
const err = Object.assign(new Error("Node already exists with label Meeting"), {
|
|
81
|
+
code: "Neo.ClientError.Schema.ConstraintValidationFailed",
|
|
82
|
+
});
|
|
83
|
+
expect(classifyStampError(err)).toBe("collided");
|
|
84
|
+
});
|
|
85
|
+
it("calls anything else a failed write, which retries next tick", () => {
|
|
86
|
+
const transient = Object.assign(new Error("Connection was closed"), {
|
|
87
|
+
code: "Neo.TransientError.General.DatabaseUnavailable",
|
|
88
|
+
});
|
|
89
|
+
expect(classifyStampError(transient)).toBe("failed");
|
|
90
|
+
expect(classifyStampError(new Error("socket hang up"))).toBe("failed");
|
|
91
|
+
expect(classifyStampError(null)).toBe("failed");
|
|
92
|
+
});
|
|
93
|
+
it("does not call every client error a collision", () => {
|
|
94
|
+
// Only the constraint code means "this uid is taken". Widening it would
|
|
95
|
+
// report a syntax error as a duplicate meeting.
|
|
96
|
+
const other = Object.assign(new Error("Invalid input"), {
|
|
97
|
+
code: "Neo.ClientError.Statement.ArgumentError",
|
|
98
|
+
});
|
|
99
|
+
expect(classifyStampError(other)).toBe("failed");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=meeting-uid-stamp.test.js.map
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-uid-stamp.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meeting-uid-stamp.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/meeting-uid-stamp.test.ts"],"names":[],"mappings":"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"}
|