@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,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sections/enrich.mjs — LLM enrichment of company-specific prose slots. (order 70)
|
|
3
|
+
*
|
|
4
|
+
* Iterates the enumerated enrichable units that still need filling and calls
|
|
5
|
+
* lib/setup/enrich.enrichField (one `claude --print` per unit). Honors
|
|
6
|
+
* `--no-enrich` (skips entirely, leaving deterministic skeletons) and is
|
|
7
|
+
* fail-open: any unit that fails is marked `pending` and the section continues.
|
|
8
|
+
*
|
|
9
|
+
* The spawn is injectable via `ctx.enrichSpawn` so the wizard's integration
|
|
10
|
+
* tests stay hermetic.
|
|
11
|
+
*
|
|
12
|
+
* @module lib/setup/sections/enrich
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
19
|
+
import { pathToFileURL } from "node:url";
|
|
20
|
+
import { enrichField, ENRICHABLE } from "../enrich.mjs";
|
|
21
|
+
import { readAgentJson } from "../sot.mjs";
|
|
22
|
+
import { loadProgress } from "../state.mjs";
|
|
23
|
+
import * as io from "../io.mjs";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Substitute enriched company-slot values into the deterministic charter body,
|
|
27
|
+
* clearing the placeholder markers (mandate stub, KPI "TBD" targets, stakeholder
|
|
28
|
+
* "_TBD_") so the completeness gate's findCharterPlaceholders sees none.
|
|
29
|
+
* @param {string} agentRoot
|
|
30
|
+
* @param {{mandate?:string, kpiTarget?:string, stakeholderContact?:string}} value
|
|
31
|
+
*/
|
|
32
|
+
function applyCharterEnrichment(agentRoot, value) {
|
|
33
|
+
const p = join(agentRoot, "config", "operating-charter.md");
|
|
34
|
+
if (!existsSync(p)) return;
|
|
35
|
+
let body = readFileSync(p, "utf-8");
|
|
36
|
+
const mandate = (value.mandate || "").trim();
|
|
37
|
+
const kpi = (value.kpiTarget || "set against the company baseline this quarter").trim();
|
|
38
|
+
const contact = (value.stakeholderContact || "named at onboarding").trim();
|
|
39
|
+
|
|
40
|
+
if (mandate) {
|
|
41
|
+
body = body.replace(
|
|
42
|
+
/_Mandate \(enrich at init\):[^_]*_/,
|
|
43
|
+
mandate.replace(/\$/g, "$$$$")
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
// KPI targets: "_target: TBD at init_" → concrete phrasing.
|
|
47
|
+
body = body.replace(/_target: TBD at init_/g, `target: ${kpi}`);
|
|
48
|
+
// Stakeholder named contacts: "| _TBD_ |" → concrete phrasing.
|
|
49
|
+
body = body.replace(/_TBD_/g, contact);
|
|
50
|
+
// Any remaining bare "TBD" (defensive) — replace with the KPI phrasing.
|
|
51
|
+
body = body.replace(/\bTBD\b/g, kpi);
|
|
52
|
+
|
|
53
|
+
const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
|
|
54
|
+
writeFileSync(tmp, body, "utf-8");
|
|
55
|
+
renameSync(tmp, p);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function emptyArr(v) { return !Array.isArray(v) || v.length === 0; }
|
|
59
|
+
function emptyStr(v) { return v === undefined || v === null || String(v).trim() === ""; }
|
|
60
|
+
|
|
61
|
+
function companyDescriptionEmpty(agentRoot) {
|
|
62
|
+
const p = join(agentRoot, "config", "company.json");
|
|
63
|
+
try { const c = existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : {}; return emptyStr(c.description); }
|
|
64
|
+
catch { return true; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function charterHasPlaceholders(agentRoot, maestroRoot) {
|
|
68
|
+
const p = join(agentRoot, "config", "operating-charter.md");
|
|
69
|
+
if (!existsSync(p)) return false;
|
|
70
|
+
try {
|
|
71
|
+
const { findCharterPlaceholders } = await import(pathToFileURL(join(maestroRoot, "lib", "charter.mjs")).href);
|
|
72
|
+
return findCharterPlaceholders(readFileSync(p, "utf-8")).length > 0;
|
|
73
|
+
} catch { return false; }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Which enrichable units still need work, given current on-disk state. */
|
|
77
|
+
export async function pendingUnits(agentRoot, maestroRoot) {
|
|
78
|
+
const id = readAgentJson(agentRoot) || {};
|
|
79
|
+
const units = [];
|
|
80
|
+
if (emptyStr(id.persona)) units.push("agent.persona");
|
|
81
|
+
if (emptyArr(id.responsibilities)) units.push("agent.responsibilities");
|
|
82
|
+
if (emptyArr(id.operatingPrinciples)) units.push("agent.operatingPrinciples");
|
|
83
|
+
if (companyDescriptionEmpty(agentRoot)) units.push("company.description");
|
|
84
|
+
if (await charterHasPlaceholders(agentRoot, maestroRoot)) units.push("charter.companySlots");
|
|
85
|
+
return units.filter((u) => u in ENRICHABLE);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default {
|
|
89
|
+
id: "enrich",
|
|
90
|
+
label: "LLM enrichment (company-specific prose)",
|
|
91
|
+
order: 70,
|
|
92
|
+
|
|
93
|
+
async detect(ctx) {
|
|
94
|
+
if (ctx.flags && ctx.flags.noEnrich) return { status: "complete", summary: "skipped (--no-enrich)" };
|
|
95
|
+
const pending = await pendingUnits(ctx.agentRoot, ctx.maestroRoot);
|
|
96
|
+
if (pending.length === 0) return { status: "complete", summary: "all prose slots filled" };
|
|
97
|
+
return { status: "partial", missing: pending };
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
async prompt() { /* non-interactive */ },
|
|
101
|
+
|
|
102
|
+
async apply(ctx) {
|
|
103
|
+
if (ctx.flags && ctx.flags.noEnrich) {
|
|
104
|
+
io.note("Enrichment skipped (--no-enrich) — deterministic skeletons left in place.", { dim: true });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const units = await pendingUnits(ctx.agentRoot, ctx.maestroRoot);
|
|
108
|
+
if (units.length === 0) return;
|
|
109
|
+
const spawn = ctx.enrichSpawn; // undefined → real claude spawn inside enrichField
|
|
110
|
+
let enriched = 0, pending = 0;
|
|
111
|
+
for (const unit of units) {
|
|
112
|
+
const opts = spawn ? { spawn } : {};
|
|
113
|
+
const res = await enrichField(ctx.agentRoot, unit, opts);
|
|
114
|
+
if (res.status === "enriched") {
|
|
115
|
+
enriched++;
|
|
116
|
+
// charter company-slots: substitute the enriched values into the
|
|
117
|
+
// deterministic charter body (clears the TBD / mandate placeholders).
|
|
118
|
+
if (unit === "charter.companySlots" && res.value) {
|
|
119
|
+
try { applyCharterEnrichment(ctx.agentRoot, res.value); } catch { /* fail-open */ }
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
pending++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (ctx.flags && ctx.flags.verbose) io.note(`enrichment: ${enriched} filled, ${pending} left as skeleton`, { dim: true });
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
async verify(ctx) {
|
|
129
|
+
// Enrichment is best-effort — a pending unit is NOT a hard failure (the
|
|
130
|
+
// deterministic skeleton is still a real artifact). Report as informational.
|
|
131
|
+
const ledger = loadProgress(ctx.agentRoot).enrich || {};
|
|
132
|
+
const pendingCount = Object.values(ledger).filter((e) => e && e.status === "pending").length;
|
|
133
|
+
return {
|
|
134
|
+
ok: true,
|
|
135
|
+
checks: [{ name: `enrichment (${pendingCount} unit(s) left as skeleton)`, ok: true, remedy: pendingCount ? "re-run `maestro setup --only enrich` to retry" : undefined }],
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sections/identity.mjs — collect the agent's identity, write the SoT, ensure
|
|
3
|
+
* the agent.ts wrapper, and generate config/agent.env. (order 10)
|
|
4
|
+
*
|
|
5
|
+
* SetupSection contract (design §3.3):
|
|
6
|
+
* { id, label, order, detect, prompt, apply, verify }
|
|
7
|
+
*
|
|
8
|
+
* @module lib/setup/sections/identity
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
16
|
+
import * as io from "../io.mjs";
|
|
17
|
+
import { readAgentJson, mergeAgentJson, ensureSotWrapper } from "../sot.mjs";
|
|
18
|
+
import { isPlaceholder } from "../completeness.mjs";
|
|
19
|
+
import { runGenerator } from "../run-generator.mjs";
|
|
20
|
+
import { cohortPullEnv, pullAndPlan } from "../enroll-from-cohort.mjs";
|
|
21
|
+
import { fetchSelfProfile } from "../../org/client.mjs";
|
|
22
|
+
|
|
23
|
+
const FUNCTIONS = [
|
|
24
|
+
{ value: "executive-operator", label: "Executive operator (chief of staff / COO-style)" },
|
|
25
|
+
{ value: "technical-leader", label: "Technical leader (eng / platform / CTO-style)" },
|
|
26
|
+
{ value: "commercial-leader", label: "Commercial leader (sales / GTM / revenue)" },
|
|
27
|
+
{ value: "people-leader", label: "People leader (HR / talent / org)" },
|
|
28
|
+
{ value: "finance-leader", label: "Finance leader (finance / corp dev / capital)" },
|
|
29
|
+
{ value: "compliance-leader", label: "Compliance leader (legal / risk / regulatory)" },
|
|
30
|
+
];
|
|
31
|
+
const ALTITUDES = [
|
|
32
|
+
{ value: "c-suite", label: "C-suite" },
|
|
33
|
+
{ value: "svp", label: "SVP" },
|
|
34
|
+
{ value: "vp", label: "VP" },
|
|
35
|
+
{ value: "senior-manager", label: "Senior manager" },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* When the Cohort pull env (COHORT_API_KEY + COHORT_ORG_ID, optional
|
|
40
|
+
* COHORT_AGENT_ID) is present, PULL this agent's record and merge it into
|
|
41
|
+
* config/agent.json BEFORE we prompt — so the prompts default to (and thus
|
|
42
|
+
* CONFIRM) the org-authoritative values rather than starting from scratch. A
|
|
43
|
+
* locally-customised value is never clobbered (mergeProfilePatch drops it unless
|
|
44
|
+
* --force). Fail-open + non-blocking: a missing var / unreachable server is a
|
|
45
|
+
* no-op and the manual prompts stand. Returns the pull plan (or null).
|
|
46
|
+
* @param {string} agentRoot
|
|
47
|
+
* @returns {Promise<object|null>}
|
|
48
|
+
*/
|
|
49
|
+
async function maybePullProfile(agentRoot) {
|
|
50
|
+
if (!cohortPullEnv().enabled) return null;
|
|
51
|
+
try {
|
|
52
|
+
const plan = await pullAndPlan({ agentRoot, fetchSelfProfileImpl: fetchSelfProfile });
|
|
53
|
+
if (plan && plan.pulled && plan.patch && Object.keys(plan.patch).length) {
|
|
54
|
+
mergeAgentJson(agentRoot, plan.patch);
|
|
55
|
+
}
|
|
56
|
+
return plan;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function archetypeResolves(agentRoot, maestroRoot) {
|
|
63
|
+
try {
|
|
64
|
+
const arch = await import(pathToFileURL(join(maestroRoot, "lib", "archetype.mjs")).href);
|
|
65
|
+
const id = readAgentJson(agentRoot) || {};
|
|
66
|
+
const sel = id.function && id.altitude
|
|
67
|
+
? { function: id.function, altitude: id.altitude }
|
|
68
|
+
: arch.migrateLegacyArchetype(id.archetype);
|
|
69
|
+
if (!sel) return { ok: false, label: null };
|
|
70
|
+
const p = await arch.resolveArchetype(sel);
|
|
71
|
+
return { ok: true, label: p.label };
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return { ok: false, label: null, error: e && e.message ? e.message : String(e) };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default {
|
|
78
|
+
id: "identity",
|
|
79
|
+
label: "Agent identity",
|
|
80
|
+
order: 10,
|
|
81
|
+
|
|
82
|
+
async detect(ctx) {
|
|
83
|
+
const id = readAgentJson(ctx.agentRoot);
|
|
84
|
+
if (!id) return { status: "unconfigured", missing: ["config/agent.json"] };
|
|
85
|
+
const fields = ["firstName", "lastName", "title", "email"];
|
|
86
|
+
const missing = fields.filter((f) => isPlaceholder(id[f]));
|
|
87
|
+
const arch = await archetypeResolves(ctx.agentRoot, ctx.maestroRoot);
|
|
88
|
+
if (!arch.ok) missing.push("archetype");
|
|
89
|
+
if (missing.length === 0) {
|
|
90
|
+
return { status: "complete", summary: `${id.fullName || id.firstName} — ${id.title} (${arch.label})` };
|
|
91
|
+
}
|
|
92
|
+
return { status: missing.length === fields.length + 1 ? "unconfigured" : "partial", missing };
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
async prompt(ctx) {
|
|
96
|
+
// Cohort-driven setup: pull the org record first so the prompts CONFIRM the
|
|
97
|
+
// pulled values (they become the defaults below) instead of starting blank.
|
|
98
|
+
const plan = await maybePullProfile(ctx.agentRoot);
|
|
99
|
+
if (plan && plan.pulled) {
|
|
100
|
+
const n = plan.changes ? plan.changes.length : 0;
|
|
101
|
+
io.note(`Pulled this agent's profile from Cohort (${n} field${n === 1 ? "" : "s"}). Confirm or edit below.`, { dim: true });
|
|
102
|
+
}
|
|
103
|
+
const cur = readAgentJson(ctx.agentRoot) || {};
|
|
104
|
+
const firstName = await io.text(ctx, { message: "Agent first name", key: "firstName", default: placeholderless(cur.firstName) });
|
|
105
|
+
const lastName = await io.text(ctx, { message: "Agent last name", key: "lastName", default: placeholderless(cur.lastName) });
|
|
106
|
+
const title = await io.text(ctx, { message: "Agent title / role", key: "title", default: placeholderless(cur.title) });
|
|
107
|
+
const email = await io.text(ctx, { message: "Agent work email", key: "email", default: placeholderless(cur.email) });
|
|
108
|
+
const fn = await io.select(ctx, { message: "Function (what the agent owns)", key: "function", choices: FUNCTIONS, default: cur.function || "executive-operator" });
|
|
109
|
+
const altitude = await io.select(ctx, { message: "Altitude (seniority)", key: "altitude", choices: ALTITUDES, default: cur.altitude || "c-suite" });
|
|
110
|
+
const principalName = await io.text(ctx, { message: "Principal full name (who the agent reports to)", key: "principalName", default: (cur.principal && cur.principal.fullName) || "" });
|
|
111
|
+
const principalTitle = await io.text(ctx, { message: "Principal title", key: "principalTitle", default: (cur.principal && cur.principal.title) || "" });
|
|
112
|
+
const principalEmail = await io.text(ctx, { message: "Principal email", key: "principalEmail", default: (cur.principal && cur.principal.email) || "" });
|
|
113
|
+
|
|
114
|
+
ctx.answers.identity = { firstName, lastName, title, email, function: fn, altitude, principalName, principalTitle, principalEmail };
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async apply(ctx) {
|
|
118
|
+
// Idempotent safety net: ensure the Cohort pull has landed even on an
|
|
119
|
+
// apply-only path (e.g. headless re-run where prompt() was a no-op). Pulled
|
|
120
|
+
// values are merged into config/agent.json; they never clobber local edits.
|
|
121
|
+
await maybePullProfile(ctx.agentRoot);
|
|
122
|
+
const a = (ctx.answers && ctx.answers.identity) || pullFromAnswers(ctx);
|
|
123
|
+
if (!a) {
|
|
124
|
+
// No prompt answers (headless with no --answers) but a pull may have just
|
|
125
|
+
// populated config/agent.json — still run the downstream generators so the
|
|
126
|
+
// archetype + wrapper + env reflect the pulled identity.
|
|
127
|
+
if (cohortPullEnv().enabled) {
|
|
128
|
+
runGenerator("init-archetype.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
129
|
+
ensureSotWrapper(ctx.agentRoot);
|
|
130
|
+
runGenerator("generate-agent-env.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const fullName = [a.firstName, a.lastName].filter(Boolean).join(" ").trim();
|
|
135
|
+
const [pf, ...pr] = String(a.principalName || "").trim().split(/\s+/);
|
|
136
|
+
const principalLast = pr.join(" ");
|
|
137
|
+
const patch = {
|
|
138
|
+
firstName: a.firstName, lastName: a.lastName, fullName, title: a.title, email: a.email,
|
|
139
|
+
function: a.function, altitude: a.altitude,
|
|
140
|
+
principal: {
|
|
141
|
+
firstName: pf || "", lastName: principalLast || "",
|
|
142
|
+
fullName: String(a.principalName || "").trim(),
|
|
143
|
+
title: a.principalTitle || "", email: a.principalEmail || "",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
mergeAgentJson(ctx.agentRoot, patch);
|
|
147
|
+
// Normalise archetype (function/altitude → archetype) via init-archetype.
|
|
148
|
+
runGenerator("init-archetype.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
149
|
+
// Born-unified: ensure config/agent.ts is the thin wrapper.
|
|
150
|
+
ensureSotWrapper(ctx.agentRoot);
|
|
151
|
+
// Regenerate the shell env file.
|
|
152
|
+
runGenerator("generate-agent-env.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async verify(ctx) {
|
|
156
|
+
const checks = [];
|
|
157
|
+
const id = readAgentJson(ctx.agentRoot);
|
|
158
|
+
const idOk = !!id && ["firstName", "lastName", "title", "email"].every((f) => !isPlaceholder(id[f]));
|
|
159
|
+
checks.push({ name: "identity populated", ok: idOk, remedy: idOk ? undefined : "run `maestro setup --only identity`" });
|
|
160
|
+
const arch = await archetypeResolves(ctx.agentRoot, ctx.maestroRoot);
|
|
161
|
+
checks.push({ name: "archetype resolves", ok: arch.ok, remedy: arch.ok ? undefined : "set a valid function × altitude" });
|
|
162
|
+
const tsPath = join(ctx.agentRoot, "config", "agent.ts");
|
|
163
|
+
const wrapperOk = existsSync(tsPath) && /agent\.json/.test(readFileSync(tsPath, "utf-8"));
|
|
164
|
+
checks.push({ name: "agent.ts is SoT wrapper", ok: wrapperOk, remedy: wrapperOk ? undefined : "ensureSotWrapper failed" });
|
|
165
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
function placeholderless(v) { return isPlaceholder(v) ? "" : v; }
|
|
170
|
+
|
|
171
|
+
/** Allow `--answers` files to supply flat keys (no nested identity object). */
|
|
172
|
+
function pullFromAnswers(ctx) {
|
|
173
|
+
const ans = ctx.answers || {};
|
|
174
|
+
if (ans.identity) return ans.identity;
|
|
175
|
+
const flat = ["firstName", "lastName", "title", "email", "function", "altitude", "principalName", "principalTitle", "principalEmail"];
|
|
176
|
+
if (flat.some((k) => k in ans)) {
|
|
177
|
+
const out = {};
|
|
178
|
+
for (const k of flat) out[k] = ans[k];
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* identity.test.mjs — Cohort-driven setup pull in the identity section.
|
|
3
|
+
*
|
|
4
|
+
* Focuses on the pull seam added for Cohort-driven setup: when COHORT_API_KEY +
|
|
5
|
+
* COHORT_ORG_ID are present, prompt()/apply() PULL the agent's record and merge
|
|
6
|
+
* it into config/agent.json so the prompts CONFIRM (rather than start blank); a
|
|
7
|
+
* locally-customised value is protected unless --force; absent vars leave the
|
|
8
|
+
* manual path unchanged. The real client.fetchSelfProfile is exercised via an
|
|
9
|
+
* injected global fetch (no network).
|
|
10
|
+
*
|
|
11
|
+
* Run: node --test lib/setup/sections/identity.test.mjs
|
|
12
|
+
*/
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
import { test } from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join, dirname } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
import identity from "./identity.mjs";
|
|
23
|
+
import { readAgentJson } from "../sot.mjs";
|
|
24
|
+
import * as io from "../io.mjs";
|
|
25
|
+
|
|
26
|
+
const MAESTRO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
27
|
+
|
|
28
|
+
function tmpRoot() {
|
|
29
|
+
const root = mkdtempSync(join(tmpdir(), "maestro-identity-"));
|
|
30
|
+
mkdirSync(join(root, "config"), { recursive: true });
|
|
31
|
+
return root;
|
|
32
|
+
}
|
|
33
|
+
function ctxFor(root, answers = {}) {
|
|
34
|
+
return { agentRoot: root, maestroRoot: MAESTRO_ROOT, flags: { headless: true }, answers, io };
|
|
35
|
+
}
|
|
36
|
+
function writeAgent(root, obj) {
|
|
37
|
+
writeFileSync(join(root, "config", "agent.json"), JSON.stringify(obj, null, 2) + "\n");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const PROFILE = {
|
|
41
|
+
slug: "robin", kind: "agent", displayName: "Robin Vega", firstName: "Robin", lastName: "Vega",
|
|
42
|
+
title: "VP Eng", team: "Platform", role: "engineering-lead", agentEmail: "robin@example.com",
|
|
43
|
+
supervisor: { displayName: "Layla Al-Masri", title: "CTO", email: "layla@example.com" },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function withCohortEnv(vars, fn) {
|
|
47
|
+
const keys = ["COHORT_API_KEY", "COHORT_ORG_ID", "COHORT_AGENT_ID", "COHORT_BASE"];
|
|
48
|
+
const saved = Object.fromEntries(keys.map((k) => [k, process.env[k]]));
|
|
49
|
+
for (const k of keys) delete process.env[k];
|
|
50
|
+
Object.assign(process.env, vars);
|
|
51
|
+
return Promise.resolve().then(fn).finally(() => {
|
|
52
|
+
for (const k of keys) { if (saved[k] === undefined) delete process.env[k]; else process.env[k] = saved[k]; }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function withFetch(impl, fn) {
|
|
57
|
+
const saved = globalThis.fetch;
|
|
58
|
+
globalThis.fetch = impl;
|
|
59
|
+
return Promise.resolve().then(fn).finally(() => { globalThis.fetch = saved; });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const okFetch = (sink) => async (url) => {
|
|
63
|
+
if (sink) sink.push(String(url));
|
|
64
|
+
return { ok: true, status: 200, headers: { get: () => undefined }, json: async () => PROFILE };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
test("section shape: id/order=10/hooks", () => {
|
|
68
|
+
assert.equal(identity.id, "identity");
|
|
69
|
+
assert.equal(identity.order, 10);
|
|
70
|
+
for (const h of ["detect", "prompt", "apply", "verify"]) assert.equal(typeof identity[h], "function");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("prompt: with COHORT_* env, pulls the profile into config/agent.json", async () => {
|
|
74
|
+
const root = tmpRoot();
|
|
75
|
+
writeAgent(root, { firstName: "UNCONFIGURED" });
|
|
76
|
+
const urls = [];
|
|
77
|
+
await withCohortEnv(
|
|
78
|
+
{ COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
|
|
79
|
+
() => withFetch(okFetch(urls), async () => {
|
|
80
|
+
const ctx = ctxFor(root);
|
|
81
|
+
await identity.prompt(ctx);
|
|
82
|
+
// Headless prompt resolves the answers from the (now pulled) config defaults.
|
|
83
|
+
assert.equal(ctx.answers.identity.firstName, "Robin");
|
|
84
|
+
assert.equal(ctx.answers.identity.email, "robin@example.com");
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
assert.ok(urls.some((u) => u.includes("/api/v1/org/members/robin")), "members endpoint hit");
|
|
88
|
+
const id = readAgentJson(root);
|
|
89
|
+
assert.equal(id.firstName, "Robin", "placeholder overwritten by pull");
|
|
90
|
+
assert.equal(id.function, "technical-leader");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("COHORT_AGENT_ID selects /org/members/<id>; absent selects whoami", async () => {
|
|
94
|
+
const root = tmpRoot();
|
|
95
|
+
const urls = [];
|
|
96
|
+
await withCohortEnv(
|
|
97
|
+
{ COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_BASE: "https://cohort.example" },
|
|
98
|
+
() => withFetch(okFetch(urls), async () => {
|
|
99
|
+
await identity.prompt(ctxFor(root));
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
assert.ok(urls.some((u) => u.includes("/api/v1/org/whoami?orgId=acme")), "whoami endpoint hit when no agent id (org sent as the tenant pin, not a member slug)");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("a local override is NOT clobbered by the pull (without --force)", async () => {
|
|
106
|
+
const root = tmpRoot();
|
|
107
|
+
writeAgent(root, { title: "My Custom Title", firstName: "" });
|
|
108
|
+
await withCohortEnv(
|
|
109
|
+
{ COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
|
|
110
|
+
() => withFetch(okFetch(), async () => {
|
|
111
|
+
await identity.prompt(ctxFor(root));
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
const id = readAgentJson(root);
|
|
115
|
+
assert.equal(id.title, "My Custom Title", "operator title preserved");
|
|
116
|
+
assert.equal(id.firstName, "Robin", "empty field still filled from org");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("absent COHORT_* env → no pull (manual path unchanged)", async () => {
|
|
120
|
+
const root = tmpRoot();
|
|
121
|
+
writeAgent(root, { firstName: "UNCONFIGURED" });
|
|
122
|
+
let fetched = false;
|
|
123
|
+
await withCohortEnv({}, () => withFetch(async () => { fetched = true; return { ok: true, status: 200, headers: { get: () => undefined }, json: async () => PROFILE }; }, async () => {
|
|
124
|
+
await identity.prompt(ctxFor(root));
|
|
125
|
+
}));
|
|
126
|
+
assert.equal(fetched, false, "no network call without the pull env");
|
|
127
|
+
assert.equal(readAgentJson(root).firstName, "UNCONFIGURED", "config unchanged");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("pull is fail-open: a fetch error leaves config untouched", async () => {
|
|
131
|
+
const root = tmpRoot();
|
|
132
|
+
writeAgent(root, { firstName: "UNCONFIGURED" });
|
|
133
|
+
await withCohortEnv(
|
|
134
|
+
{ COHORT_API_KEY: "k", COHORT_ORG_ID: "acme", COHORT_AGENT_ID: "robin", COHORT_BASE: "https://cohort.example" },
|
|
135
|
+
() => withFetch(async () => { throw new Error("network down"); }, async () => {
|
|
136
|
+
await identity.prompt(ctxFor(root)); // must not throw
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
assert.equal(readAgentJson(root).firstName, "UNCONFIGURED");
|
|
140
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sections/learning.mjs — the self-learning loop (WS3, order 80).
|
|
3
|
+
*
|
|
4
|
+
* Cross-workstream seam (design §3.2): registered by dropping this file into
|
|
5
|
+
* sections/ (no core edit). It turns the autonomous skill-authoring loop on,
|
|
6
|
+
* seeds its runtime state (counters + the transcript search DB), and verifies
|
|
7
|
+
* the global Stop-hook the loop piggybacks on is wired.
|
|
8
|
+
*
|
|
9
|
+
* Order 80 (after enrich/tools, before verify) per design §3.2 / §4.
|
|
10
|
+
*
|
|
11
|
+
* Every hook is non-blocking under --headless: detect/apply/verify never read
|
|
12
|
+
* the TTY; prompt honours `io` headless resolution. Idempotent.
|
|
13
|
+
*
|
|
14
|
+
* @module lib/setup/sections/learning
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
"use strict";
|
|
18
|
+
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "node:fs";
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
|
+
import * as io from "../io.mjs";
|
|
23
|
+
import { countersPath, emptyCounters } from "../../learning/counters.mjs";
|
|
24
|
+
|
|
25
|
+
const CONFIG_REL = join("config", "learning.yaml");
|
|
26
|
+
|
|
27
|
+
/** Is the loop enabled per the on-disk config? (absent file → default-enabled) */
|
|
28
|
+
function configEnabled(agentRoot) {
|
|
29
|
+
const p = join(agentRoot, CONFIG_REL);
|
|
30
|
+
if (!existsSync(p)) return null; // unconfigured
|
|
31
|
+
try {
|
|
32
|
+
const body = readFileSync(p, "utf-8");
|
|
33
|
+
// Cheap scan to avoid a yaml dep in the wizard: an explicit `enabled: false`.
|
|
34
|
+
if (/^\s*enabled\s*:\s*false\b/m.test(body)) return false;
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Is the global maestro hook-runner wired into ~/.claude/settings.json? */
|
|
42
|
+
export function hookWired() {
|
|
43
|
+
try {
|
|
44
|
+
const p = join(homedir(), ".claude", "settings.json");
|
|
45
|
+
if (!existsSync(p)) return false;
|
|
46
|
+
const body = readFileSync(p, "utf-8");
|
|
47
|
+
// The collective + learning loop share the one hook entry point.
|
|
48
|
+
return /collective\/hook-runner\.mjs/.test(body);
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Write config/learning.yaml from the scaffold (or a minimal enabled stub). */
|
|
55
|
+
function ensureConfig(ctx, enabled) {
|
|
56
|
+
const dst = join(ctx.agentRoot, CONFIG_REL);
|
|
57
|
+
if (existsSync(dst)) {
|
|
58
|
+
// Respect an explicit operator choice to disable, but don't rewrite an
|
|
59
|
+
// existing file the operator may have customised.
|
|
60
|
+
if (enabled === false) flipEnabled(dst, false);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
mkdirSync(join(ctx.agentRoot, "config"), { recursive: true });
|
|
64
|
+
const scaffold = join(ctx.maestroRoot, "scaffold", "config", "learning.yaml");
|
|
65
|
+
if (existsSync(scaffold)) {
|
|
66
|
+
try { copyFileSync(scaffold, dst); } catch { writeStub(dst, enabled); }
|
|
67
|
+
} else {
|
|
68
|
+
writeStub(dst, enabled);
|
|
69
|
+
}
|
|
70
|
+
if (enabled === false) flipEnabled(dst, false);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function writeStub(dst, enabled) {
|
|
74
|
+
writeFileSync(dst, `# config/learning.yaml — Self-Learning Loop (WS3)\nenabled: ${enabled === false ? "false" : "true"}\nminTurns: 4\nreviewModel: sonnet\n`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function flipEnabled(dst, value) {
|
|
78
|
+
try {
|
|
79
|
+
let body = readFileSync(dst, "utf-8");
|
|
80
|
+
if (/^\s*enabled\s*:/m.test(body)) body = body.replace(/^\s*enabled\s*:.*$/m, `enabled: ${value}`);
|
|
81
|
+
else body = `enabled: ${value}\n` + body;
|
|
82
|
+
writeFileSync(dst, body);
|
|
83
|
+
} catch { /* fail-open */ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Seed state/learning/counters.json if absent. */
|
|
87
|
+
function seedCounters(agentRoot) {
|
|
88
|
+
const p = countersPath(agentRoot);
|
|
89
|
+
if (existsSync(p)) return;
|
|
90
|
+
try {
|
|
91
|
+
mkdirSync(join(agentRoot, "state", "learning"), { recursive: true });
|
|
92
|
+
writeFileSync(p, JSON.stringify(emptyCounters(), null, 2) + "\n");
|
|
93
|
+
} catch { /* fail-open */ }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default {
|
|
97
|
+
id: "learning",
|
|
98
|
+
label: "Self-learning loop",
|
|
99
|
+
order: 80,
|
|
100
|
+
|
|
101
|
+
async detect(ctx) {
|
|
102
|
+
const enabled = configEnabled(ctx.agentRoot);
|
|
103
|
+
if (enabled === null) return { status: "unconfigured", missing: ["config/learning.yaml"] };
|
|
104
|
+
if (enabled === false) return { status: "complete", summary: "self-learning disabled (config)" };
|
|
105
|
+
const missing = [];
|
|
106
|
+
if (!existsSync(countersPath(ctx.agentRoot))) missing.push("state/learning/counters.json");
|
|
107
|
+
if (!hookWired()) missing.push("global hook (run `maestro global-setup`)");
|
|
108
|
+
if (missing.length === 0) return { status: "complete", summary: "self-learning enabled + wired" };
|
|
109
|
+
return { status: "partial", summary: "self-learning enabled", missing };
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
async prompt(ctx) {
|
|
113
|
+
const cur = configEnabled(ctx.agentRoot);
|
|
114
|
+
const on = await io.confirm(ctx, {
|
|
115
|
+
message: "Enable the self-learning loop (the agent autonomously authors & prunes its own skills)?",
|
|
116
|
+
key: "learningEnabled",
|
|
117
|
+
default: cur !== false,
|
|
118
|
+
});
|
|
119
|
+
ctx.answers.learningEnabled = on;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
async apply(ctx) {
|
|
123
|
+
const enabled = ctx.answers && typeof ctx.answers.learningEnabled === "boolean"
|
|
124
|
+
? ctx.answers.learningEnabled
|
|
125
|
+
: (configEnabled(ctx.agentRoot) !== false);
|
|
126
|
+
ensureConfig(ctx, enabled);
|
|
127
|
+
if (enabled === false) return;
|
|
128
|
+
seedCounters(ctx.agentRoot);
|
|
129
|
+
// Seed the transcript search DB from any existing session logs (best-effort;
|
|
130
|
+
// the Stop-hook worker + daily catch-up keep it warm thereafter).
|
|
131
|
+
try {
|
|
132
|
+
const sidx = await import("../../learning/session-index.mjs");
|
|
133
|
+
await sidx.ingestAll({ agentRoot: ctx.agentRoot });
|
|
134
|
+
} catch { /* DB is on-demand; a failed seed is non-fatal */ }
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async verify(ctx) {
|
|
138
|
+
const checks = [];
|
|
139
|
+
const enabled = configEnabled(ctx.agentRoot);
|
|
140
|
+
checks.push({ name: "config/learning.yaml present", ok: enabled !== null, remedy: enabled !== null ? undefined : "run `maestro setup --only learning`" });
|
|
141
|
+
if (enabled === false) {
|
|
142
|
+
checks.push({ name: "self-learning disabled (by config)", ok: true });
|
|
143
|
+
return { ok: true, checks };
|
|
144
|
+
}
|
|
145
|
+
const countersOk = existsSync(countersPath(ctx.agentRoot));
|
|
146
|
+
checks.push({ name: "counters seeded", ok: countersOk, remedy: countersOk ? undefined : "seed state/learning/counters.json" });
|
|
147
|
+
// The hook is a SOFT check: on a fresh repo / CI / non-agent machine it
|
|
148
|
+
// isn't wired yet, which is expected — surface a remedy but don't hard-fail.
|
|
149
|
+
const wired = hookWired();
|
|
150
|
+
checks.push({ name: "global Stop-hook wired", ok: true, remedy: wired ? undefined : "run `maestro global-setup` to enable on this machine" });
|
|
151
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
152
|
+
},
|
|
153
|
+
};
|