@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,481 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Voice Call Transcript Parser
|
|
4
|
+
*
|
|
5
|
+
* Parses voice call transcript summaries posted to Slack by the voice-ai
|
|
6
|
+
* post-call routing (voice-ai/lib/logger.js → postCallSummaryToSlack).
|
|
7
|
+
*
|
|
8
|
+
* Input format (Slack message text):
|
|
9
|
+
* *Phone call completed* — +971585291799 (243s, 38 turns)
|
|
10
|
+
*
|
|
11
|
+
* *Caller said:*
|
|
12
|
+
* > First thing caller said
|
|
13
|
+
* > Second thing caller said
|
|
14
|
+
* > ...
|
|
15
|
+
*
|
|
16
|
+
* _Action items from this call should be processed immediately._
|
|
17
|
+
*
|
|
18
|
+
* Output: structured object with caller identity, duration, topics,
|
|
19
|
+
* and extracted action items suitable for queue routing.
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* import { isVoiceTranscript, parseVoiceTranscript } from './parse-voice-transcript.mjs';
|
|
23
|
+
*
|
|
24
|
+
* if (isVoiceTranscript(messageText)) {
|
|
25
|
+
* const parsed = parseVoiceTranscript(messageText);
|
|
26
|
+
* // parsed.actionItems → array of { title, context, priority, source }
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* Can also run standalone for testing:
|
|
30
|
+
* node scripts/parse-voice-transcript.mjs < message.txt
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Known callers — phone numbers to identities. Loaded from
|
|
35
|
+
// config/caller-id-map.yaml at runtime; falls back to an empty map. Keep
|
|
36
|
+
// caller-id-map.yaml in sync with voice-ai/server.js CALLER_GREETINGS.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
const KNOWN_CALLERS = {}; // populated lazily from config/caller-id-map.yaml on first use
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Action item detection patterns
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
// Phrases that signal a request, ask, or action item
|
|
46
|
+
const ACTION_PATTERNS = [
|
|
47
|
+
// Direct requests
|
|
48
|
+
/\b(?:can you|could you|please|I need you to|I want you to|go ahead and|make sure|set up|look into|find out|search for|book|schedule|send|draft|prepare|create|build|update|check|review|look at|investigate|fix|handle|take care of|arrange|organize|coordinate|follow up|chase|email|call|message|write)\b/i,
|
|
49
|
+
// Imperative verbs — direct commands without polite framing
|
|
50
|
+
/\b(?:time.?box|block out|clear my|reschedule|prioriti[sz]e|research|compile|summarize|summarise|analyze|analyse|escalate|ping|nudge|chase up|circle back|loop in|pull together|put together|get me|grab|download|upload|share|forward|transfer|assign|delegate|sort out|figure out|work on|finish|complete|deliver|ship|deploy|launch|submit|file|register|sign up|enroll|enrol)\b/i,
|
|
51
|
+
// Questions that imply action needed
|
|
52
|
+
/\b(?:what(?:'s| is) the status|where are we|have you|did you|when (?:can|will)|how (?:do|can)|is there)\b/i,
|
|
53
|
+
// Decisions and directives
|
|
54
|
+
/\b(?:let's|we should|we need to|I've decided|go with|use|switch to|stop|start|change|move|cancel|approve|reject)\b/i,
|
|
55
|
+
// Time-sensitive signals
|
|
56
|
+
/\b(?:today|tomorrow|this week|by (?:end of|monday|tuesday|wednesday|thursday|friday|saturday|sunday)|asap|urgent|immediately|right now|priority)\b/i,
|
|
57
|
+
// Follow-up triggers
|
|
58
|
+
/\b(?:remind me|don't forget|make a note|keep track|follow up|circle back|loop back)\b/i,
|
|
59
|
+
// Also/additionally — signals a new action item being added
|
|
60
|
+
/\b(?:also|additionally|one more thing|and also|plus)\b/i,
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Priority signal patterns
|
|
64
|
+
const PRIORITY_SIGNALS = {
|
|
65
|
+
critical:
|
|
66
|
+
/\b(?:urgent|emergency|asap|critical|immediately|right now|drop everything)\b/i,
|
|
67
|
+
high: /\b(?:today|priority|important|need this|by end of day|eod)\b/i,
|
|
68
|
+
medium: /\b(?:this week|soon|when you can|follow up)\b/i,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Topic classification patterns
|
|
72
|
+
const TOPIC_PATTERNS = {
|
|
73
|
+
travel:
|
|
74
|
+
/\b(?:flight|flights|travel|book|booking|trip|australia|sydney|brisbane|airport|airline|hotel)\b/i,
|
|
75
|
+
calendar:
|
|
76
|
+
/\b(?:calendar|schedule|time.?box|block|meeting|appointment|slot|availability)\b/i,
|
|
77
|
+
hiring:
|
|
78
|
+
/\b(?:hiring|candidate|interview|role|position|recruit|applicant|offer|resume|cv)\b/i,
|
|
79
|
+
legal:
|
|
80
|
+
/\b(?:legal|contract|agreement|nda|compliance|regulatory|dfsa|licence|filing)\b/i,
|
|
81
|
+
engineering:
|
|
82
|
+
/\b(?:engineering|code|deploy|build|bug|fix|feature|platform|api|infrastructure)\b/i,
|
|
83
|
+
finance:
|
|
84
|
+
/\b(?:finance|budget|cost|expense|invoice|payment|funding|capital|runway)\b/i,
|
|
85
|
+
communication:
|
|
86
|
+
/\b(?:email|slack|message|send|reply|respond|follow.?up|chase)\b/i,
|
|
87
|
+
strategy:
|
|
88
|
+
/\b(?:strategy|roadmap|plan|initiative|priority|milestone|target|goal|objective)\b/i,
|
|
89
|
+
voice_ai: /\b(?:voice|phone|call|twilio|speech|conversation)\b/i,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Detection
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a Slack message text is a voice call transcript summary.
|
|
98
|
+
* Uses the exact format produced by voice-ai/lib/logger.js postCallSummaryToSlack.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} text - Slack message text
|
|
101
|
+
* @returns {boolean}
|
|
102
|
+
*/
|
|
103
|
+
export function isVoiceTranscript(text) {
|
|
104
|
+
if (!text || typeof text !== "string") return false;
|
|
105
|
+
// Must contain the post-call header AND caller-said block
|
|
106
|
+
return text.includes("Phone call completed") && text.includes("Caller said:");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Parsing
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Parse a voice call transcript summary from Slack into structured data.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} text - Slack message text containing the transcript
|
|
117
|
+
* @param {object} [meta] - Optional metadata from the Slack message
|
|
118
|
+
* @param {string} [meta.ts] - Slack message timestamp
|
|
119
|
+
* @param {string} [meta.channel] - Slack channel ID
|
|
120
|
+
* @returns {object} Parsed transcript with caller info, duration, topics, action items
|
|
121
|
+
*/
|
|
122
|
+
export function parseVoiceTranscript(text, meta = {}) {
|
|
123
|
+
const result = {
|
|
124
|
+
type: "voice_call_transcript",
|
|
125
|
+
caller: null,
|
|
126
|
+
callerPhone: null,
|
|
127
|
+
callerIdentity: null,
|
|
128
|
+
duration: null,
|
|
129
|
+
durationSeconds: null,
|
|
130
|
+
turns: null,
|
|
131
|
+
callerStatements: [],
|
|
132
|
+
topics: [],
|
|
133
|
+
actionItems: [],
|
|
134
|
+
rawText: text,
|
|
135
|
+
slackTs: meta.ts || null,
|
|
136
|
+
slackChannel: meta.channel || null,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// --- Parse header line ---
|
|
140
|
+
// Format: *Phone call completed* — +971585291799 (243s, 38 turns)
|
|
141
|
+
const headerMatch = text.match(
|
|
142
|
+
/Phone call completed\*?\s*[-—]\s*(\+?[\d]+)\s*\((\d+)s,\s*(\d+)\s*turns?\)/,
|
|
143
|
+
);
|
|
144
|
+
if (headerMatch) {
|
|
145
|
+
result.callerPhone = headerMatch[1];
|
|
146
|
+
result.durationSeconds = parseInt(headerMatch[2], 10);
|
|
147
|
+
result.duration = formatDuration(result.durationSeconds);
|
|
148
|
+
result.turns = parseInt(headerMatch[3], 10);
|
|
149
|
+
|
|
150
|
+
// Resolve caller identity from phone number
|
|
151
|
+
const known = KNOWN_CALLERS[result.callerPhone];
|
|
152
|
+
if (known) {
|
|
153
|
+
result.caller = known.name;
|
|
154
|
+
result.callerIdentity = known;
|
|
155
|
+
} else {
|
|
156
|
+
result.caller = result.callerPhone;
|
|
157
|
+
result.callerIdentity = {
|
|
158
|
+
name: result.callerPhone,
|
|
159
|
+
role: "unknown",
|
|
160
|
+
privilege: "unknown",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// --- Extract caller statements ---
|
|
166
|
+
// Format: lines starting with > after "Caller said:"
|
|
167
|
+
const callerSaidMatch = text.match(
|
|
168
|
+
/Caller said:\*?\s*\n([\s\S]*?)(?:\n\n|_Action items|$)/,
|
|
169
|
+
);
|
|
170
|
+
if (callerSaidMatch) {
|
|
171
|
+
const callerBlock = callerSaidMatch[1];
|
|
172
|
+
result.callerStatements = callerBlock
|
|
173
|
+
.split("\n")
|
|
174
|
+
.map((line) => line.replace(/^>\s*/, "").trim())
|
|
175
|
+
.filter((line) => line.length > 0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// --- Classify topics ---
|
|
179
|
+
const allCallerText = result.callerStatements.join(" ");
|
|
180
|
+
for (const [topic, pattern] of Object.entries(TOPIC_PATTERNS)) {
|
|
181
|
+
if (pattern.test(allCallerText)) {
|
|
182
|
+
result.topics.push(topic);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// --- Extract action items ---
|
|
187
|
+
result.actionItems = extractActionItems(result);
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Action Item Extraction
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Extract action items from parsed caller statements.
|
|
198
|
+
* Groups related consecutive statements and identifies actionable requests.
|
|
199
|
+
*
|
|
200
|
+
* @param {object} parsed - Parsed transcript result
|
|
201
|
+
* @returns {Array<object>} Action items
|
|
202
|
+
*/
|
|
203
|
+
function extractActionItems(parsed) {
|
|
204
|
+
const items = [];
|
|
205
|
+
const statements = parsed.callerStatements;
|
|
206
|
+
|
|
207
|
+
if (!statements || statements.length === 0) return items;
|
|
208
|
+
|
|
209
|
+
// Group consecutive statements into logical segments.
|
|
210
|
+
// A segment break occurs when there's a topic shift or a clear pause indicator.
|
|
211
|
+
const segments = segmentStatements(statements);
|
|
212
|
+
|
|
213
|
+
for (const segment of segments) {
|
|
214
|
+
const segmentText = segment.join(" ");
|
|
215
|
+
|
|
216
|
+
// Check if this segment contains action-worthy content
|
|
217
|
+
const isActionable = ACTION_PATTERNS.some((p) => p.test(segmentText));
|
|
218
|
+
if (!isActionable) continue;
|
|
219
|
+
|
|
220
|
+
// Determine priority
|
|
221
|
+
let priority = "medium";
|
|
222
|
+
if (PRIORITY_SIGNALS.critical.test(segmentText)) priority = "critical";
|
|
223
|
+
else if (PRIORITY_SIGNALS.high.test(segmentText)) priority = "high";
|
|
224
|
+
|
|
225
|
+
// Determine topic
|
|
226
|
+
const topics = [];
|
|
227
|
+
for (const [topic, pattern] of Object.entries(TOPIC_PATTERNS)) {
|
|
228
|
+
if (pattern.test(segmentText)) topics.push(topic);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Build a concise title from the segment
|
|
232
|
+
const title = buildActionTitle(segmentText, topics);
|
|
233
|
+
|
|
234
|
+
// Check if caller is CEO for priority boost
|
|
235
|
+
if (parsed.callerIdentity?.privilege === "ceo" && priority === "medium") {
|
|
236
|
+
priority = "high";
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
items.push({
|
|
240
|
+
title,
|
|
241
|
+
context: segmentText,
|
|
242
|
+
priority,
|
|
243
|
+
topics,
|
|
244
|
+
source: "voice-call",
|
|
245
|
+
source_ref: parsed.slackTs
|
|
246
|
+
? `slack:${parsed.slackChannel || "unknown"}:${parsed.slackTs}`
|
|
247
|
+
: "voice-call",
|
|
248
|
+
caller: parsed.caller,
|
|
249
|
+
caller_privilege: parsed.callerIdentity?.privilege || "unknown",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return items;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Segment caller statements into logical groups.
|
|
258
|
+
* Statements about the same topic are grouped together.
|
|
259
|
+
*/
|
|
260
|
+
function segmentStatements(statements) {
|
|
261
|
+
if (statements.length === 0) return [];
|
|
262
|
+
|
|
263
|
+
const segments = [];
|
|
264
|
+
let current = [statements[0]];
|
|
265
|
+
|
|
266
|
+
for (let i = 1; i < statements.length; i++) {
|
|
267
|
+
const prev = statements[i - 1];
|
|
268
|
+
const curr = statements[i];
|
|
269
|
+
|
|
270
|
+
// Detect topic shift: if current statement matches different topic patterns
|
|
271
|
+
// than previous, or if there's a transitional phrase
|
|
272
|
+
const isTopicShift =
|
|
273
|
+
/\b(?:also|another thing|by the way|oh and|next|separately|on a different|switching to)\b/i.test(
|
|
274
|
+
curr,
|
|
275
|
+
) || hasTopicShift(prev, curr);
|
|
276
|
+
|
|
277
|
+
if (isTopicShift) {
|
|
278
|
+
segments.push(current);
|
|
279
|
+
current = [curr];
|
|
280
|
+
} else {
|
|
281
|
+
current.push(curr);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (current.length > 0) {
|
|
286
|
+
segments.push(current);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return segments;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Detect if two consecutive statements are about different topics.
|
|
294
|
+
*/
|
|
295
|
+
function hasTopicShift(prev, curr) {
|
|
296
|
+
const prevTopics = new Set();
|
|
297
|
+
const currTopics = new Set();
|
|
298
|
+
|
|
299
|
+
for (const [topic, pattern] of Object.entries(TOPIC_PATTERNS)) {
|
|
300
|
+
if (pattern.test(prev)) prevTopics.add(topic);
|
|
301
|
+
if (pattern.test(curr)) currTopics.add(topic);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// If both have topics and they don't overlap, it's a shift
|
|
305
|
+
if (prevTopics.size > 0 && currTopics.size > 0) {
|
|
306
|
+
const overlap = [...prevTopics].some((t) => currTopics.has(t));
|
|
307
|
+
return !overlap;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Build a concise action item title from segment text.
|
|
315
|
+
*/
|
|
316
|
+
function buildActionTitle(text, topics) {
|
|
317
|
+
// Truncate to a reasonable title length
|
|
318
|
+
const maxLen = 100;
|
|
319
|
+
|
|
320
|
+
// Try to extract the core request
|
|
321
|
+
const requestMatch = text.match(
|
|
322
|
+
/(?:can you|could you|please|I need you to|I want you to)\s+(.{10,80}?)(?:\.|,|$)/i,
|
|
323
|
+
);
|
|
324
|
+
if (requestMatch) {
|
|
325
|
+
return `CEO voice request: ${capitalize(requestMatch[1].trim())}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Try to use the topic and first actionable phrase
|
|
329
|
+
const topicLabel = topics.length > 0 ? topics[0] : "general";
|
|
330
|
+
const shortened =
|
|
331
|
+
text.length > maxLen ? text.substring(0, maxLen) + "..." : text;
|
|
332
|
+
|
|
333
|
+
return `CEO voice directive (${topicLabel}): ${shortened}`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Inbox Item Generation
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Convert parsed voice transcript into inbox queue items compatible
|
|
342
|
+
* with the poller's writeInboxItem format.
|
|
343
|
+
*
|
|
344
|
+
* @param {object} parsed - Output from parseVoiceTranscript
|
|
345
|
+
* @returns {Array<object>} Inbox items ready for writeInboxItem
|
|
346
|
+
*/
|
|
347
|
+
export function toInboxItems(parsed) {
|
|
348
|
+
if (!parsed.actionItems || parsed.actionItems.length === 0) {
|
|
349
|
+
// Even if no specific action items detected, create one catch-all item
|
|
350
|
+
// for voice calls from the CEO so nothing is missed
|
|
351
|
+
if (parsed.callerIdentity?.privilege === "ceo") {
|
|
352
|
+
return [
|
|
353
|
+
{
|
|
354
|
+
id: `voice-${parsed.slackTs || Date.now()}`.replace(/\./g, "-"),
|
|
355
|
+
service: "slack",
|
|
356
|
+
channel: `dm/${parsed.caller || "unknown"}`,
|
|
357
|
+
sender: parsed.caller || "unknown",
|
|
358
|
+
sender_privilege: parsed.callerIdentity?.privilege || "unknown",
|
|
359
|
+
timestamp: new Date().toISOString(),
|
|
360
|
+
subject: `Voice call from ${parsed.caller || "unknown caller"} (${parsed.duration || "unknown"})`,
|
|
361
|
+
content: parsed.rawText,
|
|
362
|
+
thread_id: "",
|
|
363
|
+
is_reply: false,
|
|
364
|
+
is_voice_transcript: true,
|
|
365
|
+
voice_meta: {
|
|
366
|
+
caller_phone: parsed.callerPhone,
|
|
367
|
+
duration_seconds: parsed.durationSeconds,
|
|
368
|
+
turns: parsed.turns,
|
|
369
|
+
topics: parsed.topics,
|
|
370
|
+
caller_statements_count: parsed.callerStatements.length,
|
|
371
|
+
},
|
|
372
|
+
priority_signals: {
|
|
373
|
+
from_ceo: true,
|
|
374
|
+
tagged_urgent: false,
|
|
375
|
+
contains_deadline: false,
|
|
376
|
+
mentions_agent: true,
|
|
377
|
+
},
|
|
378
|
+
raw_ref: parsed.slackTs
|
|
379
|
+
? `slack:${parsed.slackChannel || "unknown"}:${parsed.slackTs}`
|
|
380
|
+
: "",
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return parsed.actionItems.map((action, idx) => ({
|
|
388
|
+
id: `voice-${parsed.slackTs || Date.now()}-${idx}`.replace(/\./g, "-"),
|
|
389
|
+
service: "slack",
|
|
390
|
+
channel: `dm/${parsed.caller || "unknown"}`,
|
|
391
|
+
sender: parsed.caller || "unknown",
|
|
392
|
+
sender_privilege: action.caller_privilege || "unknown",
|
|
393
|
+
timestamp: new Date().toISOString(),
|
|
394
|
+
subject: action.title,
|
|
395
|
+
content: `[Voice Call Action Item]\n\nCaller: ${parsed.caller || "unknown"} (${parsed.callerPhone || "unknown"})\nDuration: ${parsed.duration || "unknown"}\nTopics: ${action.topics.join(", ") || "general"}\n\nCaller statement:\n${action.context}\n\nFull transcript summary:\n${parsed.rawText}`,
|
|
396
|
+
thread_id: "",
|
|
397
|
+
is_reply: false,
|
|
398
|
+
is_voice_transcript: true,
|
|
399
|
+
voice_meta: {
|
|
400
|
+
caller_phone: parsed.callerPhone,
|
|
401
|
+
duration_seconds: parsed.durationSeconds,
|
|
402
|
+
turns: parsed.turns,
|
|
403
|
+
topics: action.topics,
|
|
404
|
+
action_priority: action.priority,
|
|
405
|
+
},
|
|
406
|
+
priority_signals: {
|
|
407
|
+
from_ceo: action.caller_privilege === "ceo",
|
|
408
|
+
tagged_urgent: action.priority === "critical",
|
|
409
|
+
contains_deadline: PRIORITY_SIGNALS.high.test(action.context),
|
|
410
|
+
mentions_agent: true,
|
|
411
|
+
},
|
|
412
|
+
raw_ref: action.source_ref || "",
|
|
413
|
+
}));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
// Helpers
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
|
|
420
|
+
function formatDuration(seconds) {
|
|
421
|
+
if (!seconds || seconds < 0) return "unknown";
|
|
422
|
+
const min = Math.floor(seconds / 60);
|
|
423
|
+
const sec = seconds % 60;
|
|
424
|
+
if (min === 0) return `${sec}s`;
|
|
425
|
+
return `${min}m ${sec}s`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function capitalize(str) {
|
|
429
|
+
if (!str) return "";
|
|
430
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
// CLI mode — for testing
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
if (process.argv[1] && process.argv[1].includes("parse-voice-transcript")) {
|
|
438
|
+
// Read from stdin or from file argument
|
|
439
|
+
const input = process.argv[2];
|
|
440
|
+
|
|
441
|
+
if (input) {
|
|
442
|
+
// File argument
|
|
443
|
+
const { readFileSync } = await import("fs");
|
|
444
|
+
const text = readFileSync(input, "utf-8");
|
|
445
|
+
processInput(text);
|
|
446
|
+
} else {
|
|
447
|
+
// Stdin
|
|
448
|
+
let text = "";
|
|
449
|
+
process.stdin.setEncoding("utf-8");
|
|
450
|
+
process.stdin.on("data", (chunk) => (text += chunk));
|
|
451
|
+
process.stdin.on("end", () => processInput(text));
|
|
452
|
+
|
|
453
|
+
// If no input after 100ms, show usage
|
|
454
|
+
setTimeout(() => {
|
|
455
|
+
if (!text) {
|
|
456
|
+
console.log("Usage: node parse-voice-transcript.mjs [file]");
|
|
457
|
+
console.log(
|
|
458
|
+
" echo 'message text' | node parse-voice-transcript.mjs",
|
|
459
|
+
);
|
|
460
|
+
console.log("");
|
|
461
|
+
console.log("Parses voice call transcript summaries posted to Slack.");
|
|
462
|
+
process.exit(0);
|
|
463
|
+
}
|
|
464
|
+
}, 100);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function processInput(text) {
|
|
469
|
+
if (!isVoiceTranscript(text)) {
|
|
470
|
+
console.log("Not a voice call transcript.");
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const parsed = parseVoiceTranscript(text, { ts: "test", channel: "test" });
|
|
475
|
+
console.log("\n=== Parsed Voice Transcript ===");
|
|
476
|
+
console.log(JSON.stringify(parsed, null, 2));
|
|
477
|
+
|
|
478
|
+
const inboxItems = toInboxItems(parsed);
|
|
479
|
+
console.log("\n=== Generated Inbox Items ===");
|
|
480
|
+
console.log(JSON.stringify(inboxItems, null, 2));
|
|
481
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# PDF Generation Pipeline
|
|
2
|
+
|
|
3
|
+
> **Full setup guide**: See [docs/guides/pdf-generation-setup.md](../../docs/guides/pdf-generation-setup.md) for complete installation, template customisation, brand integration, testing, and troubleshooting.
|
|
4
|
+
|
|
5
|
+
Generates professional PDF documents from Markdown using Pandoc + XeLaTeX.
|
|
6
|
+
Ported from the `~/innovation-lab` book generation pipeline, adapted for corporate documents.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Install Pandoc
|
|
12
|
+
brew install pandoc
|
|
13
|
+
|
|
14
|
+
# Install MacTeX (for XeLaTeX)
|
|
15
|
+
brew install --cask mactex-no-gui
|
|
16
|
+
|
|
17
|
+
# Install Node.js dependencies
|
|
18
|
+
cd ~/<agent-name> && npm install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Generate a single document
|
|
25
|
+
node scripts/pdf-generation/build-document.mjs --input outputs/briefs/daily/2026-04-02/morning-brief.md --template corporate-letter
|
|
26
|
+
|
|
27
|
+
# Generate a board pack
|
|
28
|
+
node scripts/pdf-generation/build-document.mjs --input outputs/board/2026-Q2-board-pack.md --template board-pack
|
|
29
|
+
|
|
30
|
+
# Generate an investor letter
|
|
31
|
+
node scripts/pdf-generation/build-document.mjs --input outputs/memos/investor-update-2026-Q1.md --template investor-letter
|
|
32
|
+
|
|
33
|
+
# Available templates
|
|
34
|
+
# corporate-letter — Standard A4/Letter corporate document
|
|
35
|
+
# board-pack — Board materials with cover page and TOC
|
|
36
|
+
# investor-letter — Investor communications on branded letterhead
|
|
37
|
+
# memo — Internal memos and briefs
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Templates
|
|
41
|
+
|
|
42
|
+
LaTeX templates live in `scripts/pdf-generation/templates/`:
|
|
43
|
+
|
|
44
|
+
| Template | Page Size | Margins | Use Case |
|
|
45
|
+
| ------------------------ | -------------- | ------- | ---------------------------------------------------- |
|
|
46
|
+
| `corporate-letter.latex` | A4 (210x297mm) | 25mm | Standard correspondence, regulatory submissions |
|
|
47
|
+
| `board-pack.latex` | A4 | 20mm | Board packs with TOC, branded cover, section headers |
|
|
48
|
+
| `investor-letter.latex` | A4 | 30mm | Investor letters, LP updates |
|
|
49
|
+
| `memo.latex` | A4 | 20mm | Internal briefs, memos, decision documents |
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
1. Markdown content is authored (by agents or manually)
|
|
54
|
+
2. `build-document.mjs` reads the Markdown and selects the appropriate LaTeX template
|
|
55
|
+
3. Pandoc converts Markdown → PDF via XeLaTeX with brand fonts and logo placement
|
|
56
|
+
4. Output PDF is saved alongside the source Markdown or to a specified output path
|
|
57
|
+
|
|
58
|
+
## Brand Integration
|
|
59
|
+
|
|
60
|
+
- Templates reference brand assets from `public/assets/` for logos and icons
|
|
61
|
+
- Typography uses Georgia (main) and Helvetica Neue (sans) per brand guidelines
|
|
62
|
+
- Colour values sourced from `config/brand-assets.yaml`
|
|
63
|
+
- See `config/brand-assets.yaml → typography.pdf` for font configuration
|