@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,685 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Prompt Builder — constructs focused prompts for claude --print sessions
|
|
3
|
+
// Each inbox item or backlog queue item gets a tailored prompt with identity,
|
|
4
|
+
// rules, context, and action instructions.
|
|
5
|
+
|
|
6
|
+
import { readFileSync, readdirSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { compileContext } from "./context-compiler.mjs";
|
|
9
|
+
import { wrapExternalContent } from "../../lib/security/external-content.mjs";
|
|
10
|
+
import { isEnabled as orgEnabled } from "../../lib/org/client.mjs";
|
|
11
|
+
import { recall as orgRecall } from "../../lib/org/knowledge.mjs";
|
|
12
|
+
import { outcomeSourceShareable } from "./session-outcomes.mjs";
|
|
13
|
+
|
|
14
|
+
const AGENT_REPO_DIR = process.env.AGENT_DIR || join(new URL(".", import.meta.url).pathname, "../..");
|
|
15
|
+
|
|
16
|
+
// Load agent identity from canonical SOT for use in prompts and fallback preamble.
|
|
17
|
+
function loadAgent() {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(join(AGENT_REPO_DIR, "config/agent.json"), "utf-8"));
|
|
20
|
+
} catch {
|
|
21
|
+
return { firstName: "Agent", fullName: "Agent", title: "agent", company: "the company", principal: { firstName: "principal", fullName: "the principal", title: "principal" } };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Legacy: Maximum lines of conversation history (used when DAEMON_CONTEXT_COMPILER is off)
|
|
26
|
+
const MAX_HISTORY_LINES = 30;
|
|
27
|
+
|
|
28
|
+
// Org shared-knowledge injection (org enrollment). Bounded so a large recall can
|
|
29
|
+
// never blow up the prompt: at most this many facts, each truncated.
|
|
30
|
+
const ORG_RECALL_MAX_FACTS = 6;
|
|
31
|
+
const ORG_RECALL_FACT_CHARS = 400;
|
|
32
|
+
|
|
33
|
+
// Cached org config (config/org.yaml) — read once per process. null until loaded.
|
|
34
|
+
let cachedOrgConfig;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Load the org config (config/org.yaml) for the recall binding. Cached for the
|
|
38
|
+
* process. Returns {} on any error so callers can null-check uniformly — the org
|
|
39
|
+
* binding must never take down the daemon's main path.
|
|
40
|
+
*/
|
|
41
|
+
async function loadOrgConfig() {
|
|
42
|
+
if (cachedOrgConfig !== undefined) return cachedOrgConfig;
|
|
43
|
+
try {
|
|
44
|
+
const p = join(AGENT_REPO_DIR, "config/org.yaml");
|
|
45
|
+
const raw = readFileSync(p, "utf-8");
|
|
46
|
+
const m = await import("js-yaml");
|
|
47
|
+
const yaml = m && m.default ? m.default : m;
|
|
48
|
+
const doc = yaml.load(raw);
|
|
49
|
+
cachedOrgConfig = doc && typeof doc === "object" ? doc : {};
|
|
50
|
+
} catch {
|
|
51
|
+
cachedOrgConfig = {};
|
|
52
|
+
}
|
|
53
|
+
return cachedOrgConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** For tests: reset the cached org config so a fresh config/org.yaml is re-read. */
|
|
57
|
+
export function _resetOrgConfigCache() {
|
|
58
|
+
cachedOrgConfig = undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Derive a short recall query from the item/classification being processed.
|
|
63
|
+
* Prefers the classifier summary, then subject, then a slice of the body.
|
|
64
|
+
*/
|
|
65
|
+
function recallQueryFor(item, classResult, queueItem) {
|
|
66
|
+
const parts = [];
|
|
67
|
+
if (classResult && classResult.summary) parts.push(String(classResult.summary));
|
|
68
|
+
if (item && item.subject) parts.push(String(item.subject));
|
|
69
|
+
if (queueItem && queueItem.title) parts.push(String(queueItem.title));
|
|
70
|
+
if (parts.length === 0 && item && item.content) parts.push(String(item.content).slice(0, 200));
|
|
71
|
+
return parts.join(" — ").trim();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Render the org's shared knowledge as a clearly-delimited context block, or
|
|
76
|
+
* null when the integration is disabled / nothing relevant came back. Fail-open
|
|
77
|
+
* and bounded: never throws, caps the number and size of injected facts. The
|
|
78
|
+
* SERVER enforces ACL — we render exactly what recall() returns.
|
|
79
|
+
*
|
|
80
|
+
* @param {object} item inbox item (may be null for backlog)
|
|
81
|
+
* @param {object} classResult classifier output
|
|
82
|
+
* @param {object} queueItem backlog queue item (may be null)
|
|
83
|
+
* @returns {Promise<string|null>}
|
|
84
|
+
*/
|
|
85
|
+
async function buildOrgKnowledgeBlock(item, classResult, queueItem) {
|
|
86
|
+
try {
|
|
87
|
+
const cfg = await loadOrgConfig();
|
|
88
|
+
if (!orgEnabled(cfg)) return null;
|
|
89
|
+
const query = recallQueryFor(item, classResult, queueItem);
|
|
90
|
+
if (!query) return null;
|
|
91
|
+
|
|
92
|
+
const facts = await orgRecall(query, { cfg, limit: ORG_RECALL_MAX_FACTS });
|
|
93
|
+
if (!Array.isArray(facts) || facts.length === 0) return null;
|
|
94
|
+
|
|
95
|
+
const lines = ["--- ORG SHARED KNOWLEDGE ---"];
|
|
96
|
+
lines.push("Facts the org already knows that may bear on this item (ACL-filtered by the");
|
|
97
|
+
lines.push("server; treat as trusted internal context). Use them to inform your reply:");
|
|
98
|
+
let n = 0;
|
|
99
|
+
for (const f of facts) {
|
|
100
|
+
if (n >= ORG_RECALL_MAX_FACTS) break;
|
|
101
|
+
const textRaw = typeof f === "string" ? f : (f && (f.text || f.content || f.body) || "");
|
|
102
|
+
const text = String(textRaw).trim();
|
|
103
|
+
if (!text) continue;
|
|
104
|
+
const clipped = text.length > ORG_RECALL_FACT_CHARS ? `${text.slice(0, ORG_RECALL_FACT_CHARS)}…` : text;
|
|
105
|
+
const id = f && typeof f === "object" && f.id != null ? ` [${String(f.id)}]` : "";
|
|
106
|
+
lines.push(`- ${clipped}${id}`);
|
|
107
|
+
n += 1;
|
|
108
|
+
}
|
|
109
|
+
if (n === 0) return null;
|
|
110
|
+
lines.push("--- END ORG SHARED KNOWLEDGE ---");
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
} catch {
|
|
113
|
+
// Fail-open: org recall must never take down the prompt build.
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Cached identity preamble — extracted from CLAUDE.md on first call
|
|
119
|
+
let cachedPreamble = null;
|
|
120
|
+
|
|
121
|
+
function loadPreamble() {
|
|
122
|
+
if (cachedPreamble) return cachedPreamble;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const raw = readFileSync(join(AGENT_REPO_DIR, "CLAUDE.md"), "utf-8");
|
|
126
|
+
cachedPreamble = extractPreamble(raw);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error(`[prompt-builder] Failed to read CLAUDE.md: ${err.message}`);
|
|
129
|
+
cachedPreamble = FALLBACK_PREAMBLE;
|
|
130
|
+
}
|
|
131
|
+
return cachedPreamble;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Extract the Identity, Operating Principles, Communication Rules,
|
|
136
|
+
* and Document Sharing sections from CLAUDE.md — keep it concise.
|
|
137
|
+
*/
|
|
138
|
+
function extractPreamble(raw) {
|
|
139
|
+
const sections = [];
|
|
140
|
+
const lines = raw.split("\n");
|
|
141
|
+
const targetHeaders = [
|
|
142
|
+
"## Identity",
|
|
143
|
+
"## Company Context",
|
|
144
|
+
"## Operating Principles",
|
|
145
|
+
"## Communication Rules",
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
let capturing = false;
|
|
149
|
+
let depth = 0;
|
|
150
|
+
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
// Start capturing when we hit a target header
|
|
153
|
+
if (targetHeaders.some((h) => line.startsWith(h))) {
|
|
154
|
+
capturing = true;
|
|
155
|
+
depth = 2; // ## level
|
|
156
|
+
sections.push(line);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Stop capturing when we hit another ## header that is NOT a sub-section of what we want
|
|
161
|
+
if (capturing && /^## [A-Z]/.test(line) && !targetHeaders.some((h) => line.startsWith(h))) {
|
|
162
|
+
capturing = false;
|
|
163
|
+
sections.push(""); // blank line separator
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Also stop at ### Document Sharing end — capture it but stop at next ##
|
|
168
|
+
if (capturing) {
|
|
169
|
+
sections.push(line);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const extracted = sections.join("\n").trim();
|
|
174
|
+
|
|
175
|
+
// If extraction got something reasonable, use it; otherwise fall back
|
|
176
|
+
if (extracted.length > 200) return extracted;
|
|
177
|
+
return FALLBACK_PREAMBLE;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Generic preamble derived from config/agent.json. This is only used when
|
|
181
|
+
// CLAUDE.md is unreadable; ordinarily the daemon pulls the full identity
|
|
182
|
+
// block straight from the agent's CLAUDE.md.
|
|
183
|
+
function buildFallbackPreamble() {
|
|
184
|
+
const a = loadAgent();
|
|
185
|
+
const principal = a.principal || {};
|
|
186
|
+
const principalName = principal.fullName || "the principal";
|
|
187
|
+
const principalTitle = principal.title || "principal";
|
|
188
|
+
return `You are ${a.fullName}, ${a.title} to ${principalName}, ${principalTitle} of ${a.company}.
|
|
189
|
+
You operate as the autonomous executive command layer for the company.
|
|
190
|
+
|
|
191
|
+
${a.companyDescription || a.company}.
|
|
192
|
+
|
|
193
|
+
Operating Principles:
|
|
194
|
+
1. Follow-through over brilliance — track every commitment until it closes
|
|
195
|
+
2. Concise over comprehensive — sharp recommendations, not exhaustive reports
|
|
196
|
+
3. Evidence over opinion — cite sources
|
|
197
|
+
4. Bias to action — act decisively on operational matters; escalate only strategic commitments
|
|
198
|
+
5. Audit everything — every action is logged
|
|
199
|
+
6. Full autonomy, full accountability
|
|
200
|
+
|
|
201
|
+
Autonomy Model:
|
|
202
|
+
- ${a.firstName} sends autonomously: all internal messages, external operational messages, candidate comms, follow-ups, calendar coordination
|
|
203
|
+
- ${a.firstName} escalates only: binding legal/financial obligations, regulatory submissions, acquisition deal terms, public statements
|
|
204
|
+
|
|
205
|
+
Document Sharing (CRITICAL):
|
|
206
|
+
- NEVER reference local file paths in outbound communications
|
|
207
|
+
- Upload to Google Drive or generate branded PDF for sharing
|
|
208
|
+
- Inline short content directly in messages
|
|
209
|
+
- Never attach raw .md or .yaml files`;
|
|
210
|
+
}
|
|
211
|
+
const FALLBACK_PREAMBLE = buildFallbackPreamble();
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Proactive self-learning habit (WS3). Injected near the top of every reactive /
|
|
215
|
+
* backlog prompt so the agent treats skill-authoring as a normal reflex. The
|
|
216
|
+
* heavy lifting (the actual write + secret-scan + pruning) is autonomous and
|
|
217
|
+
* happens out-of-band in the Stop-hook reflect worker — this paragraph just
|
|
218
|
+
* sets the disposition.
|
|
219
|
+
*/
|
|
220
|
+
const SKILLS_GUIDANCE = [
|
|
221
|
+
"SELF-LEARNING: You can author your own Claude Code skills. When you work out a",
|
|
222
|
+
"repeatable, multi-step method that is likely to recur, prefer capturing it as a",
|
|
223
|
+
"skill (under plugins/agent-skills/) over re-deriving it next time — a future",
|
|
224
|
+
"session loads it automatically. Bias toward capturing the genuinely reusable",
|
|
225
|
+
"procedure; do NOT capture one-off steps, transient failures, or \"tool X doesn't",
|
|
226
|
+
"work\" claims. Skills are pruned automatically when they go stale, so capturing",
|
|
227
|
+
"is cheap and safe. To recall what you did before, the `session-search` skill",
|
|
228
|
+
"searches your own past sessions verbatim.",
|
|
229
|
+
].join("\n");
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* OUTCOME DISCIPLINE (parity with hq's meeting chair). A turn that decides things
|
|
233
|
+
* and commits to next steps must not leave them in a session log — nobody reads a
|
|
234
|
+
* session log, so the commitment is lost and the next turn re-decides it.
|
|
235
|
+
*
|
|
236
|
+
* This asks for ONE machine-readable block at the very end of the turn. The
|
|
237
|
+
* landing itself is DRIVEN, not left to the model: the daemon's post-session hook
|
|
238
|
+
* (scripts/daemon/session-outcomes.mjs) parses this block off the turn's final
|
|
239
|
+
* text and writes it through the org protocol (board.create + knowledge.append).
|
|
240
|
+
* So the model's only job is to be honest about what it committed to.
|
|
241
|
+
*
|
|
242
|
+
* Injected only when DAEMON_SESSION_OUTCOMES=1 (the same flag that arms the hook)
|
|
243
|
+
* AND the turn's source is org-visible (the same fail-closed gate the hook
|
|
244
|
+
* applies) — a landed outcome is readable by the whole org, so a private DM turn
|
|
245
|
+
* is neither asked for the block nor swept for one.
|
|
246
|
+
*/
|
|
247
|
+
const OUTCOME_DISCIPLINE = [
|
|
248
|
+
"OUTCOMES BLOCK (required, last thing in your reply):",
|
|
249
|
+
"End your turn with a block in exactly this shape:",
|
|
250
|
+
"",
|
|
251
|
+
"OUTCOMES:",
|
|
252
|
+
"- <the commitment, as a concrete next step> — owner: <name> — due: <when>",
|
|
253
|
+
"",
|
|
254
|
+
"Rules:",
|
|
255
|
+
"- List ONLY things that are genuinely committed to and NOT already done in this",
|
|
256
|
+
" turn — a future action someone now owes. Work you completed here is not an",
|
|
257
|
+
" outcome.",
|
|
258
|
+
"- `owner:` and `due:` are optional; include them only when they were actually",
|
|
259
|
+
" stated. Never guess an owner.",
|
|
260
|
+
"- Most turns commit to nothing. When that is true, write `OUTCOMES: none` — an",
|
|
261
|
+
" invented next step is worse than an empty block.",
|
|
262
|
+
"- Maximum 8 lines, one clause each, no sub-bullets.",
|
|
263
|
+
"- This block is machine-read and landed on the org board automatically; do not",
|
|
264
|
+
" also create board items for the same commitments by hand, and do not mention",
|
|
265
|
+
" the block in the message you send to the user.",
|
|
266
|
+
].join("\n");
|
|
267
|
+
|
|
268
|
+
const ACTION_INSTRUCTIONS = {
|
|
269
|
+
respond: `ACTION: Respond to this message.
|
|
270
|
+
- Read the message carefully and understand the request
|
|
271
|
+
- Check for any relevant context in thread history
|
|
272
|
+
- Draft the substantive response directly — do NOT re-acknowledge if a holding message was already sent
|
|
273
|
+
|
|
274
|
+
READING / CONTEXT (MCP tools are fine here):
|
|
275
|
+
- Use MCP Slack for reading: \`mcp__claude_ai_Slack__slack_search_messages\`, \`slack_conversations_history\`, \`slack_users_info\`, etc.
|
|
276
|
+
- Use MCP Gmail for reading: \`mcp__claude_ai_Gmail__gmail_search\`, \`gmail_read_email\`, etc.
|
|
277
|
+
- Use MCP Google Calendar for scheduling lookups and event reads
|
|
278
|
+
- These are authorised by the user session and are the preferred way to gather context before composing a reply
|
|
279
|
+
|
|
280
|
+
SENDING (CLI scripts only — MCP writes will be blocked by pre-tool hooks):
|
|
281
|
+
- Slack send: \`./scripts/slack-send.sh "<channel_id>" "<message_text>" --responding_to "<inbound_message_ts>"\`
|
|
282
|
+
- Add \`--thread_ts "<thread_ts>"\` if replying inside an existing thread
|
|
283
|
+
- The channel_id is in the inbox item as \`channel_id\` (starts with C, D, or G)
|
|
284
|
+
- Always pass \`--responding_to\` with the inbound message ts so dedup works
|
|
285
|
+
- Gmail send: \`python3 scripts/send-email-threaded.py "<to>" "<subject>" "<body>" --reply-to-subject "<original_subject>"\`
|
|
286
|
+
- Use \`--attachment <path>\` to include files
|
|
287
|
+
- The \`From\` header and HTML signature are injected automatically
|
|
288
|
+
- Workspace email (service \`orgmail\`) send: \`node scripts/org/send-orgmail.mjs --thread "<thread_id>" --body-file <path>\`
|
|
289
|
+
- NEVER raw curl and never the MCP \`email_send\` tool — this CLI is the sanctioned lane
|
|
290
|
+
- Recipients and subject derive from the thread server-side (reply rule); the thread_id is the inbox item's \`thread_id\`/\`channel_id\`
|
|
291
|
+
- A first-contact recipient returns approval_required — re-run with \`--request-approval --wait\` after a human approves in Cohort
|
|
292
|
+
- SMS send: \`./scripts/send-sms.sh --to "<e164_number>" --body "<text>"\`
|
|
293
|
+
- WhatsApp send: \`./scripts/send-whatsapp.sh --to "whatsapp:<e164_number>" --body "<text>"\`
|
|
294
|
+
|
|
295
|
+
Why split read vs write: MCP tools are ideal for reading because they have full API coverage and the session is already authorised. But writes must go through the CLI scripts so the outbound message carries the agent's own User OAuth Token (xoxp-) and appears as the agent's Slack identity — not tagged as "Sent using @Claude". The pre-tool-use hook \`scripts/hooks/block-mcp-slack-send.sh\` will reject any attempt to call \`slack_send_message\` / \`slack_schedule_message\` / \`slack_send_message_draft\` and remind you to use the CLI. Gmail write hooks behave the same way, and \`scripts/hooks/block-mcp-cohort-send.sh\` blocks the org-MCP outbound writes (\`mcp__cohort__messaging_send\` / \`email_send\` / \`org_rpc\`).
|
|
296
|
+
|
|
297
|
+
- Match the tone and urgency of the sender
|
|
298
|
+
- If the sender is CEO, prioritise speed and directness
|
|
299
|
+
- If you need to share documents, upload to Google Drive or send as PDF — NEVER reference local file paths
|
|
300
|
+
- Verify the send succeeded by checking the script's exit code and stdout before considering the task complete`,
|
|
301
|
+
|
|
302
|
+
draft: `ACTION: Draft a response or document — do NOT send it yet.
|
|
303
|
+
- Read the message and understand what is being requested
|
|
304
|
+
- Draft the response or document
|
|
305
|
+
- Save the draft to outputs/ with a descriptive filename
|
|
306
|
+
- If it is a document (memo, brief, letter), use the appropriate format
|
|
307
|
+
- Log that a draft has been created and is awaiting review`,
|
|
308
|
+
|
|
309
|
+
research: `ACTION: Research this topic and write findings.
|
|
310
|
+
- Investigate the topic thoroughly using available tools and knowledge
|
|
311
|
+
- Check relevant files in knowledge/, docs/, and memory/
|
|
312
|
+
- Write concise findings to outputs/research/ with today's date prefix
|
|
313
|
+
- Include sources and evidence for all claims
|
|
314
|
+
- Highlight actionable recommendations`,
|
|
315
|
+
|
|
316
|
+
queue: `ACTION: Create a queue item for later processing.
|
|
317
|
+
- This item needs tracking but not immediate action
|
|
318
|
+
- Create or update a queue item in the appropriate state/queues/ file
|
|
319
|
+
- Set appropriate priority, owner, SLA deadline, and next_action
|
|
320
|
+
- Log the queuing decision`,
|
|
321
|
+
|
|
322
|
+
archive: `ACTION: Archive — no action needed.
|
|
323
|
+
- Mark this item as processed
|
|
324
|
+
- Log that it was reviewed and archived with a brief reason
|
|
325
|
+
- No response required`,
|
|
326
|
+
|
|
327
|
+
ignore: `ACTION: Ignore — skip entirely.
|
|
328
|
+
- This item requires no processing
|
|
329
|
+
- Log only that it was classified as ignore`,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
function today() {
|
|
333
|
+
return new Date().toISOString().split("T")[0];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Load recent conversation history for a sender/channel from interaction logs.
|
|
338
|
+
* Searches memory/interactions/slack/{channel-or-sender}/ for recent JSONL entries.
|
|
339
|
+
* Returns the most recent entries as formatted context text.
|
|
340
|
+
*/
|
|
341
|
+
function loadConversationHistory(item) {
|
|
342
|
+
if (!item) return null;
|
|
343
|
+
const date = today();
|
|
344
|
+
|
|
345
|
+
// Try multiple paths where interaction logs may live
|
|
346
|
+
const channelId = extractChannelId(item);
|
|
347
|
+
const senderSlug = item.sender ? item.sender.replace(/\s+/g, "-").toLowerCase() : null;
|
|
348
|
+
|
|
349
|
+
const candidateDirs = [];
|
|
350
|
+
if (channelId) candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", channelId));
|
|
351
|
+
if (senderSlug) {
|
|
352
|
+
candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", `dm-${senderSlug}`));
|
|
353
|
+
candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", senderSlug));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const entries = [];
|
|
357
|
+
|
|
358
|
+
for (const dir of candidateDirs) {
|
|
359
|
+
try {
|
|
360
|
+
// Read today's and yesterday's logs
|
|
361
|
+
const files = readdirSync(dir)
|
|
362
|
+
.filter((f) => f.endsWith(".jsonl"))
|
|
363
|
+
.sort()
|
|
364
|
+
.reverse()
|
|
365
|
+
.slice(0, 2); // Today + yesterday at most
|
|
366
|
+
|
|
367
|
+
for (const file of files) {
|
|
368
|
+
try {
|
|
369
|
+
const content = readFileSync(join(dir, file), "utf-8").trim();
|
|
370
|
+
if (!content) continue;
|
|
371
|
+
const lines = content.split("\n").filter(Boolean);
|
|
372
|
+
for (const line of lines) {
|
|
373
|
+
try {
|
|
374
|
+
entries.push(JSON.parse(line));
|
|
375
|
+
} catch { /* skip malformed lines */ }
|
|
376
|
+
}
|
|
377
|
+
} catch { /* skip unreadable files */ }
|
|
378
|
+
}
|
|
379
|
+
} catch { /* dir doesn't exist — try next */ }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (entries.length === 0) return null;
|
|
383
|
+
|
|
384
|
+
// Sort by timestamp and take recent entries
|
|
385
|
+
entries.sort((a, b) => {
|
|
386
|
+
const tsA = a.ts || a.received_at || "";
|
|
387
|
+
const tsB = b.ts || b.received_at || "";
|
|
388
|
+
return tsA.localeCompare(tsB);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const recent = entries.slice(-MAX_HISTORY_LINES);
|
|
392
|
+
|
|
393
|
+
// Format into readable context
|
|
394
|
+
const lines = ["## Recent conversation history with this sender"];
|
|
395
|
+
for (const entry of recent) {
|
|
396
|
+
const from = entry.from || entry.user_name || entry.sender || "unknown";
|
|
397
|
+
const text = entry.content || entry.text || "(no text)";
|
|
398
|
+
const ts = entry.received_at || entry.processed_at || entry.ts || "";
|
|
399
|
+
const actions = entry.actions_taken
|
|
400
|
+
? ` [Actions: ${Array.isArray(entry.actions_taken) ? entry.actions_taken.join(", ") : entry.actions_taken}]`
|
|
401
|
+
: entry.action_taken
|
|
402
|
+
? ` [Action: ${entry.action_taken}]`
|
|
403
|
+
: "";
|
|
404
|
+
const responded = entry.response_sent ? " [Responded]" : "";
|
|
405
|
+
lines.push(`[${ts}] ${from}: ${text.substring(0, 300)}${actions}${responded}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return lines.join("\n");
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Extract Slack channel ID from item metadata.
|
|
413
|
+
*/
|
|
414
|
+
function extractChannelId(item) {
|
|
415
|
+
if (!item) return null;
|
|
416
|
+
// Direct channel ID field
|
|
417
|
+
if (item.channel_id) return item.channel_id;
|
|
418
|
+
// From raw_ref format: "slack:D099N1JGKRQ:1775331885.690669"
|
|
419
|
+
if (item.raw_ref) {
|
|
420
|
+
const match = item.raw_ref.match(/slack:([DC][A-Z0-9]+):/);
|
|
421
|
+
if (match) return match[1];
|
|
422
|
+
}
|
|
423
|
+
// Channel field if it looks like an ID
|
|
424
|
+
if (item.channel && /^[DC][A-Z0-9]{8,}$/.test(item.channel)) return item.channel;
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Build a sender identifier string for the prompt
|
|
430
|
+
*/
|
|
431
|
+
function formatSender(item) {
|
|
432
|
+
const parts = [];
|
|
433
|
+
if (item.sender) parts.push(item.sender);
|
|
434
|
+
if (item.sender_privilege && item.sender_privilege !== "unknown") {
|
|
435
|
+
parts.push(`(${item.sender_privilege})`);
|
|
436
|
+
}
|
|
437
|
+
return parts.join(" ") || "unknown sender";
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Build the item context block for inbox items
|
|
442
|
+
*/
|
|
443
|
+
export function buildInboxContext(item) {
|
|
444
|
+
const lines = [];
|
|
445
|
+
lines.push("--- INCOMING MESSAGE ---");
|
|
446
|
+
lines.push(`From: ${formatSender(item)}`);
|
|
447
|
+
lines.push(`Service: ${item.service || "unknown"}`);
|
|
448
|
+
lines.push(`Channel: ${item.channel || "unknown"}`);
|
|
449
|
+
if (item.subject) lines.push(`Subject: ${item.subject}`);
|
|
450
|
+
lines.push(`Timestamp: ${item.timestamp || new Date().toISOString()}`);
|
|
451
|
+
if (item.is_reply) lines.push(`Is reply: yes`);
|
|
452
|
+
if (item.thread_id) lines.push(`Thread ID: ${item.thread_id}`);
|
|
453
|
+
if (item.raw_ref) lines.push(`Reference: ${item.raw_ref}`);
|
|
454
|
+
lines.push("");
|
|
455
|
+
|
|
456
|
+
const source = item.service || item.channel || "external";
|
|
457
|
+
|
|
458
|
+
if (item.thread_context) {
|
|
459
|
+
lines.push("Thread context:");
|
|
460
|
+
// Thread history is attacker-influenced; fence it as untrusted DATA (H2).
|
|
461
|
+
lines.push(wrapExternalContent(item.thread_context, { source }));
|
|
462
|
+
lines.push("");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
lines.push("Message content:");
|
|
466
|
+
// The message body is the primary prompt-injection surface: inbound email/
|
|
467
|
+
// Slack/etc. text flows into a claude --print session. Fence it so an
|
|
468
|
+
// embedded "ignore previous instructions" arrives as data, not commands,
|
|
469
|
+
// and annotate a heuristic suspicion score. See lib/security/external-content.mjs.
|
|
470
|
+
lines.push(
|
|
471
|
+
item.content
|
|
472
|
+
? wrapExternalContent(item.content, { source, annotateSuspicion: true })
|
|
473
|
+
: "(empty)"
|
|
474
|
+
);
|
|
475
|
+
lines.push("--- END MESSAGE ---");
|
|
476
|
+
|
|
477
|
+
return lines.join("\n");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Build the item context block for backlog queue items
|
|
482
|
+
*/
|
|
483
|
+
function buildBacklogContext(queueItem) {
|
|
484
|
+
const lines = [];
|
|
485
|
+
lines.push("--- QUEUE ITEM ---");
|
|
486
|
+
lines.push(`ID: ${queueItem.id || "unknown"}`);
|
|
487
|
+
lines.push(`Title: ${queueItem.title || "untitled"}`);
|
|
488
|
+
lines.push(`Status: ${queueItem.status || "open"}`);
|
|
489
|
+
lines.push(`Priority: ${queueItem.priority || "normal"}`);
|
|
490
|
+
lines.push(`Owner: ${queueItem.owner || loadAgent().firstName.toLowerCase()}`);
|
|
491
|
+
if (queueItem.source) lines.push(`Source: ${queueItem.source}`);
|
|
492
|
+
if (queueItem.source_ref) lines.push(`Source ref: ${queueItem.source_ref}`);
|
|
493
|
+
if (queueItem.due) lines.push(`Due: ${queueItem.due}`);
|
|
494
|
+
if (queueItem.sla_deadline) lines.push(`SLA deadline: ${queueItem.sla_deadline}`);
|
|
495
|
+
lines.push("");
|
|
496
|
+
|
|
497
|
+
if (queueItem.context) {
|
|
498
|
+
lines.push("Context:");
|
|
499
|
+
lines.push(queueItem.context);
|
|
500
|
+
lines.push("");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (queueItem.next_action) {
|
|
504
|
+
lines.push("Next action required:");
|
|
505
|
+
lines.push(queueItem.next_action);
|
|
506
|
+
lines.push("");
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (queueItem.blocked_by) {
|
|
510
|
+
lines.push(`Blocked by: ${queueItem.blocked_by}`);
|
|
511
|
+
lines.push("");
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (queueItem.success_criteria) {
|
|
515
|
+
lines.push("Success criteria:");
|
|
516
|
+
if (Array.isArray(queueItem.success_criteria)) {
|
|
517
|
+
for (const c of queueItem.success_criteria) lines.push(`- ${c}`);
|
|
518
|
+
} else {
|
|
519
|
+
lines.push(queueItem.success_criteria);
|
|
520
|
+
}
|
|
521
|
+
lines.push("");
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Include recent history for context
|
|
525
|
+
if (queueItem.history && Array.isArray(queueItem.history) && queueItem.history.length > 0) {
|
|
526
|
+
const recent = queueItem.history.slice(-3);
|
|
527
|
+
lines.push("Recent history:");
|
|
528
|
+
for (const h of recent) {
|
|
529
|
+
lines.push(` [${h.timestamp}] ${h.action} (by ${h.by || "unknown"})`);
|
|
530
|
+
}
|
|
531
|
+
lines.push("");
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
lines.push("--- END QUEUE ITEM ---");
|
|
535
|
+
return lines.join("\n");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Build a focused prompt for a claude --print session.
|
|
540
|
+
*
|
|
541
|
+
* @param {object} item - The inbox item (sender, content, channel, service, subject, thread_context, etc.)
|
|
542
|
+
* @param {object} classResult - Classifier output (priority, action, model, summary, category)
|
|
543
|
+
* @param {object} options - { type: "inbox" | "backlog", queueItem?: object }
|
|
544
|
+
* @returns {string} Prompt string ready for claude --print
|
|
545
|
+
*/
|
|
546
|
+
export async function buildPrompt(item, classResult, options = {}) {
|
|
547
|
+
const { type = "inbox", queueItem, holdingMessage } = options;
|
|
548
|
+
const preamble = loadPreamble();
|
|
549
|
+
const action = classResult.action || "respond";
|
|
550
|
+
const actionBlock = ACTION_INSTRUCTIONS[action] || ACTION_INSTRUCTIONS.respond;
|
|
551
|
+
const date = today();
|
|
552
|
+
|
|
553
|
+
const parts = [];
|
|
554
|
+
|
|
555
|
+
// 1. Identity preamble (cached)
|
|
556
|
+
parts.push(preamble);
|
|
557
|
+
parts.push("");
|
|
558
|
+
|
|
559
|
+
// 1b. Self-learning habit (WS3) — sets the disposition to capture reusable
|
|
560
|
+
// procedures as skills. Cheap, always-on; the autonomous write happens later
|
|
561
|
+
// in the Stop-hook reflect worker.
|
|
562
|
+
parts.push(SKILLS_GUIDANCE);
|
|
563
|
+
parts.push("");
|
|
564
|
+
|
|
565
|
+
// 1a. Holding message warning — TOP OF PROMPT so Claude sees it before action instructions.
|
|
566
|
+
// This is the most critical instruction in the prompt: prevents double-replies.
|
|
567
|
+
// We repeat it at section 7a as well, immediately before the action block.
|
|
568
|
+
if (holdingMessage) {
|
|
569
|
+
parts.push("===== STOP — READ THIS FIRST =====");
|
|
570
|
+
parts.push(`A HOLDING MESSAGE has ALREADY been sent to the sender by the daemon. The exact text was:`);
|
|
571
|
+
parts.push(` "${holdingMessage}"`);
|
|
572
|
+
parts.push("");
|
|
573
|
+
parts.push("Your job is to deliver the FULL substantive response or complete the actual work — NOT to acknowledge again.");
|
|
574
|
+
parts.push("- Do NOT start your reply with 'Got it', 'On it', 'Looking into this', 'Will get back to you', 'Thanks for reaching out', or any similar acknowledgment phrase.");
|
|
575
|
+
parts.push("- Do NOT echo what the user asked — they already received the holding note confirming receipt.");
|
|
576
|
+
parts.push("- Open with the actual answer, the actual draft, or the actual finding. Be direct.");
|
|
577
|
+
parts.push("- If after investigation you still cannot deliver a substantive response and need more time, send a SECOND-LEVEL UPDATE (specific blocker, ETA, what you need from the user) — never a generic 'still looking into it'.");
|
|
578
|
+
parts.push("===== END WARNING =====");
|
|
579
|
+
parts.push("");
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 2. Session context
|
|
583
|
+
parts.push(`Date: ${date}`);
|
|
584
|
+
parts.push(`Classification: priority=${classResult.priority}, action=${action}, category=${classResult.category || "unclassified"}`);
|
|
585
|
+
parts.push(`Summary: ${classResult.summary || "No summary"}`);
|
|
586
|
+
parts.push("");
|
|
587
|
+
|
|
588
|
+
// 3. Item context — inbox or backlog
|
|
589
|
+
if (type === "backlog" && queueItem) {
|
|
590
|
+
parts.push(buildBacklogContext(queueItem));
|
|
591
|
+
} else {
|
|
592
|
+
parts.push(buildInboxContext(item));
|
|
593
|
+
}
|
|
594
|
+
parts.push("");
|
|
595
|
+
|
|
596
|
+
// 3b. Org shared knowledge (org enrollment). When config/org.yaml enables the
|
|
597
|
+
// Cohort plane, consult the org-wide ACL'd RAG source for this item's topic and
|
|
598
|
+
// inject what the fleet already knows. Fail-open + bounded: a disabled/unreachable
|
|
599
|
+
// server (or empty recall) injects nothing; the SERVER enforces ACL — we just
|
|
600
|
+
// render what recall() returns.
|
|
601
|
+
// When the compiled-context path is active it performs the org recall itself
|
|
602
|
+
// (context-compiler.mjs → loadOrgMemory), so skip here to avoid a duplicate
|
|
603
|
+
// recall + duplicated context in the same prompt. Backlog items pass their
|
|
604
|
+
// queueItem into compileContext (below) so entity coverage is preserved.
|
|
605
|
+
if (process.env.DAEMON_CONTEXT_COMPILER !== "1") {
|
|
606
|
+
const orgBlock = await buildOrgKnowledgeBlock(item, classResult, queueItem);
|
|
607
|
+
if (orgBlock) {
|
|
608
|
+
parts.push(orgBlock);
|
|
609
|
+
parts.push("");
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// 4. Compiled context (profile + history + disclosure boundaries + active sessions + sent messages)
|
|
614
|
+
if (process.env.DAEMON_CONTEXT_COMPILER === "1" && (type === "inbox" ? item : true)) {
|
|
615
|
+
try {
|
|
616
|
+
const { contextBlock } = await compileContext(
|
|
617
|
+
item || {},
|
|
618
|
+
classResult,
|
|
619
|
+
{ type, queueItem }
|
|
620
|
+
);
|
|
621
|
+
parts.push(contextBlock);
|
|
622
|
+
parts.push("");
|
|
623
|
+
} catch (err) {
|
|
624
|
+
console.error(`[prompt-builder] Context compilation failed: ${err.message}`);
|
|
625
|
+
// Fall through to legacy behaviour
|
|
626
|
+
if (type === "inbox" && item) {
|
|
627
|
+
const history = loadConversationHistory(item);
|
|
628
|
+
if (history) { parts.push(history); parts.push(""); }
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
} else if (type === "inbox" && item) {
|
|
632
|
+
// Legacy path (feature flag off)
|
|
633
|
+
const history = loadConversationHistory(item);
|
|
634
|
+
if (history) { parts.push(history); parts.push(""); }
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// 5. Action instructions
|
|
638
|
+
// If a holding message was already sent, prepend a second reminder so the
|
|
639
|
+
// action block is unambiguous about not re-acknowledging.
|
|
640
|
+
if (holdingMessage) {
|
|
641
|
+
parts.push("REMINDER: A holding message was already sent (see top of prompt). The action below describes WHAT to do — but you must NOT begin your reply with another acknowledgment. Open with substance.");
|
|
642
|
+
parts.push("");
|
|
643
|
+
}
|
|
644
|
+
parts.push(actionBlock);
|
|
645
|
+
parts.push("");
|
|
646
|
+
|
|
647
|
+
// 6. User profile lookup — legacy only (when feature flag is off, profile is in compiled context)
|
|
648
|
+
if (process.env.DAEMON_CONTEXT_COMPILER !== "1" && type === "inbox" && item && item.sender) {
|
|
649
|
+
const profileName = item.sender.replace(/\s+/g, "-").toLowerCase();
|
|
650
|
+
parts.push(`Before responding, read memory/profiles/users/${profileName}.yaml for sender preferences, standing instructions, and relationship context. If the file does not exist, proceed without it.`);
|
|
651
|
+
parts.push("");
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// 7. Backlog-specific: queue update instructions (with history dedup — ib-20260405-001)
|
|
655
|
+
if (type === "backlog" && queueItem) {
|
|
656
|
+
parts.push(`When this task is complete:
|
|
657
|
+
- Update the queue item status to "resolved" in the appropriate state/queues/ file
|
|
658
|
+
- HISTORY DEDUP RULE (CRITICAL): Before appending a history entry, read the item's existing history and check the LAST entry. If the last entry has substantially the same action text (same status, same priority, same blocked_by, same conclusion — e.g. "confirmed no changes", "still open", "re-verified"), do NOT append a new history entry. Instead, ONLY update the last_updated timestamp on the queue item. Only append a new history entry when something actually changed (status changed, new information, blocker resolved, action taken that differs from the last entry).
|
|
659
|
+
- When you DO add a new history entry, include: timestamp, action taken, and by: agent-daemon
|
|
660
|
+
- If the task cannot be completed, set status to "blocked" and record what is blocking it`);
|
|
661
|
+
parts.push("");
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// 8. (Holding message warning is now at the TOP of the prompt — see section 1a.
|
|
665
|
+
// A second reminder is injected before the action block in section 5.)
|
|
666
|
+
|
|
667
|
+
// 8b. Outcome discipline — ask for the machine-read OUTCOMES block so the
|
|
668
|
+
// post-session hook can land this turn's commitments on the org board. Gated by
|
|
669
|
+
// the same flag that arms the hook AND by the same fail-closed confidentiality
|
|
670
|
+
// gate the hook applies, so the block is only ever asked for on a turn whose
|
|
671
|
+
// commitments may actually become org-visible. Asking a private DM turn for a
|
|
672
|
+
// block that would then be discarded is just noise the user can see.
|
|
673
|
+
if (
|
|
674
|
+
process.env.DAEMON_SESSION_OUTCOMES === "1" &&
|
|
675
|
+
outcomeSourceShareable(item).shareable
|
|
676
|
+
) {
|
|
677
|
+
parts.push(OUTCOME_DISCIPLINE);
|
|
678
|
+
parts.push("");
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 9. Audit instruction
|
|
682
|
+
parts.push(`Log all actions taken to logs/audit/${date}-actions.jsonl as a JSON line with: timestamp, action_type, target, result, session context.`);
|
|
683
|
+
|
|
684
|
+
return parts.join("\n");
|
|
685
|
+
}
|