@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,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — Voice tool bridge
|
|
3
|
+
*
|
|
4
|
+
* Converts maestro's existing action handlers (defined in
|
|
5
|
+
* lib/tool-definitions.js + executed via lib/action-executor.js) into
|
|
6
|
+
* the function-tool format OpenAI Realtime expects, and wires the
|
|
7
|
+
* provider's tool.call events back through the same action executor and
|
|
8
|
+
* action-classification policy used by the Slack / Gmail surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Why this matters:
|
|
11
|
+
* - The voice channel must NOT bypass the approval policy that protects
|
|
12
|
+
* the Slack / Gmail surfaces. A user saying "wire fifty thousand to
|
|
13
|
+
* this account" should require the same verbal-confirmation gate that
|
|
14
|
+
* a Slack-typed equivalent would, not silently fire the transfer.
|
|
15
|
+
* - We surface that gate by tagging high-risk actions in the Realtime
|
|
16
|
+
* tool description and (optionally) by inserting a pre-execution
|
|
17
|
+
* verbal-confirm step that asks the model to read back the action
|
|
18
|
+
* before invoking it.
|
|
19
|
+
*
|
|
20
|
+
* Public API:
|
|
21
|
+
* actionsToRealtimeTools(opts) → array of Realtime function tool defs
|
|
22
|
+
* registerToolBridge(provider, opts) → attach handler to provider events
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
26
|
+
import { createRequire } from "node:module";
|
|
27
|
+
import { redactSensitiveUrl, isSensitiveParamName, REDACTED } from "../security/redact.mjs";
|
|
28
|
+
|
|
29
|
+
// js-yaml is a declared maestro dependency, but action policies are usually
|
|
30
|
+
// authored as JSON, so we keep YAML loading optional. createRequire gives us a
|
|
31
|
+
// working `require` inside this ESM module — a bare `require("js-yaml")` here is
|
|
32
|
+
// a ReferenceError under "type":"module", which previously got swallowed and
|
|
33
|
+
// silently disabled the verbal-confirm gate for YAML policies.
|
|
34
|
+
const require = createRequire(import.meta.url);
|
|
35
|
+
import { join, resolve } from "node:path";
|
|
36
|
+
|
|
37
|
+
// We dynamically import the action-executor only on first invocation
|
|
38
|
+
// to avoid circular import order issues during module load.
|
|
39
|
+
let _toolDefs = null;
|
|
40
|
+
let _executor = null;
|
|
41
|
+
|
|
42
|
+
async function loadMaestroBindings() {
|
|
43
|
+
if (_toolDefs && _executor) return { _toolDefs, _executor };
|
|
44
|
+
try {
|
|
45
|
+
const td = await import("../tool-definitions.js");
|
|
46
|
+
const ae = await import("../action-executor.js");
|
|
47
|
+
_toolDefs = td;
|
|
48
|
+
_executor = ae;
|
|
49
|
+
return { _toolDefs, _executor };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw new Error(`tool-bridge: failed to load maestro bindings: ${err.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Load the action-classification policy (YAML or JSON) so we know which
|
|
57
|
+
* actions are low/medium/high risk and how each should be gated.
|
|
58
|
+
*
|
|
59
|
+
* Returns a normalised map: { actionName: { risk, approval, ... } }
|
|
60
|
+
*/
|
|
61
|
+
export function loadActionClassification(agentRoot, opts = {}) {
|
|
62
|
+
const root = resolve(
|
|
63
|
+
agentRoot || opts.agentRoot || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd()
|
|
64
|
+
);
|
|
65
|
+
// Try JSON first (no js-yaml dependency), then YAML.
|
|
66
|
+
const candidates = [
|
|
67
|
+
join(root, "policies/action-classification.json"),
|
|
68
|
+
join(root, "policies/action-classification.yaml"),
|
|
69
|
+
];
|
|
70
|
+
for (const path of candidates) {
|
|
71
|
+
if (!existsSync(path)) continue;
|
|
72
|
+
const raw = readFileSync(path, "utf-8");
|
|
73
|
+
if (path.endsWith(".json")) return normaliseClassification(JSON.parse(raw));
|
|
74
|
+
// YAML policy file. createRequire makes this resolve correctly when
|
|
75
|
+
// js-yaml is installed; if it genuinely is not, we fall back to {} rather
|
|
76
|
+
// than crashing — but we log it so a misconfigured policy is visible
|
|
77
|
+
// instead of silently disabling the gate.
|
|
78
|
+
try {
|
|
79
|
+
const yaml = require("js-yaml");
|
|
80
|
+
return normaliseClassification(yaml.load(raw));
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(
|
|
83
|
+
`[tool-bridge] Could not load YAML action policy at ${path} ` +
|
|
84
|
+
`(${err.message}); action-classification gate is INACTIVE for this session.`
|
|
85
|
+
);
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normaliseClassification(raw) {
|
|
93
|
+
if (!raw || typeof raw !== "object") return {};
|
|
94
|
+
const out = {};
|
|
95
|
+
// Two shapes we accept:
|
|
96
|
+
// { actions: { send_slack_message: { risk: "high", ... } } }
|
|
97
|
+
// { send_slack_message: { risk: "high", ... } }
|
|
98
|
+
const actions = raw.actions || raw;
|
|
99
|
+
for (const [name, def] of Object.entries(actions)) {
|
|
100
|
+
if (!def || typeof def !== "object") continue;
|
|
101
|
+
out[name] = {
|
|
102
|
+
risk: def.risk || def.level || "low",
|
|
103
|
+
approval: def.approval || def.gate || "none",
|
|
104
|
+
verbal_confirm: def.verbal_confirm ?? (def.risk === "high"),
|
|
105
|
+
notes: def.notes || "",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build the array of function tools for the Realtime session. By default
|
|
113
|
+
* exposes every "communication"-tier maestro tool (send-slack, send-email,
|
|
114
|
+
* send-whatsapp, etc.) plus the "low-risk" research tools.
|
|
115
|
+
*
|
|
116
|
+
* @param {object} opts
|
|
117
|
+
* @param {string[]} [opts.include] Specific tool names to include
|
|
118
|
+
* @param {string[]} [opts.exclude] Names to exclude
|
|
119
|
+
* @param {string} [opts.accessLevel] "voice" (default), "full"
|
|
120
|
+
* @param {object} [opts.classification] action-classification map
|
|
121
|
+
* @returns {Promise<Array<RealtimeTool>>}
|
|
122
|
+
*/
|
|
123
|
+
export async function actionsToRealtimeTools(opts = {}) {
|
|
124
|
+
const { _toolDefs } = await loadMaestroBindings();
|
|
125
|
+
const classification = opts.classification || loadActionClassification(opts.agentRoot);
|
|
126
|
+
const accessLevel = opts.accessLevel || "voice";
|
|
127
|
+
let sourceTools;
|
|
128
|
+
if (typeof _toolDefs.getToolsForAccessLevel === "function") {
|
|
129
|
+
// The framework already exposes this — use it so the voice surface
|
|
130
|
+
// automatically picks up new tools added to the registry.
|
|
131
|
+
sourceTools = _toolDefs.getToolsForAccessLevel(accessLevel);
|
|
132
|
+
} else if (Array.isArray(_toolDefs.ALL_TOOLS)) {
|
|
133
|
+
sourceTools = _toolDefs.ALL_TOOLS;
|
|
134
|
+
} else {
|
|
135
|
+
sourceTools = [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const include = new Set(opts.include || []);
|
|
139
|
+
const exclude = new Set(opts.exclude || []);
|
|
140
|
+
|
|
141
|
+
const out = [];
|
|
142
|
+
for (const t of sourceTools) {
|
|
143
|
+
const name = t.name || t.toolName;
|
|
144
|
+
if (!name) continue;
|
|
145
|
+
if (include.size && !include.has(name)) continue;
|
|
146
|
+
if (exclude.has(name)) continue;
|
|
147
|
+
const risk = classification[name]?.risk || "low";
|
|
148
|
+
const verbalConfirm = classification[name]?.verbal_confirm ?? (risk === "high");
|
|
149
|
+
const baseDesc = t.description || `Maestro action: ${name}`;
|
|
150
|
+
const description = verbalConfirm
|
|
151
|
+
? `${baseDesc}\n\n⚠ HIGH-RISK action — before calling this tool you MUST verbally read back the parameters (recipient, amount, key fields) and obtain explicit user confirmation in the SAME turn. Do not call this tool on the first turn it is mentioned.`
|
|
152
|
+
: baseDesc;
|
|
153
|
+
|
|
154
|
+
out.push({
|
|
155
|
+
type: "function",
|
|
156
|
+
name,
|
|
157
|
+
description,
|
|
158
|
+
parameters: toolParameters(t),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function toolParameters(t) {
|
|
165
|
+
// We accept either: t.parameters (already JSON Schema) OR t.input_schema
|
|
166
|
+
// (Anthropic-style). Both pass through unchanged; the OpenAI Realtime
|
|
167
|
+
// function format wants JSON Schema in `parameters`.
|
|
168
|
+
if (t.parameters && typeof t.parameters === "object") return t.parameters;
|
|
169
|
+
if (t.input_schema && typeof t.input_schema === "object") return t.input_schema;
|
|
170
|
+
if (t.inputSchema && typeof t.inputSchema === "object") return t.inputSchema;
|
|
171
|
+
return { type: "object", properties: {}, additionalProperties: true };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Audit redaction (L11) ─────────────────────────────────────────────────
|
|
175
|
+
// The audit hook receives raw tool arguments and results, which on the voice
|
|
176
|
+
// surface routinely contain PII and message bodies (recipients, amounts, email
|
|
177
|
+
// text, etc.). Callers that ship audit entries to a shared sink can opt in to
|
|
178
|
+
// redaction so secrets/PII don't leak into logs. Default behaviour is unchanged
|
|
179
|
+
// (no redaction) — you must pass opts.redact, opts.auditAllowlist, or
|
|
180
|
+
// opts.redactAudit to enable any of this.
|
|
181
|
+
|
|
182
|
+
const DEFAULT_TRUNCATE_LEN = 256;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Deep-redact a JSON-ish value for audit logging:
|
|
186
|
+
* - values of object keys whose NAME looks sensitive (token/secret/password/
|
|
187
|
+
* auth/… per lib/security/redact.mjs) are replaced with REDACTED ("***")
|
|
188
|
+
* - string values are passed through redactSensitiveUrl (strips embedded
|
|
189
|
+
* credentials + masks sensitive query params) and truncated to maxLen
|
|
190
|
+
* - arrays/objects are walked recursively (bounded depth, cycle-safe)
|
|
191
|
+
*
|
|
192
|
+
* Pure; never throws.
|
|
193
|
+
*
|
|
194
|
+
* @param {*} value
|
|
195
|
+
* @param {{ maxLen?: number, allowlist?: Set<string>|null, depth?: number,
|
|
196
|
+
* seen?: WeakSet<object> }} [opts]
|
|
197
|
+
* @returns {*}
|
|
198
|
+
*/
|
|
199
|
+
export function redactAuditValue(value, opts = {}) {
|
|
200
|
+
const maxLen = opts.maxLen ?? DEFAULT_TRUNCATE_LEN;
|
|
201
|
+
const allowlist = opts.allowlist || null;
|
|
202
|
+
const depth = opts.depth ?? 0;
|
|
203
|
+
const seen = opts.seen || new WeakSet();
|
|
204
|
+
|
|
205
|
+
if (value == null) return value;
|
|
206
|
+
if (typeof value === "string") {
|
|
207
|
+
let out = redactSensitiveUrl(value);
|
|
208
|
+
if (maxLen > 0 && out.length > maxLen) out = out.slice(0, maxLen) + "…";
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
212
|
+
if (typeof value !== "object") return REDACTED; // function/symbol/bigint → mask
|
|
213
|
+
if (depth >= 6) return REDACTED; // bound recursion
|
|
214
|
+
if (seen.has(value)) return REDACTED; // cycle guard
|
|
215
|
+
seen.add(value);
|
|
216
|
+
|
|
217
|
+
if (Array.isArray(value)) {
|
|
218
|
+
return value.map((v) =>
|
|
219
|
+
redactAuditValue(v, { maxLen, allowlist, depth: depth + 1, seen })
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const out = {};
|
|
224
|
+
for (const [k, v] of Object.entries(value)) {
|
|
225
|
+
// Field allowlist: if provided, only top-level keys (depth 0) on the list
|
|
226
|
+
// survive; the rest are dropped entirely.
|
|
227
|
+
if (allowlist && depth === 0 && !allowlist.has(k)) continue;
|
|
228
|
+
if (isSensitiveParamName(k)) {
|
|
229
|
+
out[k] = REDACTED;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
out[k] = redactAuditValue(v, { maxLen, allowlist, depth: depth + 1, seen });
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Build the redactor applied to each audit entry's args/result, from
|
|
239
|
+
* registerToolBridge opts. Returns null when no redaction was requested (so the
|
|
240
|
+
* default no-op path stays allocation-free).
|
|
241
|
+
*
|
|
242
|
+
* @param {object} opts
|
|
243
|
+
* @returns {((entry:object)=>object)|null}
|
|
244
|
+
*/
|
|
245
|
+
function buildAuditRedactor(opts) {
|
|
246
|
+
// Fully-custom redactor wins.
|
|
247
|
+
if (typeof opts.redact === "function") return opts.redact;
|
|
248
|
+
|
|
249
|
+
const wantAllowlist = Array.isArray(opts.auditAllowlist);
|
|
250
|
+
const wantRedact = opts.redactAudit === true || wantAllowlist;
|
|
251
|
+
if (!wantRedact) return null;
|
|
252
|
+
|
|
253
|
+
const allowlist = wantAllowlist ? new Set(opts.auditAllowlist) : null;
|
|
254
|
+
const maxLen = typeof opts.auditMaxLen === "number" ? opts.auditMaxLen : DEFAULT_TRUNCATE_LEN;
|
|
255
|
+
return (entry) => {
|
|
256
|
+
const next = { ...entry };
|
|
257
|
+
if ("args" in next) next.args = redactAuditValue(next.args, { allowlist, maxLen });
|
|
258
|
+
if ("result" in next) next.result = redactAuditValue(next.result, { allowlist, maxLen });
|
|
259
|
+
return next;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Attach a tool-call handler to a voice provider. When the model emits a
|
|
265
|
+
* tool.call event, we look up the action in maestro's executor, run it,
|
|
266
|
+
* and feed the result back into the conversation via
|
|
267
|
+
* provider.sendToolResult().
|
|
268
|
+
*
|
|
269
|
+
* Returns a teardown fn that removes the listeners.
|
|
270
|
+
*
|
|
271
|
+
* @param {VoiceProvider} provider
|
|
272
|
+
* @param {object} opts
|
|
273
|
+
* @param {(name:string,args:object)=>Promise<object>} [opts.executor]
|
|
274
|
+
* Override the default executor (useful for tests).
|
|
275
|
+
* @param {Array<string>} [opts.allow] Allow-list of tool names; others
|
|
276
|
+
* are refused without invocation.
|
|
277
|
+
* @param {object} [opts.classification] action-classification map
|
|
278
|
+
* @param {(entry:object)=>void} [opts.audit] Hook called for each tool
|
|
279
|
+
* call with {name, args, result, error, started_at, finished_at}.
|
|
280
|
+
* @param {(entry:object)=>object} [opts.redact] Optional redactor applied to
|
|
281
|
+
* the audit entry BEFORE it reaches opts.audit. Receives the full entry,
|
|
282
|
+
* returns the (redacted) entry to log. Use this for full control.
|
|
283
|
+
* @param {boolean} [opts.redactAudit] Convenience flag: when true, apply the
|
|
284
|
+
* built-in deep redactor to entry.args/entry.result — masks sensitive
|
|
285
|
+
* field values, strips URL credentials, and truncates long strings.
|
|
286
|
+
* @param {string[]} [opts.auditAllowlist] Convenience: keep only these
|
|
287
|
+
* top-level arg/result fields in the audit entry (implies redactAudit).
|
|
288
|
+
* @param {number} [opts.auditMaxLen] Max length for redacted strings
|
|
289
|
+
* (default 256). Only used by the built-in redactor.
|
|
290
|
+
*
|
|
291
|
+
* Default behaviour is unchanged: with none of the redact options set, raw
|
|
292
|
+
* args/results reach the audit sink exactly as before.
|
|
293
|
+
*/
|
|
294
|
+
export function registerToolBridge(provider, opts = {}) {
|
|
295
|
+
const allow = opts.allow ? new Set(opts.allow) : null;
|
|
296
|
+
const classification = opts.classification || {};
|
|
297
|
+
const rawAudit = typeof opts.audit === "function" ? opts.audit : () => {};
|
|
298
|
+
const redactor = buildAuditRedactor(opts);
|
|
299
|
+
// Single choke point so every audit site goes through the redactor (if any).
|
|
300
|
+
// Redaction must never break auditing, so failures fall back to the raw entry.
|
|
301
|
+
const audit = redactor
|
|
302
|
+
? (entry) => {
|
|
303
|
+
let safe = entry;
|
|
304
|
+
try {
|
|
305
|
+
safe = redactor(entry);
|
|
306
|
+
} catch {
|
|
307
|
+
safe = entry;
|
|
308
|
+
}
|
|
309
|
+
rawAudit(safe);
|
|
310
|
+
}
|
|
311
|
+
: rawAudit;
|
|
312
|
+
|
|
313
|
+
const handler = async (evt) => {
|
|
314
|
+
const { call_id, name, arguments: args } = evt;
|
|
315
|
+
const startedAt = new Date().toISOString();
|
|
316
|
+
|
|
317
|
+
if (!call_id || !name) {
|
|
318
|
+
audit({ name, args, error: "missing_call_id_or_name", started_at: startedAt, finished_at: new Date().toISOString() });
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (allow && !allow.has(name)) {
|
|
322
|
+
provider.sendToolResult(call_id, {
|
|
323
|
+
ok: false,
|
|
324
|
+
error: "tool_not_allowed",
|
|
325
|
+
message: `Tool "${name}" is not in the voice-session allow-list. The agent must invoke a different tool or escalate to a human.`,
|
|
326
|
+
});
|
|
327
|
+
audit({ name, args, error: "tool_not_allowed", started_at: startedAt, finished_at: new Date().toISOString() });
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
let result;
|
|
332
|
+
let error = null;
|
|
333
|
+
try {
|
|
334
|
+
if (opts.executor) {
|
|
335
|
+
result = await opts.executor(name, args || {});
|
|
336
|
+
} else {
|
|
337
|
+
const { _executor } = await loadMaestroBindings();
|
|
338
|
+
const fn = _executor.executeAction || _executor.default;
|
|
339
|
+
if (typeof fn !== "function") {
|
|
340
|
+
throw new Error("maestro action-executor is missing executeAction()");
|
|
341
|
+
}
|
|
342
|
+
result = await fn(name, args || {});
|
|
343
|
+
}
|
|
344
|
+
} catch (err) {
|
|
345
|
+
error = err.message || String(err);
|
|
346
|
+
result = { ok: false, error };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
provider.sendToolResult(call_id, result);
|
|
350
|
+
audit({
|
|
351
|
+
name,
|
|
352
|
+
args,
|
|
353
|
+
result,
|
|
354
|
+
error,
|
|
355
|
+
risk: classification[name]?.risk || "low",
|
|
356
|
+
started_at: startedAt,
|
|
357
|
+
finished_at: new Date().toISOString(),
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
provider.on("tool.call", handler);
|
|
362
|
+
return () => provider.off("tool.call", handler);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export default {
|
|
366
|
+
actionsToRealtimeTools,
|
|
367
|
+
registerToolBridge,
|
|
368
|
+
loadActionClassification,
|
|
369
|
+
redactAuditValue,
|
|
370
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — Text-to-speech helper for outbound voice messages.
|
|
3
|
+
*
|
|
4
|
+
* Wraps OpenAI's `/v1/audio/speech` endpoint. Used by the voice channel
|
|
5
|
+
* adapter's send() path when an agent wants to deliver a voice note
|
|
6
|
+
* over WhatsApp, Slack, or a one-shot Twilio <Play> phone call.
|
|
7
|
+
*
|
|
8
|
+
* Why a separate helper:
|
|
9
|
+
* - One-shot TTS is much cheaper than spinning up a full Realtime
|
|
10
|
+
* session ($0.015/M chars for gpt-4o-mini-tts vs. $32-$64/M tokens
|
|
11
|
+
* for gpt-realtime-2).
|
|
12
|
+
* - WhatsApp / Slack / SMS can't take live audio anyway — they want
|
|
13
|
+
* a file. TTS is the right primitive for asynchronous delivery.
|
|
14
|
+
* - The Realtime API is reserved for true live conversations.
|
|
15
|
+
*
|
|
16
|
+
* Public API:
|
|
17
|
+
* synthesizeSpeech({ text, voice, format, model }) → Promise<Buffer>
|
|
18
|
+
* ttsCostEstimate(text, model) → number (USD)
|
|
19
|
+
*
|
|
20
|
+
* Voices (May 2026): alloy, ash, ballad, coral, echo, sage, shimmer,
|
|
21
|
+
* verse + the GA-only Realtime voices cedar / marin (only available
|
|
22
|
+
* via gpt-realtime-2; tts-1 / gpt-4o-mini-tts use the original eight).
|
|
23
|
+
*
|
|
24
|
+
* Formats: mp3 (default; ~16 kbps for tts-1), opus (best for WhatsApp
|
|
25
|
+
* PTT), aac, flac, wav, pcm.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const OPENAI_TTS_URL = "https://api.openai.com/v1/audio/speech";
|
|
29
|
+
|
|
30
|
+
const DEFAULT_MODEL = "gpt-4o-mini-tts"; // best quality/cost for prose
|
|
31
|
+
const DEFAULT_VOICE = "alloy";
|
|
32
|
+
const DEFAULT_FORMAT = "mp3";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {object} opts
|
|
36
|
+
* @param {string} opts.text The text to synthesize (max ~4096 chars per call).
|
|
37
|
+
* @param {string} [opts.voice] Voice name (default: alloy).
|
|
38
|
+
* @param {string} [opts.format] Audio format (default: mp3). Use "opus" for WhatsApp PTT.
|
|
39
|
+
* @param {string} [opts.model] TTS model id (default: gpt-4o-mini-tts).
|
|
40
|
+
* @param {number} [opts.speed] Playback speed multiplier 0.25–4.0 (default: 1.0).
|
|
41
|
+
* @param {string} [opts.apiKey] Override OPENAI_API_KEY.
|
|
42
|
+
* @returns {Promise<Buffer>} Audio bytes.
|
|
43
|
+
*/
|
|
44
|
+
export async function synthesizeSpeech(opts = {}) {
|
|
45
|
+
const text = String(opts.text || "").trim();
|
|
46
|
+
if (!text) throw new Error("synthesizeSpeech: text required");
|
|
47
|
+
if (text.length > 4096) {
|
|
48
|
+
throw new Error(`synthesizeSpeech: text > 4096 chars (got ${text.length}); chunk it first`);
|
|
49
|
+
}
|
|
50
|
+
const apiKey = opts.apiKey || process.env.OPENAI_API_KEY;
|
|
51
|
+
if (!apiKey) throw new Error("OPENAI_API_KEY not set");
|
|
52
|
+
|
|
53
|
+
const body = {
|
|
54
|
+
model: opts.model || DEFAULT_MODEL,
|
|
55
|
+
input: text,
|
|
56
|
+
voice: opts.voice || DEFAULT_VOICE,
|
|
57
|
+
response_format: opts.format || DEFAULT_FORMAT,
|
|
58
|
+
};
|
|
59
|
+
if (typeof opts.speed === "number") body.speed = opts.speed;
|
|
60
|
+
|
|
61
|
+
const r = await fetch(OPENAI_TTS_URL, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${apiKey}`,
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(body),
|
|
68
|
+
});
|
|
69
|
+
if (!r.ok) {
|
|
70
|
+
const errText = await r.text();
|
|
71
|
+
throw new Error(`tts failed: HTTP ${r.status} ${errText.slice(0, 300)}`);
|
|
72
|
+
}
|
|
73
|
+
const arr = await r.arrayBuffer();
|
|
74
|
+
return Buffer.from(arr);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Rough USD cost estimate for a TTS call.
|
|
79
|
+
* gpt-4o-mini-tts: $0.015 / 1k characters
|
|
80
|
+
* tts-1 / tts-1-hd: $0.015 / 1k characters (legacy)
|
|
81
|
+
*/
|
|
82
|
+
export function ttsCostEstimate(text, model = DEFAULT_MODEL) {
|
|
83
|
+
const chars = String(text || "").length;
|
|
84
|
+
return Number(((chars / 1000) * 0.015).toFixed(6));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Choose a sensible default voice for an agent based on its config.
|
|
89
|
+
* Falls back to "alloy" so any agent gets a working default without
|
|
90
|
+
* needing to declare one. Operators can pin a specific voice in
|
|
91
|
+
* config/voice.yaml under `voice:`.
|
|
92
|
+
*/
|
|
93
|
+
export function pickDefaultVoice(agentVoiceConfig = {}) {
|
|
94
|
+
const v = (agentVoiceConfig.voice || "").toLowerCase();
|
|
95
|
+
// tts-1 / gpt-4o-mini-tts voices (not the Realtime-only cedar/marin)
|
|
96
|
+
const valid = ["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"];
|
|
97
|
+
if (valid.includes(v)) return v;
|
|
98
|
+
// Realtime-only voices fall back gracefully.
|
|
99
|
+
if (v === "cedar") return "ash";
|
|
100
|
+
if (v === "marin") return "shimmer";
|
|
101
|
+
return "alloy";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default { synthesizeSpeech, ttsCostEstimate, pickDefaultVoice };
|