@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,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro — Shared Agent Root Resolver
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for "where is this agent's root directory?" so that
|
|
5
|
+
* every module that needs to locate `state/` (singleton locks, cadence bus,
|
|
6
|
+
* etc.) agrees on the same path.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order (first match wins), absolutized:
|
|
9
|
+
* 1. AGENT_ROOT — explicit override (preferred)
|
|
10
|
+
* 2. AGENT_DIR — legacy/alternate name used elsewhere in the codebase
|
|
11
|
+
* 3. process.cwd() — fallback when neither env var is set
|
|
12
|
+
*
|
|
13
|
+
* Resolved per call (not memoised at import) so that callers and tests that
|
|
14
|
+
* mutate the environment before invoking a lock operation see the change.
|
|
15
|
+
* This deliberately mirrors the resolution used by the cadence bus
|
|
16
|
+
* (`resolveAgentRoot` in cadence-bus.mjs); both should stay in sync, and the
|
|
17
|
+
* cadence bus should eventually import from here to remove the duplicate.
|
|
18
|
+
*
|
|
19
|
+
* @module agent-root
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { resolve } from "node:path";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the agent root directory.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} [agentRoot] Optional explicit override (highest precedence).
|
|
28
|
+
* @returns {string} Absolute path to the agent root.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveAgentRoot(agentRoot) {
|
|
31
|
+
return resolve(
|
|
32
|
+
agentRoot ||
|
|
33
|
+
process.env.AGENT_ROOT ||
|
|
34
|
+
process.env.AGENT_DIR ||
|
|
35
|
+
process.cwd()
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archetype.mjs — the function × altitude archetype resolver.
|
|
3
|
+
*
|
|
4
|
+
* Maestro's operating model is data, not prose. A chosen archetype is a pair
|
|
5
|
+
* `{ function, altitude }`. This module composes a fully-resolved operating
|
|
6
|
+
* profile by deep-merging three versioned YAML overlays:
|
|
7
|
+
*
|
|
8
|
+
* base.yaml ⊕ functions/<function>.yaml ⊕ altitudes/<altitude>.yaml
|
|
9
|
+
*
|
|
10
|
+
* Function overlays contribute WHAT the agent owns (domain, towers, KPI
|
|
11
|
+
* categories, stakeholders, backlog streams, tool/skill/template packs, cadence
|
|
12
|
+
* family). Altitude overlays OVERRIDE how the agent operates (autonomy bands,
|
|
13
|
+
* decision-rights authority, escalation, KPI altitude, backlog granularity,
|
|
14
|
+
* delegation breadth, comms formality). The resolved profile is what every
|
|
15
|
+
* downstream generator reads — charter, backlog, cadences, capabilities, comms.
|
|
16
|
+
*
|
|
17
|
+
* Merge semantics (deterministic, see {@link deepMerge}):
|
|
18
|
+
* - plain objects → deep-merge key by key
|
|
19
|
+
* - arrays of {id,...} → upsert by `id` (later layer wins; new ids appended)
|
|
20
|
+
* - arrays of scalars → order-preserving union (deduped)
|
|
21
|
+
* - scalars / mismatch → last layer wins (altitude beats function beats base)
|
|
22
|
+
*
|
|
23
|
+
* Validation is dependency-free ({@link validateProfile}); no ajv. The JSON
|
|
24
|
+
* Schema in archetypes/archetype.schema.json documents the same contract for
|
|
25
|
+
* editors/tooling.
|
|
26
|
+
*
|
|
27
|
+
* @module archetype
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
"use strict";
|
|
31
|
+
|
|
32
|
+
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
33
|
+
import path from "node:path";
|
|
34
|
+
import { fileURLToPath } from "node:url";
|
|
35
|
+
|
|
36
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
|
|
38
|
+
/** Absolute path to the framework's archetype library (ships in the package). */
|
|
39
|
+
export const ARCHETYPES_DIR = path.resolve(__dirname, "..", "archetypes");
|
|
40
|
+
|
|
41
|
+
/** Allowed values for the autonomy default posture. @type {string[]} */
|
|
42
|
+
export const AUTONOMY_DEFAULTS = ["observe", "draft", "execute", "escalate-first"];
|
|
43
|
+
/** Allowed decision-right authority levels. @type {string[]} */
|
|
44
|
+
export const AUTHORITIES = ["owns", "recommends", "escalates", "informed"];
|
|
45
|
+
/** Allowed backlog granularities. @type {string[]} */
|
|
46
|
+
export const GRANULARITIES = ["streams", "epics", "tasks"];
|
|
47
|
+
/** Altitude assumed when migrating a legacy single-axis `archetype` value. */
|
|
48
|
+
export const LEGACY_ALTITUDE_DEFAULT = "c-suite";
|
|
49
|
+
|
|
50
|
+
let _yaml = null;
|
|
51
|
+
async function getYaml() {
|
|
52
|
+
if (_yaml) return _yaml;
|
|
53
|
+
const mod = await import("js-yaml");
|
|
54
|
+
_yaml = mod && mod.default ? mod.default : mod;
|
|
55
|
+
return _yaml;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** @param {*} v @returns {boolean} true for a non-array, non-null object */
|
|
59
|
+
function isPlainObject(v) {
|
|
60
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Merge two arrays per the documented rules: if every element of the combined
|
|
65
|
+
* set is an object carrying an `id`, upsert by id (later wins, deep-merging
|
|
66
|
+
* matching ids); otherwise produce an order-preserving, deduped union.
|
|
67
|
+
*
|
|
68
|
+
* @param {any[]} a @param {any[]} b @returns {any[]}
|
|
69
|
+
*/
|
|
70
|
+
function mergeArrays(a, b) {
|
|
71
|
+
const all = [...a, ...b];
|
|
72
|
+
const allKeyedObjects = all.length > 0 && all.every((e) => isPlainObject(e) && "id" in e);
|
|
73
|
+
if (allKeyedObjects) {
|
|
74
|
+
const byId = new Map();
|
|
75
|
+
for (const e of a) byId.set(e.id, e);
|
|
76
|
+
for (const e of b) {
|
|
77
|
+
byId.set(e.id, byId.has(e.id) && isPlainObject(byId.get(e.id)) ? deepMerge(byId.get(e.id), e) : e);
|
|
78
|
+
}
|
|
79
|
+
return [...byId.values()];
|
|
80
|
+
}
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const e of all) {
|
|
84
|
+
const key = isPlainObject(e) ? JSON.stringify(e) : String(e);
|
|
85
|
+
if (seen.has(key)) continue;
|
|
86
|
+
seen.add(key);
|
|
87
|
+
out.push(e);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Deep-merge `b` onto `a` per the module's documented semantics. Pure.
|
|
94
|
+
*
|
|
95
|
+
* @param {*} a base value @param {*} b overriding value @returns {*}
|
|
96
|
+
*/
|
|
97
|
+
export function deepMerge(a, b) {
|
|
98
|
+
if (b === undefined) return a;
|
|
99
|
+
if (Array.isArray(a) && Array.isArray(b)) return mergeArrays(a, b);
|
|
100
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
101
|
+
const out = { ...a };
|
|
102
|
+
for (const k of Object.keys(b)) out[k] = k in out ? deepMerge(out[k], b[k]) : b[k];
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
return b;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** List available function ids (archetypes/functions/*.yaml). @returns {string[]} */
|
|
109
|
+
export function listFunctions() {
|
|
110
|
+
const dir = path.join(ARCHETYPES_DIR, "functions");
|
|
111
|
+
if (!existsSync(dir)) return [];
|
|
112
|
+
return readdirSync(dir).filter((f) => f.endsWith(".yaml")).map((f) => f.slice(0, -5)).sort();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** List available altitude ids (archetypes/altitudes/*.yaml). @returns {string[]} */
|
|
116
|
+
export function listAltitudes() {
|
|
117
|
+
const dir = path.join(ARCHETYPES_DIR, "altitudes");
|
|
118
|
+
if (!existsSync(dir)) return [];
|
|
119
|
+
return readdirSync(dir).filter((f) => f.endsWith(".yaml")).map((f) => f.slice(0, -5)).sort();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function loadYamlFile(file, yaml) {
|
|
123
|
+
if (!existsSync(file)) return {};
|
|
124
|
+
return yaml.load(readFileSync(file, "utf8")) || {};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Resolve a `{ function, altitude }` pair into a validated operating profile.
|
|
129
|
+
*
|
|
130
|
+
* @param {{ function: string, altitude: string }} sel
|
|
131
|
+
* @returns {Promise<object>} the resolved, validated profile
|
|
132
|
+
* @throws if the function/altitude is unknown or the resolved profile is invalid
|
|
133
|
+
*/
|
|
134
|
+
export async function resolveArchetype(sel = {}) {
|
|
135
|
+
const fn = sel.function;
|
|
136
|
+
const altitude = sel.altitude;
|
|
137
|
+
if (!fn || !altitude) {
|
|
138
|
+
throw new Error("resolveArchetype: requires { function, altitude }");
|
|
139
|
+
}
|
|
140
|
+
const funcs = listFunctions();
|
|
141
|
+
const alts = listAltitudes();
|
|
142
|
+
if (!funcs.includes(fn)) {
|
|
143
|
+
throw new Error(`resolveArchetype: unknown function "${fn}" (known: ${funcs.join(", ") || "none"})`);
|
|
144
|
+
}
|
|
145
|
+
if (!alts.includes(altitude)) {
|
|
146
|
+
throw new Error(`resolveArchetype: unknown altitude "${altitude}" (known: ${alts.join(", ") || "none"})`);
|
|
147
|
+
}
|
|
148
|
+
const yaml = await getYaml();
|
|
149
|
+
const base = loadYamlFile(path.join(ARCHETYPES_DIR, "base.yaml"), yaml);
|
|
150
|
+
const fObj = loadYamlFile(path.join(ARCHETYPES_DIR, "functions", `${fn}.yaml`), yaml);
|
|
151
|
+
const aObj = loadYamlFile(path.join(ARCHETYPES_DIR, "altitudes", `${altitude}.yaml`), yaml);
|
|
152
|
+
|
|
153
|
+
const profile = deepMerge(deepMerge(base, fObj), aObj);
|
|
154
|
+
profile.function = fn;
|
|
155
|
+
profile.altitude = altitude;
|
|
156
|
+
if (!profile.label) {
|
|
157
|
+
const at = profile.altitudeTitle || altitude;
|
|
158
|
+
const ft = profile.functionTitle || fn;
|
|
159
|
+
profile.label = `${at}, ${ft}`;
|
|
160
|
+
}
|
|
161
|
+
validateProfile(profile);
|
|
162
|
+
return profile;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Validate a resolved profile against the contract. Fails closed (throws) so an
|
|
167
|
+
* incomplete overlay can never silently produce a half-formed operating model.
|
|
168
|
+
*
|
|
169
|
+
* @param {object} p @returns {true} @throws Error with all violations listed
|
|
170
|
+
*/
|
|
171
|
+
export function validateProfile(p) {
|
|
172
|
+
const errs = [];
|
|
173
|
+
if (!isPlainObject(p)) throw new Error("validateProfile: profile must be an object");
|
|
174
|
+
if (p.schemaVersion !== 1) errs.push("schemaVersion must be 1");
|
|
175
|
+
for (const k of ["function", "altitude", "label"]) {
|
|
176
|
+
if (typeof p[k] !== "string" || !p[k]) errs.push(`${k} must be a non-empty string`);
|
|
177
|
+
}
|
|
178
|
+
if (!Array.isArray(p.towers) || p.towers.length === 0) errs.push("towers must be a non-empty array");
|
|
179
|
+
else for (const t of p.towers) if (!isPlainObject(t) || !t.id || !t.name) errs.push("each tower needs id + name");
|
|
180
|
+
|
|
181
|
+
if (!isPlainObject(p.autonomy)) errs.push("autonomy must be an object");
|
|
182
|
+
else {
|
|
183
|
+
if (!AUTONOMY_DEFAULTS.includes(p.autonomy.default)) errs.push(`autonomy.default must be one of: ${AUTONOMY_DEFAULTS.join(", ")}`);
|
|
184
|
+
for (const band of ["green", "amber", "red"]) {
|
|
185
|
+
if (!Array.isArray(p.autonomy[band])) errs.push(`autonomy.${band} must be an array`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!Array.isArray(p.decisionRights) || p.decisionRights.length === 0) errs.push("decisionRights must be a non-empty array");
|
|
190
|
+
else for (const d of p.decisionRights) {
|
|
191
|
+
if (!isPlainObject(d) || !d.id || !d.scope) errs.push("each decisionRight needs id + scope");
|
|
192
|
+
else if (!AUTHORITIES.includes(d.authority)) errs.push(`decisionRight "${d.id}": authority must be one of: ${AUTHORITIES.join(", ")}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!Array.isArray(p.kpiCategories) || p.kpiCategories.length === 0) errs.push("kpiCategories must be a non-empty array");
|
|
196
|
+
if (!Array.isArray(p.backlogStreams) || p.backlogStreams.length === 0) errs.push("backlogStreams must be a non-empty array");
|
|
197
|
+
if (!GRANULARITIES.includes(p.backlogGranularity)) errs.push(`backlogGranularity must be one of: ${GRANULARITIES.join(", ")}`);
|
|
198
|
+
if (!isPlainObject(p.escalation) || !Array.isArray(p.escalation.to)) errs.push("escalation.to must be an array");
|
|
199
|
+
if (!Array.isArray(p.voiceModes) || p.voiceModes.length === 0) errs.push("voiceModes must be a non-empty array");
|
|
200
|
+
|
|
201
|
+
if (errs.length) {
|
|
202
|
+
throw new Error(`Invalid archetype profile (${p.function || "?"}@${p.altitude || "?"}):\n - ${errs.join("\n - ")}`);
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Legacy single-axis `archetype` value for back-compat: the six legacy
|
|
209
|
+
* archetypes ARE the six functions, so the function id is the legacy value.
|
|
210
|
+
*
|
|
211
|
+
* @param {{ function?: string, archetype?: string }} cfg @returns {string|null}
|
|
212
|
+
*/
|
|
213
|
+
export function deriveLegacyArchetype(cfg) {
|
|
214
|
+
if (!cfg) return null;
|
|
215
|
+
return cfg.function || cfg.archetype || null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Migrate a legacy single-axis `archetype` value to a { function, altitude }
|
|
220
|
+
* pair. Legacy values map to the same function at the default (c-suite) altitude.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} oldArchetype @returns {{ function: string, altitude: string } | null}
|
|
223
|
+
*/
|
|
224
|
+
export function migrateLegacyArchetype(oldArchetype) {
|
|
225
|
+
if (listFunctions().includes(oldArchetype)) {
|
|
226
|
+
return { function: oldArchetype, altitude: LEGACY_ALTITUDE_DEFAULT };
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Every resolvable cell as `{ function, altitude }`. @returns {Array<{function:string,altitude:string}>} */
|
|
232
|
+
export function listCells() {
|
|
233
|
+
const cells = [];
|
|
234
|
+
for (const fn of listFunctions()) for (const altitude of listAltitudes()) cells.push({ function: fn, altitude });
|
|
235
|
+
return cells;
|
|
236
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archetype.test.mjs — tests for the function × altitude archetype resolver.
|
|
3
|
+
*
|
|
4
|
+
* Covers the pure merge engine (deterministic, no I/O) and the live resolution
|
|
5
|
+
* of the reference cell (technical-leader × vp). A full-matrix "every cell
|
|
6
|
+
* resolves & validates" test guards the whole library once all overlays exist.
|
|
7
|
+
*
|
|
8
|
+
* Run: `node --test lib/archetype.test.mjs`
|
|
9
|
+
* @module archetype.test
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
import { test } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
deepMerge,
|
|
19
|
+
resolveArchetype,
|
|
20
|
+
validateProfile,
|
|
21
|
+
listFunctions,
|
|
22
|
+
listAltitudes,
|
|
23
|
+
listCells,
|
|
24
|
+
deriveLegacyArchetype,
|
|
25
|
+
migrateLegacyArchetype,
|
|
26
|
+
LEGACY_ALTITUDE_DEFAULT,
|
|
27
|
+
} from "./archetype.mjs";
|
|
28
|
+
|
|
29
|
+
// --- deepMerge: the deterministic merge engine ---
|
|
30
|
+
|
|
31
|
+
test("deepMerge: scalars — last layer wins", () => {
|
|
32
|
+
assert.equal(deepMerge("draft", "execute"), "execute");
|
|
33
|
+
assert.equal(deepMerge(1, 2), 2);
|
|
34
|
+
assert.equal(deepMerge("x", undefined), "x"); // undefined never overrides
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("deepMerge: plain objects merge key by key", () => {
|
|
38
|
+
assert.deepEqual(
|
|
39
|
+
deepMerge({ a: 1, n: { x: 1, y: 1 } }, { b: 2, n: { y: 2, z: 3 } }),
|
|
40
|
+
{ a: 1, b: 2, n: { x: 1, y: 2, z: 3 } }
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("deepMerge: arrays of {id} upsert by id (later wins, new ids appended)", () => {
|
|
45
|
+
const a = [{ id: "k", authority: "recommends" }, { id: "m", authority: "owns" }];
|
|
46
|
+
const b = [{ id: "k", authority: "escalates" }, { id: "n", authority: "owns" }];
|
|
47
|
+
const merged = deepMerge(a, b);
|
|
48
|
+
assert.deepEqual(merged.map((e) => e.id), ["k", "m", "n"]);
|
|
49
|
+
assert.equal(merged.find((e) => e.id === "k").authority, "escalates"); // b wins
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("deepMerge: scalar arrays union (order-preserving, deduped)", () => {
|
|
53
|
+
assert.deepEqual(deepMerge(["a", "b"], ["b", "c"]), ["a", "b", "c"]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// --- library is discoverable ---
|
|
57
|
+
|
|
58
|
+
test("listFunctions/listAltitudes include the reference overlays", () => {
|
|
59
|
+
assert.ok(listFunctions().includes("technical-leader"));
|
|
60
|
+
assert.ok(listAltitudes().includes("vp"));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// --- live resolution of the reference cell ---
|
|
64
|
+
|
|
65
|
+
test("resolveArchetype(technical-leader × vp) composes a valid profile", async () => {
|
|
66
|
+
const p = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
67
|
+
assert.equal(p.schemaVersion, 1);
|
|
68
|
+
assert.equal(p.function, "technical-leader");
|
|
69
|
+
assert.equal(p.altitude, "vp");
|
|
70
|
+
assert.equal(p.label, "VP, Engineering");
|
|
71
|
+
// function-provided WHAT:
|
|
72
|
+
assert.ok(p.towers.length >= 5);
|
|
73
|
+
assert.ok(p.backlogStreams.some((s) => s.id === "roadmap-delivery"));
|
|
74
|
+
assert.ok(p.kpiCategories.some((k) => k.id === "reliability"));
|
|
75
|
+
// altitude-provided HOW (overrides base):
|
|
76
|
+
assert.equal(p.autonomy.default, "execute"); // base 'draft' → altitude 'execute'
|
|
77
|
+
assert.equal(p.backlogGranularity, "epics");
|
|
78
|
+
assert.equal(p.kpiAltitude, "operational");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("decision rights merge: function defaults + altitude overrides coexist", async () => {
|
|
82
|
+
const p = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
83
|
+
const byId = Object.fromEntries(p.decisionRights.map((d) => [d.id, d.authority]));
|
|
84
|
+
assert.equal(byId.architecture, "owns"); // from function
|
|
85
|
+
assert.equal(byId["external-commitments"], "escalates"); // from altitude
|
|
86
|
+
assert.equal(byId.strategy, "recommends"); // altitude-only scope
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("autonomy red band unions base (universal reds) with altitude reds", async () => {
|
|
90
|
+
const p = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
91
|
+
assert.ok(p.autonomy.red.some((r) => /Binding legal or financial/i.test(r))); // base
|
|
92
|
+
assert.ok(p.autonomy.red.some((r) => /strategy|restructure|headcount/i.test(r))); // altitude
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("operatingPrinciples union base + function + altitude", async () => {
|
|
96
|
+
const p = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
97
|
+
assert.ok(p.operatingPrinciples.includes("Audit everything")); // base
|
|
98
|
+
assert.ok(p.operatingPrinciples.includes("Technical rigour over speed")); // function
|
|
99
|
+
assert.ok(p.operatingPrinciples.some((x) => /escalate the tradeoff/i.test(x))); // altitude
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// --- failure modes ---
|
|
103
|
+
|
|
104
|
+
test("resolveArchetype throws on unknown function or altitude", async () => {
|
|
105
|
+
await assert.rejects(() => resolveArchetype({ function: "nope", altitude: "vp" }), /unknown function/);
|
|
106
|
+
await assert.rejects(() => resolveArchetype({ function: "technical-leader", altitude: "nope" }), /unknown altitude/);
|
|
107
|
+
await assert.rejects(() => resolveArchetype({}), /requires \{ function, altitude \}/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("validateProfile rejects a malformed profile", () => {
|
|
111
|
+
assert.throws(() => validateProfile({ schemaVersion: 1, function: "x", altitude: "y", label: "z" }), /towers must be/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// --- legacy back-compat ---
|
|
115
|
+
|
|
116
|
+
test("legacy archetype derivation + migration", () => {
|
|
117
|
+
assert.equal(deriveLegacyArchetype({ function: "technical-leader" }), "technical-leader");
|
|
118
|
+
assert.deepEqual(migrateLegacyArchetype("technical-leader"), { function: "technical-leader", altitude: LEGACY_ALTITUDE_DEFAULT });
|
|
119
|
+
assert.equal(migrateLegacyArchetype("not-a-function"), null);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// --- full-matrix guard: every authored cell resolves & validates ---
|
|
123
|
+
|
|
124
|
+
test("every (function × altitude) cell resolves and validates", async () => {
|
|
125
|
+
const cells = listCells();
|
|
126
|
+
assert.ok(cells.length >= 1);
|
|
127
|
+
for (const cell of cells) {
|
|
128
|
+
const p = await resolveArchetype(cell); // resolveArchetype validates internally
|
|
129
|
+
assert.equal(p.function, cell.function);
|
|
130
|
+
assert.equal(p.altitude, cell.altitude);
|
|
131
|
+
}
|
|
132
|
+
});
|
package/lib/autonomy.mjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autonomy.mjs — derive the agent's action-classification policy from its
|
|
3
|
+
* resolved archetype (function × altitude).
|
|
4
|
+
*
|
|
5
|
+
* Autonomy/decision-rights must be a function of the archetype, and must be
|
|
6
|
+
* ENFORCED, not decorative. This builds an action-classification policy that
|
|
7
|
+
* carries BOTH shapes:
|
|
8
|
+
*
|
|
9
|
+
* - `action_levels` (human / charter view): observe / draft / execute_autonomous
|
|
10
|
+
* (= the profile's green band) / approval_preferred (= amber) / escalate
|
|
11
|
+
* (= red), plus decision_rights and the escalation protocol — straight from
|
|
12
|
+
* the resolved profile, so a founder's bands differ from a senior-manager's.
|
|
13
|
+
*
|
|
14
|
+
* - `actions` (the per-action map lib/voice/tool-bridge.mjs actually reads:
|
|
15
|
+
* `{ actionName: { risk, verbal_confirm, approval } }`). Built from a risk
|
|
16
|
+
* baseline, then shifted by the archetype's delegation breadth — broad
|
|
17
|
+
* (founder/C-suite) downshifts routine sends toward autonomous; narrow
|
|
18
|
+
* (senior-manager) upshifts them toward approval. This is what makes the
|
|
19
|
+
* voice/comms gate live instead of defaulting everything to "low".
|
|
20
|
+
*
|
|
21
|
+
* Pure (no I/O). ESM. The generator (scripts/setup/generate-autonomy.mjs) writes
|
|
22
|
+
* the result to the agent's policies/action-classification.yaml.
|
|
23
|
+
*
|
|
24
|
+
* @module autonomy
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
"use strict";
|
|
28
|
+
|
|
29
|
+
/** Baseline per-action risk for the actions the comms/voice gate sees. */
|
|
30
|
+
export const ACTION_RISK_BASELINE = {
|
|
31
|
+
send_email: "high",
|
|
32
|
+
send_slack_message: "medium",
|
|
33
|
+
send_whatsapp_message: "medium",
|
|
34
|
+
send_sms: "medium",
|
|
35
|
+
place_voice_call: "high",
|
|
36
|
+
send_voice_message: "high",
|
|
37
|
+
create_calendar_event: "low",
|
|
38
|
+
update_calendar_event: "low",
|
|
39
|
+
search_email: "low",
|
|
40
|
+
search_calendar: "low",
|
|
41
|
+
rag_search: "low",
|
|
42
|
+
generate_report: "low",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const APPROVAL_FOR = { high: "explicit", medium: "preferred", low: "autonomous" };
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Build the archetype-derived action-classification policy.
|
|
49
|
+
* @param {object} profile resolved archetype profile (lib/archetype.mjs)
|
|
50
|
+
* @param {object} [identity] config/agent.json (optional, for labelling)
|
|
51
|
+
* @returns {object} the action-classification structure (action_levels + actions + decision_rights)
|
|
52
|
+
*/
|
|
53
|
+
export function buildActionClassification(profile, identity = {}) {
|
|
54
|
+
if (!profile) throw new Error("buildActionClassification: requires a resolved profile");
|
|
55
|
+
const a = profile.autonomy || {};
|
|
56
|
+
const breadth = profile.delegationBreadth || "moderate";
|
|
57
|
+
const downshift = breadth === "broad"; // more autonomous
|
|
58
|
+
const upshift = breadth === "narrow" || breadth === "none"; // more approval-gated
|
|
59
|
+
|
|
60
|
+
const actions = {};
|
|
61
|
+
for (const [name, base] of Object.entries(ACTION_RISK_BASELINE)) {
|
|
62
|
+
let risk = base;
|
|
63
|
+
if (downshift && risk === "medium") risk = "low";
|
|
64
|
+
if (upshift && risk === "medium") risk = "high";
|
|
65
|
+
if (upshift && risk === "low" && /^(send_|place_)/.test(name)) risk = "medium";
|
|
66
|
+
actions[name] = {
|
|
67
|
+
risk,
|
|
68
|
+
verbal_confirm: risk === "high",
|
|
69
|
+
approval: APPROVAL_FOR[risk] || "autonomous",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const esc = profile.escalation || {};
|
|
74
|
+
|
|
75
|
+
// Org source-of-truth override (guarded + minimal): when enrollment persisted
|
|
76
|
+
// the member's authoritative operating fields (config/agent.json `orgProfile`
|
|
77
|
+
// from the Cohort MemberProfileDTO), PREFER the org's `autonomyLevel` over the
|
|
78
|
+
// archetype default and the org's `escalationPath` over the archetype escalation
|
|
79
|
+
// targets. When the fields are absent the classification is byte-identical to
|
|
80
|
+
// before — existing behaviour is untouched.
|
|
81
|
+
const org = (identity && identity.orgProfile) || {};
|
|
82
|
+
const orgAutonomy = typeof org.autonomyLevel === "string" && org.autonomyLevel.trim() ? org.autonomyLevel.trim() : "";
|
|
83
|
+
const orgEscalation = typeof org.escalationPath === "string" && org.escalationPath.trim() ? org.escalationPath.trim() : "";
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
_doc:
|
|
87
|
+
"Archetype-derived action classification (function × altitude). action_levels is the human/charter view (green/amber/red); `actions` is the per-action map the voice/comms gate enforces. Generated by scripts/setup/generate-autonomy.mjs — regenerate after an archetype change; do not hand-edit. autonomy_default and escalate_to honour the org's authoritative autonomyLevel/escalationPath when present.",
|
|
88
|
+
archetype: `${profile.function}@${profile.altitude}`,
|
|
89
|
+
autonomy_default: orgAutonomy || a.default || "draft",
|
|
90
|
+
...(orgAutonomy ? { org_autonomy_level: orgAutonomy } : {}),
|
|
91
|
+
delegation_breadth: breadth,
|
|
92
|
+
action_levels: {
|
|
93
|
+
observe: { risk: "low", approval: "none", description: "Read-only: viewing, scanning, searching, monitoring." },
|
|
94
|
+
draft: { risk: "low", approval: "none", description: "Preparing content without sending or committing." },
|
|
95
|
+
execute_autonomous: { risk: "low-to-medium", approval: "autonomous", examples: a.green || [] },
|
|
96
|
+
approval_preferred: { risk: "medium", approval: "principal_preferred", examples: a.amber || [] },
|
|
97
|
+
escalate: {
|
|
98
|
+
risk: "high-to-critical",
|
|
99
|
+
approval: "explicit_required",
|
|
100
|
+
examples: a.red || [],
|
|
101
|
+
escalate_to: orgEscalation ? [orgEscalation] : (esc.to || ["principal"]),
|
|
102
|
+
triggers: esc.triggers || [],
|
|
103
|
+
...(orgEscalation ? { escalation_path: orgEscalation } : {}),
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
decision_rights: (profile.decisionRights || []).map((d) => ({ id: d.id, scope: d.scope, authority: d.authority })),
|
|
107
|
+
actions,
|
|
108
|
+
emergency: {
|
|
109
|
+
kill_switch: "scripts/emergency-stop.sh",
|
|
110
|
+
pause_all_sends: true,
|
|
111
|
+
resume_requires: "scripts/resume-operations.sh",
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autonomy.test.mjs — tests for the archetype-derived action-classification.
|
|
3
|
+
* Run: `node --test lib/autonomy.test.mjs`
|
|
4
|
+
* @module autonomy.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 { buildActionClassification } from "./autonomy.mjs";
|
|
13
|
+
|
|
14
|
+
test("action_levels bands come straight from the resolved profile", async () => {
|
|
15
|
+
const profile = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
16
|
+
const p = buildActionClassification(profile);
|
|
17
|
+
assert.deepEqual(p.action_levels.execute_autonomous.examples, profile.autonomy.green);
|
|
18
|
+
assert.deepEqual(p.action_levels.approval_preferred.examples, profile.autonomy.amber);
|
|
19
|
+
assert.deepEqual(p.action_levels.escalate.examples, profile.autonomy.red);
|
|
20
|
+
assert.ok(p.decision_rights.length >= 1 && p.decision_rights[0].authority);
|
|
21
|
+
assert.deepEqual(p.action_levels.escalate.escalate_to, profile.escalation.to);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("per-action map is in the shape the voice gate reads (risk + verbal_confirm + approval)", async () => {
|
|
25
|
+
const p = buildActionClassification(await resolveArchetype({ function: "commercial-leader", altitude: "vp" }));
|
|
26
|
+
assert.ok(p.actions.send_email && typeof p.actions.send_email.risk === "string");
|
|
27
|
+
assert.equal(typeof p.actions.send_email.verbal_confirm, "boolean");
|
|
28
|
+
assert.ok(["explicit", "preferred", "autonomous"].includes(p.actions.send_email.approval));
|
|
29
|
+
// high-risk actions demand a verbal confirm
|
|
30
|
+
for (const a of Object.values(p.actions)) if (a.risk === "high") assert.equal(a.verbal_confirm, true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("breadth tunes the gate: founder (broad) is more autonomous than senior-manager (narrow)", async () => {
|
|
34
|
+
const founder = buildActionClassification(await resolveArchetype({ function: "technical-leader", altitude: "founder" }));
|
|
35
|
+
const srMgr = buildActionClassification(await resolveArchetype({ function: "technical-leader", altitude: "senior-manager" }));
|
|
36
|
+
// a routine send: broad → low/autonomous; narrow → high/gated
|
|
37
|
+
assert.equal(founder.actions.send_slack_message.risk, "low");
|
|
38
|
+
assert.equal(srMgr.actions.send_slack_message.risk, "high");
|
|
39
|
+
assert.equal(srMgr.actions.send_email.risk, "high");
|
|
40
|
+
assert.notEqual(founder.delegation_breadth, srMgr.delegation_breadth);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("buildActionClassification requires a profile", () => {
|
|
44
|
+
assert.throws(() => buildActionClassification(null), /requires/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("org autonomyLevel/escalationPath (identity.orgProfile) override the archetype default", async () => {
|
|
48
|
+
const profile = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
49
|
+
const identity = { orgProfile: { autonomyLevel: "supervised", escalationPath: "CTO then CEO" } };
|
|
50
|
+
const p = buildActionClassification(profile, identity);
|
|
51
|
+
assert.equal(p.autonomy_default, "supervised", "org autonomyLevel wins over archetype default");
|
|
52
|
+
assert.equal(p.org_autonomy_level, "supervised");
|
|
53
|
+
assert.deepEqual(p.action_levels.escalate.escalate_to, ["CTO then CEO"], "org escalationPath wins");
|
|
54
|
+
assert.equal(p.action_levels.escalate.escalation_path, "CTO then CEO");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("absent org fields → classification is unchanged (existing behaviour preserved)", async () => {
|
|
58
|
+
const profile = await resolveArchetype({ function: "technical-leader", altitude: "vp" });
|
|
59
|
+
const without = buildActionClassification(profile, {});
|
|
60
|
+
const baseline = buildActionClassification(profile); // no identity at all
|
|
61
|
+
assert.equal(without.autonomy_default, profile.autonomy.default || "draft");
|
|
62
|
+
assert.equal(without.org_autonomy_level, undefined);
|
|
63
|
+
assert.deepEqual(without.action_levels.escalate.escalate_to, profile.escalation.to);
|
|
64
|
+
assert.equal(without.action_levels.escalate.escalation_path, undefined);
|
|
65
|
+
assert.deepEqual(without, baseline);
|
|
66
|
+
});
|