@rubytech/create-maxy-code 0.1.572 → 0.1.574
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/config/brand.json +2 -2
- package/payload/platform/lib/admins-write/__tests__/index.test.ts +97 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts +12 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/admins-write/dist/index.js +27 -5
- package/payload/platform/lib/admins-write/dist/index.js.map +1 -1
- package/payload/platform/lib/admins-write/src/index.ts +35 -5
- package/payload/platform/lib/admins-write/vitest.config.ts +14 -0
- package/payload/platform/lib/dispatch-read/dist/session.d.ts.map +1 -1
- package/payload/platform/lib/dispatch-read/dist/session.js +19 -4
- package/payload/platform/lib/dispatch-read/dist/session.js.map +1 -1
- package/payload/platform/lib/dispatch-read/src/__tests__/session.test.ts +91 -0
- package/payload/platform/lib/dispatch-read/src/session.ts +25 -6
- package/payload/platform/lib/routine-templates/dist/index.d.ts +2 -2
- package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/routine-templates/dist/index.js +9 -1
- package/payload/platform/lib/routine-templates/dist/index.js.map +1 -1
- package/payload/platform/lib/routine-templates/src/index.ts +14 -2
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +8 -3
- package/payload/platform/plugins/admin/hooks/__tests__/html-structure-gate.test.sh +264 -0
- package/payload/platform/plugins/admin/hooks/__tests__/quote-render-pdf-conformance.test.sh +122 -24
- package/payload/platform/plugins/admin/hooks/__tests__/specialist-dispatch-gate.test.sh +158 -0
- package/payload/platform/plugins/admin/hooks/html-structure-gate.sh +165 -0
- package/payload/platform/plugins/admin/hooks/quote-render-pdf-conformance.sh +111 -19
- package/payload/platform/plugins/admin/hooks/specialist-dispatch-gate.sh +159 -0
- package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.d.ts +27 -0
- package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.js +29 -0
- package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/binding-validate.d.ts +11 -0
- package/payload/platform/plugins/admin/mcp/dist/binding-validate.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/binding-validate.js +20 -0
- package/payload/platform/plugins/admin/mcp/dist/binding-validate.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +137 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +32 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +192 -90
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +18 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +71 -31
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +50 -57
- package/payload/platform/plugins/docs/references/internals.md +6 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +31 -1
- package/payload/platform/plugins/docs/references/telegram-guide.md +23 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +10 -0
- package/payload/platform/plugins/email/PLUGIN.md +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js +36 -5
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +10 -5
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +12 -6
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +18 -8
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js +41 -10
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +12 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +3 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.js +177 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.js +130 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.js +162 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.js +168 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.d.ts +64 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.js +110 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.d.ts +119 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.js +0 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.d.ts +8 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.js +247 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js +10 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +21 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts +16 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js +5 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +4 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +2 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +24 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +2 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js +29 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js.map +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +4 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.js +21 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.d.ts +13 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.js +10 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +54 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +78 -5
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +133 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +11 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +30 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.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 +8 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +37 -6
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
- package/payload/platform/plugins/telegram/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/telegram/PLUGIN.md +14 -2
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-census.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-census.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-census.test.js +77 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-census.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-guard.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-guard.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-guard.test.js +49 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-guard.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-methods.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-methods.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-methods.test.js +25 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-methods.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-tool.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-tool.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-tool.test.js +115 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/api-tool.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/bot-api.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/bot-api.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/bot-api.test.js +83 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/bot-api.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js +42 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.d.ts +32 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.js +59 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-census.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-guard.d.ts +18 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-guard.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-guard.js +35 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-guard.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-methods.d.ts +38 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-methods.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-methods.js +21 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/api-methods.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/bot-api.d.ts +24 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/bot-api.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/bot-api.js +57 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/bot-api.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api-census.d.ts +16 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api-census.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api-census.js +34 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api-census.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api.d.ts +26 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api.js +80 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/api.js.map +1 -0
- package/payload/platform/plugins/telegram/skills/build/SKILL.md +110 -0
- package/payload/platform/plugins/telegram/skills/build/references/methods.md +219 -0
- package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
- package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.d.ts +32 -0
- package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.js +144 -0
- package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/html-structure-census.d.ts +44 -0
- package/payload/platform/services/claude-session-manager/dist/html-structure-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/html-structure-census.js +185 -0
- package/payload/platform/services/claude-session-manager/dist/html-structure-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +32 -5
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +125 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +12 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +13 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/quote-print-census.d.ts +42 -0
- package/payload/platform/services/claude-session-manager/dist/quote-print-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/quote-print-census.js +254 -0
- package/payload/platform/services/claude-session-manager/dist/quote-print-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-census.d.ts +92 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-census.js +227 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-census.js.map +1 -0
- package/payload/platform/services/telegram-channel/dist/jsonl.d.ts +8 -0
- package/payload/platform/services/telegram-channel/dist/jsonl.d.ts.map +1 -0
- package/payload/platform/services/telegram-channel/dist/jsonl.js +42 -0
- package/payload/platform/services/telegram-channel/dist/jsonl.js.map +1 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +0 -31
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +4 -33
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.d.ts +0 -5
- package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.js +7 -101
- package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +0 -66
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +2 -64
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts +0 -12
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +15 -268
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/templates/account-settings.json +8 -4
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/agents.html +5 -5
- package/payload/server/public/assets/{AdminLoginScreens-CMdj1W5p.js → AdminLoginScreens-Dp6k1XgD.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-Dp6k1XgD.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-Dp6k1XgD.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-CZcaW3BQ.js → AdminShell-D-wB90c3.js} +1 -1
- package/payload/server/public/assets/AdminShell-D-wB90c3.js.br +0 -0
- package/payload/server/public/assets/AdminShell-D-wB90c3.js.gz +0 -0
- package/payload/server/public/assets/{activity-CAhibgb-.js → activity-BJxhblz-.js} +1 -1
- package/payload/server/public/assets/activity-BJxhblz-.js.br +0 -0
- package/payload/server/public/assets/activity-BJxhblz-.js.gz +0 -0
- package/payload/server/public/assets/{admin-xEXv3w9e.js → admin-DzEi8shs.js} +1 -1
- package/payload/server/public/assets/admin-DzEi8shs.js.br +0 -0
- package/payload/server/public/assets/admin-DzEi8shs.js.gz +0 -0
- package/payload/server/public/assets/{agents-CiAmzTaT.js → agents-DTL8kjfD.js} +1 -1
- package/payload/server/public/assets/agents-DTL8kjfD.js.br +0 -0
- package/payload/server/public/assets/agents-DTL8kjfD.js.gz +0 -0
- package/payload/server/public/assets/{browser-D36BiYhS.js → browser-UZp75zhx.js} +1 -1
- package/payload/server/public/assets/browser-UZp75zhx.js.br +0 -0
- package/payload/server/public/assets/browser-UZp75zhx.js.gz +0 -0
- package/payload/server/public/assets/{calendar-CJAM-iXn.js → calendar-Dfo0O2K_.js} +1 -1
- package/payload/server/public/assets/calendar-Dfo0O2K_.js.br +0 -0
- package/payload/server/public/assets/calendar-Dfo0O2K_.js.gz +0 -0
- package/payload/server/public/assets/chat-ByJAvEWZ.js +1 -0
- package/payload/server/public/assets/chat-ByJAvEWZ.js.br +0 -0
- package/payload/server/public/assets/chat-ByJAvEWZ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-iG7sYNm7.js +1 -0
- package/payload/server/public/assets/chevron-left-iG7sYNm7.js.br +0 -0
- package/payload/server/public/assets/clock-xSs0UF10.js +1 -0
- package/payload/server/public/assets/clock-xSs0UF10.js.br +0 -0
- package/payload/server/public/assets/clock-xSs0UF10.js.gz +0 -0
- package/payload/server/public/assets/{copy-axBe92iY.js → copy-CGI2jioK.js} +1 -1
- package/payload/server/public/assets/copy-CGI2jioK.js.br +0 -0
- package/payload/server/public/assets/copy-CGI2jioK.js.gz +0 -0
- package/payload/server/public/assets/data-dvwK4BcP.js +1 -0
- package/payload/server/public/assets/data-dvwK4BcP.js.br +0 -0
- package/payload/server/public/assets/data-dvwK4BcP.js.gz +0 -0
- package/payload/server/public/assets/{dispatch-D_i2ko7K.js → dispatch-Dx9-ty1K.js} +1 -1
- package/payload/server/public/assets/dispatch-Dx9-ty1K.js.br +0 -0
- package/payload/server/public/assets/dispatch-Dx9-ty1K.js.gz +0 -0
- package/payload/server/public/assets/{field-DFgkqq1P.js → field-D-P6CsWA.js} +1 -1
- package/payload/server/public/assets/field-D-P6CsWA.js.br +0 -0
- package/payload/server/public/assets/field-D-P6CsWA.js.gz +0 -0
- package/payload/server/public/assets/{file-text-CEFoq0Je.js → file-text-RWintp9q.js} +1 -1
- package/payload/server/public/assets/file-text-RWintp9q.js.br +0 -0
- package/payload/server/public/assets/file-text-RWintp9q.js.gz +0 -0
- package/payload/server/public/assets/{graph-BrpbilNr.js → graph-CpBlfYrJ.js} +1 -1
- package/payload/server/public/assets/graph-CpBlfYrJ.js.br +0 -0
- package/payload/server/public/assets/graph-CpBlfYrJ.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-BtmcWzo7.js → graph-labels-BOMyPppX.js} +1 -1
- package/payload/server/public/assets/graph-labels-BOMyPppX.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BOMyPppX.js.gz +0 -0
- package/payload/server/public/assets/jsx-runtime-DxsQ5Y-z.css +1 -0
- package/payload/server/public/assets/jsx-runtime-DxsQ5Y-z.css.br +0 -0
- package/payload/server/public/assets/jsx-runtime-DxsQ5Y-z.css.gz +0 -0
- package/payload/server/public/assets/{operator-Dl7dJt1G.js → operator-DFgW6GiH.js} +1 -1
- package/payload/server/public/assets/operator-DFgW6GiH.js.br +0 -0
- package/payload/server/public/assets/operator-DFgW6GiH.js.gz +0 -0
- package/payload/server/public/assets/{page-nx31Ij0T.js → page-BRShHSH0.js} +1 -1
- package/payload/server/public/assets/page-BRShHSH0.js.br +0 -0
- package/payload/server/public/assets/page-BRShHSH0.js.gz +0 -0
- package/payload/server/public/assets/{page-zC66xzNT.js → page-BbBCu9iP.js} +5 -5
- package/payload/server/public/assets/page-BbBCu9iP.js.br +0 -0
- package/payload/server/public/assets/page-BbBCu9iP.js.gz +0 -0
- package/payload/server/public/assets/play-BJxUaf6-.js +1 -0
- package/payload/server/public/assets/play-BJxUaf6-.js.br +0 -0
- package/payload/server/public/assets/play-BJxUaf6-.js.gz +0 -0
- package/payload/server/public/assets/{public-DEKnH5Q0.js → public-DkObqkzL.js} +1 -1
- package/payload/server/public/assets/public-DkObqkzL.js.br +0 -0
- package/payload/server/public/assets/public-DkObqkzL.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-CBex8gon.js → rotate-ccw-DgMO69nh.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-DgMO69nh.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-DgMO69nh.js.gz +0 -0
- package/payload/server/public/assets/routines-D9kvg_Xu.js +2 -0
- package/payload/server/public/assets/routines-D9kvg_Xu.js.br +0 -0
- package/payload/server/public/assets/routines-D9kvg_Xu.js.gz +0 -0
- package/payload/server/public/assets/{skills-BmxjPOmq.js → skills-l5I9HZ3D.js} +1 -1
- package/payload/server/public/assets/skills-l5I9HZ3D.js.br +0 -0
- package/payload/server/public/assets/skills-l5I9HZ3D.js.gz +0 -0
- package/payload/server/public/assets/{tasks-BTFYO9vT.js → tasks-C2qJMv6i.js} +1 -1
- package/payload/server/public/assets/tasks-C2qJMv6i.js.br +0 -0
- package/payload/server/public/assets/tasks-C2qJMv6i.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-px1YRJJ7.js → triangle-alert-BnC67uSs.js} +1 -1
- package/payload/server/public/assets/triangle-alert-BnC67uSs.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BnC67uSs.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-nMXwRcDX.js → useCopyFeedback-1RmgkTFh.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-1RmgkTFh.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-1RmgkTFh.js.gz +0 -0
- package/payload/server/public/assets/{useMediaQuery-DjbOTk2X.js → useMediaQuery-BQslddHQ.js} +1 -1
- package/payload/server/public/assets/useMediaQuery-BQslddHQ.js.br +0 -0
- package/payload/server/public/assets/useMediaQuery-BQslddHQ.js.gz +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-Cx7AOmib.js → useVoiceRecorder-BeZNQEC4.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-BeZNQEC4.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BeZNQEC4.js.gz +0 -0
- package/payload/server/public/assets/{wrench-Cexvlx6J.js → wrench-D4ufQG6C.js} +1 -1
- package/payload/server/public/assets/wrench-D4ufQG6C.js.br +0 -0
- package/payload/server/public/assets/wrench-D4ufQG6C.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/dispatch.html +5 -5
- 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 +1088 -694
- package/payload/server/public/assets/AdminLoginScreens-CMdj1W5p.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-CMdj1W5p.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-CZcaW3BQ.js.br +0 -0
- package/payload/server/public/assets/AdminShell-CZcaW3BQ.js.gz +0 -0
- package/payload/server/public/assets/activity-CAhibgb-.js.br +0 -0
- package/payload/server/public/assets/activity-CAhibgb-.js.gz +0 -0
- package/payload/server/public/assets/admin-xEXv3w9e.js.br +0 -0
- package/payload/server/public/assets/admin-xEXv3w9e.js.gz +0 -0
- package/payload/server/public/assets/agents-CiAmzTaT.js.br +0 -0
- package/payload/server/public/assets/agents-CiAmzTaT.js.gz +0 -0
- package/payload/server/public/assets/browser-D36BiYhS.js.br +0 -0
- package/payload/server/public/assets/browser-D36BiYhS.js.gz +0 -0
- package/payload/server/public/assets/calendar-CJAM-iXn.js.br +0 -0
- package/payload/server/public/assets/calendar-CJAM-iXn.js.gz +0 -0
- package/payload/server/public/assets/chat-DhZSvtZK.js +0 -1
- package/payload/server/public/assets/chat-DhZSvtZK.js.br +0 -0
- package/payload/server/public/assets/chat-DhZSvtZK.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-CXEcNlc3.js +0 -1
- package/payload/server/public/assets/chevron-left-CXEcNlc3.js.br +0 -0
- package/payload/server/public/assets/clock-DdxIJwTr.js +0 -1
- package/payload/server/public/assets/clock-DdxIJwTr.js.br +0 -0
- package/payload/server/public/assets/clock-DdxIJwTr.js.gz +0 -0
- package/payload/server/public/assets/copy-axBe92iY.js.br +0 -0
- package/payload/server/public/assets/copy-axBe92iY.js.gz +0 -0
- package/payload/server/public/assets/data-DUvkI-zq.js +0 -1
- package/payload/server/public/assets/data-DUvkI-zq.js.br +0 -0
- package/payload/server/public/assets/data-DUvkI-zq.js.gz +0 -0
- package/payload/server/public/assets/dispatch-D_i2ko7K.js.br +0 -0
- package/payload/server/public/assets/dispatch-D_i2ko7K.js.gz +0 -0
- package/payload/server/public/assets/field-DFgkqq1P.js.br +0 -0
- package/payload/server/public/assets/field-DFgkqq1P.js.gz +0 -0
- package/payload/server/public/assets/file-text-CEFoq0Je.js.br +0 -0
- package/payload/server/public/assets/file-text-CEFoq0Je.js.gz +0 -0
- package/payload/server/public/assets/graph-BrpbilNr.js.br +0 -0
- package/payload/server/public/assets/graph-BrpbilNr.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BtmcWzo7.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BtmcWzo7.js.gz +0 -0
- package/payload/server/public/assets/jsx-runtime-XuYKjgVb.css +0 -1
- package/payload/server/public/assets/jsx-runtime-XuYKjgVb.css.br +0 -0
- package/payload/server/public/assets/jsx-runtime-XuYKjgVb.css.gz +0 -0
- package/payload/server/public/assets/operator-Dl7dJt1G.js.br +0 -3
- package/payload/server/public/assets/operator-Dl7dJt1G.js.gz +0 -0
- package/payload/server/public/assets/page-nx31Ij0T.js.br +0 -0
- package/payload/server/public/assets/page-nx31Ij0T.js.gz +0 -0
- package/payload/server/public/assets/page-zC66xzNT.js.br +0 -0
- package/payload/server/public/assets/page-zC66xzNT.js.gz +0 -0
- package/payload/server/public/assets/play-MbU8O2iC.js +0 -1
- package/payload/server/public/assets/play-MbU8O2iC.js.br +0 -0
- package/payload/server/public/assets/play-MbU8O2iC.js.gz +0 -0
- package/payload/server/public/assets/public-DEKnH5Q0.js.br +0 -0
- package/payload/server/public/assets/public-DEKnH5Q0.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-CBex8gon.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-CBex8gon.js.gz +0 -0
- package/payload/server/public/assets/routines-BqCmbvFJ.js +0 -2
- package/payload/server/public/assets/routines-BqCmbvFJ.js.br +0 -0
- package/payload/server/public/assets/routines-BqCmbvFJ.js.gz +0 -0
- package/payload/server/public/assets/skills-BmxjPOmq.js.br +0 -0
- package/payload/server/public/assets/skills-BmxjPOmq.js.gz +0 -0
- package/payload/server/public/assets/tasks-BTFYO9vT.js.br +0 -0
- package/payload/server/public/assets/tasks-BTFYO9vT.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-px1YRJJ7.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-px1YRJJ7.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-nMXwRcDX.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-nMXwRcDX.js.gz +0 -0
- package/payload/server/public/assets/useMediaQuery-DjbOTk2X.js.br +0 -0
- package/payload/server/public/assets/useMediaQuery-DjbOTk2X.js.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-Cx7AOmib.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-Cx7AOmib.js.gz +0 -0
- package/payload/server/public/assets/wrench-Cexvlx6J.js.br +0 -0
- package/payload/server/public/assets/wrench-Cexvlx6J.js.gz +0 -0
- /package/payload/server/public/assets/{jsx-runtime-C5Hcfj84.js → jsx-runtime-5lmxFBIw.js} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-C5Hcfj84.js.br → jsx-runtime-5lmxFBIw.js.br} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-C5Hcfj84.js.gz → jsx-runtime-5lmxFBIw.js.gz} +0 -0
|
@@ -4,20 +4,37 @@
|
|
|
4
4
|
# The companion to the PreToolUse quote-render-gate: that gate checks the
|
|
5
5
|
# rendered HTML on the deterministic path (via the render receipt) before the
|
|
6
6
|
# PDF is printed; the HTML the tool gated cannot yet be a PDF at PreToolUse, so
|
|
7
|
-
# the
|
|
7
|
+
# the PDF-only conformance checks run here, once the PDF exists.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# PAGE CHROME — receipt-free and path-agnostic, so it also covers template-seeded
|
|
10
|
+
# documents (budget estimates, valuations, works orders) that never pass through
|
|
11
|
+
# the quote renderer. Each check gates only a document that DECLARES the thing it
|
|
12
|
+
# checks, so a brochure is never touched:
|
|
13
|
+
# 1. a running header on every physical page, when the source declares a text
|
|
14
|
+
# margin box. Chrome decides pagination at print time, so a header placed
|
|
15
|
+
# next to a hand-declared page break misses every bleed page.
|
|
16
|
+
# 2. that header's image no wider than a third of the page, when the source
|
|
17
|
+
# declares an image margin box. Chromium paints a margin box's content:url()
|
|
18
|
+
# at intrinsic pixel size and does NOT scale it to the declared box.
|
|
19
|
+
# Both are offline, and both report before either exits, so one print surfaces
|
|
20
|
+
# both faults rather than the second appearing only after the first is reprinted.
|
|
21
|
+
#
|
|
22
|
+
# LINKS — for a gated client-quote PDF (memory/users/<phone>/documents/
|
|
23
|
+
# quote-<jobId>.pdf or ...-breakdown.pdf) whose render receipt names the source:
|
|
24
|
+
# 3. the PDF's /URI annotation count >= the source HTML's absolute-link count
|
|
12
25
|
# (a print that dropped links — a rasterised or flattened copy — carries
|
|
13
26
|
# fewer /URI than the HTML had anchors);
|
|
14
|
-
#
|
|
27
|
+
# 4. every embedded https:// anchor in the source HTML answers 200 (a pending
|
|
15
28
|
# redeploy that would 404 the Sign-Online link is caught before the PDF is
|
|
16
29
|
# sent).
|
|
17
30
|
# The /URI-count check is offline and runs first; the network fetch runs only
|
|
18
31
|
# when the count check passes, so a link-loss regression never depends on the
|
|
19
32
|
# network being up.
|
|
20
33
|
#
|
|
34
|
+
# Every path that inspects nothing emits `op=skip reason=…`: a silent return is
|
|
35
|
+
# indistinguishable from a gate that never ran, which is the state an account
|
|
36
|
+
# with a stale PostToolUse matcher is actually in.
|
|
37
|
+
#
|
|
21
38
|
# Exit codes: 0 = allow, 2 = block (stderr shown to the agent). Fail OPEN on any
|
|
22
39
|
# uninspectable call (tty / empty stdin / no python3 / no receipt / PDF not
|
|
23
40
|
# written): the no-receipt case is already the PreToolUse gate's to block, and
|
|
@@ -184,16 +201,73 @@ for p in range(first, pages + 1):
|
|
|
184
201
|
missing.append(p)
|
|
185
202
|
|
|
186
203
|
checked = pages - first + 1
|
|
204
|
+
print("LOG|op=pdf-header-conformance file=%s pages=%d pagesChecked=%d pagesHeaderless=%d"
|
|
205
|
+
% (base, pages, checked, len(missing)))
|
|
187
206
|
if missing:
|
|
188
|
-
print("
|
|
189
|
-
% (base,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
print("HBLOCK|%s|pagesHeaderless=%d pages=%s"
|
|
208
|
+
% (base, len(missing), ",".join(str(x) for x in missing)))
|
|
209
|
+
|
|
210
|
+
# --- running-image geometry -------------------------------------------------
|
|
211
|
+
# Chromium paints a page-margin box's content:url() at INTRINSIC pixel size (one
|
|
212
|
+
# image px to one CSS px at 96 px/in) and does NOT scale it to the box's declared
|
|
213
|
+
# width/height, so a full-resolution logo covers most of the page on every page
|
|
214
|
+
# the box paints. Measured 2026-08-05 on a real print: a 700x252 asset in a
|
|
215
|
+
# declared 34mm box rendered 525.0 pt wide on a 594.96 pt A4 page, 88%.
|
|
216
|
+
#
|
|
217
|
+
# The assertion is scoped to the RUNNING image — the object present on every
|
|
218
|
+
# checked page, which is what a running header is by construction — never to
|
|
219
|
+
# every image. A content photo legitimately runs wider: measured across three
|
|
220
|
+
# real quotes, running headers occupy 9.0% to 18.9% of page width while
|
|
221
|
+
# single-page content photos occupy 38.7% to 77.2%, so a whole-page threshold
|
|
222
|
+
# blocks legitimate documents while the recurrence-scoped one separates the
|
|
223
|
+
# correct case from the defect by more than four to one.
|
|
224
|
+
#
|
|
225
|
+
# This runs after the header verdict rather than instead of it, so one print
|
|
226
|
+
# reports a headerless page AND an oversized logo together. Both are offline, so
|
|
227
|
+
# the file's offline-before-network ordering still holds.
|
|
228
|
+
if re.search(r'@top-(?:left|center|right)\s*\{[^}]*?content\s*:\s*url\(', doc, re.S) and checked >= 2:
|
|
229
|
+
try:
|
|
230
|
+
listing = subprocess.run(["pdfimages", "-list", pdf],
|
|
231
|
+
capture_output=True, text=True, timeout=30).stdout
|
|
232
|
+
except Exception:
|
|
233
|
+
listing = ""
|
|
234
|
+
if not listing:
|
|
235
|
+
out("LOG|op=pdf-image-geometry file=%s pagesChecked=0 reason=no-poppler" % base)
|
|
236
|
+
# Columns, zero-based: 0 page, 3 width, 10 object, 12 x-ppi. A row whose
|
|
237
|
+
# object or ppi is not numeric (an inline image) is skipped rather than
|
|
238
|
+
# guessed at.
|
|
239
|
+
per = {}
|
|
240
|
+
for row in listing.splitlines()[2:]:
|
|
241
|
+
f = row.split()
|
|
242
|
+
if len(f) < 13:
|
|
243
|
+
continue
|
|
244
|
+
try:
|
|
245
|
+
pg, wpx, obj, ppi = int(f[0]), int(f[3]), f[10], float(f[12])
|
|
246
|
+
except ValueError:
|
|
247
|
+
continue
|
|
248
|
+
if pg < first or pg > pages or ppi <= 0:
|
|
249
|
+
continue
|
|
250
|
+
seen = per.setdefault(obj, {})
|
|
251
|
+
seen[pg] = max(seen.get(pg, 0.0), wpx / ppi * 72.0)
|
|
252
|
+
limit = pw / 3.0
|
|
253
|
+
running = {o: max(v.values()) for o, v in per.items() if len(v) == checked}
|
|
254
|
+
widest = max(running.values()) if running else 0.0
|
|
255
|
+
over = sorted(w for w in running.values() if w > limit)
|
|
256
|
+
# Logged whether or not it fails: this is the line that decides the verdict.
|
|
257
|
+
print("LOG|op=pdf-image-geometry file=%s pagesChecked=%d runningImages=%d "
|
|
258
|
+
"widestPt=%.1f fracPage=%.1f%% limitPt=%.1f"
|
|
259
|
+
% (base, checked, len(running), widest, widest / pw * 100.0, limit))
|
|
260
|
+
if over:
|
|
261
|
+
print("GBLOCK|%s|widestPt=%.1f fracPage=%.1f%% limitPt=%.1f"
|
|
262
|
+
% (base, over[-1], over[-1] / pw * 100.0, limit))
|
|
263
|
+
sys.exit(0)
|
|
194
264
|
PY
|
|
195
265
|
)
|
|
196
266
|
|
|
267
|
+
# Both page-chrome faults are collected before exiting, so a document with a
|
|
268
|
+
# headerless page AND an oversized running logo reports both on one print rather
|
|
269
|
+
# than surfacing the second only after the first is fixed and reprinted.
|
|
270
|
+
BLOCKED=0
|
|
197
271
|
while IFS= read -r hline; do
|
|
198
272
|
case "$hline" in
|
|
199
273
|
LOG\|*) emit_log "${hline#LOG|}" ;;
|
|
@@ -203,32 +277,49 @@ while IFS= read -r hline; do
|
|
|
203
277
|
hdetail=${hrest#*|}
|
|
204
278
|
emit_log "op=bypass file=$hfile reason=pdf-header-conformance detail=$hdetail"
|
|
205
279
|
echo "Blocked: this printed document has physical pages carrying no running header, so it would go out with unbranded pages ($hdetail). The header must be a property of the page itself, a page margin box, not a header block placed next to a declared page break, because the browser decides the real page boundaries at print time. Fix the document's print CSS, print it again, and check every page." >&2
|
|
206
|
-
|
|
280
|
+
BLOCKED=1
|
|
281
|
+
;;
|
|
282
|
+
GBLOCK\|*)
|
|
283
|
+
grest=${hline#GBLOCK|}
|
|
284
|
+
gfile=${grest%%|*}
|
|
285
|
+
gdetail=${grest#*|}
|
|
286
|
+
emit_log "op=bypass file=$gfile reason=pdf-header-image-geometry detail=$gdetail"
|
|
287
|
+
echo "Blocked: the running header image on this printed document covers most of the page width ($gdetail). An image used as a page margin box's content is painted at its own pixel size and is not scaled down to the box you declared, so the image file itself has to be made the size the box expects. Shrink the asset, rebuild the document, print it again, and check a page after the first." >&2
|
|
288
|
+
BLOCKED=1
|
|
207
289
|
;;
|
|
208
290
|
esac
|
|
209
291
|
done <<< "$HRESULT"
|
|
292
|
+
[ "$BLOCKED" -eq 1 ] && exit 2
|
|
210
293
|
|
|
211
294
|
RESULT=$(INPUT="$INPUT" BASE="$PWD" python3 - <<'PY' 2>/dev/null || true
|
|
212
295
|
import os, json, re, sys, urllib.request
|
|
213
296
|
|
|
297
|
+
# Every return that inspects nothing names its reason. A silent return is
|
|
298
|
+
# indistinguishable from a gate that never ran at all, which is the state a
|
|
299
|
+
# stale PostToolUse matcher leaves an account in; fail-open behaviour itself is
|
|
300
|
+
# unchanged, only its visibility.
|
|
301
|
+
def skip(reason):
|
|
302
|
+
print("SKIP|%s" % reason)
|
|
303
|
+
sys.exit(0)
|
|
304
|
+
|
|
214
305
|
try:
|
|
215
306
|
d = json.loads(os.environ["INPUT"])
|
|
216
307
|
except Exception:
|
|
217
|
-
|
|
308
|
+
skip("bad-envelope")
|
|
218
309
|
|
|
219
310
|
tool = d.get("tool_name", "") or ""
|
|
220
311
|
if not tool.endswith("browser-pdf-save"):
|
|
221
|
-
|
|
312
|
+
skip("not-browser-tool")
|
|
222
313
|
path = (d.get("tool_input", {}) or {}).get("path", "") or ""
|
|
223
314
|
if not path:
|
|
224
|
-
|
|
315
|
+
skip("no-path")
|
|
225
316
|
norm = os.path.normpath(path)
|
|
226
317
|
m = re.search(r'(?:^|/)memory/users/[^/]+/documents/quote-(.+?)(-breakdown)?\.pdf$', norm)
|
|
227
318
|
if not m:
|
|
228
|
-
|
|
319
|
+
skip("not-gated") # not a gated client-quote artefact
|
|
229
320
|
jobid = m.group(1)
|
|
230
321
|
if "/" in jobid or ".." in jobid:
|
|
231
|
-
|
|
322
|
+
skip("bad-jobid") # the PreToolUse gate owns bad-jobid; here fail open
|
|
232
323
|
variant = m.group(2) # "-breakdown" or None
|
|
233
324
|
|
|
234
325
|
base = os.environ["BASE"]
|
|
@@ -236,12 +327,12 @@ receipt_path = os.path.join(base, "quoting", "jobs", jobid, "render-receipt.json
|
|
|
236
327
|
try:
|
|
237
328
|
receipt = json.load(open(receipt_path))
|
|
238
329
|
except Exception:
|
|
239
|
-
|
|
330
|
+
skip("no-receipt") # PreToolUse gate already blocks that; fail open here
|
|
240
331
|
|
|
241
332
|
filed = receipt.get("filed", {}) or {}
|
|
242
333
|
html_path = filed.get("breakdown" if variant else "quote", "")
|
|
243
334
|
if not html_path or not os.path.isfile(html_path) or not os.path.isfile(path):
|
|
244
|
-
|
|
335
|
+
skip("no-source") # nothing to compare against -> fail open
|
|
245
336
|
|
|
246
337
|
html = open(html_path, encoding="utf-8", errors="replace").read()
|
|
247
338
|
# Anchors that become a /URI annotation in the printed PDF: absolute http(s) or
|
|
@@ -274,6 +365,7 @@ PY
|
|
|
274
365
|
)
|
|
275
366
|
|
|
276
367
|
case "$RESULT" in
|
|
368
|
+
SKIP\|*) emit_log "op=skip reason=${RESULT#SKIP|}" ;;
|
|
277
369
|
BLOCK\|*)
|
|
278
370
|
rest=${RESULT#BLOCK|}
|
|
279
371
|
jobid=${rest%%|*}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# specialist-dispatch-gate — PreToolUse gate on every tool.
|
|
3
|
+
#
|
|
4
|
+
# An account's own specialists own their tools. This gate refuses the main
|
|
5
|
+
# conversation a tool one of the account's authored cards declares, and names the
|
|
6
|
+
# specialist to dispatch instead. It exists because skipped delegation writes no
|
|
7
|
+
# record: an inline draft logs an ordinary success, so nothing in any log
|
|
8
|
+
# separates "the clerk did it" from "the admin did it by hand". On one account
|
|
9
|
+
# four authored clerks owned email, works orders, invoices and quotes while the
|
|
10
|
+
# admin produced all of them inline for three days.
|
|
11
|
+
#
|
|
12
|
+
# Two narrowings, both load-bearing.
|
|
13
|
+
#
|
|
14
|
+
# Card axis — the account's OWN cards only. Provisioning copies every shipped
|
|
15
|
+
# template into each account, and one of them declares roughly 130 tools
|
|
16
|
+
# including the whole email, WhatsApp, calendar and browser surfaces, so reading
|
|
17
|
+
# every card would refuse the admin nearly its entire seat. A card is the
|
|
18
|
+
# account's own when its filename appears in none of the shipped roots. The
|
|
19
|
+
# account's own plugin root is deliberately NOT one of those: a card filed there
|
|
20
|
+
# is that account's durable copy, not something we ship.
|
|
21
|
+
#
|
|
22
|
+
# Tool axis — native tools are never gated. Every card declares some of Bash,
|
|
23
|
+
# Read, Write and Edit, and refusing those would brick the seat. The NATIVE set
|
|
24
|
+
# below mirrors CC_NATIVE_TOOLS in the session manager, which a bash hook cannot
|
|
25
|
+
# import; the gate's own test compares the two sets rather than trusting them to
|
|
26
|
+
# stay equal.
|
|
27
|
+
#
|
|
28
|
+
# Caller identity is `agent_type` from the hook envelope, which Claude Code sets
|
|
29
|
+
# when a hook fires inside a subagent. A non-empty value exits 0 unconditionally:
|
|
30
|
+
# this gate must never filter a subagent's tool surface, which is the failure an
|
|
31
|
+
# inherited CLI deny produced when it stripped the writers from a specialist's
|
|
32
|
+
# own frontmatter. The MAXY_SPECIALIST env is not usable — it is stamped only for
|
|
33
|
+
# a specialist spawned as its own PTY process, and native Agent-tool subagents
|
|
34
|
+
# inherit the admin process env.
|
|
35
|
+
#
|
|
36
|
+
# Exit codes: 0 = allow, 2 = block (stderr shown to the agent). Fail OPEN on an
|
|
37
|
+
# uninspectable envelope, and say so with op=degraded when the card set cannot be
|
|
38
|
+
# read: failing open is fine, failing open silently is the fault. No task numbers
|
|
39
|
+
# or internal refs in any operator-visible string.
|
|
40
|
+
|
|
41
|
+
set -uo pipefail
|
|
42
|
+
|
|
43
|
+
# No envelope (tty or empty stdin) → allow: cannot inspect, must not brick tools.
|
|
44
|
+
if [ -t 0 ]; then exit 0; fi
|
|
45
|
+
INPUT=$(cat)
|
|
46
|
+
[ -z "$INPUT" ] && exit 0
|
|
47
|
+
|
|
48
|
+
# Prints nothing for an out-of-scope call, "block <specialist>" for a refusal, or
|
|
49
|
+
# "degraded <reason>" when the card set could not be read.
|
|
50
|
+
DECISION=$(INPUT="$INPUT" python3 - <<'PY' 2>/dev/null || true
|
|
51
|
+
import os, json, re, sys, glob
|
|
52
|
+
|
|
53
|
+
# Mirrors CC_NATIVE_TOOLS (claude-session-manager/src/specialist-drift.ts). A
|
|
54
|
+
# card that declares these declares them for its own use; they are the admin's
|
|
55
|
+
# too, and gating them would refuse the seat its own disk and shell.
|
|
56
|
+
NATIVE = {
|
|
57
|
+
"Read", "Write", "Edit", "MultiEdit", "Glob", "Grep", "LS", "Bash",
|
|
58
|
+
"BashOutput", "KillShell", "Skill", "WebSearch", "WebFetch", "Task",
|
|
59
|
+
"TodoWrite", "NotebookEdit", "NotebookRead", "AskUserQuestion",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
d = json.loads(os.environ["INPUT"])
|
|
64
|
+
except Exception:
|
|
65
|
+
sys.exit(0)
|
|
66
|
+
|
|
67
|
+
tool = (d.get("tool_name", "") or "").strip()
|
|
68
|
+
if not tool or tool in NATIVE:
|
|
69
|
+
sys.exit(0)
|
|
70
|
+
|
|
71
|
+
# A subagent is never filtered. This is the whole reason the gate can be broad:
|
|
72
|
+
# it constrains one seat, not a tool surface.
|
|
73
|
+
if (d.get("agent_type") or "").strip():
|
|
74
|
+
sys.exit(0)
|
|
75
|
+
|
|
76
|
+
agents_dir = os.path.join(os.getcwd(), "specialists", "agents")
|
|
77
|
+
platform_root = os.environ.get("PLATFORM_ROOT", "")
|
|
78
|
+
|
|
79
|
+
shipped = set()
|
|
80
|
+
try:
|
|
81
|
+
if platform_root:
|
|
82
|
+
for f in glob.glob(os.path.join(platform_root, "templates", "specialists", "agents", "*.md")):
|
|
83
|
+
shipped.add(os.path.basename(f))
|
|
84
|
+
for f in glob.glob(os.path.join(platform_root, "plugins", "*", "agents", "*.md")):
|
|
85
|
+
shipped.add(os.path.basename(f))
|
|
86
|
+
premium = os.path.join(os.path.dirname(platform_root), "premium-plugins")
|
|
87
|
+
for f in glob.glob(os.path.join(premium, "*", "agents", "*.md")):
|
|
88
|
+
shipped.add(os.path.basename(f))
|
|
89
|
+
if not os.path.isdir(agents_dir):
|
|
90
|
+
# An account with no specialists directory is normal, not degraded.
|
|
91
|
+
sys.exit(0)
|
|
92
|
+
cards = sorted(os.listdir(agents_dir))
|
|
93
|
+
except Exception as err:
|
|
94
|
+
print("degraded %s" % type(err).__name__)
|
|
95
|
+
sys.exit(0)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def fields(path):
|
|
99
|
+
"""(name, tools) from a card's frontmatter, or None when there is none."""
|
|
100
|
+
try:
|
|
101
|
+
with open(path, encoding="utf-8") as fh:
|
|
102
|
+
lines = fh.read().split("\n")
|
|
103
|
+
except Exception:
|
|
104
|
+
return None
|
|
105
|
+
if not lines or lines[0].strip() != "---":
|
|
106
|
+
return None
|
|
107
|
+
name = tools = None
|
|
108
|
+
for line in lines[1:]:
|
|
109
|
+
if line.strip() == "---":
|
|
110
|
+
break
|
|
111
|
+
m = re.match(r'^name:\s*(.+?)\s*$', line)
|
|
112
|
+
if m:
|
|
113
|
+
name = m.group(1).strip().strip('"').strip("'")
|
|
114
|
+
m = re.match(r'^tools:\s*(.+?)\s*$', line)
|
|
115
|
+
if m:
|
|
116
|
+
tools = m.group(1)
|
|
117
|
+
return (name, tools)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
for filename in cards:
|
|
121
|
+
if not filename.endswith(".md"):
|
|
122
|
+
continue
|
|
123
|
+
if filename in shipped:
|
|
124
|
+
continue
|
|
125
|
+
got = fields(os.path.join(agents_dir, filename))
|
|
126
|
+
if not got:
|
|
127
|
+
continue
|
|
128
|
+
name, tools = got
|
|
129
|
+
if not name or not tools:
|
|
130
|
+
continue
|
|
131
|
+
# Match the tool itself, never a substring of a longer name.
|
|
132
|
+
for declared in tools.split(","):
|
|
133
|
+
declared = declared.strip()
|
|
134
|
+
if not declared or declared in NATIVE:
|
|
135
|
+
continue
|
|
136
|
+
if declared == tool:
|
|
137
|
+
print("block %s" % name)
|
|
138
|
+
sys.exit(0)
|
|
139
|
+
PY
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
[ -z "$DECISION" ] && exit 0
|
|
143
|
+
|
|
144
|
+
VERB=${DECISION%% *}
|
|
145
|
+
ARG=${DECISION#* }
|
|
146
|
+
|
|
147
|
+
if [ "$VERB" = "degraded" ]; then
|
|
148
|
+
echo "[specialist-gate] op=degraded reason=$ARG" >&2
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
ACCOUNT_ID8=$(basename "$PWD" | cut -c1-8)
|
|
153
|
+
TOOL=$(printf '%s' "$INPUT" \
|
|
154
|
+
| python3 -c 'import sys,json;print(json.load(sys.stdin).get("tool_name",""))' 2>/dev/null \
|
|
155
|
+
|| echo "")
|
|
156
|
+
|
|
157
|
+
echo "[specialist-gate] op=refuse account=$ACCOUNT_ID8 tool=$TOOL specialist=$ARG" >&2
|
|
158
|
+
echo "Blocked: this tool belongs to your $ARG. Dispatch that specialist with the Agent tool and let it do the work — it holds the tool and the standards that go with it." >&2
|
|
159
|
+
exit 2
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type AdminBindVerdict = {
|
|
2
|
+
kind: "ok";
|
|
3
|
+
pin: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "nothing-to-set";
|
|
6
|
+
} | {
|
|
7
|
+
kind: "bad-phone";
|
|
8
|
+
} | {
|
|
9
|
+
kind: "bad-telegram";
|
|
10
|
+
} | {
|
|
11
|
+
kind: "no-such-user";
|
|
12
|
+
} | {
|
|
13
|
+
kind: "not-an-admin";
|
|
14
|
+
};
|
|
15
|
+
export declare function classifyAdminBind(input: {
|
|
16
|
+
userId: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
telegram?: string;
|
|
19
|
+
users: ReadonlyArray<{
|
|
20
|
+
userId: string;
|
|
21
|
+
pin: string;
|
|
22
|
+
}>;
|
|
23
|
+
admins: ReadonlyArray<{
|
|
24
|
+
userId: string;
|
|
25
|
+
}>;
|
|
26
|
+
}): AdminBindVerdict;
|
|
27
|
+
//# sourceMappingURL=admin-bind-classify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-bind-classify.d.ts","sourceRoot":"","sources":["../src/admin-bind-classify.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC;AAE7B,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,MAAM,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3C,GAAG,gBAAgB,CAUnB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2531 — the pure refusal policy for `admin-bind`, split from the IO
|
|
3
|
+
* wrapper for the same reason `resolve-admin-userid.ts` splits its picker: every
|
|
4
|
+
* refusal is then unit-testable without a filesystem, and the policy cannot
|
|
5
|
+
* drift from the tool that enforces it.
|
|
6
|
+
*
|
|
7
|
+
* Never infers a userId. `addAdminPhone` takes an account directory and a phone
|
|
8
|
+
* with no userId, which is exactly why it cannot write a binding: the allowlist
|
|
9
|
+
* knows a number, not a person. Binding runs the other way, so the caller names
|
|
10
|
+
* whom and an unknown userId is refused rather than guessed at.
|
|
11
|
+
*/
|
|
12
|
+
import { isE164, isTelegramChatId } from "./binding-validate.js";
|
|
13
|
+
export function classifyAdminBind(input) {
|
|
14
|
+
if (input.phone === undefined && input.telegram === undefined)
|
|
15
|
+
return { kind: "nothing-to-set" };
|
|
16
|
+
if (input.phone !== undefined && !isE164(input.phone))
|
|
17
|
+
return { kind: "bad-phone" };
|
|
18
|
+
if (input.telegram !== undefined && !isTelegramChatId(input.telegram))
|
|
19
|
+
return { kind: "bad-telegram" };
|
|
20
|
+
const row = input.users.find(u => u.userId === input.userId);
|
|
21
|
+
if (!row)
|
|
22
|
+
return { kind: "no-such-user" };
|
|
23
|
+
if (!input.admins.some(a => a.userId === input.userId))
|
|
24
|
+
return { kind: "not-an-admin" };
|
|
25
|
+
// The existing hash, returned so the caller can route through writeAdminEntry
|
|
26
|
+
// (which requires a pin) without rotating the credential it is not touching.
|
|
27
|
+
return { kind: "ok", pin: row.pin };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=admin-bind-classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-bind-classify.js","sourceRoot":"","sources":["../src/admin-bind-classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUjE,MAAM,UAAU,iBAAiB,CAAC,KAMjC;IACC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACjG,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACpF,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IACvG,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IACxF,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2531 — the two channel-binding shapes, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Imported by `index.ts` (admin-add's own validation) and by
|
|
5
|
+
* `admin-bind-classify.ts` (admin-bind's refusal policy), so the tool and the
|
|
6
|
+
* policy cannot disagree about what a valid binding is. Two copies of a
|
|
7
|
+
* validation rule drift the same way two copies of the record type did.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isE164(v: string): boolean;
|
|
10
|
+
export declare function isTelegramChatId(v: string): boolean;
|
|
11
|
+
//# sourceMappingURL=binding-validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding-validate.d.ts","sourceRoot":"","sources":["../src/binding-validate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2531 — the two channel-binding shapes, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Imported by `index.ts` (admin-add's own validation) and by
|
|
5
|
+
* `admin-bind-classify.ts` (admin-bind's refusal policy), so the tool and the
|
|
6
|
+
* policy cannot disagree about what a valid binding is. Two copies of a
|
|
7
|
+
* validation rule drift the same way two copies of the record type did.
|
|
8
|
+
*/
|
|
9
|
+
/** E.164, the same shape `config-persist.ts` enforces for `adminPhones`. */
|
|
10
|
+
const E164 = /^\+[1-9]\d{1,14}$/;
|
|
11
|
+
export function isE164(v) {
|
|
12
|
+
return E164.test(v);
|
|
13
|
+
}
|
|
14
|
+
/** A Telegram chat id is an integer. A group chat's is negative, so the leading
|
|
15
|
+
* minus is legal and refusing it would reject a real binding. */
|
|
16
|
+
const TG_CHAT_ID = /^-?\d+$/;
|
|
17
|
+
export function isTelegramChatId(v) {
|
|
18
|
+
return TG_CHAT_ID.test(v);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=binding-validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding-validate.js","sourceRoot":"","sources":["../src/binding-validate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,4EAA4E;AAC5E,MAAM,IAAI,GAAG,mBAAmB,CAAC;AAEjC,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED;kEACkE;AAClE,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC"}
|