@cohortapp/agent-sdk 2.3.0
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/.claude/commands/init-agent.md +104 -0
- package/.claude/commands/init-maestro.md +1187 -0
- package/.claude/settings.json +161 -0
- package/.env.example +216 -0
- package/README.md +632 -0
- package/agents/browser-operator/agent.md +52 -0
- package/agents/calendar-ops/agent.md +50 -0
- package/agents/communications/agent.md +96 -0
- package/agents/decision-log/agent.md +65 -0
- package/agents/desktop-operator/agent.md +59 -0
- package/agents/gmail-operator/agent.md +62 -0
- package/agents/inbound-dispatcher/agent.md +66 -0
- package/agents/inbox-processor/agent.md +39 -0
- package/agents/pmo-execution/agent.md +60 -0
- package/agents/session-spawner/agent.md +64 -0
- package/agents/slack-operator/agent.md +60 -0
- package/agents/whatsapp-operator/agent.md +60 -0
- package/agents/workflow-automation/agent.md +61 -0
- package/archetypes/altitudes/c-suite.yaml +58 -0
- package/archetypes/altitudes/founder.yaml +68 -0
- package/archetypes/altitudes/senior-manager.yaml +63 -0
- package/archetypes/altitudes/svp.yaml +60 -0
- package/archetypes/altitudes/vp.yaml +50 -0
- package/archetypes/archetype.schema.json +77 -0
- package/archetypes/base.yaml +47 -0
- package/archetypes/capabilities/commercial-leader.yaml +159 -0
- package/archetypes/capabilities/compliance-officer.yaml +159 -0
- package/archetypes/capabilities/executive-operator.yaml +169 -0
- package/archetypes/capabilities/finance-leader.yaml +162 -0
- package/archetypes/capabilities/operations-leader.yaml +154 -0
- package/archetypes/capabilities/product-leader.yaml +148 -0
- package/archetypes/capabilities/technical-leader.yaml +146 -0
- package/archetypes/functions/commercial-leader.yaml +62 -0
- package/archetypes/functions/compliance-officer.yaml +64 -0
- package/archetypes/functions/executive-operator.yaml +70 -0
- package/archetypes/functions/finance-leader.yaml +70 -0
- package/archetypes/functions/operations-leader.yaml +62 -0
- package/archetypes/functions/product-leader.yaml +61 -0
- package/archetypes/functions/technical-leader.yaml +57 -0
- package/bin/cohort-mcp.mjs +81 -0
- package/bin/maestro.mjs +3516 -0
- package/bin/maestro.test.mjs +1015 -0
- package/desktop-control/README.md +56 -0
- package/desktop-control/app-profiles/gmail.yaml +120 -0
- package/desktop-control/app-profiles/slack.yaml +315 -0
- package/desktop-control/app-profiles/whatsapp.yaml +107 -0
- package/docs/architecture/agent-topology.md +2239 -0
- package/docs/architecture/archetype-agent-factory.md +110 -0
- package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
- package/docs/architecture/continuous-monitoring.md +221 -0
- package/docs/architecture/mcp-capability-map.md +585 -0
- package/docs/architecture/system-architecture.md +1272 -0
- package/docs/company-context/README.md +40 -0
- package/docs/guides/agent-persona-setup.md +600 -0
- package/docs/guides/agents-observe-setup.md +64 -0
- package/docs/guides/billing-console-keys.md +88 -0
- package/docs/guides/ccxray-diagnostics.md +65 -0
- package/docs/guides/channel-bus.md +127 -0
- package/docs/guides/claude-mem-setup.md +79 -0
- package/docs/guides/claude-pace-setup.md +56 -0
- package/docs/guides/claudraband-sessions.md +98 -0
- package/docs/guides/clawteam-swarm.md +116 -0
- package/docs/guides/code-review-graph-setup.md +86 -0
- package/docs/guides/email-setup.md +431 -0
- package/docs/guides/mac-mini.md +119 -0
- package/docs/guides/media-generation-setup.md +349 -0
- package/docs/guides/model-routing.md +162 -0
- package/docs/guides/observability-otel.md +265 -0
- package/docs/guides/org-onboarding.md +132 -0
- package/docs/guides/outbound-governance-setup.md +437 -0
- package/docs/guides/pdf-generation-setup.md +315 -0
- package/docs/guides/poller-daemon-setup.md +563 -0
- package/docs/guides/rag-context-setup.md +459 -0
- package/docs/guides/self-optimization-pattern.md +82 -0
- package/docs/guides/setup-wizard.md +178 -0
- package/docs/guides/slack-setup.md +350 -0
- package/docs/guides/telegram-setup.md +227 -0
- package/docs/guides/twilio-subaccounts-setup.md +223 -0
- package/docs/guides/verification.md +128 -0
- package/docs/guides/voice-mode.md +188 -0
- package/docs/guides/voice-sms-setup.md +698 -0
- package/docs/guides/webhook-relay-setup.md +349 -0
- package/docs/guides/whatsapp-setup.md +288 -0
- package/docs/prompts/board-pack-cover-template.md +36 -0
- package/docs/prompts/decision-recommendation-template.md +88 -0
- package/docs/prompts/followup-message-template.md +141 -0
- package/docs/prompts/investor-letter-template.md +52 -0
- package/docs/prompts/morning-brief-template.md +82 -0
- package/docs/prompts/presentation-template.md +58 -0
- package/docs/prompts/weekly-strategic-memo-template.md +104 -0
- package/docs/research/hallucinated-tool-output-investigation.md +151 -0
- package/docs/runbooks/backup-restore.md +205 -0
- package/docs/runbooks/cohort-cutover.md +129 -0
- package/docs/runbooks/fleet-operations.md +200 -0
- package/docs/runbooks/incident-response.md +226 -0
- package/docs/runbooks/mac-mini-bootstrap.md +431 -0
- package/docs/runbooks/perpetual-operations.md +509 -0
- package/docs/runbooks/recovery-and-failover.md +260 -0
- package/framework-features.json +267 -0
- package/ingest/README.md +87 -0
- package/lib/action-executor.js +689 -0
- package/lib/action-executor.test.mjs +871 -0
- package/lib/agent-root.mjs +37 -0
- package/lib/archetype.mjs +236 -0
- package/lib/archetype.test.mjs +132 -0
- package/lib/autonomy.mjs +114 -0
- package/lib/autonomy.test.mjs +66 -0
- package/lib/backlog.mjs +358 -0
- package/lib/backlog.test.mjs +266 -0
- package/lib/budget-guard.mjs +279 -0
- package/lib/budget-guard.test.mjs +291 -0
- package/lib/cadence-bus-schedule.test.mjs +194 -0
- package/lib/cadence-bus.mjs +1120 -0
- package/lib/cadence-bus.test.mjs +720 -0
- package/lib/cadences.mjs +205 -0
- package/lib/cadences.test.mjs +125 -0
- package/lib/capability.mjs +154 -0
- package/lib/capability.test.mjs +78 -0
- package/lib/channels/base-adapter.mjs +719 -0
- package/lib/channels/base-adapter.test.mjs +590 -0
- package/lib/channels/channel.mjs +128 -0
- package/lib/channels/channels.test.mjs +371 -0
- package/lib/channels/contract.mjs +215 -0
- package/lib/channels/contract.test.mjs +137 -0
- package/lib/channels/conversation-resolver.mjs +95 -0
- package/lib/channels/gmail/adapter.mjs +87 -0
- package/lib/channels/inbox-item.mjs +255 -0
- package/lib/channels/inbox-item.test.mjs +335 -0
- package/lib/channels/index.mjs +94 -0
- package/lib/channels/orgmail/adapter.mjs +353 -0
- package/lib/channels/orgmail/adapter.test.mjs +311 -0
- package/lib/channels/pairing.mjs +363 -0
- package/lib/channels/pairing.test.mjs +270 -0
- package/lib/channels/registry.mjs +164 -0
- package/lib/channels/slack/adapter.mjs +317 -0
- package/lib/channels/slack-adapter.test.mjs +212 -0
- package/lib/channels/sms/adapter.mjs +43 -0
- package/lib/channels/telegram/adapter.mjs +432 -0
- package/lib/channels/telegram-adapter.test.mjs +306 -0
- package/lib/channels/voice/adapter.mjs +301 -0
- package/lib/channels/voice/adapter.test.mjs +278 -0
- package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
- package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
- package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
- package/lib/charter.mjs +256 -0
- package/lib/charter.test.mjs +89 -0
- package/lib/claude-bin.mjs +134 -0
- package/lib/claude-bin.test.mjs +75 -0
- package/lib/collective/capture.mjs +185 -0
- package/lib/collective/capture.test.mjs +121 -0
- package/lib/collective/cards.mjs +201 -0
- package/lib/collective/cards.test.mjs +114 -0
- package/lib/collective/config.mjs +186 -0
- package/lib/collective/config.test.mjs +123 -0
- package/lib/collective/global-config.mjs +113 -0
- package/lib/collective/global-config.test.mjs +75 -0
- package/lib/collective/presence.mjs +201 -0
- package/lib/collective/presence.test.mjs +95 -0
- package/lib/collective/recall.mjs +215 -0
- package/lib/collective/recall.test.mjs +116 -0
- package/lib/comms/send-gate.mjs +554 -0
- package/lib/comms/send-gate.test.mjs +577 -0
- package/lib/comms.mjs +67 -0
- package/lib/comms.test.mjs +41 -0
- package/lib/diagnostics/alerts.mjs +424 -0
- package/lib/diagnostics/alerts.test.mjs +318 -0
- package/lib/diagnostics/backup-freshness.mjs +188 -0
- package/lib/diagnostics/backup-freshness.test.mjs +185 -0
- package/lib/diagnostics/counters.mjs +269 -0
- package/lib/diagnostics/counters.test.mjs +206 -0
- package/lib/diagnostics/events.mjs +188 -0
- package/lib/diagnostics/events.test.mjs +290 -0
- package/lib/diagnostics/otel.mjs +237 -0
- package/lib/diagnostics/otel.test.mjs +196 -0
- package/lib/diagnostics/trace.mjs +216 -0
- package/lib/diagnostics/trace.test.mjs +251 -0
- package/lib/env-compat.mjs +74 -0
- package/lib/env-compat.test.mjs +104 -0
- package/lib/feature-init.mjs +331 -0
- package/lib/fs-atomic.mjs +112 -0
- package/lib/fs-atomic.test.mjs +72 -0
- package/lib/fs-ownership.mjs +111 -0
- package/lib/fs-ownership.test.mjs +158 -0
- package/lib/hooks/bus.mjs +347 -0
- package/lib/hooks/bus.test.mjs +387 -0
- package/lib/index.js +16 -0
- package/lib/learning/config.mjs +106 -0
- package/lib/learning/config.test.mjs +75 -0
- package/lib/learning/counters.mjs +156 -0
- package/lib/learning/counters.test.mjs +69 -0
- package/lib/learning/curator-consolidate.test.mjs +238 -0
- package/lib/learning/curator.mjs +453 -0
- package/lib/learning/curator.test.mjs +106 -0
- package/lib/learning/log.mjs +40 -0
- package/lib/learning/reflect.mjs +534 -0
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.mjs +352 -0
- package/lib/learning/session-index.test.mjs +125 -0
- package/lib/learning/skill-writer.mjs +474 -0
- package/lib/learning/skill-writer.test.mjs +210 -0
- package/lib/mcp/server.mjs +328 -0
- package/lib/mcp/server.test.mjs +400 -0
- package/lib/model-router/auth-profiles.mjs +758 -0
- package/lib/model-router/auth-profiles.test.mjs +580 -0
- package/lib/model-router/catalog/anthropic.yaml +153 -0
- package/lib/model-router/catalog/deepseek.yaml +86 -0
- package/lib/model-router/catalog/moonshot.yaml +81 -0
- package/lib/model-router/catalog/qwen.yaml +114 -0
- package/lib/model-router/catalog.mjs +925 -0
- package/lib/model-router/catalog.test.mjs +385 -0
- package/lib/model-router/economics.mjs +564 -0
- package/lib/model-router/economics.test.mjs +344 -0
- package/lib/model-router/failover.mjs +298 -0
- package/lib/model-router/failover.test.mjs +439 -0
- package/lib/model-router/health.mjs +453 -0
- package/lib/model-router/health.test.mjs +338 -0
- package/lib/model-router/integration-coverage.test.mjs +829 -0
- package/lib/model-router/integration.test.mjs +564 -0
- package/lib/model-router/ledger.mjs +402 -0
- package/lib/model-router/ledger.test.mjs +382 -0
- package/lib/model-router/llm-task.mjs +515 -0
- package/lib/model-router/llm-task.test.mjs +392 -0
- package/lib/model-router/org-credentials.mjs +260 -0
- package/lib/model-router/org-credentials.test.mjs +265 -0
- package/lib/model-router/pricing-refresh.mjs +463 -0
- package/lib/model-router/pricing-refresh.test.mjs +286 -0
- package/lib/model-router/reconcile.mjs +429 -0
- package/lib/model-router/reconcile.test.mjs +316 -0
- package/lib/model-router/repair.mjs +471 -0
- package/lib/model-router/repair.test.mjs +180 -0
- package/lib/model-router/resolve.mjs +1206 -0
- package/lib/model-router/spawn.mjs +497 -0
- package/lib/model-router/spawn.test.mjs +425 -0
- package/lib/model-router/taxonomy.mjs +893 -0
- package/lib/model-router/taxonomy.test.mjs +410 -0
- package/lib/model-router.mjs +677 -0
- package/lib/model-router.test.mjs +907 -0
- package/lib/org/activity.mjs +211 -0
- package/lib/org/activity.test.mjs +134 -0
- package/lib/org/approvals.mjs +448 -0
- package/lib/org/approvals.test.mjs +216 -0
- package/lib/org/awareness.mjs +222 -0
- package/lib/org/awareness.test.mjs +159 -0
- package/lib/org/board.mjs +229 -0
- package/lib/org/board.test.mjs +177 -0
- package/lib/org/bootstrap-context.mjs +169 -0
- package/lib/org/bootstrap-context.test.mjs +153 -0
- package/lib/org/client.mjs +1628 -0
- package/lib/org/client.test.mjs +1107 -0
- package/lib/org/cohort-client.mjs +67 -0
- package/lib/org/cohort-client.test.mjs +126 -0
- package/lib/org/cost-sync.mjs +227 -0
- package/lib/org/cost-sync.test.mjs +153 -0
- package/lib/org/doctor.mjs +212 -0
- package/lib/org/doctor.test.mjs +212 -0
- package/lib/org/handoff.mjs +293 -0
- package/lib/org/handoff.test.mjs +269 -0
- package/lib/org/integration-tools.mjs +182 -0
- package/lib/org/integration-tools.test.mjs +160 -0
- package/lib/org/keys.mjs +131 -0
- package/lib/org/keys.test.mjs +92 -0
- package/lib/org/knowledge.mjs +463 -0
- package/lib/org/knowledge.test.mjs +319 -0
- package/lib/org/leases.mjs +335 -0
- package/lib/org/leases.test.mjs +235 -0
- package/lib/org/mesh-integration.test.mjs +127 -0
- package/lib/org/mesh.mjs +459 -0
- package/lib/org/mesh.test.mjs +345 -0
- package/lib/org/messaging.mjs +503 -0
- package/lib/org/messaging.test.mjs +238 -0
- package/lib/org/policy.mjs +345 -0
- package/lib/org/policy.test.mjs +237 -0
- package/lib/org/protocol.checksum +1 -0
- package/lib/org/protocol.checksum.test.mjs +90 -0
- package/lib/org/protocol.mjs +967 -0
- package/lib/org/protocol.test.mjs +264 -0
- package/lib/org/registry.mjs +194 -0
- package/lib/org/registry.test.mjs +100 -0
- package/lib/org/tool-surface-integration.test.mjs +120 -0
- package/lib/org/tool-surface.mjs +2535 -0
- package/lib/org/tool-surface.test.mjs +589 -0
- package/lib/org/ui-parity.mjs +3236 -0
- package/lib/org/ui-parity.test.mjs +348 -0
- package/lib/org/verify.mjs +176 -0
- package/lib/org/verify.test.mjs +194 -0
- package/lib/rag/embed.mjs +188 -0
- package/lib/rag/indexer.mjs +425 -0
- package/lib/rag/rag.test.mjs +505 -0
- package/lib/rag/search.mjs +475 -0
- package/lib/rate-guard.mjs +246 -0
- package/lib/rate-guard.test.mjs +201 -0
- package/lib/render.mjs +112 -0
- package/lib/render.test.mjs +68 -0
- package/lib/resource-governor.mjs +297 -0
- package/lib/resource-governor.test.mjs +262 -0
- package/lib/scheduling/dynamic-jobs.mjs +675 -0
- package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
- package/lib/scheduling/jitter.mjs +0 -0
- package/lib/scheduling/jitter.test.mjs +140 -0
- package/lib/secrets/broker.mjs +315 -0
- package/lib/secrets/broker.test.mjs +280 -0
- package/lib/secrets/providers.mjs +461 -0
- package/lib/secrets/providers.test.mjs +274 -0
- package/lib/security/audit-engine.mjs +684 -0
- package/lib/security/audit-engine.test.mjs +389 -0
- package/lib/security/coerce-args.mjs +552 -0
- package/lib/security/coerce-args.test.mjs +281 -0
- package/lib/security/dangerous-tools.mjs +97 -0
- package/lib/security/dangerous-tools.test.mjs +68 -0
- package/lib/security/external-content.mjs +145 -0
- package/lib/security/external-content.test.mjs +67 -0
- package/lib/security/redact.mjs +592 -0
- package/lib/security/redact.test.mjs +441 -0
- package/lib/security/secret-equal.mjs +73 -0
- package/lib/security/secret-equal.test.mjs +55 -0
- package/lib/session-permissions.mjs +101 -0
- package/lib/session-permissions.test.mjs +100 -0
- package/lib/setup/claude-probe.mjs +74 -0
- package/lib/setup/completeness.mjs +175 -0
- package/lib/setup/completeness.test.mjs +110 -0
- package/lib/setup/context-pack.mjs +173 -0
- package/lib/setup/context-pack.test.mjs +89 -0
- package/lib/setup/enrich.mjs +277 -0
- package/lib/setup/enrich.test.mjs +115 -0
- package/lib/setup/enroll-from-cohort.mjs +441 -0
- package/lib/setup/enroll-from-cohort.test.mjs +233 -0
- package/lib/setup/integration.test.mjs +162 -0
- package/lib/setup/io.mjs +360 -0
- package/lib/setup/io.test.mjs +77 -0
- package/lib/setup/run-generator.mjs +81 -0
- package/lib/setup/runner.mjs +244 -0
- package/lib/setup/runner.test.mjs +132 -0
- package/lib/setup/sections/comms.mjs +173 -0
- package/lib/setup/sections/company.mjs +120 -0
- package/lib/setup/sections/enrich.mjs +138 -0
- package/lib/setup/sections/identity.mjs +182 -0
- package/lib/setup/sections/identity.test.mjs +140 -0
- package/lib/setup/sections/learning.mjs +153 -0
- package/lib/setup/sections/learning.test.mjs +81 -0
- package/lib/setup/sections/messaging.mjs +219 -0
- package/lib/setup/sections/messaging.test.mjs +127 -0
- package/lib/setup/sections/model.mjs +102 -0
- package/lib/setup/sections/operating-model.mjs +78 -0
- package/lib/setup/sections/org.mjs +475 -0
- package/lib/setup/sections/org.test.mjs +313 -0
- package/lib/setup/sections/orgmail.mjs +173 -0
- package/lib/setup/sections/orgmail.test.mjs +118 -0
- package/lib/setup/sections/recovery.mjs +159 -0
- package/lib/setup/sections/recovery.test.mjs +98 -0
- package/lib/setup/sections/tools.mjs +132 -0
- package/lib/setup/sections/verify.mjs +97 -0
- package/lib/setup/sot.mjs +205 -0
- package/lib/setup/sot.test.mjs +81 -0
- package/lib/setup/state.mjs +151 -0
- package/lib/setup/state.test.mjs +92 -0
- package/lib/singleton.js +229 -0
- package/lib/singleton.test.mjs +135 -0
- package/lib/telemetry/alerts.mjs +216 -0
- package/lib/telemetry/alerts.test.mjs +109 -0
- package/lib/telemetry/collect.mjs +512 -0
- package/lib/telemetry/collect.test.mjs +202 -0
- package/lib/tool-definitions-integration.test.mjs +83 -0
- package/lib/tool-definitions.js +738 -0
- package/lib/tool-definitions.test.mjs +437 -0
- package/lib/util/fetch-timeout.mjs +136 -0
- package/lib/util/fetch-timeout.test.mjs +202 -0
- package/lib/util/reconnect.mjs +343 -0
- package/lib/util/reconnect.test.mjs +369 -0
- package/lib/util/unhandled.mjs +205 -0
- package/lib/util/unhandled.test.mjs +216 -0
- package/lib/voice/context-loader.mjs +466 -0
- package/lib/voice/index.mjs +100 -0
- package/lib/voice/openai-realtime.mjs +510 -0
- package/lib/voice/outbound.mjs +542 -0
- package/lib/voice/outbound.test.mjs +69 -0
- package/lib/voice/post-call-brief.mjs +428 -0
- package/lib/voice/provider.mjs +52 -0
- package/lib/voice/session-rotation.mjs +257 -0
- package/lib/voice/stt.mjs +161 -0
- package/lib/voice/stt.test.mjs +226 -0
- package/lib/voice/tool-bridge.mjs +370 -0
- package/lib/voice/tts.mjs +104 -0
- package/lib/voice/twilio-sip-bridge.mjs +288 -0
- package/lib/voice/voice.test.mjs +990 -0
- package/mcp/README.md +80 -0
- package/package.json +151 -0
- package/plugins/maestro-skills/plugin.json +139 -0
- package/plugins/maestro-skills/skills/agents-observe.md +110 -0
- package/plugins/maestro-skills/skills/board-deck.md +68 -0
- package/plugins/maestro-skills/skills/books-close.md +77 -0
- package/plugins/maestro-skills/skills/brand-steward.md +121 -0
- package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
- package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
- package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
- package/plugins/maestro-skills/skills/claude-pace.md +61 -0
- package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
- package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
- package/plugins/maestro-skills/skills/decision-brief.md +89 -0
- package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
- package/plugins/maestro-skills/skills/draft-comms.md +84 -0
- package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
- package/plugins/maestro-skills/skills/files-find.md +65 -0
- package/plugins/maestro-skills/skills/generative-ui.md +228 -0
- package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
- package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
- package/plugins/maestro-skills/skills/mail-triage.md +86 -0
- package/plugins/maestro-skills/skills/morning-brief.md +54 -0
- package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
- package/plugins/maestro-skills/skills/org-board.md +133 -0
- package/plugins/maestro-skills/skills/org-credential.md +68 -0
- package/plugins/maestro-skills/skills/org-recall.md +81 -0
- package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
- package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
- package/plugins/maestro-skills/skills/router-why.md +78 -0
- package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
- package/plugins/maestro-skills/skills/session-search.md +71 -0
- package/plugins/maestro-skills/skills/set-reminder.md +93 -0
- package/plugins/maestro-skills/skills/slack-followup.md +64 -0
- package/plugins/maestro-skills/skills/team-activity.md +86 -0
- package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
- package/policies/action-classification.yaml +114 -0
- package/policies/ai-disclosure.yaml +294 -0
- package/policies/communication-style.md +139 -0
- package/policies/information-barriers.yaml +118 -0
- package/policies/prompt-injection-defence.yaml +138 -0
- package/public/assets/icon-dark.png +0 -0
- package/public/assets/icon-dark.svg +9 -0
- package/public/assets/icon-light.svg +9 -0
- package/public/assets/logo-dark.svg +15 -0
- package/public/assets/logo-light.svg +15 -0
- package/scaffold/.mcp.json +7 -0
- package/scaffold/CLAUDE.md +368 -0
- package/scaffold/config/agent.json +55 -0
- package/scaffold/config/agent.ts +76 -0
- package/scaffold/config/agent.ts.example +89 -0
- package/scaffold/config/alerts.yaml +23 -0
- package/scaffold/config/allowlist.yaml.example +25 -0
- package/scaffold/config/caller-id-map.yaml +46 -0
- package/scaffold/config/collective.yaml +49 -0
- package/scaffold/config/company.json +20 -0
- package/scaffold/config/known-agents.json +6 -0
- package/scaffold/config/learning.yaml +55 -0
- package/scaffold/config/model-routing.yaml.example +104 -0
- package/scaffold/config/org.yaml +25 -0
- package/scaffold/config/orgmail.yaml.example +19 -0
- package/scaffold/config/recovery.yaml +72 -0
- package/scaffold/config/secrets.yaml +27 -0
- package/scaffold/config/slack.yaml.example +35 -0
- package/scaffold/config/telegram.yaml.example +38 -0
- package/scaffold/config/voice.yaml.example +89 -0
- package/scaffold/config/whatsapp.yaml.example +39 -0
- package/schedules/README.md +49 -0
- package/schedules/triggers/backlog-executor.md +102 -0
- package/schedules/triggers/brand-steward.md +72 -0
- package/schedules/triggers/daily-evening-wrap.md +159 -0
- package/schedules/triggers/daily-midday-sweep.md +58 -0
- package/schedules/triggers/daily-morning-brief.md +55 -0
- package/schedules/triggers/directory-hygiene.md +81 -0
- package/schedules/triggers/dynamic-jobs.md +40 -0
- package/schedules/triggers/inbox-processor.md +115 -0
- package/schedules/triggers/meeting-action-capture.md +60 -0
- package/schedules/triggers/meeting-prep.md +69 -0
- package/schedules/triggers/messaging-inbound.md +50 -0
- package/schedules/triggers/org-pulse.md +24 -0
- package/schedules/triggers/quarterly-self-assessment.md +54 -0
- package/schedules/triggers/weekly-engineering-health.md +37 -0
- package/schedules/triggers/weekly-execution.md +65 -0
- package/schedules/triggers/weekly-hiring.md +53 -0
- package/schedules/triggers/weekly-priorities.md +38 -0
- package/schedules/triggers/weekly-strategic-memo.md +124 -0
- package/scripts/archive-email.sh +55 -0
- package/scripts/cadence/cadence-status.mjs +36 -0
- package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
- package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
- package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
- package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
- package/scripts/ci/check-docs-accuracy.mjs +493 -0
- package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
- package/scripts/ci/check-exports-exist.mjs +140 -0
- package/scripts/ci/check-files-exist.mjs +107 -0
- package/scripts/ci/check-no-build-artifacts.mjs +111 -0
- package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
- package/scripts/ci/check-no-confidential.mjs +198 -0
- package/scripts/ci/check-no-conflict-markers.mjs +169 -0
- package/scripts/ci/check-no-residual-identity.mjs +163 -0
- package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
- package/scripts/ci/check-tarball-fidelity.mjs +205 -0
- package/scripts/ci/check-unresolved-tokens.mjs +83 -0
- package/scripts/ci/check.mjs +109 -0
- package/scripts/ci/check.test.mjs +194 -0
- package/scripts/ci/run-coverage.mjs +82 -0
- package/scripts/ci/run-tests.mjs +71 -0
- package/scripts/cloud-relay/README.md +59 -0
- package/scripts/cloud-relay/index.mjs +233 -0
- package/scripts/cloud-relay/package.json +15 -0
- package/scripts/cloud-relay/railway.json +13 -0
- package/scripts/cloud-relay/voice/README.md +94 -0
- package/scripts/cloud-relay/voice/package-lock.json +39 -0
- package/scripts/cloud-relay/voice/package.json +16 -0
- package/scripts/cloud-relay/voice/railway.json +13 -0
- package/scripts/cloud-relay/voice/server.mjs +532 -0
- package/scripts/collective/hook-runner.mjs +211 -0
- package/scripts/collective/hook-runner.test.mjs +90 -0
- package/scripts/collective/org-pulse.mjs +72 -0
- package/scripts/collective/org-sync.mjs +61 -0
- package/scripts/collective/recall.mjs +45 -0
- package/scripts/collective/who.mjs +30 -0
- package/scripts/comms-monitor.sh +288 -0
- package/scripts/configure-whatsapp-sandbox.sh +201 -0
- package/scripts/continuous-monitor.sh +91 -0
- package/scripts/cost/fleet-digest.mjs +407 -0
- package/scripts/cost/fleet-digest.test.mjs +207 -0
- package/scripts/cost/track-claude-usage.mjs +169 -0
- package/scripts/daemon/agent-daemon.mjs +989 -0
- package/scripts/daemon/agent-daemon.test.mjs +525 -0
- package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
- package/scripts/daemon/cadence-consumer.mjs +1080 -0
- package/scripts/daemon/cadence-consumer.test.mjs +770 -0
- package/scripts/daemon/cadence-handlers.mjs +1121 -0
- package/scripts/daemon/cadence-handlers.test.mjs +617 -0
- package/scripts/daemon/classifier.mjs +704 -0
- package/scripts/daemon/classifier.test.mjs +238 -0
- package/scripts/daemon/classify-kind.mjs +54 -0
- package/scripts/daemon/classify-kind.test.mjs +40 -0
- package/scripts/daemon/context-compiler.mjs +605 -0
- package/scripts/daemon/context-compiler.test.mjs +300 -0
- package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
- package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
- package/scripts/daemon/dispatcher.mjs +1516 -0
- package/scripts/daemon/health.mjs +72 -0
- package/scripts/daemon/inbox-deferral.mjs +210 -0
- package/scripts/daemon/inbox-deferral.test.mjs +242 -0
- package/scripts/daemon/integration.test.mjs +149 -0
- package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
- package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
- package/scripts/daemon/launchd-wrapper.sh +91 -0
- package/scripts/daemon/lib/session-router.mjs +274 -0
- package/scripts/daemon/lib/session-router.test.mjs +295 -0
- package/scripts/daemon/maestro-daemon.mjs +275 -0
- package/scripts/daemon/prompt-builder.mjs +685 -0
- package/scripts/daemon/prompt-builder.test.mjs +213 -0
- package/scripts/daemon/responder.mjs +854 -0
- package/scripts/daemon/session-lock.mjs +721 -0
- package/scripts/daemon/session-lock.test.mjs +252 -0
- package/scripts/daemon/session-outcomes.mjs +640 -0
- package/scripts/daemon/session-outcomes.test.mjs +533 -0
- package/scripts/daemon/typing-registry.mjs +90 -0
- package/scripts/daemon/typing-registry.test.mjs +77 -0
- package/scripts/daemon/voice-webhook-server.mjs +804 -0
- package/scripts/decisions/capture-decision.mjs +116 -0
- package/scripts/disclosure_assessment.py +873 -0
- package/scripts/disclosure_boundaries.py +562 -0
- package/scripts/email-signature-principal.html +52 -0
- package/scripts/email-signature.html +60 -0
- package/scripts/email_quote_thread.py +167 -0
- package/scripts/email_thread_dedup.py +362 -0
- package/scripts/emergency-stop.sh +81 -0
- package/scripts/healthcheck.sh +116 -0
- package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
- package/scripts/hooks/block-mcp-slack-send.sh +7 -0
- package/scripts/hooks/post-action-log.sh +126 -0
- package/scripts/hooks/pre-send-audit.sh +174 -0
- package/scripts/hooks/pre-send-audit.test.mjs +215 -0
- package/scripts/hooks/session-end-log.sh +27 -0
- package/scripts/hooks/session-start-banner.sh +115 -0
- package/scripts/huddle/audio-bridge.mjs +664 -0
- package/scripts/huddle/boot-slack-cdp.sh +102 -0
- package/scripts/huddle/huddle-controller.mjs +942 -0
- package/scripts/huddle/huddle-server.mjs +1229 -0
- package/scripts/huddle/launch-slack.sh +232 -0
- package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
- package/scripts/huddle/package-lock.json +62 -0
- package/scripts/huddle/package.json +22 -0
- package/scripts/huddle/setup-audio.sh +239 -0
- package/scripts/huddle/start-call.mjs +318 -0
- package/scripts/huddle/test-pipeline.mjs +263 -0
- package/scripts/learning/consolidate-skills.mjs +72 -0
- package/scripts/learning/session-search.mjs +125 -0
- package/scripts/llm_email_dedup.py +442 -0
- package/scripts/local-triggers/generate-plists.sh +432 -0
- package/scripts/local-triggers/generate-plists.test.mjs +413 -0
- package/scripts/local-triggers/install-all.sh +49 -0
- package/scripts/local-triggers/plists/.gitkeep +0 -0
- package/scripts/local-triggers/run-trigger.sh +63 -0
- package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
- package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
- package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
- package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
- package/scripts/maintenance/backup-to-cloud.sh +124 -0
- package/scripts/maintenance/health-check.sh +377 -0
- package/scripts/media-generation/README.md +105 -0
- package/scripts/media-generation/gemini-image-client.mjs +173 -0
- package/scripts/media-generation/generate-assets.mjs +289 -0
- package/scripts/media-generation/veo-video-client.mjs +219 -0
- package/scripts/org/send-orgmail.mjs +227 -0
- package/scripts/outbound-dedup-cleanup.sh +43 -0
- package/scripts/outbound-dedup.sh +477 -0
- package/scripts/outbound_dedup.py +115 -0
- package/scripts/parse-voice-transcript.mjs +481 -0
- package/scripts/pdf-generation/README.md +63 -0
- package/scripts/pdf-generation/build-document.mjs +247 -0
- package/scripts/pdf-generation/templates/board-pack.latex +136 -0
- package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
- package/scripts/pdf-generation/templates/memo.latex +114 -0
- package/scripts/poll-slack-events.sh +35 -0
- package/scripts/poller/calendar-poller.mjs +12 -0
- package/scripts/poller/gmail-poller.mjs +192 -0
- package/scripts/poller/imap-client.mjs +289 -0
- package/scripts/poller/inbox-scan-poller.mjs +156 -0
- package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
- package/scripts/poller/index.mjs +73 -0
- package/scripts/poller/intra-session-check.mjs +285 -0
- package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
- package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
- package/scripts/poller/secondary-gmail-poller.mjs +132 -0
- package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
- package/scripts/poller/slack-poller.mjs +854 -0
- package/scripts/poller/slack-socket-mode.mjs +917 -0
- package/scripts/poller/slack-socket-mode.test.mjs +753 -0
- package/scripts/poller/trigger.mjs +75 -0
- package/scripts/poller/utils.mjs +371 -0
- package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
- package/scripts/poller/voice-poller.mjs +236 -0
- package/scripts/poller-launchd/install.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +40 -0
- package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
- package/scripts/post-interaction-indexer.py +1598 -0
- package/scripts/pre-draft-context.py +994 -0
- package/scripts/pre_draft_lookup.py +258 -0
- package/scripts/rag/build-index.mjs +47 -0
- package/scripts/rag/ingest.mjs +111 -0
- package/scripts/rag/search.mjs +119 -0
- package/scripts/rag-indexer.py +629 -0
- package/scripts/restore-from-backup.sh +248 -0
- package/scripts/restore-from-backup.test.mjs +178 -0
- package/scripts/resume-operations.sh +80 -0
- package/scripts/search-secondary-inbox.py +181 -0
- package/scripts/secondary-inbox-poller.py +437 -0
- package/scripts/self-optimization/compute-metrics.py +398 -0
- package/scripts/send-email-as-principal.py +369 -0
- package/scripts/send-email-threaded.py +392 -0
- package/scripts/send-email-with-attachment.py +377 -0
- package/scripts/send-email.sh +131 -0
- package/scripts/send-sms.sh +175 -0
- package/scripts/send-whatsapp.sh +292 -0
- package/scripts/session-start.sh +106 -0
- package/scripts/setup/boot-claude-session.sh +94 -0
- package/scripts/setup/configure-macos.sh +674 -0
- package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
- package/scripts/setup/configure-voice-tunnel.mjs +182 -0
- package/scripts/setup/generate-agent-env.mjs +92 -0
- package/scripts/setup/generate-agent-package-json.mjs +222 -0
- package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
- package/scripts/setup/generate-autonomy.mjs +60 -0
- package/scripts/setup/generate-backlog.mjs +101 -0
- package/scripts/setup/generate-cadences.mjs +92 -0
- package/scripts/setup/generate-capability.mjs +162 -0
- package/scripts/setup/generate-charter.mjs +76 -0
- package/scripts/setup/generate-comms.mjs +59 -0
- package/scripts/setup/generate-company.mjs +92 -0
- package/scripts/setup/init-agent.sh +547 -0
- package/scripts/setup/init-agent.test.mjs +151 -0
- package/scripts/setup/init-archetype.mjs +74 -0
- package/scripts/setup/init-backup.mjs +54 -0
- package/scripts/setup/init-cadence-bus.mjs +60 -0
- package/scripts/setup/init-channel-bus.mjs +46 -0
- package/scripts/setup/init-cost-tracking.mjs +45 -0
- package/scripts/setup/init-decision-capture.mjs +66 -0
- package/scripts/setup/init-known-agents.mjs +57 -0
- package/scripts/setup/init-learning.mjs +70 -0
- package/scripts/setup/init-memory-executive.mjs +45 -0
- package/scripts/setup/init-model-router.mjs +124 -0
- package/scripts/setup/init-rag.mjs +174 -0
- package/scripts/setup/init-session-router.mjs +38 -0
- package/scripts/setup/init-slack-socket-mode.mjs +260 -0
- package/scripts/setup/init-telegram.mjs +165 -0
- package/scripts/setup/init-voice-realtime.mjs +204 -0
- package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
- package/scripts/setup/install-dev-tools.sh +150 -0
- package/scripts/setup/lib/install-plist.mjs +95 -0
- package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
- package/scripts/setup/render-environment-yaml.mjs +133 -0
- package/scripts/slack-events-ctl.sh +177 -0
- package/scripts/slack-events-server.mjs +1045 -0
- package/scripts/slack-react.mjs +89 -0
- package/scripts/slack-responded.sh +232 -0
- package/scripts/slack-send.sh +287 -0
- package/scripts/slack-typing.mjs +196 -0
- package/scripts/slack-upload-v2.py +95 -0
- package/scripts/sms-handler.mjs +450 -0
- package/scripts/spawn-session.sh +120 -0
- package/scripts/sync-protocol.mjs +217 -0
- package/scripts/system-verify.sh +184 -0
- package/scripts/test-email-thread-dedup.py +239 -0
- package/scripts/test-information-barriers.py +484 -0
- package/scripts/test-llm-email-dedup.py +251 -0
- package/scripts/test-pre-draft-integration.py +203 -0
- package/scripts/test-rag-phase2.sh +442 -0
- package/scripts/test-rag-search.sh +251 -0
- package/scripts/test-voice-parser.mjs +316 -0
- package/scripts/user-context-search.py +659 -0
- package/scripts/validate_outbound.py +1504 -0
- package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
- package/scripts/watchdog/force-reboot.sh +157 -0
- package/scripts/watchdog/memory-watchdog.sh +473 -0
- package/scripts/whatsapp-handler.mjs +538 -0
- package/teams/desktop-operations.yaml +34 -0
- package/teams/executive-office.yaml +27 -0
- package/teams/legal-and-regulatory.yaml +24 -0
- package/teams/platform-and-engineering.yaml +23 -0
- package/teams/strategy-and-growth.yaml +29 -0
- package/workflows/continuous/backlog-executor.yaml +141 -0
- package/workflows/continuous/inbound-monitor.yaml +168 -0
- package/workflows/daily/applicant-triage.yaml +197 -0
- package/workflows/daily/comms-triage.yaml +80 -0
- package/workflows/daily/evening-wrap.yaml +105 -0
- package/workflows/daily/morning-brief.yaml +164 -0
- package/workflows/daily/slack-followup-sweep.yaml +87 -0
- package/workflows/event-driven/README.md +50 -0
- package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
- package/workflows/event-driven/pr-review.yaml +107 -0
- package/workflows/monthly/board-readiness.yaml +76 -0
- package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
- package/workflows/session-protocol.md +171 -0
- package/workflows/weekly/engineering-health.yaml +154 -0
- package/workflows/weekly/hiring-review.yaml +169 -0
- package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
- package/workflows/weekly/strategic-memo.yaml +79 -0
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/org/protocol.mjs — SHARED Cohort org-protocol contract (transport-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth for the wire contract between maestro
|
|
5
|
+
* agents and the Cohort org server (SPEC-org-server §0.3 / §2.4): method names,
|
|
6
|
+
* the scope each method requires, which methods mutate state, the event families,
|
|
7
|
+
* the board status vocabulary, the error codes, and — critically — the
|
|
8
|
+
* `computeRowHash` function that defines the append-only `org_events` hash chain.
|
|
9
|
+
*
|
|
10
|
+
* Both sides import this module so they agree byte-for-byte:
|
|
11
|
+
* - the server (services/cohort) writes events and signs snapshots using these
|
|
12
|
+
* names/scopes and this hash function;
|
|
13
|
+
* - the agent client (lib/org/client.mjs) and the detective layer
|
|
14
|
+
* (lib/org/verify.mjs) re-derive the same hashes to detect tampering.
|
|
15
|
+
*
|
|
16
|
+
* The protocol is defined independently of HTTP vs WebSocket: phase 1 implements
|
|
17
|
+
* only the HTTP binding, but nothing here changes when the WS binding lands.
|
|
18
|
+
*
|
|
19
|
+
* Pure, Node-builtin only (node:crypto). ESM.
|
|
20
|
+
*
|
|
21
|
+
* @module lib/org/protocol
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
"use strict";
|
|
25
|
+
|
|
26
|
+
import { createHash } from "node:crypto";
|
|
27
|
+
|
|
28
|
+
/** Wire protocol version. Sent as the `X-Org-Protocol` header. */
|
|
29
|
+
export const PROTOCOL_VERSION = 1;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Event families. Every mutation appends to org_events under one of these, and
|
|
33
|
+
* every method's `family` must be one of them (CI-lintable invariant).
|
|
34
|
+
* @type {readonly string[]}
|
|
35
|
+
*/
|
|
36
|
+
export const FAMILIES = Object.freeze([
|
|
37
|
+
"pairing", "registry", "presence", "board", "hierarchy", "admin",
|
|
38
|
+
"lease", "handoff", "governance",
|
|
39
|
+
"approval", "decision", "cost", "policy",
|
|
40
|
+
"knowledge", "contacts", "meetings",
|
|
41
|
+
"credential",
|
|
42
|
+
"messaging", "calling",
|
|
43
|
+
// agent UI-parity families
|
|
44
|
+
"charter", "member", "team", "channel", "persona", "profile", "sop", "escalation", "annotation", "contact", "file", "memory", "compact", "notification", "invitation", "user", "settings", "billing", "org", "integration",
|
|
45
|
+
// Brand Studio v2 write surface (branding-generation / brand-templates / brand-export)
|
|
46
|
+
"branding",
|
|
47
|
+
// agent workspace email (SPEC 2026-07-21 agent-email-server)
|
|
48
|
+
"email",
|
|
49
|
+
// generative-UI artifacts (GU program 2026-07): interactive cards in feeds
|
|
50
|
+
"artifact",
|
|
51
|
+
// agent desks (2026-07): Books desk, workspace calendar, CRM desk, and the
|
|
52
|
+
// workspace drive. NB "files" (Docs/Sheets/Decks drive) coexists with the
|
|
53
|
+
// UI-parity "file" family (pins/comments on chat attachments) — distinct.
|
|
54
|
+
"books", "calendar", "crm", "files",
|
|
55
|
+
// the Directory (2026-07): the canonical registry of external people and
|
|
56
|
+
// organisations every other desk resolves its counterparties through.
|
|
57
|
+
"directory",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Reserved method namespaces that ALWAYS resolve to the `admin` scope,
|
|
62
|
+
* regardless of a method descriptor — privileged control surfaces that an
|
|
63
|
+
* ordinary agent token must never reach (SPEC §2.4).
|
|
64
|
+
* @type {readonly string[]}
|
|
65
|
+
*/
|
|
66
|
+
export const RESERVED_ADMIN_NAMESPACES = Object.freeze(["admin", "pairing", "policy", "governance"]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The board work-item status vocabulary (SPEC org-server board kernel) plus its
|
|
70
|
+
* mapping onto the A2A Task lifecycle, so the board can later serialize to the
|
|
71
|
+
* A2A interop endpoints without a second enum.
|
|
72
|
+
* @type {readonly string[]}
|
|
73
|
+
*/
|
|
74
|
+
export const BOARD_STATUS = Object.freeze([
|
|
75
|
+
"triage", "backlog", "todo", "scheduled", "ready", "running", "review", "blocked", "done", "archived",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
/** Statuses an item can be claimed from (work is dispatchable). */
|
|
79
|
+
export const CLAIMABLE_STATUS = Object.freeze(["ready", "todo"]);
|
|
80
|
+
|
|
81
|
+
/** Terminal statuses (no further work; excluded from the ready feed). */
|
|
82
|
+
export const TERMINAL_STATUS = Object.freeze(["done", "archived"]);
|
|
83
|
+
|
|
84
|
+
/** A2A Task-state mapping for the board statuses (interop, phase 4). */
|
|
85
|
+
export const BOARD_STATUS_A2A = Object.freeze({
|
|
86
|
+
triage: "submitted", backlog: "submitted", todo: "submitted", scheduled: "submitted",
|
|
87
|
+
ready: "submitted", running: "working", review: "input-required", blocked: "input-required",
|
|
88
|
+
done: "completed", archived: "canceled",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Scopes (capabilities). Default-deny: a token carries a set of these; a method
|
|
93
|
+
* is permitted only if the actor holds its required scope (or `admin`).
|
|
94
|
+
* @type {readonly string[]}
|
|
95
|
+
*/
|
|
96
|
+
export const SCOPES = Object.freeze([
|
|
97
|
+
"org.read", // read directory / hierarchy / snapshot / events
|
|
98
|
+
"registry.write", // register self, heartbeat
|
|
99
|
+
"presence.write", // presence beats
|
|
100
|
+
"board.read", // read board feeds
|
|
101
|
+
"board.write", // create / comment / decompose / link items
|
|
102
|
+
"board.work", // claim / heartbeat / complete items
|
|
103
|
+
"lease.write", // claim/heartbeat/release generic leases (thread ownership, singletons)
|
|
104
|
+
"handoff.send", // offer/accept/decline/cancel delegations (gated by governance_ready)
|
|
105
|
+
"approval.request",// request approval for a classified action; read own approvals
|
|
106
|
+
"approval.decide", // resolve (approve/reject) an approval — granted to approvers/humans only
|
|
107
|
+
"decision.write", // propose a decision into the registry
|
|
108
|
+
"cost.report", // report per-session usage into the rollup
|
|
109
|
+
"knowledge.read", // search ACL'd shared facts (RLS-floored; barrier cells deny-override)
|
|
110
|
+
"knowledge.write", // append episodes / replace facts / upsert contacts+meetings
|
|
111
|
+
"credential.use", // lease an org-level third-party credential (deepseek/kimi/...) — the
|
|
112
|
+
// single SoT: agents never hold the org key, they lease it short-lived
|
|
113
|
+
"messaging.read", // read channels + message history in the human messaging app
|
|
114
|
+
"messaging.write", // send / react / edit messages in the human messaging app
|
|
115
|
+
"calling.write", // start / join / end / invite — call lifecycle in the human app
|
|
116
|
+
"charter.write", // EDITOR-equivalent: author/amend member charters, clauses, sections, and colleague-charter fields. Reads use org.read.
|
|
117
|
+
"org.write", // EDITOR-equivalent: write org-structural content (members, teams, personas, profiles, SOPs, escalations, annotations, contacts, files, compacts, notification prefs, reporting lines, chart layout). Reads use org.read.
|
|
118
|
+
"email.read", // read own-mailbox inbox / messages / threads + the Inbox-app read surface (mailboxes/threads/messages/drafts/approvals/rules/signatures)
|
|
119
|
+
"email.write", // send workspace email, mark read + the Inbox-app write surface (triage/move/drafts/rules/folders/signatures; guardian verbs enforced in-domain)
|
|
120
|
+
// agent-desk scope pairs (2026-07): one read/write pair per desk family so a
|
|
121
|
+
// key can be minted for exactly one desk (e.g. a Books-only finance seat).
|
|
122
|
+
"files.read", // workspace drive reads: list/get + doc/sheet/deck reads; exportRequest is an AUDITED side-effecting export under this read scope
|
|
123
|
+
"files.write", // workspace drive writes: create/upload/rename/move/star/pin/delete/recover/shares + doc/sheet/deck revisions
|
|
124
|
+
"calendar.read", // workspace calendar reads: list/get + the deterministic findATime scan
|
|
125
|
+
"calendar.write",// workspace calendar writes: create/update/reschedule/rsvp/delete (queue real invite/update/cancel emails)
|
|
126
|
+
"crm.read", // CRM desk reads: deals/escalations/accounts/contacts/interactions/playbooks/targets + pure compile previews
|
|
127
|
+
"crm.write", // CRM desk writes: deal lifecycle, accounts/contacts, playbooks, targets — human-only verbs (signEscalation, targetCents) refuse agent actors in-domain
|
|
128
|
+
"books.read", // Books desk reads: accounts/journals/feed/invoices/bills/tax/reports/assets + grounded books.ask (AI-gateway self-audited)
|
|
129
|
+
"books.write", // Books desk writes: AR/AP/expenses/payroll/tax/assets — the signature law (PB-B1..B4) is enforced in-domain; scope alone cannot move money
|
|
130
|
+
"directory.read", // Directory reads: people/organisations/channels/lists/review queues + grounded directory.ask. AWARENESS-SCOPED: a named-only record is ABSENT from every result for a seat without a grant, never redacted
|
|
131
|
+
"directory.write", // Directory writes: create/update parties, channels, captures, links, lists. Structural verbs (merge execution, awareness change, archive) refuse an agent actor in-domain and create a review item instead
|
|
132
|
+
"design.read", // Design reads: the SAVED brand foundation snapshot (never a draft), voice, visible templates. Renders and voice rewrites are AUDITED side-effecting reads under this scope (files.exportRequest precedent)
|
|
133
|
+
"design.write", // Design writes: generate imagery, propose a foundation change. Foundation mutation itself is admin-only and human-gated -- a proposal creates a reviewable diff, it does not write
|
|
134
|
+
"admin", // pairing approval, deactivate (kill switch), governance, policy, credential put/revoke
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
/** Default scope set granted to a normal paired agent (no admin, no approval.decide). */
|
|
138
|
+
export const DEFAULT_AGENT_SCOPES = Object.freeze([
|
|
139
|
+
"org.read", "registry.write", "presence.write", "board.read", "board.write", "board.work",
|
|
140
|
+
"lease.write", "handoff.send", "approval.request", "decision.write", "cost.report",
|
|
141
|
+
"knowledge.read", "knowledge.write", "credential.use",
|
|
142
|
+
"messaging.read", "messaging.write", "calling.write",
|
|
143
|
+
"charter.write", "org.write", // agent UI-parity (EDITOR-equivalent)
|
|
144
|
+
"email.read", "email.write", // workspace email + Inbox app (messaging precedent)
|
|
145
|
+
// agent desks (2026-07): a normal paired agent works the product surfaces its
|
|
146
|
+
// seat's human role could — dangerous verbs stay held in the domain layer
|
|
147
|
+
// (guardian-only, human-only, signature law), never behind scope alone.
|
|
148
|
+
"files.read", "files.write", "calendar.read", "calendar.write",
|
|
149
|
+
"crm.read", "crm.write", "books.read", "books.write",
|
|
150
|
+
"directory.read", "directory.write", "design.read", "design.write",
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
/** Knowledge group scopes: org-wide, per-unit, or an information-barrier cell (deny-override). */
|
|
154
|
+
export const KNOWLEDGE_GROUP_KINDS = Object.freeze(["org", "unit", "barrier"]);
|
|
155
|
+
|
|
156
|
+
/** Generic lease scopes (one primitive, several scopes of use). */
|
|
157
|
+
export const LEASE_SCOPES = Object.freeze(["work-claim", "thread-ownership", "session-leadership", "singleton-cadence"]);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The method registry contract (the server globs one file per method; this is
|
|
161
|
+
* the authoritative name→descriptor map both sides validate against).
|
|
162
|
+
* `auth:false` ⇒ pre-auth method (pairing handshake). `sideEffecting:true` ⇒
|
|
163
|
+
* runs in a transaction and MUST append to the event chain.
|
|
164
|
+
* @type {Readonly<Record<string,{family:string,scope:string,sideEffecting:boolean,auth?:boolean,idempotent?:boolean}>>}
|
|
165
|
+
*/
|
|
166
|
+
export const METHODS = Object.freeze({
|
|
167
|
+
// --- pairing (pre-auth handshake + admin approval) ---
|
|
168
|
+
"pairing.request": { family: "pairing", scope: "admin", sideEffecting: true, auth: false },
|
|
169
|
+
"pairing.approve": { family: "pairing", scope: "admin", sideEffecting: true },
|
|
170
|
+
// --- registry / presence ---
|
|
171
|
+
"registry.register": { family: "registry", scope: "registry.write", sideEffecting: true, idempotent: true },
|
|
172
|
+
"registry.heartbeat": { family: "registry", scope: "registry.write", sideEffecting: true },
|
|
173
|
+
"presence.beat": { family: "presence", scope: "presence.write", sideEffecting: true },
|
|
174
|
+
// --- board (kanban / backlog) ---
|
|
175
|
+
"board.create": { family: "board", scope: "board.write", sideEffecting: true, idempotent: true },
|
|
176
|
+
"board.claim": { family: "board", scope: "board.work", sideEffecting: true },
|
|
177
|
+
"board.heartbeat": { family: "board", scope: "board.work", sideEffecting: true },
|
|
178
|
+
"board.complete": { family: "board", scope: "board.work", sideEffecting: true },
|
|
179
|
+
"board.block": { family: "board", scope: "board.work", sideEffecting: true },
|
|
180
|
+
"board.comment": { family: "board", scope: "board.write", sideEffecting: true },
|
|
181
|
+
// --- board DAG (phase 2) — decompose/assign are governance-gated ---
|
|
182
|
+
"board.decompose": { family: "board", scope: "board.write", sideEffecting: true, governanceGated: true },
|
|
183
|
+
"board.assign": { family: "board", scope: "board.write", sideEffecting: true, governanceGated: true },
|
|
184
|
+
"board.link": { family: "board", scope: "board.write", sideEffecting: true },
|
|
185
|
+
// --- lease (phase 2): one generic primitive (work-claim / thread-ownership / singleton-cadence / session-leadership) ---
|
|
186
|
+
"lease.claim": { family: "lease", scope: "lease.write", sideEffecting: true },
|
|
187
|
+
"lease.heartbeat": { family: "lease", scope: "lease.write", sideEffecting: true },
|
|
188
|
+
"lease.release": { family: "lease", scope: "lease.write", sideEffecting: true },
|
|
189
|
+
// --- handoff (phase 2): signed delegation lifecycle — ALL governance-gated (§0.4) ---
|
|
190
|
+
"handoff.offer": { family: "handoff", scope: "handoff.send", sideEffecting: true, governanceGated: true },
|
|
191
|
+
"handoff.accept": { family: "handoff", scope: "handoff.send", sideEffecting: true, governanceGated: true },
|
|
192
|
+
"handoff.decline": { family: "handoff", scope: "handoff.send", sideEffecting: true, governanceGated: true },
|
|
193
|
+
"handoff.cancel": { family: "handoff", scope: "handoff.send", sideEffecting: true, governanceGated: true },
|
|
194
|
+
// --- hierarchy (org SoT — reads in phase 1; writes via governance later) ---
|
|
195
|
+
"hierarchy.set": { family: "hierarchy", scope: "admin", sideEffecting: true },
|
|
196
|
+
// --- governance (decision-rights ingest gates handoffs/assignment per §0.4) ---
|
|
197
|
+
"governance.set_decision_rights": { family: "governance", scope: "admin", sideEffecting: true },
|
|
198
|
+
"governance.status": { family: "governance", scope: "org.read", sideEffecting: false },
|
|
199
|
+
// --- approvals (phase 3): requester != approver enforced as a DB CHECK (G5) ---
|
|
200
|
+
"approval.request": { family: "approval", scope: "approval.request", sideEffecting: true, idempotent: true },
|
|
201
|
+
"approval.resolve": { family: "approval", scope: "approval.decide", sideEffecting: true },
|
|
202
|
+
"approval.get": { family: "approval", scope: "approval.request", sideEffecting: false },
|
|
203
|
+
"approval.list": { family: "approval", scope: "approval.request", sideEffecting: false },
|
|
204
|
+
// --- decisions registry (phase 3): adopt/supersede are governance-gated ---
|
|
205
|
+
"decision.propose": { family: "decision", scope: "decision.write", sideEffecting: true, idempotent: true },
|
|
206
|
+
"decision.adopt": { family: "decision", scope: "decision.write", sideEffecting: true, governanceGated: true },
|
|
207
|
+
"decision.supersede": { family: "decision", scope: "decision.write", sideEffecting: true, governanceGated: true },
|
|
208
|
+
// --- cost rollups (phase 3): agents report usage; rollups read org-wide ---
|
|
209
|
+
"cost.report": { family: "cost", scope: "cost.report", sideEffecting: true, idempotent: true },
|
|
210
|
+
// --- policy distribution (phase 3): publish is admin (reserved); agents fetch via the read surface ---
|
|
211
|
+
"policy.publish": { family: "policy", scope: "admin", sideEffecting: true },
|
|
212
|
+
// --- knowledge plane (phase 4): RLS-floored shared facts; barrier cells deny-override.
|
|
213
|
+
// Mutable-artifact semantics: append (commutes) / replace (CAS) / rewrite (owner-only).
|
|
214
|
+
// search is side-effecting because every retrieval is logged to org_events (read-audit). ---
|
|
215
|
+
"knowledge.append": { family: "knowledge", scope: "knowledge.write", sideEffecting: true, idempotent: true },
|
|
216
|
+
"knowledge.replace": { family: "knowledge", scope: "knowledge.write", sideEffecting: true },
|
|
217
|
+
"knowledge.rewrite": { family: "knowledge", scope: "knowledge.write", sideEffecting: true },
|
|
218
|
+
"knowledge.invalidate": { family: "knowledge", scope: "knowledge.write", sideEffecting: true },
|
|
219
|
+
"knowledge.search": { family: "knowledge", scope: "knowledge.read", sideEffecting: true },
|
|
220
|
+
"contacts.upsert": { family: "contacts", scope: "knowledge.write", sideEffecting: true, idempotent: true },
|
|
221
|
+
"meetings.record": { family: "meetings", scope: "knowledge.write", sideEffecting: true, idempotent: true },
|
|
222
|
+
// meetings.recapFile — file a held meeting's recap + transcript into the
|
|
223
|
+
// workspace drive (two DOCs + a MeetingRecord, links back); the
|
|
224
|
+
// notetaker's landing verb. Knowledge-capture like meetings.record, so it
|
|
225
|
+
// rides knowledge.write (the integration manifest's declared contract). ---
|
|
226
|
+
"meetings.recapFile": { family: "meetings", scope: "knowledge.write", sideEffecting: true, idempotent: true },
|
|
227
|
+
// --- org credential broker: the single source of truth for org-level third-party
|
|
228
|
+
// creds (DeepSeek/Kimi/OpenAI/...). Admin sets once; agents LEASE short-lived,
|
|
229
|
+
// role-scoped, audited — the org key never lives on the 50-100 agent machines. ---
|
|
230
|
+
"credential.put": { family: "credential", scope: "admin", sideEffecting: true },
|
|
231
|
+
"credential.lease": { family: "credential", scope: "credential.use", sideEffecting: true },
|
|
232
|
+
"credential.list": { family: "credential", scope: "admin", sideEffecting: false },
|
|
233
|
+
"credential.revoke": { family: "credential", scope: "admin", sideEffecting: true },
|
|
234
|
+
// --- messaging (SP3): agents participate in the human messaging app. send is
|
|
235
|
+
// idempotent via the client-supplied message id; audit rows are REDACTED
|
|
236
|
+
// (no message body lands in org_events). NOT a reserved-admin namespace. ---
|
|
237
|
+
"messaging.send": { family: "messaging", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
238
|
+
"messaging.createPoll": { family: "messaging", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
239
|
+
"messaging.votePoll": { family: "messaging", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
240
|
+
"messaging.react": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
241
|
+
"messaging.edit": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
242
|
+
"messaging.history": { family: "messaging", scope: "messaging.read", sideEffecting: false },
|
|
243
|
+
"messaging.channels": { family: "messaging", scope: "messaging.read", sideEffecting: false },
|
|
244
|
+
// --- calling (SP3): call lifecycle in the human app. NOT reserved-admin. ---
|
|
245
|
+
"calling.start": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
246
|
+
"calling.join": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
247
|
+
"calling.end": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
248
|
+
"calling.invite": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
249
|
+
// --- calling agent-operability: in-call actions + reads for authorised agents.
|
|
250
|
+
// react/raiseHand/lowerHand are pure-event (the chain event IS the record,
|
|
251
|
+
// no projection row); getDetails/getTranscript are participation-gated reads. ---
|
|
252
|
+
"calling.react": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
253
|
+
"calling.raiseHand": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
254
|
+
"calling.lowerHand": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
255
|
+
"calling.getDetails": { family: "calling", scope: "org.read", sideEffecting: false },
|
|
256
|
+
"calling.getTranscript": { family: "calling", scope: "org.read", sideEffecting: false },
|
|
257
|
+
"calling.recordingStart": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
258
|
+
"calling.recordingStop": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
259
|
+
"calling.setMute": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
260
|
+
// --- calling agent transcript + in-call chat: authorised agents write finalized
|
|
261
|
+
// transcript lines and post to the in-call chat panel. Both persist a
|
|
262
|
+
// projection row (CallTranscriptLine / CallChatMessage) and append exactly
|
|
263
|
+
// one REDACTED calling event (no line/message body in the chain). ---
|
|
264
|
+
"calling.addTranscriptLine": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
265
|
+
"calling.chat": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
266
|
+
// --- calling host moderation (2026-08): remove a participant from the live
|
|
267
|
+
// room + lock the room against NEW joins (existing participants and
|
|
268
|
+
// reconnects are unaffected; start/end are unaffected). Host-gated in the
|
|
269
|
+
// hq domain (the call starter), and an agent may act only on AI targets.
|
|
270
|
+
// Governance refusals return as RESULTS with one audited *_refused event
|
|
271
|
+
// — never a thrown error. ---
|
|
272
|
+
"calling.removeParticipant": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
273
|
+
"calling.setLock": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
274
|
+
// --- calling working sessions (2026-08): an AI participant "presents" a Cohort
|
|
275
|
+
// surface (doc/sheet/channel/board/charter) on the call stage — an app
|
|
276
|
+
// share rendered locally by every client and driven by step frames over
|
|
277
|
+
// the room data lane. Lifecycle + coarse act batches persist a
|
|
278
|
+
// CallShareSession projection and append REDACTED share.* events (never
|
|
279
|
+
// step text/content). getAppShare hydrates late joiners; getVisualContext
|
|
280
|
+
// reads the narrated visual stream (screen/camera stills interpreted into
|
|
281
|
+
// text — frames are never persisted); getCallEvents reads recent
|
|
282
|
+
// non-speech call events (reactions/hands/joins/share lifecycle). ---
|
|
283
|
+
"calling.appShareStart": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
284
|
+
"calling.appShareAct": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
285
|
+
"calling.appShareEnd": { family: "calling", scope: "calling.write", sideEffecting: true },
|
|
286
|
+
"calling.getAppShare": { family: "calling", scope: "org.read", sideEffecting: false },
|
|
287
|
+
"calling.getVisualContext": { family: "calling", scope: "org.read", sideEffecting: false },
|
|
288
|
+
"calling.getCallEvents": { family: "calling", scope: "org.read", sideEffecting: false },
|
|
289
|
+
// --- admin (kill switch) ---
|
|
290
|
+
"admin.deactivate": { family: "admin", scope: "admin", sideEffecting: true },
|
|
291
|
+
"admin.reactivate": { family: "admin", scope: "admin", sideEffecting: true },
|
|
292
|
+
|
|
293
|
+
// ============================================================
|
|
294
|
+
// Agent UI-parity methods (full human-action mirror)
|
|
295
|
+
// ============================================================
|
|
296
|
+
// --- charter ---
|
|
297
|
+
"charter.updateClauses": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
298
|
+
"charter.update": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
299
|
+
"charter.addSection": { family: "charter", scope: "charter.write", sideEffecting: true, idempotent: true },
|
|
300
|
+
"charter.updateSection": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
301
|
+
"charter.deleteSection": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
302
|
+
"charter.reorderSections": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
303
|
+
"charter.amend": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
304
|
+
"charter.editColleagueField": { family: "charter", scope: "charter.write", sideEffecting: true },
|
|
305
|
+
"charter.get": { family: "charter", scope: "org.read", sideEffecting: false },
|
|
306
|
+
// --- member ---
|
|
307
|
+
"member.create": { family: "member", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
308
|
+
"member.update": { family: "member", scope: "org.write", sideEffecting: true },
|
|
309
|
+
"member.archive": { family: "member", scope: "org.write", sideEffecting: true },
|
|
310
|
+
"member.assignTeam": { family: "member", scope: "org.write", sideEffecting: true },
|
|
311
|
+
"member.reassignReporting": { family: "member", scope: "org.write", sideEffecting: true },
|
|
312
|
+
"member.savePosition": { family: "member", scope: "org.write", sideEffecting: true },
|
|
313
|
+
"member.resetPositions": { family: "member", scope: "org.write", sideEffecting: true },
|
|
314
|
+
"member.get": { family: "member", scope: "org.read", sideEffecting: false },
|
|
315
|
+
"member.list": { family: "member", scope: "org.read", sideEffecting: false },
|
|
316
|
+
"member.getEditorOptions": { family: "member", scope: "org.read", sideEffecting: false },
|
|
317
|
+
// --- team ---
|
|
318
|
+
"team.create": { family: "team", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
319
|
+
"team.update": { family: "team", scope: "org.write", sideEffecting: true },
|
|
320
|
+
"team.delete": { family: "team", scope: "org.write", sideEffecting: true },
|
|
321
|
+
"team.list": { family: "team", scope: "org.read", sideEffecting: false },
|
|
322
|
+
// --- channel ---
|
|
323
|
+
"channel.create": { family: "channel", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
324
|
+
"channel.update": { family: "channel", scope: "messaging.write", sideEffecting: true },
|
|
325
|
+
"channel.addMember": { family: "channel", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
326
|
+
"channel.removeMember": { family: "channel", scope: "messaging.write", sideEffecting: true },
|
|
327
|
+
"channel.updateMemberRole": { family: "channel", scope: "messaging.write", sideEffecting: true },
|
|
328
|
+
"channel.markRead": { family: "channel", scope: "messaging.write", sideEffecting: true },
|
|
329
|
+
"channel.markUnreadFrom": { family: "channel", scope: "messaging.write", sideEffecting: true },
|
|
330
|
+
"channel.resolveOrCreateDm": { family: "channel", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
331
|
+
"channel.createConversation": { family: "channel", scope: "messaging.write", sideEffecting: true, idempotent: true },
|
|
332
|
+
// --- persona ---
|
|
333
|
+
"persona.editField": { family: "persona", scope: "org.write", sideEffecting: true },
|
|
334
|
+
"persona.regenerate": { family: "persona", scope: "org.write", sideEffecting: true },
|
|
335
|
+
"persona.generateAgentId": { family: "persona", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
336
|
+
// --- profile ---
|
|
337
|
+
"profile.update": { family: "profile", scope: "org.write", sideEffecting: true },
|
|
338
|
+
"profile.addSection": { family: "profile", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
339
|
+
"profile.updateSection": { family: "profile", scope: "org.write", sideEffecting: true },
|
|
340
|
+
"profile.deleteSection": { family: "profile", scope: "org.write", sideEffecting: true },
|
|
341
|
+
"profile.updateAvatar": { family: "profile", scope: "org.write", sideEffecting: true },
|
|
342
|
+
"profile.get": { family: "profile", scope: "org.read", sideEffecting: false },
|
|
343
|
+
// --- sop ---
|
|
344
|
+
"sop.create": { family: "sop", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
345
|
+
"sop.update": { family: "sop", scope: "org.write", sideEffecting: true },
|
|
346
|
+
"sop.delete": { family: "sop", scope: "org.write", sideEffecting: true },
|
|
347
|
+
"sop.list": { family: "sop", scope: "org.read", sideEffecting: false },
|
|
348
|
+
// --- escalation ---
|
|
349
|
+
"escalation.create": { family: "escalation", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
350
|
+
"escalation.resolve": { family: "escalation", scope: "org.write", sideEffecting: true },
|
|
351
|
+
"escalation.list": { family: "escalation", scope: "org.read", sideEffecting: false },
|
|
352
|
+
// --- annotation ---
|
|
353
|
+
"annotation.create": { family: "annotation", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
354
|
+
"annotation.comment": { family: "annotation", scope: "org.write", sideEffecting: true },
|
|
355
|
+
"annotation.resolve": { family: "annotation", scope: "org.write", sideEffecting: true },
|
|
356
|
+
"annotation.dismiss": { family: "annotation", scope: "org.write", sideEffecting: true },
|
|
357
|
+
"annotation.list": { family: "annotation", scope: "org.read", sideEffecting: false },
|
|
358
|
+
"annotation.getThread": { family: "annotation", scope: "org.read", sideEffecting: false },
|
|
359
|
+
// --- contact ---
|
|
360
|
+
"contact.add": { family: "contact", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
361
|
+
"contact.update": { family: "contact", scope: "org.write", sideEffecting: true },
|
|
362
|
+
"contact.delete": { family: "contact", scope: "org.write", sideEffecting: true },
|
|
363
|
+
"contact.list": { family: "contact", scope: "org.read", sideEffecting: false },
|
|
364
|
+
// --- file ---
|
|
365
|
+
"file.pin": { family: "file", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
366
|
+
"file.unpin": { family: "file", scope: "org.write", sideEffecting: true },
|
|
367
|
+
"file.addComment": { family: "file", scope: "org.write", sideEffecting: true },
|
|
368
|
+
"file.listComments": { family: "file", scope: "org.read", sideEffecting: false },
|
|
369
|
+
"file.listPinned": { family: "file", scope: "org.read", sideEffecting: false },
|
|
370
|
+
// --- memory ---
|
|
371
|
+
"memory.author": { family: "memory", scope: "knowledge.write", sideEffecting: true, idempotent: true },
|
|
372
|
+
"memory.proposeAmendment": { family: "memory", scope: "knowledge.write", sideEffecting: true },
|
|
373
|
+
"memory.mergeAmendment": { family: "memory", scope: "knowledge.write", sideEffecting: true },
|
|
374
|
+
"memory.list": { family: "memory", scope: "knowledge.read", sideEffecting: false },
|
|
375
|
+
// --- compact ---
|
|
376
|
+
"compact.upsert": { family: "compact", scope: "org.write", sideEffecting: true, idempotent: true },
|
|
377
|
+
// --- notification ---
|
|
378
|
+
"notification.updatePreferences": { family: "notification", scope: "org.write", sideEffecting: true },
|
|
379
|
+
"notification.getPreferences": { family: "notification", scope: "org.read", sideEffecting: false },
|
|
380
|
+
// --- decision ---
|
|
381
|
+
"decision.comment": { family: "decision", scope: "decision.write", sideEffecting: true },
|
|
382
|
+
"decision.sign": { family: "decision", scope: "decision.write", sideEffecting: true },
|
|
383
|
+
"decision.reverse": { family: "decision", scope: "decision.write", sideEffecting: true },
|
|
384
|
+
"decision.requestAdjustment": { family: "decision", scope: "decision.write", sideEffecting: true },
|
|
385
|
+
"decision.listComments": { family: "decision", scope: "org.read", sideEffecting: false },
|
|
386
|
+
// --- messaging ---
|
|
387
|
+
"messaging.schedule": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
388
|
+
"messaging.delete": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
389
|
+
"messaging.pin": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
390
|
+
"messaging.unpin": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
391
|
+
"messaging.bookmark": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
392
|
+
"messaging.unbookmark": { family: "messaging", scope: "messaging.write", sideEffecting: true },
|
|
393
|
+
// --- board ---
|
|
394
|
+
"board.createWorkstream": { family: "board", scope: "board.write", sideEffecting: true, idempotent: true },
|
|
395
|
+
"board.updateWorkstream": { family: "board", scope: "board.write", sideEffecting: true },
|
|
396
|
+
"board.deleteWorkstream": { family: "board", scope: "board.write", sideEffecting: true },
|
|
397
|
+
"board.moveWorkstream": { family: "board", scope: "board.write", sideEffecting: true },
|
|
398
|
+
"board.createTask": { family: "board", scope: "board.write", sideEffecting: true, idempotent: true },
|
|
399
|
+
"board.updateTask": { family: "board", scope: "board.write", sideEffecting: true },
|
|
400
|
+
"board.moveTask": { family: "board", scope: "board.write", sideEffecting: true },
|
|
401
|
+
"board.assignTask": { family: "board", scope: "board.write", sideEffecting: true },
|
|
402
|
+
"board.addTaskComment": { family: "board", scope: "board.write", sideEffecting: true },
|
|
403
|
+
"board.addTaskAttachment": { family: "board", scope: "board.write", sideEffecting: true, idempotent: true },
|
|
404
|
+
"board.updateTaskAttachment": { family: "board", scope: "board.write", sideEffecting: true },
|
|
405
|
+
"board.removeTaskAttachment": { family: "board", scope: "board.write", sideEffecting: true },
|
|
406
|
+
"board.taskComments": { family: "board", scope: "board.read", sideEffecting: false },
|
|
407
|
+
"board.taskAttachments": { family: "board", scope: "board.read", sideEffecting: false },
|
|
408
|
+
// --- invitation ---
|
|
409
|
+
"invitation.create": { family: "invitation", scope: "admin", sideEffecting: true, idempotent: true },
|
|
410
|
+
"invitation.revoke": { family: "invitation", scope: "admin", sideEffecting: true },
|
|
411
|
+
"invitation.resend": { family: "invitation", scope: "admin", sideEffecting: true },
|
|
412
|
+
// --- user ---
|
|
413
|
+
"user.updateRole": { family: "user", scope: "admin", sideEffecting: true },
|
|
414
|
+
"user.deactivate": { family: "user", scope: "admin", sideEffecting: true },
|
|
415
|
+
// --- settings ---
|
|
416
|
+
"settings.mintApiKey": { family: "settings", scope: "admin", sideEffecting: true, idempotent: true },
|
|
417
|
+
"settings.renameApiKey": { family: "settings", scope: "admin", sideEffecting: true },
|
|
418
|
+
"settings.revokeApiKey": { family: "settings", scope: "admin", sideEffecting: true },
|
|
419
|
+
"settings.recordSecretRotation": { family: "settings", scope: "admin", sideEffecting: true },
|
|
420
|
+
"settings.upsertSecret": { family: "settings", scope: "admin", sideEffecting: true, idempotent: true },
|
|
421
|
+
"settings.deleteSecret": { family: "settings", scope: "admin", sideEffecting: true },
|
|
422
|
+
"settings.upsertOfficeLocation": { family: "settings", scope: "admin", sideEffecting: true, idempotent: true },
|
|
423
|
+
"settings.deleteOfficeLocation": { family: "settings", scope: "admin", sideEffecting: true },
|
|
424
|
+
"settings.saveCompanyProfile": { family: "settings", scope: "admin", sideEffecting: true, idempotent: true },
|
|
425
|
+
"settings.saveDesignTokens": { family: "settings", scope: "admin", sideEffecting: true, idempotent: true },
|
|
426
|
+
"settings.getDesignTokens": { family: "settings", scope: "org.read", sideEffecting: false },
|
|
427
|
+
"settings.listApiKeys": { family: "settings", scope: "admin", sideEffecting: false },
|
|
428
|
+
// --- billing ---
|
|
429
|
+
"billing.update": { family: "billing", scope: "admin", sideEffecting: true },
|
|
430
|
+
// --- org ---
|
|
431
|
+
"org.create": { family: "org", scope: "admin", sideEffecting: true, idempotent: true },
|
|
432
|
+
"org.delete": { family: "org", scope: "admin", sideEffecting: true },
|
|
433
|
+
"org.mintApiKey": { family: "org", scope: "admin", sideEffecting: true, idempotent: true },
|
|
434
|
+
"org.revokeApiKey": { family: "org", scope: "admin", sideEffecting: true },
|
|
435
|
+
"org.list": { family: "org", scope: "org.read", sideEffecting: false },
|
|
436
|
+
// --- integration ---
|
|
437
|
+
"integration.connect": { family: "integration", scope: "admin", sideEffecting: true, idempotent: true },
|
|
438
|
+
"integration.disconnect": { family: "integration", scope: "admin", sideEffecting: true },
|
|
439
|
+
"integration.list": { family: "integration", scope: "org.read", sideEffecting: false },
|
|
440
|
+
// Live Integrations SP1 — tool-set versioning + propagation. Read-only pulls a
|
|
441
|
+
// runtime consumer (hq responder, MCP server, maestro daemon) uses to hot-refresh
|
|
442
|
+
// its in-memory tool cache without a restart: toolsetVersion is the cheap ETag
|
|
443
|
+
// poll; listAgentTools is the authoritative granted-tool manifest (schemas only —
|
|
444
|
+
// never plugin code or secrets). Org-scoped by the credential.
|
|
445
|
+
"integration.toolsetVersion": { family: "integration", scope: "org.read", sideEffecting: false },
|
|
446
|
+
"integration.listAgentTools": { family: "integration", scope: "org.read", sideEffecting: false },
|
|
447
|
+
// Live Integrations SP5 — execute one GRANTED plugin tool as the authenticated
|
|
448
|
+
// seat, returning the tool's raw output. The org + seat are resolved from the
|
|
449
|
+
// credential server-side (never a param), and the per-seat grant check is the
|
|
450
|
+
// real authorization gate. scope org.write (EDITOR-tier — in DEFAULT_AGENT_SCOPES,
|
|
451
|
+
// so a normal paired agent can invoke; a read-only VIEWER agent cannot). NOT a
|
|
452
|
+
// reserved-admin namespace. sideEffecting:false at the DISPATCHER layer: the hq
|
|
453
|
+
// handler proxies to runIntegrationTool, which owns its own execution + self-heal
|
|
454
|
+
// + audit and may run a long sandbox/external call — so, like the branding write
|
|
455
|
+
// surface, it must NOT run inside the dispatcher's single-append transaction.
|
|
456
|
+
"integration.invokeTool": { family: "integration", scope: "org.write", sideEffecting: false },
|
|
457
|
+
// --- branding (Brand Studio v2 write surface) — the agent mirror of the human
|
|
458
|
+
// branding-generation / brand-templates / brand-export server actions.
|
|
459
|
+
// sideEffecting:false at the dispatcher layer: the branding surface owns its
|
|
460
|
+
// OWN versioned mutation + audit (the BrandVersion spine + self-appended
|
|
461
|
+
// CohortEvents) and may run long AI/render passes, so it must NOT execute
|
|
462
|
+
// inside the dispatcher's single-append transaction. Writes require ADMIN
|
|
463
|
+
// (mirroring requireWorkspaceRole("ADMIN")); the two reads use org.read. ---
|
|
464
|
+
// --- directory (2026-07): the canonical party registry. Reads are AWARENESS
|
|
465
|
+
// SCOPED at the query layer -- a named-only record is ABSENT from results
|
|
466
|
+
// for a seat without a grant (never redacted, never inferable). Structural
|
|
467
|
+
// verbs (executeMerge, revertMerge, setAwareness, archive*) are declared
|
|
468
|
+
// here so a human bearer can call them; an AGENT actor is refused in-domain
|
|
469
|
+
// and the call creates a review item instead. ---
|
|
470
|
+
"directory.search": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
471
|
+
"directory.listPeople": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
472
|
+
"directory.listOrgs": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
473
|
+
"directory.getPerson": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
474
|
+
"directory.getOrg": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
475
|
+
"directory.ask": { family: "directory", scope: "directory.read", sideEffecting: true },
|
|
476
|
+
"directory.exportVcard": { family: "directory", scope: "directory.read", sideEffecting: true },
|
|
477
|
+
"directory.listCaptures": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
478
|
+
"directory.listMerges": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
479
|
+
"directory.listAwarenessRequests": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
480
|
+
"directory.listLists": { family: "directory", scope: "directory.read", sideEffecting: false },
|
|
481
|
+
"directory.createPerson": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
482
|
+
"directory.updatePerson": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
483
|
+
"directory.archivePerson": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
484
|
+
"directory.createOrg": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
485
|
+
"directory.updateOrg": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
486
|
+
"directory.archiveOrg": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
487
|
+
"directory.addChannel": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
488
|
+
"directory.removeChannel": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
489
|
+
"directory.setPrimaryChannel": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
490
|
+
"directory.proposeCapture": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
491
|
+
"directory.resolveCapture": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
492
|
+
"directory.proposeMerge": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
493
|
+
"directory.executeMerge": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
494
|
+
"directory.revertMerge": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
495
|
+
"directory.requestAwareness": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
496
|
+
"directory.setAwareness": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
497
|
+
"directory.resolveAwarenessRequest": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
498
|
+
"directory.linkParty": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
499
|
+
"directory.unlinkParty": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
500
|
+
"directory.createList": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
501
|
+
"directory.addToList": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
502
|
+
"directory.removeFromList": { family: "directory", scope: "directory.write", sideEffecting: true },
|
|
503
|
+
"directory.shareList": { family: "directory", scope: "directory.write", sideEffecting: true, idempotent: true },
|
|
504
|
+
"branding.regenerate": { family: "branding", scope: "admin", sideEffecting: false },
|
|
505
|
+
"branding.regenerateTemplates": { family: "branding", scope: "admin", sideEffecting: false },
|
|
506
|
+
"branding.regenerateTemplate": { family: "branding", scope: "admin", sideEffecting: false },
|
|
507
|
+
"branding.regenerateImages": { family: "branding", scope: "admin", sideEffecting: false },
|
|
508
|
+
"branding.uploadImage": { family: "branding", scope: "admin", sideEffecting: false },
|
|
509
|
+
"branding.amendArtifact": { family: "branding", scope: "admin", sideEffecting: false },
|
|
510
|
+
"branding.amendTemplate": { family: "branding", scope: "admin", sideEffecting: false },
|
|
511
|
+
"branding.cloneTemplate": { family: "branding", scope: "admin", sideEffecting: false },
|
|
512
|
+
"branding.deleteTemplate": { family: "branding", scope: "admin", sideEffecting: false },
|
|
513
|
+
"branding.restoreVersion": { family: "branding", scope: "admin", sideEffecting: false },
|
|
514
|
+
"branding.undo": { family: "branding", scope: "admin", sideEffecting: false },
|
|
515
|
+
"branding.redo": { family: "branding", scope: "admin", sideEffecting: false },
|
|
516
|
+
"branding.listVersions": { family: "branding", scope: "org.read", sideEffecting: false },
|
|
517
|
+
"branding.exportTemplate": { family: "branding", scope: "org.read", sideEffecting: false },
|
|
518
|
+
// --- Design section (2026-07): the versioned brand foundation. The UI section is
|
|
519
|
+
// called "Design"; the wire family stays `branding` so enrolled agents keep
|
|
520
|
+
// working. Agents read the SAVED snapshot only -- never a draft -- and can
|
|
521
|
+
// render/generate within charter budgets. Foundation MUTATION is admin +
|
|
522
|
+
// human-gated: `proposeChange` creates a reviewable diff, it never writes. ---
|
|
523
|
+
"branding.getFoundation": { family: "branding", scope: "design.read", sideEffecting: false },
|
|
524
|
+
"branding.getVoice": { family: "branding", scope: "design.read", sideEffecting: false },
|
|
525
|
+
"branding.listTemplates": { family: "branding", scope: "design.read", sideEffecting: false },
|
|
526
|
+
"branding.renderTemplate": { family: "branding", scope: "design.read", sideEffecting: true, idempotent: true },
|
|
527
|
+
"branding.rewriteInVoice": { family: "branding", scope: "design.read", sideEffecting: true },
|
|
528
|
+
"branding.exportKit": { family: "branding", scope: "design.read", sideEffecting: true },
|
|
529
|
+
"branding.generateImage": { family: "branding", scope: "design.write", sideEffecting: true, idempotent: true },
|
|
530
|
+
"branding.proposeChange": { family: "branding", scope: "design.write", sideEffecting: true },
|
|
531
|
+
"branding.saveFoundation": { family: "branding", scope: "admin", sideEffecting: true, idempotent: true },
|
|
532
|
+
"branding.restoreFoundation": { family: "branding", scope: "admin", sideEffecting: true },
|
|
533
|
+
// --- email (agent workspace email; SPEC 2026-07-21) — send is outbox-queued;
|
|
534
|
+
// bodies NEVER enter org_events (redacted like messaging). Reads mirror
|
|
535
|
+
// messaging.history (sideEffecting:false METHODS, not READS). ---
|
|
536
|
+
"email.send": { family: "email", scope: "email.write", sideEffecting: true, idempotent: true },
|
|
537
|
+
"email.markRead": { family: "email", scope: "email.write", sideEffecting: true },
|
|
538
|
+
"email.inbox": { family: "email", scope: "email.read", sideEffecting: false },
|
|
539
|
+
"email.message": { family: "email", scope: "email.read", sideEffecting: false },
|
|
540
|
+
"email.thread": { family: "email", scope: "email.read", sideEffecting: false },
|
|
541
|
+
// --- email Inbox app (agent-lane mirror of the human Inbox; program
|
|
542
|
+
// upgrade A) — THIN wrappers over the email domain services, the SAME
|
|
543
|
+
// code the human server actions call, so mailbox entitlements, rules,
|
|
544
|
+
// per-thread holds and the guardian-only verbs (approve/amend/return,
|
|
545
|
+
// redirect) are enforced in the domain layer identically on both lanes.
|
|
546
|
+
// Bodies stay REDACTED on-chain (the email.send precedent); reads
|
|
547
|
+
// mirror email.inbox (sideEffecting:false METHODS, not READS). ---
|
|
548
|
+
"email.mailboxes": { family: "email", scope: "email.read", sideEffecting: false },
|
|
549
|
+
"email.threads": { family: "email", scope: "email.read", sideEffecting: false },
|
|
550
|
+
"email.messages": { family: "email", scope: "email.read", sideEffecting: false },
|
|
551
|
+
"email.drafts": { family: "email", scope: "email.read", sideEffecting: false },
|
|
552
|
+
"email.approvals": { family: "email", scope: "email.read", sideEffecting: false },
|
|
553
|
+
"email.rules": { family: "email", scope: "email.read", sideEffecting: false },
|
|
554
|
+
"email.signatures": { family: "email", scope: "email.read", sideEffecting: false },
|
|
555
|
+
"email.triage": { family: "email", scope: "email.write", sideEffecting: true },
|
|
556
|
+
"email.triageNow": { family: "email", scope: "email.write", sideEffecting: true },
|
|
557
|
+
"email.move": { family: "email", scope: "email.write", sideEffecting: true },
|
|
558
|
+
"email.markAllRead": { family: "email", scope: "email.write", sideEffecting: true, idempotent: true },
|
|
559
|
+
"email.delegate": { family: "email", scope: "email.write", sideEffecting: true },
|
|
560
|
+
"email.holdReplies": { family: "email", scope: "email.write", sideEffecting: true, idempotent: true },
|
|
561
|
+
"email.redirect": { family: "email", scope: "email.write", sideEffecting: true },
|
|
562
|
+
"email.draftSave": { family: "email", scope: "email.write", sideEffecting: true, idempotent: true },
|
|
563
|
+
"email.draftDiscard": { family: "email", scope: "email.write", sideEffecting: true },
|
|
564
|
+
"email.draftSend": { family: "email", scope: "email.write", sideEffecting: true },
|
|
565
|
+
"email.undoSend": { family: "email", scope: "email.write", sideEffecting: true },
|
|
566
|
+
"email.approve": { family: "email", scope: "email.write", sideEffecting: true },
|
|
567
|
+
"email.approveAll": { family: "email", scope: "email.write", sideEffecting: true },
|
|
568
|
+
"email.amend": { family: "email", scope: "email.write", sideEffecting: true },
|
|
569
|
+
"email.return": { family: "email", scope: "email.write", sideEffecting: true },
|
|
570
|
+
"email.ruleSave": { family: "email", scope: "email.write", sideEffecting: true },
|
|
571
|
+
"email.ruleSetEnabled": { family: "email", scope: "email.write", sideEffecting: true, idempotent: true },
|
|
572
|
+
"email.folderRename": { family: "email", scope: "email.write", sideEffecting: true },
|
|
573
|
+
"email.folderDelete": { family: "email", scope: "email.write", sideEffecting: true },
|
|
574
|
+
"email.signatureSave": { family: "email", scope: "email.write", sideEffecting: true },
|
|
575
|
+
"email.signatureDelete": { family: "email", scope: "email.write", sideEffecting: true },
|
|
576
|
+
"email.logDecision": { family: "email", scope: "email.write", sideEffecting: true },
|
|
577
|
+
"email.extractAction": { family: "email", scope: "email.write", sideEffecting: true },
|
|
578
|
+
"email.fileAttachment": { family: "email", scope: "email.write", sideEffecting: true },
|
|
579
|
+
// --- email mobile-program delta (2026-08): spam disposition (blocklist
|
|
580
|
+
// maintained in-domain), message reactions, cross-desk thread move, the
|
|
581
|
+
// stored thread brief (closes inbox-report gap #3), and the grounded
|
|
582
|
+
// mail ask (books.ask posture: read scope, gateway self-metered). ---
|
|
583
|
+
"email.reportSpam": { family: "email", scope: "email.write", sideEffecting: true },
|
|
584
|
+
"email.react": { family: "email", scope: "email.write", sideEffecting: true },
|
|
585
|
+
"email.moveMailbox": { family: "email", scope: "email.write", sideEffecting: true },
|
|
586
|
+
"email.summarise": { family: "email", scope: "email.write", sideEffecting: true },
|
|
587
|
+
"email.ask": { family: "email", scope: "email.read", sideEffecting: false },
|
|
588
|
+
// --- artifact (generative-UI artifact system; GU program 2026-07) — rich
|
|
589
|
+
// interactive cards agents post into conversational feeds, every rendered
|
|
590
|
+
// action equally invocable programmatically (the two-sided law). Writes are
|
|
591
|
+
// sideEffecting:false at the DISPATCHER: the hq artifact service owns its
|
|
592
|
+
// own multi-write (Message + Artifact rows + self-appended chain events +
|
|
593
|
+
// realtime fan-out + approval gating) exactly like branding.* /
|
|
594
|
+
// integration.invokeTool, so it must NOT run inside the dispatcher's
|
|
595
|
+
// single-append transaction. act idempotency is self-managed via an
|
|
596
|
+
// (artifact, actor, action, idempotencyKey) dedupe in the service. Scopes
|
|
597
|
+
// ride the messaging pair — artifacts live in conversation, and default
|
|
598
|
+
// agent tokens already hold them, so deployed SDKs pick the family up on
|
|
599
|
+
// protocol sync with no scope migration. ---
|
|
600
|
+
"artifact.create": { family: "artifact", scope: "messaging.write", sideEffecting: false },
|
|
601
|
+
"artifact.act": { family: "artifact", scope: "messaging.write", sideEffecting: false },
|
|
602
|
+
"artifact.patch": { family: "artifact", scope: "messaging.write", sideEffecting: false },
|
|
603
|
+
"artifact.get": { family: "artifact", scope: "messaging.read", sideEffecting: false },
|
|
604
|
+
"artifact.list": { family: "artifact", scope: "messaging.read", sideEffecting: false },
|
|
605
|
+
"artifact.catalog": { family: "artifact", scope: "messaging.read", sideEffecting: false },
|
|
606
|
+
// --- books (agent Books desk; PB-B1..B4) — THIN wrappers over the Books
|
|
607
|
+
// domain services, the SAME code the human UI calls, so floors, holds
|
|
608
|
+
// and the signature law behave identically on the agent lane. Writes run
|
|
609
|
+
// in the dispatcher transaction and append exactly one chain event via
|
|
610
|
+
// the domain op. The money/signature verbs (payrollSign, billScheduleRun,
|
|
611
|
+
// taxSignLodge; floor/new-payee billApprove releases) additionally
|
|
612
|
+
// require a verified HUMAN signature token inside the domain layer
|
|
613
|
+
// (SIGNATURE_REQUIRED otherwise) — books.write alone cannot move money.
|
|
614
|
+
// books.ask is a grounded ask-the-books via the shared AI gateway, which
|
|
615
|
+
// self-audits its own model call (sideEffecting:false, the branding
|
|
616
|
+
// precedent). Reads mirror email.inbox (sideEffecting:false METHODS).
|
|
617
|
+
// Writes are idempotent-by-state (a replayed transition no-ops) except
|
|
618
|
+
// the explicitly non-idempotent verbs (lineRecode, invoicePromise,
|
|
619
|
+
// statementRun, taxReturn, stpResubmit — each run is a new act). ---
|
|
620
|
+
"books.journalPost": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
621
|
+
"books.matchAccept": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
622
|
+
"books.matchHoldResolve": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
623
|
+
"books.receiptsRegister": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
624
|
+
"books.lineRecode": { family: "books", scope: "books.write", sideEffecting: true },
|
|
625
|
+
"books.invoiceDraft": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
626
|
+
"books.invoiceSend": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
627
|
+
"books.invoiceChaseStep": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
628
|
+
"books.invoiceRecordPayment": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
629
|
+
"books.invoiceCreditNote": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
630
|
+
"books.invoiceVoid": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
631
|
+
"books.invoicePromise": { family: "books", scope: "books.write", sideEffecting: true },
|
|
632
|
+
"books.statementRun": { family: "books", scope: "books.write", sideEffecting: true },
|
|
633
|
+
"books.billCapture": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
634
|
+
"books.billApprove": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
635
|
+
"books.billHold": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
636
|
+
"books.billReturn": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
637
|
+
"books.billScheduleRun": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
638
|
+
"books.expenseClaim": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
639
|
+
"books.expenseApprove": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
640
|
+
"books.payrollDraft": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
641
|
+
"books.payrollSign": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
642
|
+
"books.payrollRecall": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
643
|
+
"books.onboardingStart": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
644
|
+
"books.onboardingAdvance": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
645
|
+
"books.leaveRequest": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
646
|
+
"books.leaveApprove": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
647
|
+
"books.taxPrepare": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
648
|
+
"books.taxHold": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
649
|
+
"books.taxReturn": { family: "books", scope: "books.write", sideEffecting: true },
|
|
650
|
+
"books.taxSignLodge": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
651
|
+
"books.assetDepreciationRun": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
652
|
+
"books.assetDispose": { family: "books", scope: "books.write", sideEffecting: true, idempotent: true },
|
|
653
|
+
"books.stpResubmit": { family: "books", scope: "books.write", sideEffecting: true },
|
|
654
|
+
"books.accounts": { family: "books", scope: "books.read", sideEffecting: false },
|
|
655
|
+
"books.journals": { family: "books", scope: "books.read", sideEffecting: false },
|
|
656
|
+
"books.feedLines": { family: "books", scope: "books.read", sideEffecting: false },
|
|
657
|
+
"books.invoices": { family: "books", scope: "books.read", sideEffecting: false },
|
|
658
|
+
"books.bills": { family: "books", scope: "books.read", sideEffecting: false },
|
|
659
|
+
"books.taxObligations": { family: "books", scope: "books.read", sideEffecting: false },
|
|
660
|
+
"books.reports": { family: "books", scope: "books.read", sideEffecting: false },
|
|
661
|
+
"books.assets": { family: "books", scope: "books.read", sideEffecting: false },
|
|
662
|
+
"books.ask": { family: "books", scope: "books.read", sideEffecting: false },
|
|
663
|
+
// --- calendar (agent workspace calendar) — binds the same governance
|
|
664
|
+
// DomainCtx as files.* (one governance layer, two families); writes
|
|
665
|
+
// append exactly one chain event via the domain op. findATime is a
|
|
666
|
+
// deterministic free-slot scan — proposing times changes nothing. ---
|
|
667
|
+
"calendar.create": { family: "calendar", scope: "calendar.write", sideEffecting: true, idempotent: true },
|
|
668
|
+
"calendar.update": { family: "calendar", scope: "calendar.write", sideEffecting: true, idempotent: true },
|
|
669
|
+
"calendar.reschedule": { family: "calendar", scope: "calendar.write", sideEffecting: true, idempotent: true },
|
|
670
|
+
"calendar.rsvp": { family: "calendar", scope: "calendar.write", sideEffecting: true, idempotent: true },
|
|
671
|
+
"calendar.delete": { family: "calendar", scope: "calendar.write", sideEffecting: true, idempotent: true },
|
|
672
|
+
"calendar.get": { family: "calendar", scope: "calendar.read", sideEffecting: false },
|
|
673
|
+
"calendar.list": { family: "calendar", scope: "calendar.read", sideEffecting: false },
|
|
674
|
+
"calendar.findATime": { family: "calendar", scope: "calendar.read", sideEffecting: false },
|
|
675
|
+
// Grounded calendar ask (books.ask posture; visibility-floored in-domain).
|
|
676
|
+
"calendar.ask": { family: "calendar", scope: "calendar.read", sideEffecting: false },
|
|
677
|
+
// --- crm (agent CRM desk) — THIN wrappers over the CRM domain services
|
|
678
|
+
// (playbook holds, human-only signatures and ledger appends shared with
|
|
679
|
+
// the UI). signEscalation / returnEscalation REFUSE non-human actors in
|
|
680
|
+
// the domain layer — an agent-plane call fails exactly like the UI would
|
|
681
|
+
// never allow it; setTarget's targetCents is likewise human-only.
|
|
682
|
+
// compilePlaybook is a PURE compile preview (nothing persisted). ---
|
|
683
|
+
"crm.createDeal": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
684
|
+
"crm.updateDeal": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
685
|
+
"crm.deleteDeal": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
686
|
+
"crm.moveStage": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
687
|
+
"crm.closeDeal": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
688
|
+
"crm.signEscalation": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
689
|
+
"crm.returnEscalation": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
690
|
+
"crm.updateHeldTerms": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
691
|
+
"crm.delegate": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
692
|
+
"crm.holdNextStep": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
693
|
+
"crm.runNextBestAction": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
694
|
+
"crm.shareDeal": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
695
|
+
"crm.proposeTerms": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
696
|
+
"crm.revokeDealShare": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
697
|
+
"crm.linkFiles": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
698
|
+
"crm.linkContact": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
699
|
+
"crm.taskFollowThrough": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
700
|
+
"crm.logDealDecision": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
701
|
+
"crm.createAccount": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
702
|
+
"crm.updateAccount": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
703
|
+
"crm.archiveAccount": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
704
|
+
"crm.createContact": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
705
|
+
"crm.updateContact": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
706
|
+
"crm.deleteContact": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
707
|
+
"crm.logInteraction": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
708
|
+
"crm.addCallGuest": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
709
|
+
"crm.mintGuestToken": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
710
|
+
"crm.linkEmail": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
711
|
+
"crm.pinNote": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
712
|
+
"crm.redactInteraction": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
713
|
+
"crm.savePlaybook": { family: "crm", scope: "crm.write", sideEffecting: true },
|
|
714
|
+
"crm.togglePlaybook": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
715
|
+
"crm.deletePlaybook": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
716
|
+
"crm.setTarget": { family: "crm", scope: "crm.write", sideEffecting: true, idempotent: true },
|
|
717
|
+
"crm.listDeals": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
718
|
+
"crm.getDeal": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
719
|
+
"crm.listEscalations": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
720
|
+
"crm.listAccounts": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
721
|
+
"crm.listContacts": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
722
|
+
"crm.listInteractions": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
723
|
+
"crm.listPlaybooks": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
724
|
+
"crm.getTargets": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
725
|
+
"crm.compilePlaybook": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
726
|
+
// Grounded pipeline ask (books.ask posture; gateway self-metered).
|
|
727
|
+
"crm.ask": { family: "crm", scope: "crm.read", sideEffecting: false },
|
|
728
|
+
// --- files (agent workspace drive: Docs/Sheets/Decks/folders + binary
|
|
729
|
+
// uploads) — binds the same Workspace DomainCtx the server actions use,
|
|
730
|
+
// so shares/ACLs (VIEW/COMMENT/EDIT) govern agents exactly as humans
|
|
731
|
+
// ("agents obey what you grant"). NB distinct from the UI-parity "file"
|
|
732
|
+
// family (pins/comments on chat attachments) — both coexist. Reads
|
|
733
|
+
// mirror email.inbox (sideEffecting:false METHODS, not READS). ---
|
|
734
|
+
"files.create": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
735
|
+
"files.uploadRegister": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
736
|
+
"files.rename": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
737
|
+
"files.move": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
738
|
+
"files.duplicate": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
739
|
+
"files.star": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
740
|
+
"files.pin": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
741
|
+
"files.delete": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
742
|
+
"files.recover": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
743
|
+
"files.shareGrant": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
744
|
+
"files.shareRevoke": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
745
|
+
"files.docWriteRevision": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
746
|
+
"files.sheetWriteCells": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
747
|
+
"files.deckWriteSlides": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
748
|
+
"files.list": { family: "files", scope: "files.read", sideEffecting: false },
|
|
749
|
+
"files.get": { family: "files", scope: "files.read", sideEffecting: false },
|
|
750
|
+
"files.docRead": { family: "files", scope: "files.read", sideEffecting: false },
|
|
751
|
+
"files.sheetRead": { family: "files", scope: "files.read", sideEffecting: false },
|
|
752
|
+
"files.deckRead": { family: "files", scope: "files.read", sideEffecting: false },
|
|
753
|
+
// files.exportRequest is an AUDITED export: it rides the READ scope (export
|
|
754
|
+
// is reading content out) but is sideEffecting — every export lands on the
|
|
755
|
+
// chain (path DIRECT_CONTENT | PRINT_VIEW | SANDBOX_CONVERTER).
|
|
756
|
+
"files.exportRequest": { family: "files", scope: "files.read", sideEffecting: true, idempotent: true },
|
|
757
|
+
// --- files mobile-editors delta (2026-08): multi-tab structure ops (one
|
|
758
|
+
// method per surface, op-discriminated — the email.move precedent),
|
|
759
|
+
// held doc suggestions, the kind-agnostic comment lane, agent-fed
|
|
760
|
+
// sheet ranges, and the grounded drive ask (books.ask posture). ---
|
|
761
|
+
"files.sheetTabOp": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
762
|
+
"files.docTabOp": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
763
|
+
"files.docSuggestResolve": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
764
|
+
"files.docSuggestions": { family: "files", scope: "files.read", sideEffecting: false },
|
|
765
|
+
"files.commentAdd": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
766
|
+
"files.commentResolve": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
767
|
+
"files.comments": { family: "files", scope: "files.read", sideEffecting: false },
|
|
768
|
+
"files.sheetFedRange": { family: "files", scope: "files.write", sideEffecting: true, idempotent: true },
|
|
769
|
+
"files.ask": { family: "files", scope: "files.read", sideEffecting: false },
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Is a method refused until the org server is `governance_ready` (decision_rights
|
|
774
|
+
* populated AND signing live)? A partially deployed control plane is worse than
|
|
775
|
+
* none (SPEC §0.4): handoffs and assignment/decomposition must not flow before
|
|
776
|
+
* the decision-rights matrix exists. Pure.
|
|
777
|
+
* @param {string} name @returns {boolean}
|
|
778
|
+
*/
|
|
779
|
+
export function isGovernanceGated(name) {
|
|
780
|
+
const def = methodDef(name);
|
|
781
|
+
return !!(def && def.governanceGated);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/** Read endpoints (GET) and their required scope. */
|
|
785
|
+
export const READS = Object.freeze({
|
|
786
|
+
"snapshot": "org.read",
|
|
787
|
+
"directory": "org.read",
|
|
788
|
+
"events": "org.read",
|
|
789
|
+
"hierarchy": "org.read",
|
|
790
|
+
"board.ready": "board.read",
|
|
791
|
+
"board.context": "board.read",
|
|
792
|
+
"ops": "org.read",
|
|
793
|
+
// phase 3 reads
|
|
794
|
+
"approval.wait": "approval.request", // bounded long-poll for an approval outcome
|
|
795
|
+
"cost.rollup": "org.read", // org-wide cost rollup (per agent/model/cadence)
|
|
796
|
+
"decision.list": "org.read", // adopted decisions registry
|
|
797
|
+
"policy": "org.read", // agents fetch the current signed policy bundle
|
|
798
|
+
// phase 4 reads
|
|
799
|
+
"contacts.list": "knowledge.read", // org contacts registry (ACL-filtered)
|
|
800
|
+
"meetings.list": "knowledge.read", // org meetings registry (ACL-filtered)
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
/** Error codes (the error contract; HTTP status hints are advisory). */
|
|
804
|
+
export const ERROR_CODES = Object.freeze({
|
|
805
|
+
BAD_REQUEST: { http: 400 },
|
|
806
|
+
UNAUTHORIZED: { http: 401 },
|
|
807
|
+
FORBIDDEN_SCOPE: { http: 403 },
|
|
808
|
+
NOT_FOUND: { http: 404 },
|
|
809
|
+
CONFLICT: { http: 409 }, // e.g. claim race loser
|
|
810
|
+
IDEMPOTENT_REPLAY: { http: 200 }, // returned cached result, not an error per se
|
|
811
|
+
GOVERNANCE_NOT_READY: { http: 423 }, // §0.4 ordering gate
|
|
812
|
+
RATE_LIMITED: { http: 429 },
|
|
813
|
+
WEAK_SECRET: { http: 401 },
|
|
814
|
+
INTERNAL: { http: 500 },
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
/** Directive kinds the presence.beat response may carry (kill-switch + nudges). */
|
|
818
|
+
export const DIRECTIVES = Object.freeze(["halt", "rotate_token", "policy_stale", "resync"]);
|
|
819
|
+
|
|
820
|
+
// ---------------------------------------------------------------------------
|
|
821
|
+
// Helpers (shared, pure)
|
|
822
|
+
// ---------------------------------------------------------------------------
|
|
823
|
+
|
|
824
|
+
/** @param {string} name @returns {object|null} the method descriptor or null. */
|
|
825
|
+
export function methodDef(name) {
|
|
826
|
+
return Object.prototype.hasOwnProperty.call(METHODS, name) ? METHODS[name] : null;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Resolve the scope a method requires. A method in a reserved namespace ALWAYS
|
|
831
|
+
* requires `admin` (defence-in-depth: a missing/typo'd descriptor cannot
|
|
832
|
+
* accidentally widen access). Unknown methods resolve to `admin` (deny).
|
|
833
|
+
* @param {string} name
|
|
834
|
+
* @returns {string}
|
|
835
|
+
*/
|
|
836
|
+
export function scopeForMethod(name) {
|
|
837
|
+
const ns = String(name || "").split(".")[0];
|
|
838
|
+
if (RESERVED_ADMIN_NAMESPACES.includes(ns)) return "admin";
|
|
839
|
+
const def = methodDef(name);
|
|
840
|
+
return def ? def.scope : "admin";
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/** @param {string} name @returns {boolean} */
|
|
844
|
+
export function isReservedAdminNamespace(name) {
|
|
845
|
+
return RESERVED_ADMIN_NAMESPACES.includes(String(name || "").split(".")[0]);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Does an actor holding `scopes` satisfy the requirement for `methodName`?
|
|
850
|
+
* `admin` satisfies everything. Pure.
|
|
851
|
+
* @param {string[]} scopes
|
|
852
|
+
* @param {string} methodName
|
|
853
|
+
* @returns {boolean}
|
|
854
|
+
*/
|
|
855
|
+
export function actorCan(scopes, methodName) {
|
|
856
|
+
const need = scopeForMethod(methodName);
|
|
857
|
+
if (!Array.isArray(scopes)) return false;
|
|
858
|
+
if (scopes.includes("admin")) return true;
|
|
859
|
+
return scopes.includes(need);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Canonical JSON for hashing/signing: keys sorted recursively so the same
|
|
864
|
+
* logical value always serializes to the same bytes on both sides.
|
|
865
|
+
* @param {unknown} value
|
|
866
|
+
* @returns {string}
|
|
867
|
+
*/
|
|
868
|
+
export function canonicalize(value) {
|
|
869
|
+
return JSON.stringify(sortDeep(value));
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function sortDeep(v) {
|
|
873
|
+
if (Array.isArray(v)) return v.map(sortDeep);
|
|
874
|
+
if (v && typeof v === "object") {
|
|
875
|
+
const out = {};
|
|
876
|
+
for (const k of Object.keys(v).sort()) out[k] = sortDeep(v[k]);
|
|
877
|
+
return out;
|
|
878
|
+
}
|
|
879
|
+
return v;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Compute the hash-chain row hash for an event. THE load-bearing function: the
|
|
884
|
+
* server writes `row_hash = computeRowHash(prevHash, row)` per event, and the
|
|
885
|
+
* detective layer recomputes it to prove the log was not rewritten. Any change
|
|
886
|
+
* to this function is a protocol break (bump PROTOCOL_VERSION).
|
|
887
|
+
*
|
|
888
|
+
* @param {string} prevHash hex hash of the previous event ("" for the genesis row)
|
|
889
|
+
* @param {{seq:number, family:string, kind:string, entity_id:string, payload:unknown, at:string, actor:string}} row
|
|
890
|
+
* @returns {string} lowercase hex sha-256
|
|
891
|
+
*/
|
|
892
|
+
export function computeRowHash(prevHash, row) {
|
|
893
|
+
const material = canonicalize({
|
|
894
|
+
prev: prevHash || "",
|
|
895
|
+
seq: row.seq,
|
|
896
|
+
family: row.family,
|
|
897
|
+
kind: row.kind,
|
|
898
|
+
entity_id: row.entity_id,
|
|
899
|
+
actor: row.actor || "",
|
|
900
|
+
at: row.at,
|
|
901
|
+
payload: row.payload ?? null,
|
|
902
|
+
});
|
|
903
|
+
return createHash("sha256").update(material).digest("hex");
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Verify a contiguous slice of the event chain links correctly. Returns the
|
|
908
|
+
* first break (index + reason) or null when the slice is internally consistent.
|
|
909
|
+
* `startPrev` is the row_hash of the event BEFORE events[0] ("" if from genesis).
|
|
910
|
+
* @param {Array<object>} events ordered by seq ascending
|
|
911
|
+
* @param {string} [startPrev=""]
|
|
912
|
+
* @returns {{ index:number, reason:string }|null}
|
|
913
|
+
*/
|
|
914
|
+
export function verifyChain(events, startPrev = "") {
|
|
915
|
+
let prev = startPrev;
|
|
916
|
+
for (let i = 0; i < events.length; i++) {
|
|
917
|
+
const e = events[i];
|
|
918
|
+
if ((e.prev_hash || "") !== prev) return { index: i, reason: "prev_hash mismatch" };
|
|
919
|
+
const expect = computeRowHash(prev, e);
|
|
920
|
+
if (e.row_hash !== expect) return { index: i, reason: "row_hash mismatch" };
|
|
921
|
+
prev = e.row_hash;
|
|
922
|
+
}
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Build a request frame for the RPC binding. Side-effecting methods require an
|
|
928
|
+
* idempotency key (the caller supplies a stable one for safe retries).
|
|
929
|
+
* @param {string} method @param {object} params @param {object} [opts] @param {string} [opts.idempotencyKey]
|
|
930
|
+
* @returns {{method:string, params:object, idempotencyKey?:string}}
|
|
931
|
+
*/
|
|
932
|
+
export function buildReqFrame(method, params = {}, opts = {}) {
|
|
933
|
+
const frame = { method, params: params || {} };
|
|
934
|
+
if (opts.idempotencyKey) frame.idempotencyKey = String(opts.idempotencyKey);
|
|
935
|
+
return frame;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Validate an inbound request frame against the registry. Returns
|
|
940
|
+
* {ok:true, def} or {ok:false, code, message}. Pure — no auth check here
|
|
941
|
+
* (that needs the actor's scopes; see actorCan).
|
|
942
|
+
* @param {any} frame
|
|
943
|
+
* @returns {{ok:true, def:object}|{ok:false, code:string, message:string}}
|
|
944
|
+
*/
|
|
945
|
+
export function validateReqFrame(frame) {
|
|
946
|
+
if (!frame || typeof frame !== "object") return { ok: false, code: "BAD_REQUEST", message: "frame not an object" };
|
|
947
|
+
if (typeof frame.method !== "string") return { ok: false, code: "BAD_REQUEST", message: "method missing" };
|
|
948
|
+
const def = methodDef(frame.method);
|
|
949
|
+
if (!def) return { ok: false, code: "NOT_FOUND", message: `unknown method ${frame.method}` };
|
|
950
|
+
if (frame.params != null && typeof frame.params !== "object") return { ok: false, code: "BAD_REQUEST", message: "params must be an object" };
|
|
951
|
+
if (def.sideEffecting && def.auth !== false && def.idempotent && !frame.idempotencyKey) {
|
|
952
|
+
// idempotent side-effecting methods want a key for safe retries; warn-not-fatal
|
|
953
|
+
// (the server may synthesize one), so this is advisory, not a hard reject.
|
|
954
|
+
}
|
|
955
|
+
return { ok: true, def };
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Build a success response frame. */
|
|
959
|
+
export function okFrame(result) {
|
|
960
|
+
return { ok: true, result: result ?? null };
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/** Build an error response frame. */
|
|
964
|
+
export function errFrame(code, message) {
|
|
965
|
+
const known = ERROR_CODES[code] ? code : "INTERNAL";
|
|
966
|
+
return { ok: false, error: { code: known, message: String(message || known) } };
|
|
967
|
+
}
|