@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
package/lib/charter.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* charter.mjs — operating-charter skeleton builder.
|
|
3
|
+
*
|
|
4
|
+
* Renders a complete, enterprise-grade operating charter (Markdown) from a
|
|
5
|
+
* resolved archetype profile (lib/archetype.mjs) + the agent's identity
|
|
6
|
+
* (config/agent.json). The output is deterministic and structurally complete on
|
|
7
|
+
* its own — towers, decision-rights matrix, autonomy bands, escalation, KPI
|
|
8
|
+
* tree, stakeholder map, operating principles, and a 30/60/90 scaffold all flow
|
|
9
|
+
* from the profile. The init wizard then ENRICHES the company-specific slots
|
|
10
|
+
* (concrete purpose, named stakeholders, real KPI targets, a real first-90)
|
|
11
|
+
* from the gathered context pack — but even un-enriched this is a real charter,
|
|
12
|
+
* not a placeholder.
|
|
13
|
+
*
|
|
14
|
+
* Pure (no I/O). The caller stamps any date/identity. ESM.
|
|
15
|
+
*
|
|
16
|
+
* @module charter
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
const AUTHORITY_LABEL = {
|
|
22
|
+
owns: "Owns",
|
|
23
|
+
recommends: "Recommends",
|
|
24
|
+
escalates: "Escalates",
|
|
25
|
+
informed: "Informed",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function mdEscape(s) {
|
|
29
|
+
return String(s == null ? "" : s).replace(/\|/g, "\\|");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function bulletList(items, fallback) {
|
|
33
|
+
const arr = Array.isArray(items) ? items.filter(Boolean) : [];
|
|
34
|
+
if (arr.length === 0) return fallback ? `- ${fallback}` : "- _(to be defined during init enrichment)_";
|
|
35
|
+
return arr.map((x) => `- ${x}`).join("\n");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build the operating charter Markdown from a resolved profile + identity.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} profile - resolved archetype profile (lib/archetype.mjs)
|
|
42
|
+
* @param {object} identity - config/agent.json (fullName, title, company, principal, …)
|
|
43
|
+
* @param {object} [opts] @param {string} [opts.date] ISO date string for the header
|
|
44
|
+
* @returns {string} charter Markdown
|
|
45
|
+
*/
|
|
46
|
+
export function buildCharterSkeleton(profile, identity, opts = {}) {
|
|
47
|
+
if (!profile || !identity) throw new Error("buildCharterSkeleton: requires (profile, identity)");
|
|
48
|
+
const id = identity;
|
|
49
|
+
const principal = id.principal || {};
|
|
50
|
+
const date = opts.date || "";
|
|
51
|
+
const L = [];
|
|
52
|
+
|
|
53
|
+
L.push(`# Operating Charter — ${id.fullName || "Unconfigured Agent"}`);
|
|
54
|
+
L.push("");
|
|
55
|
+
L.push(`> Generated by Maestro from the \`${profile.function} × ${profile.altitude}\` archetype.${date ? ` ${date}.` : ""}`);
|
|
56
|
+
L.push("> Company-specific sections are enriched at init from the gathered context pack.");
|
|
57
|
+
L.push("");
|
|
58
|
+
L.push(`- **Role:** ${id.title || profile.label}`);
|
|
59
|
+
L.push(`- **Archetype:** ${profile.label} — \`${profile.function}\` × \`${profile.altitude}\``);
|
|
60
|
+
L.push(`- **Operating posture:** ${profile.posture || "—"}`);
|
|
61
|
+
if (principal.fullName) L.push(`- **Principal:** ${principal.fullName}${principal.title ? `, ${principal.title}` : ""}`);
|
|
62
|
+
if (id.company) L.push(`- **Company:** ${id.company}`);
|
|
63
|
+
L.push("");
|
|
64
|
+
|
|
65
|
+
// --- Purpose & Mandate ---
|
|
66
|
+
L.push("## 1. Purpose & Mandate");
|
|
67
|
+
L.push("");
|
|
68
|
+
L.push(`As ${id.title || profile.label}${id.company ? ` at ${id.company}` : ""}, this agent owns **${profile.domain || profile.label}**. ${profile.posture || ""}`.trim());
|
|
69
|
+
L.push("");
|
|
70
|
+
L.push("_Mandate (enrich at init): the specific charge the principal is giving this agent, and the outcomes it is accountable for._");
|
|
71
|
+
L.push("");
|
|
72
|
+
|
|
73
|
+
// --- Control Towers ---
|
|
74
|
+
L.push("## 2. Control Towers");
|
|
75
|
+
L.push("");
|
|
76
|
+
L.push("The standing areas of responsibility this agent monitors and manages.");
|
|
77
|
+
L.push("");
|
|
78
|
+
L.push("| # | Tower | Focus |");
|
|
79
|
+
L.push("| - | ----- | ----- |");
|
|
80
|
+
(profile.towers || []).forEach((t, i) => {
|
|
81
|
+
L.push(`| ${i + 1} | ${mdEscape(t.name)} | ${mdEscape(t.focus || "")} |`);
|
|
82
|
+
});
|
|
83
|
+
L.push("");
|
|
84
|
+
|
|
85
|
+
// --- Decision Rights ---
|
|
86
|
+
L.push("## 3. Decision Rights");
|
|
87
|
+
L.push("");
|
|
88
|
+
L.push(`Authority by scope, calibrated to the **${profile.altitude}** altitude. "Owns" = decides and acts; "Recommends" = proposes, principal decides; "Escalates" = must route up; "Informed" = kept in the loop.`);
|
|
89
|
+
L.push("");
|
|
90
|
+
L.push("| Scope | Authority |");
|
|
91
|
+
L.push("| ----- | --------- |");
|
|
92
|
+
(profile.decisionRights || []).forEach((d) => {
|
|
93
|
+
L.push(`| ${mdEscape(d.scope)} | ${AUTHORITY_LABEL[d.authority] || d.authority} |`);
|
|
94
|
+
});
|
|
95
|
+
L.push("");
|
|
96
|
+
|
|
97
|
+
// --- Autonomy Model ---
|
|
98
|
+
const a = profile.autonomy || {};
|
|
99
|
+
L.push("## 4. Autonomy Model");
|
|
100
|
+
L.push("");
|
|
101
|
+
L.push(`Default posture: **${a.default || "draft"}**.`);
|
|
102
|
+
L.push("");
|
|
103
|
+
L.push("**Green — act autonomously**");
|
|
104
|
+
L.push(bulletList(a.green));
|
|
105
|
+
L.push("");
|
|
106
|
+
L.push("**Amber — approval preferred**");
|
|
107
|
+
L.push(bulletList(a.amber));
|
|
108
|
+
L.push("");
|
|
109
|
+
L.push("**Red — explicit approval required**");
|
|
110
|
+
L.push(bulletList(a.red));
|
|
111
|
+
L.push("");
|
|
112
|
+
|
|
113
|
+
// --- Escalation ---
|
|
114
|
+
const esc = profile.escalation || {};
|
|
115
|
+
L.push("## 5. Escalation Protocol");
|
|
116
|
+
L.push("");
|
|
117
|
+
L.push(`Escalates to: ${(esc.to || []).join(", ") || "the principal"}.`);
|
|
118
|
+
L.push("");
|
|
119
|
+
L.push("Escalation triggers:");
|
|
120
|
+
L.push(bulletList(esc.triggers, "Any binding, irreversible, or beyond-mandate decision"));
|
|
121
|
+
L.push("");
|
|
122
|
+
|
|
123
|
+
// --- KPI Tree ---
|
|
124
|
+
L.push("## 6. KPI Tree");
|
|
125
|
+
L.push("");
|
|
126
|
+
L.push(`KPI altitude: **${profile.kpiAltitude || "operational"}**. Targets are set at init against company baselines.`);
|
|
127
|
+
L.push("");
|
|
128
|
+
(profile.kpiCategories || []).forEach((k) => {
|
|
129
|
+
L.push(`### ${k.name}`);
|
|
130
|
+
L.push(bulletList((k.examples || []).map((e) => `${e} — _target: TBD at init_`)));
|
|
131
|
+
L.push("");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// --- Stakeholder Map ---
|
|
135
|
+
L.push("## 7. Stakeholder Map");
|
|
136
|
+
L.push("");
|
|
137
|
+
L.push("| Stakeholder | Typical relationship | Named contact (enrich at init) |");
|
|
138
|
+
L.push("| ----------- | -------------------- | ------------------------------ |");
|
|
139
|
+
(profile.stakeholderSlots || []).forEach((s) => {
|
|
140
|
+
L.push(`| ${mdEscape(s.role)} | ${mdEscape(s.typicalRelationship || "")} | _TBD_ |`);
|
|
141
|
+
});
|
|
142
|
+
L.push("");
|
|
143
|
+
|
|
144
|
+
// --- Operating Principles ---
|
|
145
|
+
L.push("## 8. Operating Principles");
|
|
146
|
+
L.push("");
|
|
147
|
+
L.push(bulletList(profile.operatingPrinciples));
|
|
148
|
+
L.push("");
|
|
149
|
+
|
|
150
|
+
// --- First 90 Days ---
|
|
151
|
+
L.push("## 9. First 90 Days");
|
|
152
|
+
L.push("");
|
|
153
|
+
L.push("_Enriched at init from the seeded backlog (see `state/backlog/wbs.yaml`)._");
|
|
154
|
+
L.push("");
|
|
155
|
+
L.push("### First 30 days — establish");
|
|
156
|
+
L.push("- Stand up the operating cadence and instrument the control towers.");
|
|
157
|
+
L.push("");
|
|
158
|
+
L.push("### Days 31–60 — execute");
|
|
159
|
+
L.push("- Drive the priority streams to their first milestones.");
|
|
160
|
+
L.push("");
|
|
161
|
+
L.push("### Days 61–90 — compound");
|
|
162
|
+
L.push("- Systematise what works; report outcomes against the KPI tree.");
|
|
163
|
+
L.push("");
|
|
164
|
+
|
|
165
|
+
L.push("---");
|
|
166
|
+
L.push("");
|
|
167
|
+
L.push(`_Tone & communication follow \`policies/communication-style.md\`. This charter is generated from the archetype library; regenerate with \`maestro\` after an archetype or identity change._`);
|
|
168
|
+
L.push("");
|
|
169
|
+
|
|
170
|
+
return L.join("\n");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Title-case a section `kind` token (e.g. "decision_rights" → "Decision Rights"). */
|
|
174
|
+
function titleizeKind(kind) {
|
|
175
|
+
return String(kind || "")
|
|
176
|
+
.replace(/[-_]+/g, " ")
|
|
177
|
+
.replace(/\s+/g, " ")
|
|
178
|
+
.trim()
|
|
179
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Render the operating charter Markdown FROM a Cohort-authored CharterDTO — the
|
|
184
|
+
* org source of truth. When a member's charter exists in Cohort
|
|
185
|
+
* (config/agent.json `charter` = { approvedByLine, gatesText, sections }), this
|
|
186
|
+
* is what `scripts/setup/generate-charter.mjs` writes, IN PLACE OF the archetype
|
|
187
|
+
* skeleton (buildCharterSkeleton). The agent thus operates on the charter its org
|
|
188
|
+
* authored, not a locally-synthesised one.
|
|
189
|
+
*
|
|
190
|
+
* Sections are rendered in `order`. Each section's heading is its `title` (or a
|
|
191
|
+
* title-cased `kind`), followed by its verbatim `body`. The approvedBy line and
|
|
192
|
+
* gates text are appended as their own blocks. Pure (no I/O).
|
|
193
|
+
*
|
|
194
|
+
* @param {object} charter - { approvedByLine?, gatesText?, sections:[{kind,order,title,body}] }
|
|
195
|
+
* @param {object} identity - config/agent.json (fullName, title, company, principal, …)
|
|
196
|
+
* @param {object} [opts] @param {string} [opts.date] ISO date string for the header
|
|
197
|
+
* @returns {string} charter Markdown
|
|
198
|
+
*/
|
|
199
|
+
export function renderCohortCharter(charter, identity, opts = {}) {
|
|
200
|
+
if (!charter || typeof charter !== "object") throw new Error("renderCohortCharter: requires a charter");
|
|
201
|
+
const id = identity || {};
|
|
202
|
+
const principal = id.principal || {};
|
|
203
|
+
const date = opts.date || "";
|
|
204
|
+
const L = [];
|
|
205
|
+
|
|
206
|
+
L.push(`# Operating Charter — ${id.fullName || "Unconfigured Agent"}`);
|
|
207
|
+
L.push("");
|
|
208
|
+
L.push(`> Authored in Cohort (org source of truth) and rendered by Maestro.${date ? ` ${date}.` : ""}`);
|
|
209
|
+
L.push("");
|
|
210
|
+
if (id.title) L.push(`- **Role:** ${id.title}`);
|
|
211
|
+
if (principal.fullName) L.push(`- **Principal:** ${principal.fullName}${principal.title ? `, ${principal.title}` : ""}`);
|
|
212
|
+
if (id.company) L.push(`- **Company:** ${id.company}`);
|
|
213
|
+
if (charter.approvedByLine) L.push(`- **Approved by:** ${charter.approvedByLine}`);
|
|
214
|
+
L.push("");
|
|
215
|
+
|
|
216
|
+
const sections = (Array.isArray(charter.sections) ? charter.sections.slice() : [])
|
|
217
|
+
.filter((s) => s && typeof s === "object")
|
|
218
|
+
.sort((a, b) => (Number(a.order) || 0) - (Number(b.order) || 0));
|
|
219
|
+
for (const s of sections) {
|
|
220
|
+
const heading = (s.title && String(s.title).trim()) || titleizeKind(s.kind) || "Section";
|
|
221
|
+
L.push(`## ${heading}`);
|
|
222
|
+
L.push("");
|
|
223
|
+
const body = s.body == null ? "" : String(s.body).trim();
|
|
224
|
+
L.push(body || "_(no content)_");
|
|
225
|
+
L.push("");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (charter.gatesText && String(charter.gatesText).trim()) {
|
|
229
|
+
L.push("## Gates");
|
|
230
|
+
L.push("");
|
|
231
|
+
L.push(String(charter.gatesText).trim());
|
|
232
|
+
L.push("");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
L.push("---");
|
|
236
|
+
L.push("");
|
|
237
|
+
L.push(`_Source of truth: the member's charter in Cohort. Regenerate with \`maestro\` after the org updates it._`);
|
|
238
|
+
L.push("");
|
|
239
|
+
|
|
240
|
+
return L.join("\n");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* True if a charter body still contains unfilled placeholders that should be
|
|
245
|
+
* enriched before it is considered init-complete (used by the doctor gate).
|
|
246
|
+
*
|
|
247
|
+
* @param {string} body @returns {string[]} the placeholder markers found
|
|
248
|
+
*/
|
|
249
|
+
export function findCharterPlaceholders(body) {
|
|
250
|
+
if (typeof body !== "string") return [];
|
|
251
|
+
const markers = [];
|
|
252
|
+
if (/\{\{[^}]+\}\}/.test(body)) markers.push("unresolved {{token}}");
|
|
253
|
+
if (/\bTBD\b/.test(body)) markers.push("TBD");
|
|
254
|
+
if (/_\(to be defined during init enrichment\)_/.test(body)) markers.push("undefined autonomy/escalation band");
|
|
255
|
+
return [...new Set(markers)];
|
|
256
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* charter.test.mjs — tests for the operating-charter skeleton builder.
|
|
3
|
+
* Run: `node --test lib/charter.test.mjs`
|
|
4
|
+
* @module charter.test
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
import { test } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { resolveArchetype } from "./archetype.mjs";
|
|
12
|
+
import { buildCharterSkeleton, findCharterPlaceholders, renderCohortCharter } from "./charter.mjs";
|
|
13
|
+
|
|
14
|
+
const IDENTITY = {
|
|
15
|
+
firstName: "Jacob", fullName: "Jacob Chen", title: "VP, Engineering", company: "Acme",
|
|
16
|
+
principal: { fullName: "Dana Reyes", title: "CEO" },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
test("buildCharterSkeleton renders a complete charter from the profile", async () => {
|
|
20
|
+
const profile = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
21
|
+
const md = buildCharterSkeleton(profile, IDENTITY, { date: "2026-06-01" });
|
|
22
|
+
|
|
23
|
+
assert.match(md, /# Operating Charter — Jacob Chen/);
|
|
24
|
+
assert.match(md, /\*\*Role:\*\* VP, Engineering/);
|
|
25
|
+
assert.match(md, /Dana Reyes, CEO/);
|
|
26
|
+
// towers from the function overlay
|
|
27
|
+
assert.match(md, /Platform & Architecture/);
|
|
28
|
+
// decision-rights matrix with altitude authorities
|
|
29
|
+
assert.match(md, /## 3\. Decision Rights/);
|
|
30
|
+
assert.match(md, /Escalates/);
|
|
31
|
+
// autonomy bands
|
|
32
|
+
assert.match(md, /Green — act autonomously/);
|
|
33
|
+
assert.match(md, /Red — explicit approval required/);
|
|
34
|
+
// KPI tree + stakeholders + principles + 90-day
|
|
35
|
+
assert.match(md, /## 6\. KPI Tree/);
|
|
36
|
+
assert.match(md, /## 7\. Stakeholder Map/);
|
|
37
|
+
assert.match(md, /Technical rigour over speed/); // a function principle
|
|
38
|
+
assert.match(md, /## 9\. First 90 Days/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("a C-suite charter differs from a senior-manager charter (altitude shows through)", async () => {
|
|
42
|
+
const cSuite = buildCharterSkeleton(await resolveArchetype({ function: "technical-leader", altitude: "c-suite" }), IDENTITY);
|
|
43
|
+
const srMgr = buildCharterSkeleton(await resolveArchetype({ function: "technical-leader", altitude: "senior-manager" }), IDENTITY);
|
|
44
|
+
assert.notEqual(cSuite, srMgr); // same function, different altitude → different charter
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("findCharterPlaceholders flags un-enriched markers (KPI targets are TBD pre-init)", async () => {
|
|
48
|
+
const profile = await resolveArchetype({ function: "commercial-leader", altitude: "svp" });
|
|
49
|
+
const md = buildCharterSkeleton(profile, IDENTITY);
|
|
50
|
+
assert.ok(findCharterPlaceholders(md).includes("TBD"));
|
|
51
|
+
// a fully-enriched body has no markers
|
|
52
|
+
assert.deepEqual(findCharterPlaceholders("# Charter\nAll concrete, no placeholders."), []);
|
|
53
|
+
assert.ok(findCharterPlaceholders("see {{agent.firstName}}").includes("unresolved {{token}}"));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("buildCharterSkeleton requires both arguments", () => {
|
|
57
|
+
assert.throws(() => buildCharterSkeleton(null, IDENTITY), /requires/);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// --- charter source-of-truth: render FROM a Cohort-authored CharterDTO -------
|
|
61
|
+
|
|
62
|
+
const ORG_CHARTER = {
|
|
63
|
+
approvedByLine: "Approved by Dana Reyes, CEO — 2026-06-01",
|
|
64
|
+
gatesText: "Irreversible spend > $25k escalates to the principal.",
|
|
65
|
+
sections: [
|
|
66
|
+
{ kind: "decision_rights", order: 2, title: null, body: "Owns the deploy cadence; recommends headcount." },
|
|
67
|
+
{ kind: "purpose", order: 1, title: "Purpose & Mandate", body: "Own platform reliability and developer velocity." },
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
test("renderCohortCharter renders sections in order with the authored bodies", () => {
|
|
72
|
+
const md = renderCohortCharter(ORG_CHARTER, IDENTITY, { date: "2026-06-01" });
|
|
73
|
+
assert.match(md, /# Operating Charter — Jacob Chen/);
|
|
74
|
+
assert.match(md, /Authored in Cohort \(org source of truth\)/);
|
|
75
|
+
assert.match(md, /Approved by Dana Reyes, CEO — 2026-06-01/);
|
|
76
|
+
// section 1 (order 1) renders before section 2 (order 2)
|
|
77
|
+
const iPurpose = md.indexOf("Purpose & Mandate");
|
|
78
|
+
const iRights = md.indexOf("Decision Rights"); // title null → title-cased kind
|
|
79
|
+
assert.ok(iPurpose > 0 && iRights > iPurpose, "sections render in `order`");
|
|
80
|
+
assert.match(md, /Own platform reliability and developer velocity\./);
|
|
81
|
+
assert.match(md, /Owns the deploy cadence; recommends headcount\./);
|
|
82
|
+
// gates block
|
|
83
|
+
assert.match(md, /## Gates/);
|
|
84
|
+
assert.match(md, /Irreversible spend > \$25k escalates/);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("renderCohortCharter requires a charter object", () => {
|
|
88
|
+
assert.throws(() => renderCohortCharter(null, IDENTITY), /requires/);
|
|
89
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — Claude CLI binary resolver.
|
|
3
|
+
*
|
|
4
|
+
* Shared utility for every code path that spawns `claude --print`.
|
|
5
|
+
* launchd's bare environment does NOT include ~/.local/bin or homebrew
|
|
6
|
+
* paths, so a plain `spawn('claude', …)` from a daemon-spawned process
|
|
7
|
+
* fails with ENOENT — even though the operator's interactive shell finds
|
|
8
|
+
* it without trouble.
|
|
9
|
+
*
|
|
10
|
+
* Caller sites:
|
|
11
|
+
* - scripts/daemon/dispatcher.mjs (reactive: replies to Slack/Gmail/etc)
|
|
12
|
+
* - scripts/daemon/responder.mjs (quick replies + holding messages)
|
|
13
|
+
* - scripts/daemon/cadence-consumer.mjs (scheduled cadence ticks)
|
|
14
|
+
* - scripts/poller/intra-session-check.mjs
|
|
15
|
+
* - any other module that spawns claude from a non-interactive context
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, statSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
|
|
22
|
+
let _resolved = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Is `p` a regular file that is safe to execute as this agent? An attacker who
|
|
26
|
+
* can rewrite the resolved binary — or point $CLAUDE_BIN at one they control —
|
|
27
|
+
* gets code execution as the agent on the next spawn, which on an unattended
|
|
28
|
+
* 24/7 Mac mini is a realistic local-persistence vector. We therefore require
|
|
29
|
+
* the file to exist, be a regular file, not be group/world-writable, and be
|
|
30
|
+
* owned by this user or root. (M2)
|
|
31
|
+
*
|
|
32
|
+
* @param {string} p
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
export function isSafeExecutable(p) {
|
|
36
|
+
try {
|
|
37
|
+
const st = statSync(p);
|
|
38
|
+
if (!st.isFile()) return false;
|
|
39
|
+
// Reject group- or world-writable binaries (someone other than the owner
|
|
40
|
+
// could swap the executable out from under us).
|
|
41
|
+
if (st.mode & 0o022) return false;
|
|
42
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
43
|
+
if (uid !== null && st.uid !== uid && st.uid !== 0) return false;
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return the absolute path to the Claude CLI. Searches, in order:
|
|
52
|
+
* 1. $CLAUDE_BIN env var (if set + executable on disk)
|
|
53
|
+
* 2. ~/.local/bin/claude (default Claude Code install location)
|
|
54
|
+
* 3. /opt/homebrew/bin/claude (Apple Silicon homebrew)
|
|
55
|
+
* 4. /usr/local/bin/claude (Intel homebrew)
|
|
56
|
+
* 5. /usr/bin/claude
|
|
57
|
+
*
|
|
58
|
+
* Falls back to bare "claude" so the spawn's own error stays informative
|
|
59
|
+
* if nothing on the search path exists.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveClaudeBin() {
|
|
62
|
+
if (_resolved) return _resolved;
|
|
63
|
+
const envOverride = process.env.CLAUDE_BIN;
|
|
64
|
+
// The env override is the highest-trust candidate, so it gets the full
|
|
65
|
+
// safety check (M2). If it is set but unsafe/missing, we log and fall
|
|
66
|
+
// through to the known-good fixed locations rather than trusting it.
|
|
67
|
+
if (envOverride) {
|
|
68
|
+
if (isSafeExecutable(envOverride)) { _resolved = envOverride; return envOverride; }
|
|
69
|
+
console.error(
|
|
70
|
+
`[claude-bin] Ignoring CLAUDE_BIN=${envOverride}: not a safe, owner-only, ` +
|
|
71
|
+
`non-writable executable. Falling back to default search paths.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const candidates = [
|
|
75
|
+
join(homedir(), ".local/bin/claude"),
|
|
76
|
+
"/opt/homebrew/bin/claude",
|
|
77
|
+
"/usr/local/bin/claude",
|
|
78
|
+
"/usr/bin/claude",
|
|
79
|
+
];
|
|
80
|
+
for (const c of candidates) {
|
|
81
|
+
if (existsSync(c)) { _resolved = c; return c; }
|
|
82
|
+
}
|
|
83
|
+
// Last resort: bare name (lets the OS PATH + spawn error stay informative).
|
|
84
|
+
// Do NOT memoize this — if the CLI gets installed later, a long-lived daemon
|
|
85
|
+
// should pick it up on the next call rather than caching the fallback. (L28)
|
|
86
|
+
return "claude";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Default args every daemon-spawned `claude --print` should receive on
|
|
91
|
+
* top of the prompt + model selection. Currently:
|
|
92
|
+
*
|
|
93
|
+
* --strict-mcp-config
|
|
94
|
+
* Skip every globally-installed Claude Code MCP plugin (Serena,
|
|
95
|
+
* Playwright, etc). Daemon spawns don't need IDE-grade code
|
|
96
|
+
* intelligence; they need to write a Slack reply / draft an email
|
|
97
|
+
* / classify an item. Loading MCPs wastes 3-5s per spawn AND opens
|
|
98
|
+
* a browser dashboard tab per session (Serena does this).
|
|
99
|
+
*
|
|
100
|
+
* --bare (opt-in via DAEMON_BARE_MODE=1)
|
|
101
|
+
* Skip hooks, LSP, plugin sync, auto-memory, keychain reads. Even
|
|
102
|
+
* more minimal. Use when you specifically want a stateless spawn —
|
|
103
|
+
* but it disables Keychain OAuth, so MAESTRO_PREFER_SUBSCRIPTION_AUTH
|
|
104
|
+
* callers should NOT set this.
|
|
105
|
+
*
|
|
106
|
+
* Operators who want full MCP support on daemon spawns can set
|
|
107
|
+
* DAEMON_LOAD_MCPS=1 in .env; the spawn sites will skip these flags.
|
|
108
|
+
*/
|
|
109
|
+
export function daemonClaudeArgs() {
|
|
110
|
+
if (process.env.DAEMON_LOAD_MCPS === "1") return [];
|
|
111
|
+
const args = ["--strict-mcp-config"];
|
|
112
|
+
if (process.env.DAEMON_BARE_MODE === "1") args.unshift("--bare");
|
|
113
|
+
return args;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Build a PATH suitable for child processes spawned from a daemon.
|
|
118
|
+
* launchd strips PATH down to /usr/bin:/bin; this returns a string that
|
|
119
|
+
* preserves the caller's PATH if set, then appends the locations claude
|
|
120
|
+
* subsessions might need to find node, jq, gh, etc.
|
|
121
|
+
*/
|
|
122
|
+
export function augmentedPath() {
|
|
123
|
+
return [
|
|
124
|
+
process.env.PATH || "",
|
|
125
|
+
`${homedir()}/.local/bin`,
|
|
126
|
+
"/opt/homebrew/bin",
|
|
127
|
+
"/opt/homebrew/sbin",
|
|
128
|
+
"/usr/local/bin",
|
|
129
|
+
"/usr/bin",
|
|
130
|
+
"/bin",
|
|
131
|
+
"/usr/sbin",
|
|
132
|
+
"/sbin",
|
|
133
|
+
].filter(Boolean).join(":");
|
|
134
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, writeFileSync, chmodSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { isSafeExecutable, augmentedPath, daemonClaudeArgs } from "./claude-bin.mjs";
|
|
7
|
+
|
|
8
|
+
function tmp() {
|
|
9
|
+
return mkdtempSync(join(tmpdir(), "claude-bin-test-"));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test("isSafeExecutable accepts an owner-only, non-writable regular file", () => {
|
|
13
|
+
const dir = tmp();
|
|
14
|
+
try {
|
|
15
|
+
const p = join(dir, "claude");
|
|
16
|
+
writeFileSync(p, "#!/bin/sh\n");
|
|
17
|
+
chmodSync(p, 0o755);
|
|
18
|
+
assert.equal(isSafeExecutable(p), true);
|
|
19
|
+
} finally {
|
|
20
|
+
rmSync(dir, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("isSafeExecutable rejects a world-writable file (swap vector)", () => {
|
|
25
|
+
const dir = tmp();
|
|
26
|
+
try {
|
|
27
|
+
const p = join(dir, "claude");
|
|
28
|
+
writeFileSync(p, "#!/bin/sh\n");
|
|
29
|
+
chmodSync(p, 0o757); // world-writable
|
|
30
|
+
assert.equal(isSafeExecutable(p), false);
|
|
31
|
+
} finally {
|
|
32
|
+
rmSync(dir, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("isSafeExecutable rejects a group-writable file", () => {
|
|
37
|
+
const dir = tmp();
|
|
38
|
+
try {
|
|
39
|
+
const p = join(dir, "claude");
|
|
40
|
+
writeFileSync(p, "#!/bin/sh\n");
|
|
41
|
+
chmodSync(p, 0o775); // group-writable
|
|
42
|
+
assert.equal(isSafeExecutable(p), false);
|
|
43
|
+
} finally {
|
|
44
|
+
rmSync(dir, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("isSafeExecutable rejects a nonexistent path without throwing", () => {
|
|
49
|
+
assert.equal(isSafeExecutable("/nope/does/not/exist/claude"), false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("isSafeExecutable rejects a directory", () => {
|
|
53
|
+
const dir = tmp();
|
|
54
|
+
try {
|
|
55
|
+
assert.equal(isSafeExecutable(dir), false);
|
|
56
|
+
} finally {
|
|
57
|
+
rmSync(dir, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("augmentedPath includes common bin locations and preserves existing PATH", () => {
|
|
62
|
+
const p = augmentedPath();
|
|
63
|
+
assert.ok(p.includes("/usr/bin"));
|
|
64
|
+
assert.ok(p.includes("/opt/homebrew/bin"));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("daemonClaudeArgs returns strict-mcp-config by default", () => {
|
|
68
|
+
const prev = process.env.DAEMON_LOAD_MCPS;
|
|
69
|
+
delete process.env.DAEMON_LOAD_MCPS;
|
|
70
|
+
try {
|
|
71
|
+
assert.deepEqual(daemonClaudeArgs(), ["--strict-mcp-config"]);
|
|
72
|
+
} finally {
|
|
73
|
+
if (prev !== undefined) process.env.DAEMON_LOAD_MCPS = prev;
|
|
74
|
+
}
|
|
75
|
+
});
|