@rubytech/create-sitedesk-code 0.1.521 → 0.1.523
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/dist/index.js +17 -3
- package/package.json +1 -1
- package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
- package/payload/platform/lib/active-rules/package-lock.json +12 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +28 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +7 -0
- package/payload/platform/lib/graph-style/src/index.ts +24 -0
- package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +16 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +83 -6
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +117 -10
- package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
- package/payload/platform/lib/routine-templates/dist/index.d.ts +151 -0
- package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/index.js +207 -0
- package/payload/platform/lib/routine-templates/dist/index.js.map +1 -0
- package/payload/platform/lib/routine-templates/dist/roster.d.ts +57 -0
- package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/roster.js +182 -0
- package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts +2 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.js +64 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.js.map +1 -0
- package/payload/platform/lib/routine-templates/package.json +6 -0
- package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +170 -0
- package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +298 -0
- package/payload/platform/lib/routine-templates/src/index.ts +299 -0
- package/payload/platform/lib/routine-templates/src/roster.ts +206 -0
- package/payload/platform/lib/routine-templates/src/seed-cli.ts +65 -0
- package/payload/platform/lib/routine-templates/tsconfig.json +9 -0
- package/payload/platform/lib/routine-templates/vitest.config.ts +9 -0
- package/payload/platform/neo4j/edge-annotations.json +2 -2
- package/payload/platform/neo4j/schema.cypher +25 -1
- package/payload/platform/package.json +3 -2
- package/payload/platform/plugins/admin/PLUGIN.md +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +39 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +319 -66
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +275 -59
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +11 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/graph/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +12 -5
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js +197 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +55 -9
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +96 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js +126 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js +7 -4
- package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +18 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +4 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -2
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +4 -4
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +21 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +126 -8
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
- package/payload/platform/plugins/outlook/references/auth.md +15 -2
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
- package/payload/platform/plugins/quickbooks/PLUGIN.md +10 -10
- package/payload/platform/plugins/scheduling/PLUGIN.md +49 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js +78 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +457 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts +35 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js +39 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +175 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +533 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +123 -9
- 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__/routine-roster-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js +189 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js +95 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js +114 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +109 -2
- 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 +340 -9
- 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.d.ts +9 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +210 -45
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +43 -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-get-agent.test.js +55 -7
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +45 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js +96 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.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 +2 -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 +5 -0
- 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 +13 -0
- 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 +4 -0
- 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 +24 -3
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts +33 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js +51 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js.map +1 -0
- 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 +12 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +30 -2
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +92 -16
- package/payload/platform/scripts/seed-neo4j.sh +41 -0
- package/payload/platform/scripts/verify-skill-tool-surface.sh +91 -2
- 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 +3 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-AZLT4LVO.js → chunk-VUXKXOJU.js} +755 -335
- package/payload/server/{chunk-UX2FRNO7.js → chunk-WT2PFGY4.js} +37 -265
- package/payload/server/{chunk-PXNY2TAP.js → chunk-XCR5D2G3.js} +239 -10
- package/payload/server/{manager-CQBSUJXR.js → manager-4TJK3YOY.js} +4 -2
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-Dar_MMJm.js → AdminLoginScreens-uknqh2Pm.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js +2 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js.br +0 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js.gz +0 -0
- package/payload/server/public/assets/{activity-B1rt4GIy.js → activity-B4KfMJqO.js} +1 -1
- package/payload/server/public/assets/activity-B4KfMJqO.js.br +0 -0
- package/payload/server/public/assets/activity-B4KfMJqO.js.gz +0 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js +1 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js.br +0 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js.gz +0 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js +1 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js.br +0 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js.gz +0 -0
- package/payload/server/public/assets/{browser-1QSCPl3X.js → browser-B3hSzI3O.js} +1 -1
- package/payload/server/public/assets/browser-B3hSzI3O.js.br +0 -0
- package/payload/server/public/assets/browser-B3hSzI3O.js.gz +0 -0
- package/payload/server/public/assets/{calendar-D009qWVV.js → calendar-BggxXxhS.js} +1 -1
- package/payload/server/public/assets/calendar-BggxXxhS.js.br +0 -0
- package/payload/server/public/assets/calendar-BggxXxhS.js.gz +0 -0
- package/payload/server/public/assets/chat-flivHt0t.js +1 -0
- package/payload/server/public/assets/chat-flivHt0t.js.br +0 -0
- package/payload/server/public/assets/chat-flivHt0t.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-nVRThX7f.js +1 -0
- package/payload/server/public/assets/chevron-right-CLqfrkRA.js +1 -0
- package/payload/server/public/assets/chevron-right-CLqfrkRA.js.br +0 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js +1 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js.br +0 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js.gz +0 -0
- package/payload/server/public/assets/data-Ckjkne54.js +1 -0
- package/payload/server/public/assets/data-Ckjkne54.js.br +3 -0
- package/payload/server/public/assets/data-Ckjkne54.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DwGAfCsd.js → file-text-BKs9eyiw.js} +1 -1
- package/payload/server/public/assets/file-text-BKs9eyiw.js.br +0 -0
- package/payload/server/public/assets/file-text-BKs9eyiw.js.gz +0 -0
- package/payload/server/public/assets/{graph-D-DGlfhW.js → graph-DTi71NaZ.js} +3 -3
- package/payload/server/public/assets/graph-DTi71NaZ.js.br +0 -0
- package/payload/server/public/assets/graph-DTi71NaZ.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js +1 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js.gz +0 -0
- package/payload/server/public/assets/{maximize-2-C_-KapxH.js → maximize-2-zC94tsxY.js} +1 -1
- package/payload/server/public/assets/maximize-2-zC94tsxY.js.br +0 -0
- package/payload/server/public/assets/maximize-2-zC94tsxY.js.gz +0 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js +1 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js.br +0 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js.gz +0 -0
- package/payload/server/public/assets/page-BFm91p0e.js +1 -0
- package/payload/server/public/assets/page-BFm91p0e.js.br +0 -0
- package/payload/server/public/assets/page-BFm91p0e.js.gz +0 -0
- package/payload/server/public/assets/{page-E74MYB18.js → page-Ccl0FazM.js} +3 -3
- package/payload/server/public/assets/page-Ccl0FazM.js.br +0 -0
- package/payload/server/public/assets/page-Ccl0FazM.js.gz +0 -0
- package/payload/server/public/assets/{public-CI6K1sNW.js → public-BpC6cn9S.js} +1 -1
- package/payload/server/public/assets/public-BpC6cn9S.js.br +0 -0
- package/payload/server/public/assets/public-BpC6cn9S.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-PWu_QGrD.js → rotate-ccw-BNcqbHkZ.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.gz +0 -0
- package/payload/server/public/assets/routines-D5iia-XG.js +1 -0
- package/payload/server/public/assets/routines-D5iia-XG.js.br +0 -0
- package/payload/server/public/assets/routines-D5iia-XG.js.gz +0 -0
- package/payload/server/public/assets/{skills-g8HvunPA.js → skills-COLAw_jt.js} +1 -1
- package/payload/server/public/assets/skills-COLAw_jt.js.br +0 -0
- package/payload/server/public/assets/skills-COLAw_jt.js.gz +0 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js +1 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js.br +0 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js.gz +0 -0
- package/payload/server/public/assets/{time-entry-format-BXWuQ6Sw.js → time-entry-format-laTFzawg.js} +1 -1
- package/payload/server/public/assets/time-entry-format-laTFzawg.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-laTFzawg.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-BNshRC13.js → triangle-alert-DrwqhEog.js} +1 -1
- package/payload/server/public/assets/triangle-alert-DrwqhEog.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-DrwqhEog.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-CwTZ9DqH.js → useCopyFeedback-Cxme5GK6.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js +14 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.gz +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-BGqV5R6u.js → useVoiceRecorder-DUBpVa2T.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.gz +0 -0
- package/payload/server/public/assets/{wrench-DQ045Pyk.js → wrench-DWuxPnzF.js} +1 -1
- package/payload/server/public/assets/wrench-DWuxPnzF.js.br +0 -0
- package/payload/server/public/assets/wrench-DWuxPnzF.js.gz +0 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +6631 -5690
- package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js +0 -2
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js.gz +0 -0
- package/payload/server/public/assets/activity-B1rt4GIy.js.br +0 -0
- package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js +0 -1
- package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
- package/payload/server/public/assets/agents-BXq8woVf.js +0 -1
- package/payload/server/public/assets/agents-BXq8woVf.js.br +0 -0
- package/payload/server/public/assets/agents-BXq8woVf.js.gz +0 -0
- package/payload/server/public/assets/browser-1QSCPl3X.js.br +0 -0
- package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
- package/payload/server/public/assets/chat-CUg1fTqJ.js +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.br +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js +0 -1
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
- package/payload/server/public/assets/chevron-right-DWa047Ys.js +0 -1
- package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js +0 -1
- package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js +0 -1
- package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js +0 -1
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js.gz +0 -0
- package/payload/server/public/assets/maximize-2-C_-KapxH.js.br +0 -0
- package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
- package/payload/server/public/assets/operator-CFrZVcv6.js +0 -1
- package/payload/server/public/assets/operator-CFrZVcv6.js.br +0 -0
- package/payload/server/public/assets/operator-CFrZVcv6.js.gz +0 -0
- package/payload/server/public/assets/page-CHt62f1_.js +0 -1
- package/payload/server/public/assets/page-CHt62f1_.js.br +0 -0
- package/payload/server/public/assets/page-CHt62f1_.js.gz +0 -0
- package/payload/server/public/assets/page-E74MYB18.js.br +0 -0
- package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js +0 -1
- package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js +0 -1
- package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +0 -2
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js +0 -14
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
|
@@ -218,14 +218,44 @@ export interface WriteNodeWithEdgesParams {
|
|
|
218
218
|
* a failed embed must never abort an otherwise-valid write.
|
|
219
219
|
*/
|
|
220
220
|
embed?: (text: string) => Promise<number[]>;
|
|
221
|
+
/**
|
|
222
|
+
* Optional natural-key merge. When present and non-empty the node write
|
|
223
|
+
* becomes a MERGE on `accountId` plus the named properties, and every
|
|
224
|
+
* relationship in the same call becomes a MERGE — a re-import of the same
|
|
225
|
+
* row then matches the existing node and its existing edges instead of
|
|
226
|
+
* creating a second copy. Absent (the default), both paths CREATE exactly
|
|
227
|
+
* as before. `accountId` is always part of the key and is never named here.
|
|
228
|
+
* Task 2132.
|
|
229
|
+
*/
|
|
230
|
+
mergeOn?: string[];
|
|
221
231
|
}
|
|
222
232
|
|
|
223
233
|
export interface WriteNodeResult {
|
|
224
234
|
nodeId: string;
|
|
225
235
|
labels: string[];
|
|
236
|
+
/**
|
|
237
|
+
* Edges created on a plain write. Under `mergeOn` this counts edges
|
|
238
|
+
* *resolved* — created plus already-present — because a re-import
|
|
239
|
+
* legitimately creates none. Both readings satisfy the same invariant the
|
|
240
|
+
* writer checks: one edge per requested relationship, or the write rolls back.
|
|
241
|
+
*/
|
|
226
242
|
edgesCreated: number;
|
|
227
243
|
}
|
|
228
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Properties a `mergeOn` write sets on creation only, so a re-import
|
|
247
|
+
* preserves the original provenance and creation time rather than restamping
|
|
248
|
+
* them with the second run's values. Mirrors the ON CREATE blocks in
|
|
249
|
+
* memory-archive-write.ts and the installer's LocalBusiness bootstrap.
|
|
250
|
+
*/
|
|
251
|
+
const MERGE_CREATE_ONLY_PROPS = new Set([
|
|
252
|
+
"createdAt",
|
|
253
|
+
"createdByAgent",
|
|
254
|
+
"createdBySession",
|
|
255
|
+
"createdByTool",
|
|
256
|
+
"createdBySource",
|
|
257
|
+
]);
|
|
258
|
+
|
|
229
259
|
/** Stamp flattened provenance into node properties. */
|
|
230
260
|
export function stampCreatedBy(
|
|
231
261
|
props: Record<string, unknown>,
|
|
@@ -316,6 +346,34 @@ export async function writeNodeWithEdges(
|
|
|
316
346
|
);
|
|
317
347
|
}
|
|
318
348
|
|
|
349
|
+
// Natural-key merge (Task 2132). Validated here so a malformed key throws
|
|
350
|
+
// before any query runs — a key naming a property the caller did not write
|
|
351
|
+
// would silently MERGE on `null` and collapse unrelated rows onto one node.
|
|
352
|
+
const mergeKeys = params.mergeOn;
|
|
353
|
+
if (mergeKeys !== undefined) {
|
|
354
|
+
if (mergeKeys.length === 0) {
|
|
355
|
+
throw new Error(
|
|
356
|
+
"mergeOn must name at least one property (accountId is always included and must not be named)."
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
for (const k of mergeKeys) {
|
|
360
|
+
if (!(k in props)) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`mergeOn property "${k}" is not present in props — the natural key must be written with the node.`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
// `k in props` is true for an explicit undefined, and Neo4j would then
|
|
366
|
+
// MERGE on null and collapse every keyless row onto one node — the exact
|
|
367
|
+
// failure this validation exists to prevent.
|
|
368
|
+
const v = (props as Record<string, unknown>)[k];
|
|
369
|
+
if (v === undefined || v === null || v === "") {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`mergeOn property "${k}" is empty — a natural key must carry a value, or the merge collapses unrelated rows onto one node.`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
319
377
|
// Embed safety net. When the node carries a vector-indexed label, lacks an
|
|
320
378
|
// embedding, and an embedder is injected, compute a bounded props embedding
|
|
321
379
|
// so no indexed node enters the graph invisible to vector search. A failed
|
|
@@ -410,10 +468,39 @@ export async function writeNodeWithEdges(
|
|
|
410
468
|
|
|
411
469
|
let nodeRes;
|
|
412
470
|
try {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
471
|
+
if (mergeKeys) {
|
|
472
|
+
// Natural-key MERGE. Creation-only properties sit under ON CREATE so a
|
|
473
|
+
// re-import preserves the original provenance and createdAt, matching
|
|
474
|
+
// PERSON_AND_CONNECTED_EDGE_CYPHER in memory-archive-write.ts and the
|
|
475
|
+
// installer's LocalBusiness bootstrap. Everything else refreshes.
|
|
476
|
+
const keyProps = ["accountId", ...mergeKeys];
|
|
477
|
+
const keyPattern = keyProps.map((k) => `${k}: $key_${k}`).join(", ");
|
|
478
|
+
const keyParams: Record<string, unknown> = {};
|
|
479
|
+
for (const k of keyProps) {
|
|
480
|
+
keyParams[`key_${k}`] = (nodeProps as Record<string, unknown>)[k];
|
|
481
|
+
}
|
|
482
|
+
const createProps: Record<string, unknown> = {};
|
|
483
|
+
const updateProps: Record<string, unknown> = {};
|
|
484
|
+
for (const [k, v] of Object.entries(nodeProps as Record<string, unknown>)) {
|
|
485
|
+
if (keyProps.includes(k)) continue;
|
|
486
|
+
(MERGE_CREATE_ONLY_PROPS.has(k) ? createProps : updateProps)[k] = v;
|
|
487
|
+
}
|
|
488
|
+
nodeRes = await tx.run(
|
|
489
|
+
`MERGE (n:${labelStr} {${keyPattern}}) ON CREATE SET n += $createProps SET n += $updateProps RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`,
|
|
490
|
+
{ ...keyParams, createProps, updateProps }
|
|
491
|
+
);
|
|
492
|
+
const outcome = nodeRes.summary?.counters.updates().nodesCreated
|
|
493
|
+
? "created"
|
|
494
|
+
: "matched";
|
|
495
|
+
process.stderr.write(
|
|
496
|
+
`[graph-write] op=merge label=${labelCsv} key=${keyProps.join(",")} outcome=${outcome}\n`
|
|
497
|
+
);
|
|
498
|
+
} else {
|
|
499
|
+
nodeRes = await tx.run(
|
|
500
|
+
`CREATE (n:${labelStr} $props) RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`,
|
|
501
|
+
{ props: nodeProps }
|
|
502
|
+
);
|
|
503
|
+
}
|
|
417
504
|
} catch (err) {
|
|
418
505
|
// surface UserProfile uniqueness violations as a structured
|
|
419
506
|
// reject line. Neo4j's ConstraintValidationFailed message names the
|
|
@@ -448,12 +535,32 @@ export async function writeNodeWithEdges(
|
|
|
448
535
|
let edgesCreated = 0;
|
|
449
536
|
for (const rel of relationships) {
|
|
450
537
|
const type = rel.type.replace(/`/g, "");
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
538
|
+
let created: number;
|
|
539
|
+
if (mergeKeys) {
|
|
540
|
+
// Under a natural-key merge the edges MERGE too: a node that matched
|
|
541
|
+
// must not accumulate a second copy of each relationship. A repeat
|
|
542
|
+
// legitimately creates zero relationships, so the unresolved-target
|
|
543
|
+
// guard below switches from the create counter to the returned row
|
|
544
|
+
// count — 0 rows means the MATCH failed to resolve an endpoint, the
|
|
545
|
+
// condition the guard was written for.
|
|
546
|
+
const dir =
|
|
547
|
+
rel.direction === "outgoing"
|
|
548
|
+
? `(a)-[r:\`${type}\`]->(b)`
|
|
549
|
+
: `(b)-[r:\`${type}\`]->(a)`;
|
|
550
|
+
const r = await tx.run(
|
|
551
|
+
`MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to MERGE ${dir} RETURN count(r) AS n`,
|
|
552
|
+
{ from: nodeId, to: rel.targetNodeId }
|
|
553
|
+
);
|
|
554
|
+
created =
|
|
555
|
+
r.records.length === 0 ? 0 : Number(r.records[0].get("n") ?? 0);
|
|
556
|
+
} else {
|
|
557
|
+
const q =
|
|
558
|
+
rel.direction === "outgoing"
|
|
559
|
+
? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)`
|
|
560
|
+
: `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
|
|
561
|
+
const r = await tx.run(q, { from: nodeId, to: rel.targetNodeId });
|
|
562
|
+
created = r.summary.counters.updates().relationshipsCreated;
|
|
563
|
+
}
|
|
457
564
|
if (created === 0) {
|
|
458
565
|
process.stderr.write(
|
|
459
566
|
`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}\n`
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2130 — the routine-roster seed.
|
|
3
|
+
*
|
|
4
|
+
* One module, two callers: `platform/scripts/seed-neo4j.sh` reaches it through
|
|
5
|
+
* `dist/seed-cli.js` on every install and upgrade (which is what covers the
|
|
6
|
+
* existing fleet), and the admin plugin's `account_create` imports it directly
|
|
7
|
+
* for a new client sub-account. Sharing the MERGE rather than writing it twice
|
|
8
|
+
* is the point: two copies drift, and the drift would only ever be noticed as
|
|
9
|
+
* "the sub-accounts have different routines from the house".
|
|
10
|
+
*
|
|
11
|
+
* The seed is inert by construction. Every row lands `eventStatus:'suspended'`
|
|
12
|
+
* with `nextRun` NULL, and all four selecting queries in `check-due-events.ts`
|
|
13
|
+
* admit only `'scheduled'`: the one-time dispatch (`:686-690`, `WHERE
|
|
14
|
+
* e.eventStatus = 'scheduled' AND e.recurrence IS NULL`), the recurring
|
|
15
|
+
* dispatch (`:739-744`), the lifecycle activate/heal probe (`:491`) and the
|
|
16
|
+
* lifecycle deactivation sweep (`:550-551`). NULL `nextRun` is the second,
|
|
17
|
+
* independent guard: the recurring query also requires `e.nextRun IS NOT NULL`
|
|
18
|
+
* (`:742`). Neither guard depends on the other holding.
|
|
19
|
+
*
|
|
20
|
+
* `nextRun` is left NULL rather than computed, and that is not tidiness. The
|
|
21
|
+
* recurring loop advances from the STORED value, not from now
|
|
22
|
+
* (`check-due-events.ts:778`, `currentDate: new Date(currentNextRun)`), so a
|
|
23
|
+
* row seeded with a computed `nextRun` and enabled a month later advances one
|
|
24
|
+
* occurrence per tick and dispatches on every one of them. A quarter-hourly
|
|
25
|
+
* routine would replay roughly 2,880 turns. The only safe value is no value,
|
|
26
|
+
* until an operator supplies a recurrence through `schedule-update`, which
|
|
27
|
+
* recomputes from now.
|
|
28
|
+
*
|
|
29
|
+
* Each line number above is paired with the construct it points at. Bare
|
|
30
|
+
* numbers drift on the next edit to that file and then send a reader somewhere
|
|
31
|
+
* unrelated, which is worse than no citation.
|
|
32
|
+
*/
|
|
33
|
+
import { type RoutineTemplate } from "./roster.js";
|
|
34
|
+
export { ROUTINE_ROSTER, ROUTINE_ROSTER_KEYS, ROUTINE_TEMPLATE_VERSION } from "./roster.js";
|
|
35
|
+
export type { RoutineTemplate } from "./roster.js";
|
|
36
|
+
/** The minimal Neo4j session surface the seed needs. Both the admin plugin's
|
|
37
|
+
* `getSession()` and a driver session from `seed-cli` satisfy it; the tests
|
|
38
|
+
* pass an in-memory fake. Deliberately not `neo4j-driver`'s `Session` type, so
|
|
39
|
+
* this module stays importable by a consumer that has no driver types. */
|
|
40
|
+
export interface SeedSessionLike {
|
|
41
|
+
run(query: string, params?: Record<string, unknown>): Promise<{
|
|
42
|
+
records: Array<{
|
|
43
|
+
get(key: string): unknown;
|
|
44
|
+
}>;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
/** What the seed did to one routine.
|
|
48
|
+
* - `create` — the row did not exist. On an already-seeded account this is the
|
|
49
|
+
* clobber signature: something deleted the row, or the MERGE key changed.
|
|
50
|
+
* - `refresh-version` — the stored template version was behind and no operator
|
|
51
|
+
* had edited the row, so the prompt and name were refreshed.
|
|
52
|
+
* - `skip-operator-edited` — a refresh was due and was withheld because the row
|
|
53
|
+
* carries `operatorEdited`. The operator's prompt wins, permanently.
|
|
54
|
+
* - `unchanged` — the row is already at this version. The steady state. */
|
|
55
|
+
export type RoutineMergeAction = "create" | "refresh-version" | "skip-operator-edited" | "unchanged";
|
|
56
|
+
export interface RoutineMergeRow {
|
|
57
|
+
key: string;
|
|
58
|
+
eventId: string;
|
|
59
|
+
action: RoutineMergeAction;
|
|
60
|
+
}
|
|
61
|
+
/** The post-condition, measured back out of the graph rather than counted from
|
|
62
|
+
* the statements issued. A seed that ran and wrote nothing must not be able to
|
|
63
|
+
* report success, which is exactly what counting its own writes would do.
|
|
64
|
+
*
|
|
65
|
+
* `expected === actual` is the health signal. `suspended` and `nextRunNull`
|
|
66
|
+
* equal `actual` on a fresh seed and legitimately fall below it once an
|
|
67
|
+
* operator enables a routine — they are measurements, not assertions. */
|
|
68
|
+
export interface RoutineVerifyCounts {
|
|
69
|
+
expected: number;
|
|
70
|
+
actual: number;
|
|
71
|
+
suspended: number;
|
|
72
|
+
nextRunNull: number;
|
|
73
|
+
}
|
|
74
|
+
export interface RoutineSeedReport {
|
|
75
|
+
accountId: string;
|
|
76
|
+
version: number;
|
|
77
|
+
timezone: string | null;
|
|
78
|
+
timezoneSource: "owner-profile" | "absent";
|
|
79
|
+
merges: RoutineMergeRow[];
|
|
80
|
+
created: number;
|
|
81
|
+
refreshed: number;
|
|
82
|
+
skippedOperatorEdited: number;
|
|
83
|
+
unchanged: number;
|
|
84
|
+
verify: RoutineVerifyCounts;
|
|
85
|
+
ranMs: number;
|
|
86
|
+
}
|
|
87
|
+
/** First eight characters of the accountId — the correlation key every
|
|
88
|
+
* `[routine-seed]` and `[schedule-audit]` line carries. */
|
|
89
|
+
export declare function acctTag(accountId: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* The owner's IANA timezone, read from the owner `:UserProfile` and accepted
|
|
92
|
+
* only when it is owner-set.
|
|
93
|
+
*
|
|
94
|
+
* A profile whose `timezoneSource` is anything else was backfilled from the
|
|
95
|
+
* box's own zone, and seeding that onto a routine would give the operator a
|
|
96
|
+
* routine that fires at the server's hour while looking entirely healthy. NULL
|
|
97
|
+
* is the honest value in that case, and the heartbeat's `op=null-timezone`
|
|
98
|
+
* count is what surfaces it — on occasion one, rather than at the first
|
|
99
|
+
* mis-timed fire.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveOwnerTimezone(session: SeedSessionLike, accountId: string): Promise<{
|
|
102
|
+
timezone: string | null;
|
|
103
|
+
source: "owner-profile" | "absent";
|
|
104
|
+
}>;
|
|
105
|
+
/** The MERGE. One statement over the whole roster.
|
|
106
|
+
*
|
|
107
|
+
* The pre-state is captured by an OPTIONAL MATCH before the MERGE, because the
|
|
108
|
+
* MERGE itself cannot report which branch it took. Without it the seed could
|
|
109
|
+
* only log what it intended; `action=create` on an already-seeded account
|
|
110
|
+
* would be indistinguishable from `action=unchanged`, and the clobber it
|
|
111
|
+
* signals would be invisible.
|
|
112
|
+
*
|
|
113
|
+
* ON MATCH writes nothing an operator controls: not `eventStatus`, not
|
|
114
|
+
* `agentChannel`, not `agentDestination`, not `recurrence`, not `timezone`. A
|
|
115
|
+
* re-seed on upgrade must not re-suspend an enabled routine, unbind a bound
|
|
116
|
+
* destination, or reset a retimed cron. Only the prompt, the name and the
|
|
117
|
+
* version move, and only when the row is behind and unedited. */
|
|
118
|
+
export declare const MERGE_CYPHER = "\nUNWIND $rows AS row\nOPTIONAL MATCH (pre:Event {accountId: $accountId, routineTemplate: row.key})\nWITH row,\n pre IS NOT NULL AS existed,\n coalesce(pre.routineTemplateVersion, 0) AS priorVersion,\n pre.operatorEdited IS NOT NULL AS edited\nWITH row, existed, priorVersion, edited,\n (existed AND NOT edited AND priorVersion < $version) AS refreshing\nMERGE (e:Event {accountId: $accountId, routineTemplate: row.key})\n ON CREATE SET e.eventId = randomUUID(),\n e.name = row.name,\n e.agentPrompt = row.prompt,\n e.recurrence = row.recurrence,\n e.routineTemplateVersion = $version,\n e.eventStatus = 'suspended',\n e.nextRun = null,\n e.agentChannel = null,\n e.agentDestination = null,\n e.actionPlugin = null,\n e.actionTool = null,\n e.actionArgs = null,\n e.sourcePlugin = null,\n e.timezone = $timezone,\n e.createdBySource = 'installer',\n e.createdByAgent = 'system',\n e.createdByTool = 'routine-roster-seed',\n e.createdAt = $now,\n e.updatedAt = $now\n ON MATCH SET e.agentPrompt = CASE WHEN refreshing THEN row.prompt ELSE e.agentPrompt END,\n e.name = CASE WHEN refreshing THEN row.name ELSE e.name END,\n e.routineTemplateVersion = CASE WHEN refreshing THEN $version ELSE e.routineTemplateVersion END,\n e.updatedAt = CASE WHEN refreshing THEN $now ELSE e.updatedAt END\nRETURN row.key AS key,\n e.eventId AS eventId,\n CASE WHEN NOT existed THEN 'create'\n WHEN priorVersion >= $version THEN 'unchanged'\n WHEN edited THEN 'skip-operator-edited'\n ELSE 'refresh-version' END AS action";
|
|
119
|
+
/** The post-condition read. Counts every roster row the account actually holds,
|
|
120
|
+
* independently of what the MERGE above believes it wrote. */
|
|
121
|
+
export declare const VERIFY_CYPHER = "\nMATCH (e:Event {accountId: $accountId})\nWHERE e.routineTemplate IS NOT NULL\nRETURN count(e) AS actual,\n sum(CASE WHEN e.eventStatus = 'suspended' THEN 1 ELSE 0 END) AS suspended,\n sum(CASE WHEN e.nextRun IS NULL THEN 1 ELSE 0 END) AS nextRunNull";
|
|
122
|
+
export interface SeedRoutineRosterArgs {
|
|
123
|
+
session: SeedSessionLike;
|
|
124
|
+
accountId: string;
|
|
125
|
+
/** Defaults to `ROUTINE_TEMPLATE_VERSION`. Injected by the tests. */
|
|
126
|
+
version?: number;
|
|
127
|
+
/** ISO timestamp stamped on created rows. Injected by the tests. */
|
|
128
|
+
now?: string;
|
|
129
|
+
/** Defaults to `ROUTINE_ROSTER`. Injected by the tests. */
|
|
130
|
+
roster?: readonly RoutineTemplate[];
|
|
131
|
+
/** Where the `[routine-seed]` lines go. Defaults to stderr. */
|
|
132
|
+
log?: (line: string) => void;
|
|
133
|
+
/** Elapsed milliseconds for `op=end`. Injected by the tests so the line is
|
|
134
|
+
* assertable; `Date.now()` either side of the work otherwise. */
|
|
135
|
+
clock?: () => number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Seed (or re-seed) the roster for one account and report what happened.
|
|
139
|
+
*
|
|
140
|
+
* Emits the full `[routine-seed]` lifecycle on every run, including a run that
|
|
141
|
+
* changes nothing: `op=begin`, `op=tz-resolved`, one `op=merge` per routine,
|
|
142
|
+
* `op=verify`, `op=end`. A no-op run that logged nothing would be
|
|
143
|
+
* indistinguishable from a seed that never ran, and "never ran" is the failure
|
|
144
|
+
* this logging exists to make visible.
|
|
145
|
+
*
|
|
146
|
+
* Throws on a Neo4j failure. Both callers catch: the installer path exits
|
|
147
|
+
* non-zero, and `account_create` reports a created-but-routine-less account
|
|
148
|
+
* with the recovery action rather than a generic create failure.
|
|
149
|
+
*/
|
|
150
|
+
export declare function seedRoutineRoster(args: SeedRoutineRosterArgs): Promise<RoutineSeedReport>;
|
|
151
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAA4C,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7F,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5F,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;2EAG2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClH;AAED;;;;;;;4EAO4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,iBAAiB,GAAG,sBAAsB,GAAG,WAAW,CAAC;AAErG,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;;;;0EAM0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,eAAe,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAYD;4DAC4D;AAC5D,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,QAAQ,CAAA;CAAE,CAAC,CAa1E;AAED;;;;;;;;;;;;kEAYkE;AAClE,eAAO,MAAM,YAAY,01DAsCwB,CAAC;AAElD;+DAC+D;AAC/D,eAAO,MAAM,aAAa,4QAK+C,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B;sEACkE;IAClE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyE/F"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VERIFY_CYPHER = exports.MERGE_CYPHER = exports.ROUTINE_TEMPLATE_VERSION = exports.ROUTINE_ROSTER_KEYS = exports.ROUTINE_ROSTER = void 0;
|
|
4
|
+
exports.acctTag = acctTag;
|
|
5
|
+
exports.resolveOwnerTimezone = resolveOwnerTimezone;
|
|
6
|
+
exports.seedRoutineRoster = seedRoutineRoster;
|
|
7
|
+
/**
|
|
8
|
+
* Task 2130 — the routine-roster seed.
|
|
9
|
+
*
|
|
10
|
+
* One module, two callers: `platform/scripts/seed-neo4j.sh` reaches it through
|
|
11
|
+
* `dist/seed-cli.js` on every install and upgrade (which is what covers the
|
|
12
|
+
* existing fleet), and the admin plugin's `account_create` imports it directly
|
|
13
|
+
* for a new client sub-account. Sharing the MERGE rather than writing it twice
|
|
14
|
+
* is the point: two copies drift, and the drift would only ever be noticed as
|
|
15
|
+
* "the sub-accounts have different routines from the house".
|
|
16
|
+
*
|
|
17
|
+
* The seed is inert by construction. Every row lands `eventStatus:'suspended'`
|
|
18
|
+
* with `nextRun` NULL, and all four selecting queries in `check-due-events.ts`
|
|
19
|
+
* admit only `'scheduled'`: the one-time dispatch (`:686-690`, `WHERE
|
|
20
|
+
* e.eventStatus = 'scheduled' AND e.recurrence IS NULL`), the recurring
|
|
21
|
+
* dispatch (`:739-744`), the lifecycle activate/heal probe (`:491`) and the
|
|
22
|
+
* lifecycle deactivation sweep (`:550-551`). NULL `nextRun` is the second,
|
|
23
|
+
* independent guard: the recurring query also requires `e.nextRun IS NOT NULL`
|
|
24
|
+
* (`:742`). Neither guard depends on the other holding.
|
|
25
|
+
*
|
|
26
|
+
* `nextRun` is left NULL rather than computed, and that is not tidiness. The
|
|
27
|
+
* recurring loop advances from the STORED value, not from now
|
|
28
|
+
* (`check-due-events.ts:778`, `currentDate: new Date(currentNextRun)`), so a
|
|
29
|
+
* row seeded with a computed `nextRun` and enabled a month later advances one
|
|
30
|
+
* occurrence per tick and dispatches on every one of them. A quarter-hourly
|
|
31
|
+
* routine would replay roughly 2,880 turns. The only safe value is no value,
|
|
32
|
+
* until an operator supplies a recurrence through `schedule-update`, which
|
|
33
|
+
* recomputes from now.
|
|
34
|
+
*
|
|
35
|
+
* Each line number above is paired with the construct it points at. Bare
|
|
36
|
+
* numbers drift on the next edit to that file and then send a reader somewhere
|
|
37
|
+
* unrelated, which is worse than no citation.
|
|
38
|
+
*/
|
|
39
|
+
const roster_js_1 = require("./roster.js");
|
|
40
|
+
var roster_js_2 = require("./roster.js");
|
|
41
|
+
Object.defineProperty(exports, "ROUTINE_ROSTER", { enumerable: true, get: function () { return roster_js_2.ROUTINE_ROSTER; } });
|
|
42
|
+
Object.defineProperty(exports, "ROUTINE_ROSTER_KEYS", { enumerable: true, get: function () { return roster_js_2.ROUTINE_ROSTER_KEYS; } });
|
|
43
|
+
Object.defineProperty(exports, "ROUTINE_TEMPLATE_VERSION", { enumerable: true, get: function () { return roster_js_2.ROUTINE_TEMPLATE_VERSION; } });
|
|
44
|
+
/** Neo4j returns integers as its own `Integer` type over bolt and as plain
|
|
45
|
+
* numbers through a test fake. Normalise both without importing the driver. */
|
|
46
|
+
function toInt(raw) {
|
|
47
|
+
if (typeof raw === "number")
|
|
48
|
+
return raw;
|
|
49
|
+
if (raw && typeof raw.toNumber === "function") {
|
|
50
|
+
return raw.toNumber();
|
|
51
|
+
}
|
|
52
|
+
return Number(raw ?? 0);
|
|
53
|
+
}
|
|
54
|
+
/** First eight characters of the accountId — the correlation key every
|
|
55
|
+
* `[routine-seed]` and `[schedule-audit]` line carries. */
|
|
56
|
+
function acctTag(accountId) {
|
|
57
|
+
return accountId.slice(0, 8);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The owner's IANA timezone, read from the owner `:UserProfile` and accepted
|
|
61
|
+
* only when it is owner-set.
|
|
62
|
+
*
|
|
63
|
+
* A profile whose `timezoneSource` is anything else was backfilled from the
|
|
64
|
+
* box's own zone, and seeding that onto a routine would give the operator a
|
|
65
|
+
* routine that fires at the server's hour while looking entirely healthy. NULL
|
|
66
|
+
* is the honest value in that case, and the heartbeat's `op=null-timezone`
|
|
67
|
+
* count is what surfaces it — on occasion one, rather than at the first
|
|
68
|
+
* mis-timed fire.
|
|
69
|
+
*/
|
|
70
|
+
async function resolveOwnerTimezone(session, accountId) {
|
|
71
|
+
const res = await session.run(`MATCH (up:UserProfile {accountId: $accountId})
|
|
72
|
+
WHERE up.timezoneSource = 'owner-set' AND up.timezone IS NOT NULL AND trim(up.timezone) <> ''
|
|
73
|
+
RETURN up.timezone AS timezone
|
|
74
|
+
LIMIT 1`, { accountId });
|
|
75
|
+
const raw = res.records[0]?.get("timezone");
|
|
76
|
+
if (typeof raw === "string" && raw.trim() !== "") {
|
|
77
|
+
return { timezone: raw.trim(), source: "owner-profile" };
|
|
78
|
+
}
|
|
79
|
+
return { timezone: null, source: "absent" };
|
|
80
|
+
}
|
|
81
|
+
/** The MERGE. One statement over the whole roster.
|
|
82
|
+
*
|
|
83
|
+
* The pre-state is captured by an OPTIONAL MATCH before the MERGE, because the
|
|
84
|
+
* MERGE itself cannot report which branch it took. Without it the seed could
|
|
85
|
+
* only log what it intended; `action=create` on an already-seeded account
|
|
86
|
+
* would be indistinguishable from `action=unchanged`, and the clobber it
|
|
87
|
+
* signals would be invisible.
|
|
88
|
+
*
|
|
89
|
+
* ON MATCH writes nothing an operator controls: not `eventStatus`, not
|
|
90
|
+
* `agentChannel`, not `agentDestination`, not `recurrence`, not `timezone`. A
|
|
91
|
+
* re-seed on upgrade must not re-suspend an enabled routine, unbind a bound
|
|
92
|
+
* destination, or reset a retimed cron. Only the prompt, the name and the
|
|
93
|
+
* version move, and only when the row is behind and unedited. */
|
|
94
|
+
exports.MERGE_CYPHER = `
|
|
95
|
+
UNWIND $rows AS row
|
|
96
|
+
OPTIONAL MATCH (pre:Event {accountId: $accountId, routineTemplate: row.key})
|
|
97
|
+
WITH row,
|
|
98
|
+
pre IS NOT NULL AS existed,
|
|
99
|
+
coalesce(pre.routineTemplateVersion, 0) AS priorVersion,
|
|
100
|
+
pre.operatorEdited IS NOT NULL AS edited
|
|
101
|
+
WITH row, existed, priorVersion, edited,
|
|
102
|
+
(existed AND NOT edited AND priorVersion < $version) AS refreshing
|
|
103
|
+
MERGE (e:Event {accountId: $accountId, routineTemplate: row.key})
|
|
104
|
+
ON CREATE SET e.eventId = randomUUID(),
|
|
105
|
+
e.name = row.name,
|
|
106
|
+
e.agentPrompt = row.prompt,
|
|
107
|
+
e.recurrence = row.recurrence,
|
|
108
|
+
e.routineTemplateVersion = $version,
|
|
109
|
+
e.eventStatus = 'suspended',
|
|
110
|
+
e.nextRun = null,
|
|
111
|
+
e.agentChannel = null,
|
|
112
|
+
e.agentDestination = null,
|
|
113
|
+
e.actionPlugin = null,
|
|
114
|
+
e.actionTool = null,
|
|
115
|
+
e.actionArgs = null,
|
|
116
|
+
e.sourcePlugin = null,
|
|
117
|
+
e.timezone = $timezone,
|
|
118
|
+
e.createdBySource = 'installer',
|
|
119
|
+
e.createdByAgent = 'system',
|
|
120
|
+
e.createdByTool = 'routine-roster-seed',
|
|
121
|
+
e.createdAt = $now,
|
|
122
|
+
e.updatedAt = $now
|
|
123
|
+
ON MATCH SET e.agentPrompt = CASE WHEN refreshing THEN row.prompt ELSE e.agentPrompt END,
|
|
124
|
+
e.name = CASE WHEN refreshing THEN row.name ELSE e.name END,
|
|
125
|
+
e.routineTemplateVersion = CASE WHEN refreshing THEN $version ELSE e.routineTemplateVersion END,
|
|
126
|
+
e.updatedAt = CASE WHEN refreshing THEN $now ELSE e.updatedAt END
|
|
127
|
+
RETURN row.key AS key,
|
|
128
|
+
e.eventId AS eventId,
|
|
129
|
+
CASE WHEN NOT existed THEN 'create'
|
|
130
|
+
WHEN priorVersion >= $version THEN 'unchanged'
|
|
131
|
+
WHEN edited THEN 'skip-operator-edited'
|
|
132
|
+
ELSE 'refresh-version' END AS action`;
|
|
133
|
+
/** The post-condition read. Counts every roster row the account actually holds,
|
|
134
|
+
* independently of what the MERGE above believes it wrote. */
|
|
135
|
+
exports.VERIFY_CYPHER = `
|
|
136
|
+
MATCH (e:Event {accountId: $accountId})
|
|
137
|
+
WHERE e.routineTemplate IS NOT NULL
|
|
138
|
+
RETURN count(e) AS actual,
|
|
139
|
+
sum(CASE WHEN e.eventStatus = 'suspended' THEN 1 ELSE 0 END) AS suspended,
|
|
140
|
+
sum(CASE WHEN e.nextRun IS NULL THEN 1 ELSE 0 END) AS nextRunNull`;
|
|
141
|
+
/**
|
|
142
|
+
* Seed (or re-seed) the roster for one account and report what happened.
|
|
143
|
+
*
|
|
144
|
+
* Emits the full `[routine-seed]` lifecycle on every run, including a run that
|
|
145
|
+
* changes nothing: `op=begin`, `op=tz-resolved`, one `op=merge` per routine,
|
|
146
|
+
* `op=verify`, `op=end`. A no-op run that logged nothing would be
|
|
147
|
+
* indistinguishable from a seed that never ran, and "never ran" is the failure
|
|
148
|
+
* this logging exists to make visible.
|
|
149
|
+
*
|
|
150
|
+
* Throws on a Neo4j failure. Both callers catch: the installer path exits
|
|
151
|
+
* non-zero, and `account_create` reports a created-but-routine-less account
|
|
152
|
+
* with the recovery action rather than a generic create failure.
|
|
153
|
+
*/
|
|
154
|
+
async function seedRoutineRoster(args) {
|
|
155
|
+
const { session, accountId, version = roster_js_1.ROUTINE_TEMPLATE_VERSION, now = new Date().toISOString(), roster = roster_js_1.ROUTINE_ROSTER, log = (line) => process.stderr.write(`${line}\n`), clock = () => Date.now(), } = args;
|
|
156
|
+
const acct = acctTag(accountId);
|
|
157
|
+
const startedAt = clock();
|
|
158
|
+
log(`[routine-seed] op=begin acct=${acct} roster=${roster.length} version=${version}`);
|
|
159
|
+
const { timezone, source } = await resolveOwnerTimezone(session, accountId);
|
|
160
|
+
log(`[routine-seed] op=tz-resolved acct=${acct} tz=${timezone ?? "null"} source=${source}`);
|
|
161
|
+
const mergeRes = await session.run(exports.MERGE_CYPHER, {
|
|
162
|
+
accountId,
|
|
163
|
+
version,
|
|
164
|
+
timezone,
|
|
165
|
+
now,
|
|
166
|
+
rows: roster.map((r) => ({ key: r.key, name: r.name, prompt: r.prompt, recurrence: r.recurrence })),
|
|
167
|
+
});
|
|
168
|
+
const merges = mergeRes.records.map((rec) => ({
|
|
169
|
+
key: String(rec.get("key")),
|
|
170
|
+
eventId: String(rec.get("eventId")),
|
|
171
|
+
action: String(rec.get("action")),
|
|
172
|
+
}));
|
|
173
|
+
for (const m of merges) {
|
|
174
|
+
log(`[routine-seed] op=merge acct=${acct} key=${m.key} action=${m.action} eventId=${m.eventId}`);
|
|
175
|
+
}
|
|
176
|
+
const verifyRes = await session.run(exports.VERIFY_CYPHER, { accountId });
|
|
177
|
+
const verifyRec = verifyRes.records[0];
|
|
178
|
+
const verify = {
|
|
179
|
+
expected: roster.length,
|
|
180
|
+
actual: toInt(verifyRec?.get("actual")),
|
|
181
|
+
suspended: toInt(verifyRec?.get("suspended")),
|
|
182
|
+
nextRunNull: toInt(verifyRec?.get("nextRunNull")),
|
|
183
|
+
};
|
|
184
|
+
log(`[routine-seed] op=verify acct=${acct} expected=${verify.expected} actual=${verify.actual} ` +
|
|
185
|
+
`suspended=${verify.suspended} nextRunNull=${verify.nextRunNull}`);
|
|
186
|
+
const created = merges.filter((m) => m.action === "create").length;
|
|
187
|
+
const refreshed = merges.filter((m) => m.action === "refresh-version").length;
|
|
188
|
+
const skippedOperatorEdited = merges.filter((m) => m.action === "skip-operator-edited").length;
|
|
189
|
+
const unchanged = merges.filter((m) => m.action === "unchanged").length;
|
|
190
|
+
const ranMs = clock() - startedAt;
|
|
191
|
+
log(`[routine-seed] op=end acct=${acct} ranMs=${ranMs} created=${created} ` +
|
|
192
|
+
`skipped=${skippedOperatorEdited} refreshed=${refreshed} unchanged=${unchanged}`);
|
|
193
|
+
return {
|
|
194
|
+
accountId,
|
|
195
|
+
version,
|
|
196
|
+
timezone,
|
|
197
|
+
timezoneSource: source,
|
|
198
|
+
merges,
|
|
199
|
+
created,
|
|
200
|
+
refreshed,
|
|
201
|
+
skippedOperatorEdited,
|
|
202
|
+
unchanged,
|
|
203
|
+
verify,
|
|
204
|
+
ranMs,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2130 — the shipped routine roster.
|
|
3
|
+
*
|
|
4
|
+
* A fresh install used to ship zero routines, so the only working examples
|
|
5
|
+
* lived in one customer's graph and every operator wrote the prompts from
|
|
6
|
+
* scratch. These eleven are the catalogue that ships with the platform instead.
|
|
7
|
+
*
|
|
8
|
+
* Two properties make a routine shippable, and both are load-bearing:
|
|
9
|
+
*
|
|
10
|
+
* 1. It resolves its own surfaces at run time. Every prompt opens on
|
|
11
|
+
* `capabilities-here`, which reports the account's live install set, and
|
|
12
|
+
* ends the turn without replying when the surface it needs is absent. That
|
|
13
|
+
* is what makes the same eleven rows correct on an estate agency, a web studio
|
|
14
|
+
* and a field-service install rather than a template someone must edit.
|
|
15
|
+
*
|
|
16
|
+
* 2. It names nobody. No prompt carries a person's name, an email address, a
|
|
17
|
+
* phone number, a hostname or a database name. `scripts/check-routine-
|
|
18
|
+
* templates-generic.sh` is the standing control for that; a review pass is
|
|
19
|
+
* not, because the failure is silent and the routine still works.
|
|
20
|
+
*
|
|
21
|
+
* The `recurrence` on each row is a PROPOSAL, not a schedule. Seeded rows carry
|
|
22
|
+
* `eventStatus:'suspended'` and `nextRun:NULL`, so nothing here fires until an
|
|
23
|
+
* operator enables it and supplies the cron and zone they actually want (see
|
|
24
|
+
* `.docs/routine-roster.md` for why the enable path takes all four arguments).
|
|
25
|
+
*
|
|
26
|
+
* Every tool name cited in a prompt exists in
|
|
27
|
+
* `platform/services/claude-session-manager/src/canonical-tool-names.generated.ts`.
|
|
28
|
+
* The roster test asserts that, so a renamed tool breaks the build rather than
|
|
29
|
+
* shipping a prompt that reaches for a tool no install serves.
|
|
30
|
+
*/
|
|
31
|
+
/** Bumped whenever any prompt in the roster changes. The seed refreshes a
|
|
32
|
+
* routine's prompt only when the stored `routineTemplateVersion` is BELOW this
|
|
33
|
+
* and no operator has edited the row, so an unbumped edit ships to new installs
|
|
34
|
+
* and never reaches existing ones — bump it in the same commit as the edit. */
|
|
35
|
+
export declare const ROUTINE_TEMPLATE_VERSION = 1;
|
|
36
|
+
export interface RoutineTemplate {
|
|
37
|
+
/** Stable identity. Half of the MERGE key `(accountId, routineTemplate)`, so
|
|
38
|
+
* it is never renamed: a renamed key seeds a duplicate row and orphans the
|
|
39
|
+
* original, which the operator then has to delete by hand. */
|
|
40
|
+
readonly key: string;
|
|
41
|
+
/** The title shown on `/routines`. */
|
|
42
|
+
readonly name: string;
|
|
43
|
+
/** The proposed cron. Written ON CREATE only, never refreshed, so an operator
|
|
44
|
+
* who retimes a routine keeps their expression across upgrades. */
|
|
45
|
+
readonly recurrence: string;
|
|
46
|
+
/** The injected turn. Refreshed on a version bump when the row is unedited. */
|
|
47
|
+
readonly prompt: string;
|
|
48
|
+
}
|
|
49
|
+
/** The eleven. Order is the display order on `/routines` before the operator
|
|
50
|
+
* retimes anything: the daily rhythm first, then the reconciles, then the
|
|
51
|
+
* weekly, then the standing check. */
|
|
52
|
+
export declare const ROUTINE_ROSTER: readonly RoutineTemplate[];
|
|
53
|
+
/** Every roster key, in roster order. The scheduling heartbeat's `op=roster`
|
|
54
|
+
* drift check reconciles this expected set against what is actually in the
|
|
55
|
+
* graph, so it is exported rather than re-derived there. */
|
|
56
|
+
export declare const ROUTINE_ROSTER_KEYS: readonly string[];
|
|
57
|
+
//# sourceMappingURL=roster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster.d.ts","sourceRoot":"","sources":["../src/roster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;gFAGgF;AAChF,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B;;mEAE+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;wEACoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAwID;;uCAEuC;AACvC,eAAO,MAAM,cAAc,EAAE,SAAS,eAAe,EAYpD,CAAC;AAEF;;6DAE6D;AAC7D,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAqC,CAAC"}
|