@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,587 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — WhatsApp (Baileys) channel adapter
|
|
3
|
+
*
|
|
4
|
+
* Baileys is the de facto open-source WhatsApp Web reverse-engineering
|
|
5
|
+
* library used by openclaw's `/extensions/whatsapp/`. It supports
|
|
6
|
+
* everything Twilio's API does (text, image, document) plus the
|
|
7
|
+
* features Twilio's API can't reach because they're WhatsApp-Web-only:
|
|
8
|
+
* - Voice notes (PTT) — both inbound and outbound
|
|
9
|
+
* - Quoted-message threading (replies in chat)
|
|
10
|
+
* - Reactions
|
|
11
|
+
* - Group chats with full member roster
|
|
12
|
+
* - Read receipts
|
|
13
|
+
* - Typing indicators
|
|
14
|
+
*
|
|
15
|
+
* Trade-offs vs. Twilio:
|
|
16
|
+
* + Free (no per-message Twilio fee)
|
|
17
|
+
* + Voice notes work natively
|
|
18
|
+
* + Group chats and quoted-reply threading
|
|
19
|
+
* - Requires QR-code login from a real WhatsApp account (no Business API)
|
|
20
|
+
* - Banned by WhatsApp's ToS for production business use — use only
|
|
21
|
+
* for personal-account scenarios (e.g. a sole-proprietor agent
|
|
22
|
+
* speaking from the owner's account)
|
|
23
|
+
* - Auth state lives on disk; lose it and you re-scan the QR
|
|
24
|
+
*
|
|
25
|
+
* This adapter dynamically imports `baileys` so the framework doesn't
|
|
26
|
+
* force the dependency on every install. Operators who opt in add
|
|
27
|
+
* `baileys` to their agent's package.json:
|
|
28
|
+
*
|
|
29
|
+
* npm i baileys @whiskeysockets/baileys
|
|
30
|
+
*
|
|
31
|
+
* Voice note pipeline:
|
|
32
|
+
* inbound voice note → buffer ogg/opus
|
|
33
|
+
* → Whisper / gpt-realtime-whisper transcript
|
|
34
|
+
* → emit MessageEvent with .attachments + .text
|
|
35
|
+
* outbound voice note → caller passes attachment {type: "voice_note", buffer}
|
|
36
|
+
* → adapter encodes to ogg/opus + sends as PTT
|
|
37
|
+
*
|
|
38
|
+
* WS2 hardening (task #30): this adapter now extends BaseAdapter so it inherits
|
|
39
|
+
* the ONE shared outbound send-gate (banned-phrase / AI-disclosure / barrier
|
|
40
|
+
* screening before every send), the bounded-backoff reconnect SUPERVISOR (a
|
|
41
|
+
* dropped socket reconnects instead of the old one-shot setTimeout restart, and
|
|
42
|
+
* a dead channel turns healthCheck() red), and the ownId / peerAgentIds
|
|
43
|
+
* bot-loop guard. Subclasses implement only the platform hooks
|
|
44
|
+
* `_connect/_toEvent/_send/_react/_setTyping`; baileys is still dynamically
|
|
45
|
+
* imported. WhatsApp's typing indicator is presence-based ("composing"/"paused")
|
|
46
|
+
* rather than the base's per-tick `_setTyping`, so this adapter overrides the
|
|
47
|
+
* heartbeat methods to drive `sock.sendPresenceUpdate` directly.
|
|
48
|
+
*
|
|
49
|
+
* @module lib/channels/whatsapp/adapter-baileys
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import { join, resolve } from "node:path";
|
|
53
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
54
|
+
import { CHANNEL_CAPABILITIES } from "../channel.mjs";
|
|
55
|
+
import { definePlatform } from "../contract.mjs";
|
|
56
|
+
import { BaseAdapter } from "../base-adapter.mjs";
|
|
57
|
+
|
|
58
|
+
const BAILEYS_CAPABILITIES = [
|
|
59
|
+
CHANNEL_CAPABILITIES.SEND,
|
|
60
|
+
CHANNEL_CAPABILITIES.REACT,
|
|
61
|
+
CHANNEL_CAPABILITIES.THREAD,
|
|
62
|
+
CHANNEL_CAPABILITIES.MEDIA_IMAGE,
|
|
63
|
+
CHANNEL_CAPABILITIES.MEDIA_AUDIO,
|
|
64
|
+
CHANNEL_CAPABILITIES.MEDIA_DOCUMENT,
|
|
65
|
+
CHANNEL_CAPABILITIES.VOICE_NOTE,
|
|
66
|
+
CHANNEL_CAPABILITIES.GROUP_CHAT,
|
|
67
|
+
CHANNEL_CAPABILITIES.READ_RECEIPTS,
|
|
68
|
+
CHANNEL_CAPABILITIES.TYPING_INDICATOR,
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/** Presence-refresh cadence for the WhatsApp typing heartbeat (ms). */
|
|
72
|
+
const TYPING_REFRESH_MS = 8000;
|
|
73
|
+
|
|
74
|
+
let _baileysCache = null;
|
|
75
|
+
async function loadBaileys(agentRoot) {
|
|
76
|
+
if (_baileysCache) return _baileysCache;
|
|
77
|
+
// Resolution strategy:
|
|
78
|
+
// 1. Resolve from the agent's node_modules (baileys is heavy; the
|
|
79
|
+
// framework doesn't bundle it — operators install it in the
|
|
80
|
+
// agent repo).
|
|
81
|
+
// 2. Fall back to framework-relative resolution if the agent path
|
|
82
|
+
// doesn't have it (e.g. when run in a dev checkout).
|
|
83
|
+
const candidates = [];
|
|
84
|
+
if (agentRoot) {
|
|
85
|
+
candidates.push(`file://${agentRoot}/node_modules/baileys/lib/index.js`);
|
|
86
|
+
candidates.push(`file://${agentRoot}/node_modules/baileys/lib/index.mjs`);
|
|
87
|
+
candidates.push(`file://${agentRoot}/node_modules/baileys/index.js`);
|
|
88
|
+
candidates.push(`file://${agentRoot}/node_modules/@whiskeysockets/baileys/lib/index.js`);
|
|
89
|
+
}
|
|
90
|
+
candidates.push("baileys");
|
|
91
|
+
candidates.push("@whiskeysockets/baileys");
|
|
92
|
+
|
|
93
|
+
const errors = [];
|
|
94
|
+
for (const spec of candidates) {
|
|
95
|
+
try {
|
|
96
|
+
const mod = await import(spec);
|
|
97
|
+
_baileysCache = mod;
|
|
98
|
+
return mod;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
errors.push(`${spec}: ${err.message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error(
|
|
104
|
+
"WhatsApp(Baileys) requires the `baileys` package installed in the agent repo. " +
|
|
105
|
+
"Run: `npm i baileys` in your agent repo. " +
|
|
106
|
+
`Tried resolutions:\n - ${errors.join("\n - ")}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* WhatsApp (Baileys) adapter. Extends BaseAdapter so `send()` runs the shared
|
|
112
|
+
* send-gate before `_send`, `_connect` is wrapped by the reconnect supervisor,
|
|
113
|
+
* and inbound from this agent / a peer agent is dropped (bot-loop guard).
|
|
114
|
+
*/
|
|
115
|
+
export class BaileysWhatsAppAdapter extends BaseAdapter {
|
|
116
|
+
/**
|
|
117
|
+
* @param {object} opts
|
|
118
|
+
* @param {string} [opts.authDir]
|
|
119
|
+
* @param {(buf:Buffer, meta:object)=>Promise<string>} [opts.transcribeVoiceNote]
|
|
120
|
+
* @param {any} [opts.sock] inject a connected Baileys socket (tests)
|
|
121
|
+
*/
|
|
122
|
+
constructor(opts = {}) {
|
|
123
|
+
super({ name: "whatsapp", capabilities: BAILEYS_CAPABILITIES, ...opts });
|
|
124
|
+
this.transport = "baileys";
|
|
125
|
+
this.authDir = opts.authDir || join(this.agentRoot, "state/whatsapp/auth");
|
|
126
|
+
this.transcribe =
|
|
127
|
+
typeof opts.transcribeVoiceNote === "function" ? opts.transcribeVoiceNote : null;
|
|
128
|
+
|
|
129
|
+
/** @type {any} the Baileys WASocket once connected */
|
|
130
|
+
this._sock = opts.sock || null;
|
|
131
|
+
|
|
132
|
+
// Per-jid "composing" presence refresh timers (typing heartbeat). WhatsApp's
|
|
133
|
+
// typing indicator auto-expires (~25s), so we re-assert "composing" on an
|
|
134
|
+
// interval while a session is working, then send "paused" on stop. Keyed by
|
|
135
|
+
// jid so a second start for the same chat doesn't stack timers. This is the
|
|
136
|
+
// presence-based heartbeat WhatsApp needs in place of the base's per-tick
|
|
137
|
+
// `_setTyping`, so the heartbeat methods are overridden below.
|
|
138
|
+
/** @type {Map<string, any>} */
|
|
139
|
+
this._presenceTimers = new Map();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Test seams (no-ops in production; never called on the live path) ──
|
|
143
|
+
/** @internal inject a fake sock without connecting (tests only). */
|
|
144
|
+
__setSock(s) { this._sock = s; }
|
|
145
|
+
/** @internal number of active per-jid typing timers (tests only). */
|
|
146
|
+
__typingTimerCount() { return this._presenceTimers.size; }
|
|
147
|
+
|
|
148
|
+
/** Best-effort presence send; a missing/disconnected sock just no-ops. */
|
|
149
|
+
_sendPresence(state, jid) {
|
|
150
|
+
try {
|
|
151
|
+
const sock = this._sock;
|
|
152
|
+
if (sock && typeof sock.sendPresenceUpdate === "function") {
|
|
153
|
+
// sendPresenceUpdate may be async; we don't await (fire-and-forget) and
|
|
154
|
+
// swallow any rejection so a dropped socket never surfaces here.
|
|
155
|
+
Promise.resolve(sock.sendPresenceUpdate(state, jid)).catch(() => {});
|
|
156
|
+
}
|
|
157
|
+
} catch { /* fail-open: disconnected/absent sock */ }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Resolve a jid from a SessionSource ({chatId}) or a bare chatId string. */
|
|
161
|
+
_jidFromSource(source) {
|
|
162
|
+
const raw = source && typeof source === "object" ? source.chatId : source;
|
|
163
|
+
if (!raw) return "";
|
|
164
|
+
try { return normaliseJid(String(raw)); } catch { return ""; }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// -- connect: open the Baileys socket, wire its events to the supervisor ----
|
|
168
|
+
|
|
169
|
+
async _connect({ onRaw, signal }) {
|
|
170
|
+
mkdirSync(this.authDir, { recursive: true });
|
|
171
|
+
|
|
172
|
+
if (!this._sock) {
|
|
173
|
+
const baileys = await loadBaileys(this.agentRoot);
|
|
174
|
+
const makeSocket = baileys.makeWASocket || baileys.default;
|
|
175
|
+
const useMultiFileAuthState = baileys.useMultiFileAuthState;
|
|
176
|
+
const fetchLatestBaileysVersion = baileys.fetchLatestBaileysVersion;
|
|
177
|
+
|
|
178
|
+
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
179
|
+
const { version } = fetchLatestBaileysVersion
|
|
180
|
+
? await fetchLatestBaileysVersion()
|
|
181
|
+
: { version: undefined };
|
|
182
|
+
|
|
183
|
+
this._sock = makeSocket({
|
|
184
|
+
version,
|
|
185
|
+
auth: state,
|
|
186
|
+
printQRInTerminal: false, // we surface the QR code via the event handler
|
|
187
|
+
// Some Baileys versions accept `logger`; we pass a no-op-ish to silence noise.
|
|
188
|
+
logger: {
|
|
189
|
+
level: "warn", info: () => {}, warn: () => {}, error: (...a) => this.log("error", a.join(" ")),
|
|
190
|
+
debug: () => {}, trace: () => {}, child: () => ({ level: "warn", info: () => {}, warn: () => {}, error: () => {}, debug: () => {}, trace: () => {}, child: () => this }),
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
this._sock.ev.on("creds.update", saveCreds);
|
|
195
|
+
}
|
|
196
|
+
const sock = this._sock;
|
|
197
|
+
const DisconnectReason = (await loadBaileysDisconnectReason(this.agentRoot)) || {};
|
|
198
|
+
|
|
199
|
+
sock.ev.on("connection.update", (update) => {
|
|
200
|
+
const { connection, lastDisconnect, qr } = update;
|
|
201
|
+
if (qr) {
|
|
202
|
+
// Persist the QR string so the operator can render it elsewhere
|
|
203
|
+
// (terminal qrcode-terminal, web UI, etc.) without forcing us
|
|
204
|
+
// to pull a QR-rendering dep into the framework.
|
|
205
|
+
try { writeFileSync(join(this.authDir, "pending-qr.txt"), qr); } catch { /* */ }
|
|
206
|
+
this.log("info", `QR code pending — scan it. Saved to ${join(this.authDir, "pending-qr.txt")}`);
|
|
207
|
+
}
|
|
208
|
+
if (connection === "open") {
|
|
209
|
+
this.log("info", "WhatsApp (Baileys) connected");
|
|
210
|
+
} else if (connection === "close") {
|
|
211
|
+
const reason = lastDisconnect?.error?.output?.statusCode;
|
|
212
|
+
// A logged-out socket can't be recovered by reconnecting (the auth state
|
|
213
|
+
// is gone) — let the supervisor give up rather than thrash. Every other
|
|
214
|
+
// drop is reported to the reconnect supervisor, which owns the bounded
|
|
215
|
+
// backoff (replacing the old one-shot setTimeout(start, 5000) that died
|
|
216
|
+
// permanently after a single swallowed failure — gaps P0-6).
|
|
217
|
+
const loggedOut = reason === (DisconnectReason.loggedOut || 401);
|
|
218
|
+
this.log("warn", `connection closed (reason=${reason}); loggedOut=${loggedOut}`);
|
|
219
|
+
if (!loggedOut && !this._stopping && !signal?.aborted) {
|
|
220
|
+
this.notifyConnectionDown(`whatsapp-close:${reason ?? "?"}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
sock.ev.on("messages.upsert", async ({ messages, type }) => {
|
|
226
|
+
if (type !== "notify") return;
|
|
227
|
+
for (const m of messages) {
|
|
228
|
+
if (m.key?.fromMe) continue;
|
|
229
|
+
try {
|
|
230
|
+
const raw = await normaliseInbound(m, { sock, transcribe: this.transcribe, agentRoot: this.agentRoot });
|
|
231
|
+
if (raw) onRaw(raw);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
this.log("error", `failed to normalise inbound: ${err.message}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (signal) signal.addEventListener?.("abort", () => { try { sock?.end?.(undefined); } catch { /* */ } });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// -- translate: normalised inbound → MessageEvent --------------------------
|
|
242
|
+
//
|
|
243
|
+
// normaliseInbound already produces a MessageEvent-shaped object (with a
|
|
244
|
+
// `source` descriptor) so the platform factory's old start-wrapper is no
|
|
245
|
+
// longer needed; _toEvent simply forwards it, with a defensive null check.
|
|
246
|
+
|
|
247
|
+
_toEvent(raw) {
|
|
248
|
+
if (!raw || typeof raw !== "object") return null;
|
|
249
|
+
return raw;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// -- outbound transport (gated by BaseAdapter.send before this runs) --------
|
|
253
|
+
|
|
254
|
+
async _send(out) {
|
|
255
|
+
const sock = this._sock;
|
|
256
|
+
if (!sock) throw new Error("WhatsApp(Baileys) not connected — call start() first");
|
|
257
|
+
const to = out?.to ?? out?.source?.chatId;
|
|
258
|
+
if (!to) throw new Error("WhatsApp._send: `to` (jid like '9715...@s.whatsapp.net' or E.164) required");
|
|
259
|
+
const jid = normaliseJid(to);
|
|
260
|
+
|
|
261
|
+
// Quoted reply
|
|
262
|
+
const threadRef = out.thread_ref ?? out.source?.threadRef;
|
|
263
|
+
const quoted = threadRef ? { key: parseQuotedRef(threadRef) } : undefined;
|
|
264
|
+
|
|
265
|
+
// Voice note path
|
|
266
|
+
if (out.attachments?.length === 1 && out.attachments[0].type === "voice_note") {
|
|
267
|
+
const att = out.attachments[0];
|
|
268
|
+
const audio = att.buffer || (att.url ? await fetchUrl(att.url) : null);
|
|
269
|
+
if (!audio) throw new Error("voice_note attachment requires buffer or url");
|
|
270
|
+
const result = await sock.sendMessage(jid, {
|
|
271
|
+
audio,
|
|
272
|
+
mimetype: att.mime || "audio/ogg; codecs=opus",
|
|
273
|
+
ptt: true,
|
|
274
|
+
}, quoted);
|
|
275
|
+
return { ok: true, message_id: result?.key?.id || "", raw: result };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Media attachments
|
|
279
|
+
if (out.attachments?.length) {
|
|
280
|
+
// Send the first attachment with a caption from out.text; for
|
|
281
|
+
// multi-attachment payloads, send them sequentially (caller
|
|
282
|
+
// gets the id of the last send).
|
|
283
|
+
let last;
|
|
284
|
+
for (let i = 0; i < out.attachments.length; i++) {
|
|
285
|
+
const att = out.attachments[i];
|
|
286
|
+
const buffer = att.buffer || (att.url ? await fetchUrl(att.url) : null);
|
|
287
|
+
if (!buffer) throw new Error(`attachment ${i} requires buffer or url`);
|
|
288
|
+
const payload = mediaPayload(att, buffer, i === 0 ? out.text : undefined);
|
|
289
|
+
last = await sock.sendMessage(jid, payload, quoted);
|
|
290
|
+
}
|
|
291
|
+
return { ok: true, message_id: last?.key?.id || "", raw: last };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Plain text
|
|
295
|
+
const result = await sock.sendMessage(jid, { text: out.text || "" }, quoted);
|
|
296
|
+
return { ok: true, message_id: result?.key?.id || "", raw: result };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async _react(out) {
|
|
300
|
+
const sock = this._sock;
|
|
301
|
+
if (!sock) throw new Error("WhatsApp(Baileys) not connected");
|
|
302
|
+
const ref = parseQuotedRef(out.messageRef);
|
|
303
|
+
await sock.sendMessage(ref.remoteJid, {
|
|
304
|
+
react: { text: out.emoji, key: ref },
|
|
305
|
+
});
|
|
306
|
+
return { ok: true };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// -- typing: presence-based heartbeat (overrides the base per-tick model) ---
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Start a presence-based "typing…" heartbeat for a conversation. Sends
|
|
313
|
+
* "composing" immediately, then refreshes it on an unref'd interval so the
|
|
314
|
+
* indicator doesn't lapse mid-session. Idempotent per jid. Fail-open: a
|
|
315
|
+
* missing/disconnected sock makes this a no-op.
|
|
316
|
+
* @param {{chatId?:string}|string} source
|
|
317
|
+
* @returns {any} the (existing or new) timer handle
|
|
318
|
+
*/
|
|
319
|
+
startTypingHeartbeat(source) {
|
|
320
|
+
try {
|
|
321
|
+
const jid = this._jidFromSource(source);
|
|
322
|
+
if (!jid) return null;
|
|
323
|
+
this._sendPresence("composing", jid);
|
|
324
|
+
const existing = this._presenceTimers.get(jid);
|
|
325
|
+
if (existing) return existing; // already heart-beating this jid — don't stack
|
|
326
|
+
const timer = this._setInterval(() => this._sendPresence("composing", jid), TYPING_REFRESH_MS);
|
|
327
|
+
if (timer && typeof timer.unref === "function") timer.unref();
|
|
328
|
+
this._presenceTimers.set(jid, timer);
|
|
329
|
+
return timer;
|
|
330
|
+
} catch {
|
|
331
|
+
return null; // fail-open
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Stop the heartbeat for a conversation: clear its refresh timer and send a
|
|
337
|
+
* best-effort "paused" presence. Fail-open; stopping an unknown jid is a
|
|
338
|
+
* clean no-op.
|
|
339
|
+
* @param {{chatId?:string}|string} source
|
|
340
|
+
*/
|
|
341
|
+
stopTypingHeartbeat(source) {
|
|
342
|
+
try {
|
|
343
|
+
const jid = this._jidFromSource(source);
|
|
344
|
+
if (!jid) return;
|
|
345
|
+
const timer = this._presenceTimers.get(jid);
|
|
346
|
+
if (timer) {
|
|
347
|
+
this._clearInterval(timer);
|
|
348
|
+
this._presenceTimers.delete(jid);
|
|
349
|
+
}
|
|
350
|
+
this._sendPresence("paused", jid);
|
|
351
|
+
} catch { /* fail-open */ }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** Clear every presence heartbeat (called from stop()). */
|
|
355
|
+
stopAllTyping() {
|
|
356
|
+
for (const t of this._presenceTimers.values()) { try { this._clearInterval(t); } catch { /* */ } }
|
|
357
|
+
this._presenceTimers.clear();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Base hook so any base-class caller of `_setTyping` still drives the right
|
|
362
|
+
* WhatsApp presence (the heartbeat methods above are the live path).
|
|
363
|
+
*/
|
|
364
|
+
async _setTyping(source, on) {
|
|
365
|
+
const jid = this._jidFromSource(source);
|
|
366
|
+
if (!jid) return;
|
|
367
|
+
this._sendPresence(on ? "composing" : "paused", jid);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// -- health / lifecycle ----------------------------------------------------
|
|
371
|
+
|
|
372
|
+
async healthCheck() {
|
|
373
|
+
// Prefer the supervisor's view when supervised (a dead channel reports RED);
|
|
374
|
+
// otherwise fall back to the socket-identity probe.
|
|
375
|
+
const base = await super.healthCheck();
|
|
376
|
+
const connected = !!this._sock?.user;
|
|
377
|
+
if (this._superviseEnabled && this._supervisor) {
|
|
378
|
+
return {
|
|
379
|
+
...base,
|
|
380
|
+
detail: connected ? `connected as ${this._sock.user.id}` : base.detail,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return { ok: connected, detail: connected ? `connected as ${this._sock.user.id}` : "not connected" };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async stop() {
|
|
387
|
+
await super.stop();
|
|
388
|
+
this.stopAllTyping();
|
|
389
|
+
try { this._sock?.end?.(undefined); } catch { /* */ }
|
|
390
|
+
this._sock = null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Factory matching the channel/platform contract. Returns a BaseAdapter
|
|
396
|
+
* subclass instance; the daemon/registry call `.start({onInbound,signal})`,
|
|
397
|
+
* `.send()`, `.react()`, `.healthCheck()`, `.stop()`, and the typing-registry
|
|
398
|
+
* drives `.startTypingHeartbeat`/`.stopTypingHeartbeat`.
|
|
399
|
+
*/
|
|
400
|
+
export function createBaileysWhatsAppChannel(opts = {}) {
|
|
401
|
+
return new BaileysWhatsAppAdapter(opts);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
/** Best-effort import of Baileys' DisconnectReason map (for loggedOut). */
|
|
407
|
+
async function loadBaileysDisconnectReason(agentRoot) {
|
|
408
|
+
try {
|
|
409
|
+
const baileys = await loadBaileys(agentRoot);
|
|
410
|
+
return baileys.DisconnectReason || null;
|
|
411
|
+
} catch {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function mediaPayload(att, buffer, caption) {
|
|
417
|
+
switch (att.type) {
|
|
418
|
+
case "image":
|
|
419
|
+
return { image: buffer, mimetype: att.mime || "image/jpeg", caption };
|
|
420
|
+
case "video":
|
|
421
|
+
return { video: buffer, mimetype: att.mime || "video/mp4", caption };
|
|
422
|
+
case "audio":
|
|
423
|
+
return { audio: buffer, mimetype: att.mime || "audio/mpeg", ptt: false };
|
|
424
|
+
case "document":
|
|
425
|
+
return { document: buffer, mimetype: att.mime || "application/pdf", fileName: att.filename, caption };
|
|
426
|
+
default:
|
|
427
|
+
return { document: buffer, mimetype: att.mime || "application/octet-stream", fileName: att.filename, caption };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function normaliseJid(to) {
|
|
432
|
+
if (String(to).includes("@")) return String(to);
|
|
433
|
+
const digits = String(to).replace(/[^\d]/g, "");
|
|
434
|
+
return `${digits}@s.whatsapp.net`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function parseQuotedRef(ref) {
|
|
438
|
+
// Accept either an object {remoteJid, id, fromMe} or a serialised
|
|
439
|
+
// string "remoteJid:id:fromMe".
|
|
440
|
+
if (typeof ref === "object" && ref?.id) return ref;
|
|
441
|
+
const [remoteJid, id, fromMe] = String(ref).split(":");
|
|
442
|
+
return { remoteJid, id, fromMe: fromMe === "true" };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async function normaliseInbound(m, { sock, transcribe, agentRoot }) {
|
|
446
|
+
const channel = "whatsapp";
|
|
447
|
+
const remoteJid = m.key.remoteJid;
|
|
448
|
+
const id = m.key.id;
|
|
449
|
+
const isGroup = remoteJid?.endsWith("@g.us");
|
|
450
|
+
const senderJid = isGroup ? m.key.participant : remoteJid;
|
|
451
|
+
const ts = m.messageTimestamp;
|
|
452
|
+
|
|
453
|
+
// Identify message body across the many Baileys message shapes.
|
|
454
|
+
let text = "";
|
|
455
|
+
const attachments = [];
|
|
456
|
+
|
|
457
|
+
const inner =
|
|
458
|
+
m.message?.conversation ??
|
|
459
|
+
m.message?.extendedTextMessage?.text ??
|
|
460
|
+
null;
|
|
461
|
+
if (typeof inner === "string") text = inner;
|
|
462
|
+
|
|
463
|
+
// Quoted message reference (thread_id)
|
|
464
|
+
let thread_id = null;
|
|
465
|
+
const ctx = m.message?.extendedTextMessage?.contextInfo;
|
|
466
|
+
if (ctx?.stanzaId) {
|
|
467
|
+
thread_id = `${ctx.remoteJid || remoteJid}:${ctx.stanzaId}:${!!ctx.fromMe}`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
let kind = "message";
|
|
471
|
+
|
|
472
|
+
// Voice note → transcribe
|
|
473
|
+
if (m.message?.audioMessage?.ptt) {
|
|
474
|
+
kind = "voice_note";
|
|
475
|
+
const buffer = await downloadMedia(sock, m, "audio", agentRoot);
|
|
476
|
+
let transcript = "";
|
|
477
|
+
if (transcribe && buffer) {
|
|
478
|
+
try { transcript = await transcribe(buffer, { mime: m.message.audioMessage.mimetype }); }
|
|
479
|
+
catch (err) { transcript = `(transcription failed: ${err.message})`; }
|
|
480
|
+
}
|
|
481
|
+
attachments.push({
|
|
482
|
+
type: "voice_note",
|
|
483
|
+
mime: m.message.audioMessage.mimetype || "audio/ogg; codecs=opus",
|
|
484
|
+
size_bytes: buffer?.length || 0,
|
|
485
|
+
transcript,
|
|
486
|
+
});
|
|
487
|
+
if (transcript) text = transcript; // surface as the message body
|
|
488
|
+
}
|
|
489
|
+
// Image, document, etc.
|
|
490
|
+
for (const [key, kindName] of [
|
|
491
|
+
["imageMessage", "image"],
|
|
492
|
+
["videoMessage", "video"],
|
|
493
|
+
["documentMessage", "document"],
|
|
494
|
+
["audioMessage", "audio"],
|
|
495
|
+
]) {
|
|
496
|
+
if (m.message?.[key]) {
|
|
497
|
+
if (key === "audioMessage" && m.message.audioMessage.ptt) continue; // handled above
|
|
498
|
+
const mime = m.message[key].mimetype || null;
|
|
499
|
+
const filename = m.message[key].fileName || null;
|
|
500
|
+
attachments.push({ type: kindName, mime, filename });
|
|
501
|
+
const caption = m.message[key].caption;
|
|
502
|
+
if (caption && !text) text = caption;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const toId = remoteJid;
|
|
507
|
+
const chatId = String(toId || senderJid || "");
|
|
508
|
+
return {
|
|
509
|
+
channel,
|
|
510
|
+
message_id: id,
|
|
511
|
+
thread_id,
|
|
512
|
+
in_reply_to: ctx?.stanzaId || null,
|
|
513
|
+
from: { id: senderJid, phone: jidToPhone(senderJid) },
|
|
514
|
+
to: { id: remoteJid },
|
|
515
|
+
text,
|
|
516
|
+
attachments,
|
|
517
|
+
kind,
|
|
518
|
+
timestamp: typeof ts === "number" ? new Date(ts * 1000).toISOString() : new Date().toISOString(),
|
|
519
|
+
raw: m,
|
|
520
|
+
// Durable source descriptor so the inbox-item seam + typing registry have
|
|
521
|
+
// the conversation descriptor (previously stamped by the platform wrapper).
|
|
522
|
+
source: {
|
|
523
|
+
channel: "whatsapp",
|
|
524
|
+
chatId,
|
|
525
|
+
threadRef: thread_id || undefined,
|
|
526
|
+
userScope: chatId.endsWith("@g.us") ? "group" : "dm",
|
|
527
|
+
peerId: senderJid,
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function downloadMedia(sock, m, type, agentRoot) {
|
|
533
|
+
try {
|
|
534
|
+
if (typeof sock.downloadMediaMessage === "function") {
|
|
535
|
+
return await sock.downloadMediaMessage(m);
|
|
536
|
+
}
|
|
537
|
+
// Newer Baileys: helper function exported on module
|
|
538
|
+
const baileys = await loadBaileys(agentRoot);
|
|
539
|
+
if (typeof baileys.downloadMediaMessage === "function") {
|
|
540
|
+
return await baileys.downloadMediaMessage(m, "buffer", {});
|
|
541
|
+
}
|
|
542
|
+
} catch { /* */ }
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function jidToPhone(jid) {
|
|
547
|
+
if (!jid) return null;
|
|
548
|
+
return "+" + String(jid).split("@")[0].split(":")[0];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async function fetchUrl(url) {
|
|
552
|
+
const res = await fetch(url);
|
|
553
|
+
if (!res.ok) throw new Error(`fetch ${url} failed: ${res.status}`);
|
|
554
|
+
return Buffer.from(await res.arrayBuffer());
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Platform factory for the daemon's generic channel-bus loop. Gated by
|
|
559
|
+
* config/whatsapp.yaml; only handles the Baileys transport (Twilio WhatsApp
|
|
560
|
+
* uses its own webhook path, not the bus). Throws a clear skip when the config
|
|
561
|
+
* selects a different transport so the loop logs and moves on.
|
|
562
|
+
*
|
|
563
|
+
* The adapter itself now stamps each inbound MessageEvent with a `source`
|
|
564
|
+
* descriptor (in normaliseInbound), and BaseAdapter.start owns the connect
|
|
565
|
+
* supervisor + send-gate + bot-loop guard, so the old start-wrapper that
|
|
566
|
+
* re-stamped `source` / forced `kind` is no longer needed.
|
|
567
|
+
*/
|
|
568
|
+
function createWhatsAppPlatform(opts = {}) {
|
|
569
|
+
const agentRoot = resolve(opts.agentRoot || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd());
|
|
570
|
+
const cfgPath = join(agentRoot, "config/whatsapp.yaml");
|
|
571
|
+
if (existsSync(cfgPath)) {
|
|
572
|
+
const body = readFileSync(cfgPath, "utf-8");
|
|
573
|
+
if (!/^transport:\s*baileys\b/m.test(body)) {
|
|
574
|
+
throw new Error("whatsapp transport is not baileys — handled outside the channel bus");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return createBaileysWhatsAppChannel(opts);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
definePlatform({
|
|
581
|
+
name: "whatsapp",
|
|
582
|
+
gateFile: "config/whatsapp.yaml",
|
|
583
|
+
factory: createWhatsAppPlatform,
|
|
584
|
+
capabilities: BAILEYS_CAPABILITIES,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
export default createBaileysWhatsAppChannel;
|