@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,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cloud-relay-dedup.test.mjs — node:test coverage for the pure SSE-parsing and
|
|
3
|
+
* reconnect-replay dedup helpers used by the Slack cloud-relay client
|
|
4
|
+
* (audit L30).
|
|
5
|
+
*
|
|
6
|
+
* These functions are pure (no network, no disk, no dotenv), so we import them
|
|
7
|
+
* directly from the extracted module — importing the client itself would pull
|
|
8
|
+
* in dotenv and open a live SSE connection.
|
|
9
|
+
*
|
|
10
|
+
* The reconnect-replay dedup (isReplayInSiblings) pins a production regression
|
|
11
|
+
* fixed in commit ccfbaed, where the same event was re-delivered after each
|
|
12
|
+
* relay reconnect; the `.processed` cases below guard that specifically.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { test } from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
createSseParser,
|
|
20
|
+
isReplayInSiblings,
|
|
21
|
+
} from "./cloud-relay-dedup.mjs";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// createSseParser — SSE framing
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
test("createSseParser emits one payload per blank-line-terminated event", () => {
|
|
28
|
+
const parser = createSseParser();
|
|
29
|
+
const out = parser.push('data: {"a":1}\n\n');
|
|
30
|
+
assert.deepEqual(out, ['{"a":1}']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("createSseParser buffers an event until its terminating blank line", () => {
|
|
34
|
+
const parser = createSseParser();
|
|
35
|
+
// No blank line yet — nothing should be emitted.
|
|
36
|
+
assert.deepEqual(parser.push('data: {"a":1}\n'), []);
|
|
37
|
+
// Blank line arrives in a later chunk — now it flushes.
|
|
38
|
+
assert.deepEqual(parser.push("\n"), ['{"a":1}']);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("createSseParser concatenates multi-line data: with newlines", () => {
|
|
42
|
+
const parser = createSseParser();
|
|
43
|
+
const out = parser.push("data: line1\ndata: line2\n\n");
|
|
44
|
+
assert.deepEqual(out, ["line1\nline2"]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("createSseParser ignores ':' comment heartbeat lines", () => {
|
|
48
|
+
const parser = createSseParser();
|
|
49
|
+
// ": hb" is a heartbeat comment and must not produce or pollute a payload.
|
|
50
|
+
const out = parser.push(': hb\ndata: {"x":true}\n\n');
|
|
51
|
+
assert.deepEqual(out, ['{"x":true}']);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("createSseParser strips exactly one leading space after 'data:'", () => {
|
|
55
|
+
const parser = createSseParser();
|
|
56
|
+
// Per the SSE spec, a single leading space after the colon is removed; a
|
|
57
|
+
// second space is significant payload content.
|
|
58
|
+
const out = parser.push("data: two-spaces\n\n");
|
|
59
|
+
assert.deepEqual(out, [" two-spaces"]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("createSseParser tolerates CRLF line endings", () => {
|
|
63
|
+
const parser = createSseParser();
|
|
64
|
+
const out = parser.push('data: {"crlf":1}\r\n\r\n');
|
|
65
|
+
assert.deepEqual(out, ['{"crlf":1}']);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("createSseParser handles two events split across chunk boundaries", () => {
|
|
69
|
+
const parser = createSseParser();
|
|
70
|
+
let out = parser.push('data: {"n":1}\n\nda');
|
|
71
|
+
assert.deepEqual(out, ['{"n":1}']);
|
|
72
|
+
out = parser.push('ta: {"n":2}\n\n');
|
|
73
|
+
assert.deepEqual(out, ['{"n":2}']);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("createSseParser emits nothing for a comment-only / empty flush", () => {
|
|
77
|
+
const parser = createSseParser();
|
|
78
|
+
// A blank line with no preceding data: lines must not emit an empty payload.
|
|
79
|
+
assert.deepEqual(parser.push("\n"), []);
|
|
80
|
+
assert.deepEqual(parser.push(": keepalive\n\n"), []);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// isReplayInSiblings — filesystem reconnect-replay dedup
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
const TS_KEY = "1700000000-001000"; // Slack ts "1700000000.001000" with dot→dash
|
|
88
|
+
|
|
89
|
+
test("isReplayInSiblings is false when no sibling matches", () => {
|
|
90
|
+
const siblings = [
|
|
91
|
+
"2026-05-31T00-00-00-000Z-9999999999-000000-cloud-relay.json",
|
|
92
|
+
"unrelated.json",
|
|
93
|
+
];
|
|
94
|
+
assert.equal(isReplayInSiblings(siblings, TS_KEY), false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("isReplayInSiblings detects a pending sibling for the same ts", () => {
|
|
98
|
+
const siblings = [
|
|
99
|
+
`2026-05-31T00-00-00-000Z-${TS_KEY}-cloud-relay.json`,
|
|
100
|
+
];
|
|
101
|
+
assert.equal(isReplayInSiblings(siblings, TS_KEY), true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("isReplayInSiblings detects an already-processed sibling for the same ts", () => {
|
|
105
|
+
// The reconnect-replay regression (ccfbaed): a .processed file for the same
|
|
106
|
+
// ts must still count as a replay so we don't re-deliver after the daemon
|
|
107
|
+
// consumed it.
|
|
108
|
+
const siblings = [
|
|
109
|
+
`2026-05-31T00-00-00-000Z-${TS_KEY}-cloud-relay.json.processed`,
|
|
110
|
+
];
|
|
111
|
+
assert.equal(isReplayInSiblings(siblings, TS_KEY), true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("isReplayInSiblings is false for a different ts key", () => {
|
|
115
|
+
const siblings = [
|
|
116
|
+
"2026-05-31T00-00-00-000Z-1700000000-999999-cloud-relay.json",
|
|
117
|
+
"2026-05-31T00-00-00-000Z-1700000000-999999-cloud-relay.json.processed",
|
|
118
|
+
];
|
|
119
|
+
assert.equal(isReplayInSiblings(siblings, TS_KEY), false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("isReplayInSiblings is false on an empty inbox", () => {
|
|
123
|
+
assert.equal(isReplayInSiblings([], TS_KEY), false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("isReplayInSiblings does not false-match a ts that is a prefix of another", () => {
|
|
127
|
+
// Guard the suffix match: a longer ts key whose filename merely contains
|
|
128
|
+
// TS_KEY before extra digits must not be treated as a replay.
|
|
129
|
+
const siblings = [
|
|
130
|
+
`2026-05-31T00-00-00-000Z-${TS_KEY}9-cloud-relay.json`,
|
|
131
|
+
];
|
|
132
|
+
assert.equal(isReplayInSiblings(siblings, TS_KEY), false);
|
|
133
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — Slash command handlers (Slack Socket Mode)
|
|
3
|
+
*
|
|
4
|
+
* Slack huddles use proprietary WebRTC with no programmatic-join API. The
|
|
5
|
+
* pragmatic equivalent is a slash command that hands the agent a "please call
|
|
6
|
+
* me" trigger, and the agent dials back via the same OpenAI Realtime + Twilio
|
|
7
|
+
* stack that powers PSTN calls. Same identity, same context-loader, same tool
|
|
8
|
+
* surface — just initiated from Slack instead of dialed in by phone.
|
|
9
|
+
*
|
|
10
|
+
* Currently supported commands:
|
|
11
|
+
* /voice — call user's phone (looked up from config/contacts.yaml by slack_id)
|
|
12
|
+
* /voice +44XXXX — call this specific E.164 number
|
|
13
|
+
* /voice "brief text" — call user, with the given brief as the call objective
|
|
14
|
+
* /voice +44XXX "brief" — both
|
|
15
|
+
*
|
|
16
|
+
* Returns: { success, message, action_taken } — the message lands in Slack via
|
|
17
|
+
* the slash-command response_url (ephemeral by default, only the invoker sees it).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Look up a Slack user's phone number from config/contacts.yaml. Falls back to
|
|
25
|
+
* an explicit override in env (PHONE_BY_SLACK_ID_<slackid>) so operators can
|
|
26
|
+
* supply numbers without committing them to the contacts file. Returns null if
|
|
27
|
+
* no phone is found.
|
|
28
|
+
*/
|
|
29
|
+
async function resolvePhoneForSlackUser(agentRoot, slackUserId) {
|
|
30
|
+
if (!slackUserId) return null;
|
|
31
|
+
|
|
32
|
+
// Env override: PHONE_BY_SLACK_ID_U099XXXX=+44XXX
|
|
33
|
+
const envKey = `PHONE_BY_SLACK_ID_${slackUserId}`;
|
|
34
|
+
if (process.env[envKey]) return process.env[envKey];
|
|
35
|
+
|
|
36
|
+
const contactsPath = join(agentRoot, "config/contacts.yaml");
|
|
37
|
+
if (!existsSync(contactsPath)) return null;
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
const yaml = await import("js-yaml");
|
|
41
|
+
parsed = yaml.load(readFileSync(contactsPath, "utf-8"));
|
|
42
|
+
} catch { return null; }
|
|
43
|
+
const list = parsed?.contacts || [];
|
|
44
|
+
for (const c of list) {
|
|
45
|
+
if (c?.slack_id === slackUserId) {
|
|
46
|
+
// Phone may live in a few field names depending on contacts.yaml shape
|
|
47
|
+
return c.phone || c.phone_e164 || c.mobile || c.cell || null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Parse `/voice` arguments. Format: optional `+E164` followed by optional
|
|
55
|
+
* quoted brief, e.g.
|
|
56
|
+
* /voice → {phone: null, brief: null}
|
|
57
|
+
* /voice +14155551212 → {phone: "+14155551212", brief: null}
|
|
58
|
+
* /voice quick scheduling chat → {phone: null, brief: "quick scheduling chat"}
|
|
59
|
+
* /voice +14155551212 status → {phone: "+14155551212", brief: "status"}
|
|
60
|
+
*/
|
|
61
|
+
export function parseVoiceArgs(text) {
|
|
62
|
+
const trimmed = String(text || "").trim();
|
|
63
|
+
if (!trimmed) return { phone: null, brief: null };
|
|
64
|
+
const phoneMatch = trimmed.match(/^(\+\d{6,16})\s*(.*)$/);
|
|
65
|
+
if (phoneMatch) {
|
|
66
|
+
return {
|
|
67
|
+
phone: phoneMatch[1],
|
|
68
|
+
brief: phoneMatch[2] ? phoneMatch[2].trim() : null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return { phone: null, brief: trimmed };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Handle `/voice` — trigger an outbound voice call back to the requester.
|
|
76
|
+
* Imports lib/voice/outbound.mjs lazily so the slash command surface doesn't
|
|
77
|
+
* pull voice dependencies for agents that don't use voice.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} payload Slack slash_commands payload
|
|
80
|
+
* @param {object} ctx { agentRoot, identity }
|
|
81
|
+
* @returns {Promise<{ok, message, response_type}>}
|
|
82
|
+
*/
|
|
83
|
+
export async function handleVoiceCommand(payload, ctx) {
|
|
84
|
+
const { agentRoot, identity } = ctx;
|
|
85
|
+
const { phone: explicitPhone, brief: argBrief } = parseVoiceArgs(payload.text);
|
|
86
|
+
|
|
87
|
+
let phone = explicitPhone;
|
|
88
|
+
if (!phone) {
|
|
89
|
+
phone = await resolvePhoneForSlackUser(agentRoot, payload.user_id);
|
|
90
|
+
}
|
|
91
|
+
if (!phone) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
response_type: "ephemeral",
|
|
95
|
+
message: `:phone: I don't have a phone number for <@${payload.user_id}>. Try \`/voice +<E164>\` (e.g. \`/voice +14155551212\`) or add your number to \`config/contacts.yaml\` under \`phone:\`.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const brief = argBrief
|
|
100
|
+
|| `Slack ${payload.user_name ? `${payload.user_name} ` : ""}requested a voice call from ${payload.channel_name ? `#${payload.channel_name}` : "Slack"}. Greet them by name and ask what they'd like to discuss.`;
|
|
101
|
+
|
|
102
|
+
// Lazy-import so non-voice agents don't pay the dependency cost.
|
|
103
|
+
let placeOutboundCall;
|
|
104
|
+
try {
|
|
105
|
+
({ placeOutboundCall } = await import("../../../lib/voice/outbound.mjs"));
|
|
106
|
+
} catch (err) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
response_type: "ephemeral",
|
|
110
|
+
message: `:warning: voice subsystem not available: ${err.message}. Run \`npx @cohortapp/agent-sdk init voice-realtime\`.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Dispatch the call asynchronously — the slash command HTTP response is
|
|
115
|
+
// synchronous and Slack times out at 3s, so we fire-and-forget.
|
|
116
|
+
placeOutboundCall({
|
|
117
|
+
to: phone,
|
|
118
|
+
brief,
|
|
119
|
+
agentRoot,
|
|
120
|
+
identity,
|
|
121
|
+
peer_name: payload.user_name || null,
|
|
122
|
+
transport: process.env.VOICE_RELAY_URL ? "media_streams" : "sip",
|
|
123
|
+
})
|
|
124
|
+
.then((res) => {
|
|
125
|
+
// The placeOutboundCall result is logged inside that module; we don't
|
|
126
|
+
// need to do anything else here.
|
|
127
|
+
return res;
|
|
128
|
+
})
|
|
129
|
+
.catch((err) => {
|
|
130
|
+
// eslint-disable-next-line no-console
|
|
131
|
+
console.warn(`[/voice] placeOutboundCall failed for ${phone}: ${err.message}`);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
response_type: "ephemeral",
|
|
137
|
+
message: `:phone: Calling you at \`${phone}\` now${argBrief ? ` — brief: _${argBrief}_` : ""}. Pick up when it rings.`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Master dispatcher — called by slack-socket-mode.mjs when an envelope of
|
|
143
|
+
* type=slash_commands arrives. Returns {ok, response_type, message} for the
|
|
144
|
+
* caller to POST to payload.response_url. Unknown commands return null so the
|
|
145
|
+
* caller can ignore (Slack will show "command not configured" to the user, which
|
|
146
|
+
* is the right UX — we don't want to claim ownership of every command).
|
|
147
|
+
*/
|
|
148
|
+
export async function handleSlashCommand(payload, ctx) {
|
|
149
|
+
const cmd = String(payload?.command || "").toLowerCase().trim();
|
|
150
|
+
switch (cmd) {
|
|
151
|
+
case "/voice":
|
|
152
|
+
return handleVoiceCommand(payload, ctx);
|
|
153
|
+
default:
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* POST the slash-command response back to Slack via response_url. Slack returns
|
|
160
|
+
* 200 quickly; we fire-and-forget if the URL is absent.
|
|
161
|
+
*/
|
|
162
|
+
export async function postSlashResponse(responseUrl, body) {
|
|
163
|
+
if (!responseUrl || !body) return;
|
|
164
|
+
try {
|
|
165
|
+
await fetch(responseUrl, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { "Content-Type": "application/json" },
|
|
168
|
+
body: JSON.stringify({
|
|
169
|
+
response_type: body.response_type || "ephemeral",
|
|
170
|
+
text: body.message || "",
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
} catch (err) {
|
|
174
|
+
// eslint-disable-next-line no-console
|
|
175
|
+
console.warn(`[slash-command] failed to POST response_url: ${err.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Principal Gmail Poller — polls the principal's inbox via native Node.js IMAP.
|
|
2
|
+
// Uses the shared imap-client.mjs module.
|
|
3
|
+
//
|
|
4
|
+
// ACCESS BOUNDARY: This poller is only enabled for agents whose archetype
|
|
5
|
+
// explicitly grants principal-Gmail access (typically Chief-of-Staff /
|
|
6
|
+
// executive-operator archetypes). For other agents, leave
|
|
7
|
+
// PRINCIPAL_GMAIL_APP_PASSWORD unset and this module will no-op.
|
|
8
|
+
//
|
|
9
|
+
// Backward compatibility: this file was previously named `secondary-gmail-poller.mjs`
|
|
10
|
+
// with `pollSecondaryGmail` as the exported function. The new export name is
|
|
11
|
+
// `pollPrincipalGmail`; the old name is re-exported as an alias.
|
|
12
|
+
|
|
13
|
+
import { readFileSync, readdirSync } from "fs";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import { AGENT_REPO_DIR, writeCursor } from "./utils.mjs";
|
|
16
|
+
import { pollImapInbox } from "./imap-client.mjs";
|
|
17
|
+
|
|
18
|
+
const INBOX_DIR = join(AGENT_REPO_DIR, "state", "inbox", "gmail");
|
|
19
|
+
|
|
20
|
+
// Load principal identity from agent.json — email comes from there, password
|
|
21
|
+
// from env (PRINCIPAL_GMAIL_APP_PASSWORD with MEHRAN_GMAIL_APP_PASSWORD fallback
|
|
22
|
+
// for repos still on the legacy var name).
|
|
23
|
+
function loadPrincipal() {
|
|
24
|
+
try {
|
|
25
|
+
const agent = JSON.parse(readFileSync(join(AGENT_REPO_DIR, "config/agent.json"), "utf-8"));
|
|
26
|
+
return agent.principal || {};
|
|
27
|
+
} catch {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadPrincipalPassword() {
|
|
33
|
+
if (process.env.PRINCIPAL_GMAIL_APP_PASSWORD) return process.env.PRINCIPAL_GMAIL_APP_PASSWORD;
|
|
34
|
+
if (process.env.MEHRAN_GMAIL_APP_PASSWORD) return process.env.MEHRAN_GMAIL_APP_PASSWORD;
|
|
35
|
+
try {
|
|
36
|
+
const envFile = readFileSync(join(AGENT_REPO_DIR, ".env"), "utf-8");
|
|
37
|
+
for (const line of envFile.split("\n")) {
|
|
38
|
+
const m = line.match(/^(PRINCIPAL_GMAIL_APP_PASSWORD|MEHRAN_GMAIL_APP_PASSWORD)=(.*)$/);
|
|
39
|
+
if (m) {
|
|
40
|
+
let val = m[2].trim();
|
|
41
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
42
|
+
val = val.slice(1, -1);
|
|
43
|
+
}
|
|
44
|
+
return val;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch { /* no .env file */ }
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function pollPrincipalGmail() {
|
|
52
|
+
const items = [];
|
|
53
|
+
const errors = [];
|
|
54
|
+
|
|
55
|
+
const principal = loadPrincipal();
|
|
56
|
+
const principalEmail = principal.email;
|
|
57
|
+
const password = loadPrincipalPassword();
|
|
58
|
+
|
|
59
|
+
// No-op if not configured — agents without principal-Gmail access just
|
|
60
|
+
// skip this entirely instead of failing every poll cycle.
|
|
61
|
+
if (!principalEmail || !password) {
|
|
62
|
+
return { items, errors };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const accountSlug = (principal.firstName || "principal").toLowerCase();
|
|
66
|
+
const fileSuffix = `${accountSlug}-email`;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const result = await pollImapInbox({
|
|
70
|
+
email: principalEmail,
|
|
71
|
+
password,
|
|
72
|
+
account: accountSlug,
|
|
73
|
+
fileSuffix,
|
|
74
|
+
eventType: `${accountSlug}_email`,
|
|
75
|
+
logPrefix: `[${accountSlug}-gmail]`,
|
|
76
|
+
});
|
|
77
|
+
if (result.errors.length > 0) {
|
|
78
|
+
errors.push(...result.errors);
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
const msg = `Principal Gmail poll failed: ${err.message}`;
|
|
82
|
+
console.error(`[${accountSlug}-gmail] ${msg}`);
|
|
83
|
+
errors.push(msg);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Read resulting JSON files
|
|
87
|
+
try {
|
|
88
|
+
const fileGlob = `-${fileSuffix}.json`;
|
|
89
|
+
const files = readdirSync(INBOX_DIR).filter(
|
|
90
|
+
(f) => f.endsWith(fileGlob) && !f.endsWith(".processed"),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
try {
|
|
95
|
+
const content = readFileSync(join(INBOX_DIR, file), "utf-8");
|
|
96
|
+
const record = JSON.parse(content);
|
|
97
|
+
|
|
98
|
+
items.push({
|
|
99
|
+
id: file.replace(fileGlob, ""),
|
|
100
|
+
service: `${accountSlug}-gmail`,
|
|
101
|
+
channel: `${accountSlug}-inbox`,
|
|
102
|
+
sender: record.email?.from || "unknown",
|
|
103
|
+
sender_privilege: "unknown",
|
|
104
|
+
timestamp: record.received_at || new Date().toISOString(),
|
|
105
|
+
subject: record.email?.subject || "",
|
|
106
|
+
content: record.email?.snippet || "",
|
|
107
|
+
thread_id: record.email?.message_id || "",
|
|
108
|
+
is_reply: false,
|
|
109
|
+
priority_signals: {
|
|
110
|
+
from_ceo: false,
|
|
111
|
+
tagged_urgent: false,
|
|
112
|
+
contains_deadline: false,
|
|
113
|
+
mentions_agent: false,
|
|
114
|
+
},
|
|
115
|
+
raw_ref: `${accountSlug}-gmail:${record.email?.message_id || ""}`,
|
|
116
|
+
});
|
|
117
|
+
} catch (parseErr) {
|
|
118
|
+
errors.push(`Failed to parse ${file}: ${parseErr.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (readErr) {
|
|
122
|
+
if (readErr.code !== "ENOENT") {
|
|
123
|
+
errors.push(`Failed to read inbox: ${readErr.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
writeCursor(`${accountSlug}-gmail`, new Date().toISOString());
|
|
128
|
+
return { items, errors };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Back-compat: old export name preserved so existing imports continue to work.
|
|
132
|
+
export const pollSecondaryGmail = pollPrincipalGmail;
|