@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,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/model-router/spawn.mjs — the router EXECUTION layer for SESSION work.
|
|
3
|
+
*
|
|
4
|
+
* `spawnRouted(request, opts)` is what the daemon calls INSTEAD of hand-building
|
|
5
|
+
* `claude -p` spawn args. It ties the three substrate layers together:
|
|
6
|
+
*
|
|
7
|
+
* 1. resolveChain(request) → a RouteDecision (which model/harness, the
|
|
8
|
+
* session-retarget env, spawn knobs, the
|
|
9
|
+
* ordered failover chain).
|
|
10
|
+
* 2. build argv + child env → from decision.spawnArgs + decision.chosen
|
|
11
|
+
* (and each failover candidate's own
|
|
12
|
+
* envForSpawn), with §7.3 child-env
|
|
13
|
+
* scrubbing (no foreign *_API_KEY leaks).
|
|
14
|
+
* 3. failover.stepChain(...) → walk the chain, applying the 16-reason
|
|
15
|
+
* DISPOSITION: retry-same where allowed,
|
|
16
|
+
* fail over to the next candidate on a
|
|
17
|
+
* failover-class reason, open breakers per
|
|
18
|
+
* the table's cooldown scope, and STOP
|
|
19
|
+
* (no failover) for context_overflow /
|
|
20
|
+
* payload_too_large / content_policy /
|
|
21
|
+
* network exactly as the table says.
|
|
22
|
+
*
|
|
23
|
+
* The actual child process is NOT launched here — the daemon injects a
|
|
24
|
+
* `spawnImpl(candidate, { args, env, decision }) -> { ok, reason?, result? }`.
|
|
25
|
+
* This keeps spawn.mjs pure orchestration (no child_process, no fs, no clock of
|
|
26
|
+
* its own) so it is hermetically testable, mirroring the DI discipline of
|
|
27
|
+
* failover.mjs / resource-governor.mjs.
|
|
28
|
+
*
|
|
29
|
+
* ROUTING IS A DISPATCH-TIME DECISION (SPEC §6.2). spawnRouted owns the fast-fail
|
|
30
|
+
* failover loop BEFORE real work begins; it never re-routes a live session (cache
|
|
31
|
+
* economics — a mid-session switch silently re-bills the entire prefix). The
|
|
32
|
+
* kill switch (MAESTRO_ROUTER_FORCE_ANTHROPIC) is honored inside resolveChain,
|
|
33
|
+
* which collapses the chain to an Anthropic-only member, so spawnRouted naturally
|
|
34
|
+
* short-circuits to stock Anthropic with zero third-party candidates to walk.
|
|
35
|
+
*
|
|
36
|
+
* House style: ESM .mjs, Node built-ins only, never throws on the orchestration
|
|
37
|
+
* path (a spawnImpl that throws is caught + classified `unknown`).
|
|
38
|
+
*
|
|
39
|
+
* @module lib/model-router/spawn
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { resolveChain } from "./resolve.mjs";
|
|
43
|
+
import { stepChain } from "./failover.mjs";
|
|
44
|
+
import { keyFor, isOpen as healthIsOpen, open as healthOpen, close as healthClose } from "./health.mjs";
|
|
45
|
+
import { lookupModel } from "./catalog.mjs";
|
|
46
|
+
import {
|
|
47
|
+
nextCredential as authNextCredential,
|
|
48
|
+
markCredentialCoolingDown as authMarkCooling,
|
|
49
|
+
} from "./auth-profiles.mjs";
|
|
50
|
+
|
|
51
|
+
// Failover reasons that indicate the CREDENTIAL itself is the problem (a 429 /
|
|
52
|
+
// auth failure on this key) — these cool THAT credential in the pool so the next
|
|
53
|
+
// attempt rotates to a sibling. A backend/model-class reason (overloaded,
|
|
54
|
+
// server_error, …) cools the breaker, not the key.
|
|
55
|
+
const CRED_COOL_REASONS = new Set(["rate_limit", "auth", "auth_permanent", "billing"]);
|
|
56
|
+
|
|
57
|
+
// Env keys that the session-retarget set is allowed to introduce. Everything
|
|
58
|
+
// else credential-shaped is scrubbed (§7.3) before injection.
|
|
59
|
+
const RETARGET_KEYS = new Set([
|
|
60
|
+
"ANTHROPIC_BASE_URL",
|
|
61
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
62
|
+
"ANTHROPIC_API_KEY",
|
|
63
|
+
"ANTHROPIC_MODEL",
|
|
64
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
65
|
+
"CLAUDE_CODE_ATTRIBUTION_HEADER",
|
|
66
|
+
"CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS",
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Child-env scrubbing (graft #3 / §7.3)
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Build the child env by allowlist: start from a scrubbed copy of `baseEnv`
|
|
75
|
+
* with every credential-shaped var (`*_API_KEY` / `*_AUTH_TOKEN`, including the
|
|
76
|
+
* break-glass key) removed, then inject ONLY the resolved candidate's
|
|
77
|
+
* session-retarget pair (decision.envForSpawn). A session routed to a third
|
|
78
|
+
* party never sees Anthropic credentials, and vice versa.
|
|
79
|
+
*
|
|
80
|
+
* The Anthropic SESSION path (envForSpawn === {}) rides the stock CLI / keychain
|
|
81
|
+
* OAuth or a Console key the spawner owns; we keep an explicitly-injected
|
|
82
|
+
* ANTHROPIC_API_KEY from the caller's base env in that case ONLY if the caller
|
|
83
|
+
* opted in via opts.keepAnthropicApiKey (default: scrub for hygiene). When we do
|
|
84
|
+
* retarget (envForSpawn has ANTHROPIC_BASE_URL), ANTHROPIC_API_KEY="" is part of
|
|
85
|
+
* the retarget set and is injected verbatim (empty-string discipline, W:
|
|
86
|
+
* keychain fallthrough).
|
|
87
|
+
*
|
|
88
|
+
* @param {object} baseEnv usually process.env
|
|
89
|
+
* @param {object} envForSpawn decision.envForSpawn (the retarget pair) or {}
|
|
90
|
+
* @param {object} [opts]
|
|
91
|
+
* @param {boolean} [opts.keepAnthropicApiKey=false]
|
|
92
|
+
* @returns {object} the child env
|
|
93
|
+
*/
|
|
94
|
+
export function buildChildEnv(baseEnv, envForSpawn = {}, opts = {}) {
|
|
95
|
+
const out = {};
|
|
96
|
+
const src = baseEnv && typeof baseEnv === "object" ? baseEnv : {};
|
|
97
|
+
for (const [k, v] of Object.entries(src)) {
|
|
98
|
+
if (isCredentialKey(k)) continue; // scrub all credential-shaped vars
|
|
99
|
+
out[k] = v;
|
|
100
|
+
}
|
|
101
|
+
// For a retarget (third-party session), force the empty-string ANTHROPIC_API_KEY
|
|
102
|
+
// so Claude Code does NOT fall back to keychain OAuth.
|
|
103
|
+
const retargeting = !!(envForSpawn && envForSpawn.ANTHROPIC_BASE_URL);
|
|
104
|
+
if (!retargeting && opts.keepAnthropicApiKey && typeof src.ANTHROPIC_API_KEY === "string") {
|
|
105
|
+
out.ANTHROPIC_API_KEY = src.ANTHROPIC_API_KEY;
|
|
106
|
+
}
|
|
107
|
+
// Inject only the retarget set (whitelisted keys).
|
|
108
|
+
for (const [k, v] of Object.entries(envForSpawn || {})) {
|
|
109
|
+
if (RETARGET_KEYS.has(k)) out[k] = v;
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isCredentialKey(k) {
|
|
115
|
+
return /(_API_KEY|_AUTH_TOKEN|_SECRET|_ACCESS_KEY|_PRIVATE_KEY)$/i.test(k) || k === "ANTHROPIC_API_KEY";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// argv construction
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Build the `claude -p` argv from a RouteDecision's spawnArgs + a concrete
|
|
124
|
+
* candidate ref (which carries the modelFlag for this chain member). The base
|
|
125
|
+
* spawnArgs come from the decision (maxTurns/effort/agentsJson/bare); the
|
|
126
|
+
* per-candidate modelFlag overrides decision.spawnArgs.modelFlag when failing
|
|
127
|
+
* over to a different chain member.
|
|
128
|
+
*
|
|
129
|
+
* Argv order is deterministic so tests can assert it exactly:
|
|
130
|
+
* [--print, --bare?, --output-format json, --model <flag>, --max-turns <n>,
|
|
131
|
+
* --effort <e>?, --agents <json>?, ...extraArgs]
|
|
132
|
+
*
|
|
133
|
+
* @param {object} spawnArgs decision.spawnArgs
|
|
134
|
+
* @param {object} [over]
|
|
135
|
+
* @param {string} [over.modelFlag] per-candidate model flag override
|
|
136
|
+
* @param {string[]} [over.extraArgs] appended verbatim (prompt file, etc.)
|
|
137
|
+
* @param {string} [over.outputFormat="json"] --output-format value
|
|
138
|
+
* @returns {string[]}
|
|
139
|
+
*/
|
|
140
|
+
export function buildSpawnArgs(spawnArgs = {}, over = {}) {
|
|
141
|
+
const args = ["--print"];
|
|
142
|
+
if (spawnArgs.bare !== false) args.push("--bare");
|
|
143
|
+
const outputFormat = over.outputFormat || "json";
|
|
144
|
+
args.push("--output-format", outputFormat);
|
|
145
|
+
|
|
146
|
+
const modelFlag = over.modelFlag || spawnArgs.modelFlag;
|
|
147
|
+
if (modelFlag) args.push("--model", String(modelFlag));
|
|
148
|
+
|
|
149
|
+
if (spawnArgs.maxTurns != null && Number.isFinite(Number(spawnArgs.maxTurns))) {
|
|
150
|
+
args.push("--max-turns", String(spawnArgs.maxTurns));
|
|
151
|
+
}
|
|
152
|
+
if (spawnArgs.effort) args.push("--effort", String(spawnArgs.effort));
|
|
153
|
+
if (spawnArgs.maxBudgetUsd != null && Number.isFinite(Number(spawnArgs.maxBudgetUsd))) {
|
|
154
|
+
args.push("--max-budget-usd", String(spawnArgs.maxBudgetUsd));
|
|
155
|
+
}
|
|
156
|
+
if (spawnArgs.agentsJson) {
|
|
157
|
+
const json =
|
|
158
|
+
typeof spawnArgs.agentsJson === "string"
|
|
159
|
+
? spawnArgs.agentsJson
|
|
160
|
+
: JSON.stringify(spawnArgs.agentsJson);
|
|
161
|
+
args.push("--agents", json);
|
|
162
|
+
}
|
|
163
|
+
if (Array.isArray(over.extraArgs) && over.extraArgs.length) {
|
|
164
|
+
for (const a of over.extraArgs) args.push(String(a));
|
|
165
|
+
}
|
|
166
|
+
return args;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Per-candidate retarget env (failover may switch backends)
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Derive the session-retarget env for a chain candidate that is NOT the decision's
|
|
175
|
+
* chosen row (a failover target). The decision only carries envForSpawn for its
|
|
176
|
+
* chosen row, so when we fail over to a different ref we re-derive the retarget
|
|
177
|
+
* pair from that ref's catalog row. Mirrors resolve.buildEnvForSpawn for the
|
|
178
|
+
* session harness.
|
|
179
|
+
*/
|
|
180
|
+
function envForCandidate(catalog, ref, harness, baseEnv, decision, authCtx) {
|
|
181
|
+
// The chosen ref reuses the decision's already-built envForSpawn verbatim ONLY
|
|
182
|
+
// when there's no pool to rotate over — a pooled set must re-pick per attempt so
|
|
183
|
+
// a failover that cooled the first key rotates to a sibling.
|
|
184
|
+
const isChosen = decision && decision.chosen && ref === decision.chosen.catalogRow?.ref;
|
|
185
|
+
const row = catalog ? lookupModel(catalog, ref) : null;
|
|
186
|
+
const provider = row ? row.provider : refProvider(ref);
|
|
187
|
+
const pooled =
|
|
188
|
+
authCtx && authCtx.profiles && typeof authCtx.profiles.isPooled === "function"
|
|
189
|
+
? authCtx.profiles.isPooled(provider)
|
|
190
|
+
: false;
|
|
191
|
+
if (isChosen && !pooled) {
|
|
192
|
+
return decision.envForSpawn || {};
|
|
193
|
+
}
|
|
194
|
+
if (!row || harness !== "session") return isChosen ? decision.envForSpawn || {} : {};
|
|
195
|
+
if (row.provider === "anthropic") return isChosen ? decision.envForSpawn || {} : {};
|
|
196
|
+
const baseUrl = row.endpoints && row.endpoints.anthropic;
|
|
197
|
+
if (!baseUrl) return isChosen ? decision.envForSpawn || {} : {};
|
|
198
|
+
const out = {
|
|
199
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
200
|
+
ANTHROPIC_API_KEY: "",
|
|
201
|
+
ANTHROPIC_MODEL: row.id,
|
|
202
|
+
ANTHROPIC_SMALL_FAST_MODEL: row.id,
|
|
203
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: "0",
|
|
204
|
+
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1",
|
|
205
|
+
};
|
|
206
|
+
// Pooled credential rotation (additive): pick the next non-cooled key, else
|
|
207
|
+
// fall back to the single env var exactly as before.
|
|
208
|
+
let tok;
|
|
209
|
+
let fp = null;
|
|
210
|
+
if (pooled && row.auth_env) {
|
|
211
|
+
const pick = authNextCredential(provider, {
|
|
212
|
+
profiles: authCtx.profiles,
|
|
213
|
+
now: authCtx.now,
|
|
214
|
+
deps: authCtx.deps,
|
|
215
|
+
});
|
|
216
|
+
if (pick && pick.key) {
|
|
217
|
+
tok = pick.key;
|
|
218
|
+
fp = pick.fingerprint;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (typeof tok !== "string") {
|
|
222
|
+
tok = baseEnv && row.auth_env ? baseEnv[row.auth_env] : undefined;
|
|
223
|
+
}
|
|
224
|
+
if (typeof tok === "string") {
|
|
225
|
+
out.ANTHROPIC_AUTH_TOKEN = tok;
|
|
226
|
+
out.__credFingerprint = fp; // internal-only; scrubbed before injection
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Provider segment of a "provider/model" ref. */
|
|
232
|
+
function refProvider(ref) {
|
|
233
|
+
return typeof ref === "string" && ref.includes("/") ? ref.split("/")[0] : ref;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The model flag for a chain candidate ref (the bare id, mirrors resolve). */
|
|
237
|
+
function modelFlagForRef(catalog, ref, fallbackFlag) {
|
|
238
|
+
const row = catalog ? lookupModel(catalog, ref) : null;
|
|
239
|
+
if (row && row.id) return row.id;
|
|
240
|
+
// ref is "provider/model"; the model segment is the flag if no row.
|
|
241
|
+
if (typeof ref === "string" && ref.includes("/")) return ref.split("/").slice(1).join("/");
|
|
242
|
+
return fallbackFlag || ref;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Build the breaker keys (backend + backend:model) for a chain candidate ref. */
|
|
246
|
+
function breakerKeysForRef(ref) {
|
|
247
|
+
if (typeof ref !== "string" || !ref.includes("/")) {
|
|
248
|
+
return { breakerKey: keyFor(ref || "unknown"), backendKey: keyFor(ref || "unknown") };
|
|
249
|
+
}
|
|
250
|
+
const [provider, ...rest] = ref.split("/");
|
|
251
|
+
const model = rest.join("/");
|
|
252
|
+
return { breakerKey: keyFor(provider, model), backendKey: keyFor(provider) };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// spawnRouted — the public entry point
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Resolve a route and EXECUTE the chosen candidate via an injected spawnImpl,
|
|
261
|
+
* failing over along the chain per the disposition table. Returns the decision,
|
|
262
|
+
* the chosen candidate, the argv + child env actually built for the successful
|
|
263
|
+
* (or last) attempt, the failover outcome, and the per-candidate tried[].
|
|
264
|
+
*
|
|
265
|
+
* NEVER throws.
|
|
266
|
+
*
|
|
267
|
+
* @param {object} request RouteRequest (§4.3)
|
|
268
|
+
* @param {object} [opts]
|
|
269
|
+
* @param {(candidate, ctx) => ({ok, reason?, result?}|Promise<...>)} opts.spawnImpl
|
|
270
|
+
* REQUIRED. ctx = { args, env, decision, candidate, attemptIndex }.
|
|
271
|
+
* Returns { ok:true, result } on success, or { ok:false, reason } where
|
|
272
|
+
* reason is a FailoverReason (taxonomy.mjs). A throw is caught → unknown.
|
|
273
|
+
* @param {object} [opts.decision] inject a pre-resolved RouteDecision (tests);
|
|
274
|
+
* otherwise resolveChain(request, opts) is called.
|
|
275
|
+
* @param {object} [opts.config] routing config (passed to resolveChain)
|
|
276
|
+
* @param {object} [opts.catalog] catalog (passed to resolveChain + retarget)
|
|
277
|
+
* @param {object} [opts.env] base env (default process.env)
|
|
278
|
+
* @param {()=>number} [opts.now]
|
|
279
|
+
* @param {(n)=>number} [opts.rng]
|
|
280
|
+
* @param {object} [opts.breaker] { isOpen, open, close } — defaults to health.mjs
|
|
281
|
+
* @param {object} [opts.breakerDeps] deps for the default health breaker (agentRoot, stateDir, now)
|
|
282
|
+
* @param {boolean} [opts.allowContentReroute=false] one content_policy re-route permitted
|
|
283
|
+
* @param {string[]} [opts.extraArgs] appended to every candidate's argv
|
|
284
|
+
* @param {string} [opts.outputFormat="json"]
|
|
285
|
+
* @param {boolean} [opts.keepAnthropicApiKey=false]
|
|
286
|
+
* @param {(rec)=>void} [opts.onAttempt] side-channel per attempt
|
|
287
|
+
* @returns {Promise<{
|
|
288
|
+
* decision, chosen, args, env, outcome,
|
|
289
|
+
* tried: Array<{ref, reason, retry?, meta?}>,
|
|
290
|
+
* ok: boolean
|
|
291
|
+
* }>}
|
|
292
|
+
*/
|
|
293
|
+
export async function spawnRouted(request = {}, opts = {}) {
|
|
294
|
+
const env = opts.env || process.env || {};
|
|
295
|
+
const decision = opts.decision || resolveChain(request, opts);
|
|
296
|
+
|
|
297
|
+
// A no-route or rules decision has no model to spawn — surface it without
|
|
298
|
+
// touching spawnImpl. The caller's deterministic fallback handles "rules".
|
|
299
|
+
if (!decision || !decision.chosen || decision.chosen.provider === "rules" || !decision.chain || !decision.chain.length) {
|
|
300
|
+
return {
|
|
301
|
+
decision,
|
|
302
|
+
chosen: decision ? decision.chosen : null,
|
|
303
|
+
args: null,
|
|
304
|
+
env: null,
|
|
305
|
+
outcome: {
|
|
306
|
+
ok: false,
|
|
307
|
+
reason: decision && decision.chosen && decision.chosen.provider === "rules" ? "rules" : "no_route",
|
|
308
|
+
tried: [],
|
|
309
|
+
soonestCooldownExpiry: null,
|
|
310
|
+
attempts: 0,
|
|
311
|
+
},
|
|
312
|
+
tried: [],
|
|
313
|
+
ok: false,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const spawnImpl = typeof opts.spawnImpl === "function" ? opts.spawnImpl : null;
|
|
318
|
+
if (!spawnImpl) {
|
|
319
|
+
return {
|
|
320
|
+
decision,
|
|
321
|
+
chosen: decision.chosen,
|
|
322
|
+
args: null,
|
|
323
|
+
env: null,
|
|
324
|
+
outcome: { ok: false, reason: "unknown", tried: [{ ref: decision.chosen.catalogRow?.ref ?? null, reason: "no_spawn_impl" }], soonestCooldownExpiry: null, attempts: 0 },
|
|
325
|
+
tried: [{ ref: decision.chosen.catalogRow?.ref ?? null, reason: "no_spawn_impl" }],
|
|
326
|
+
ok: false,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const catalog = resolveCatalogFromOpts(opts, decision);
|
|
331
|
+
const harness = decision.chosen.harness || "session";
|
|
332
|
+
|
|
333
|
+
// Build the candidate chain from decision.chain. Each candidate carries the
|
|
334
|
+
// breaker keys (so stepChain skips open keys + opens the right scope) and the
|
|
335
|
+
// per-candidate argv/env materials.
|
|
336
|
+
const candidates = decision.chain
|
|
337
|
+
.filter((c) => c && c.ref && c.transport !== "rules")
|
|
338
|
+
.map((c) => {
|
|
339
|
+
const keys = breakerKeysForRef(c.ref);
|
|
340
|
+
return {
|
|
341
|
+
ref: c.ref,
|
|
342
|
+
harness: c.harness || harness,
|
|
343
|
+
transport: c.transport,
|
|
344
|
+
breakerKey: keys.breakerKey,
|
|
345
|
+
backendKey: keys.backendKey,
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
if (!candidates.length) {
|
|
350
|
+
return {
|
|
351
|
+
decision,
|
|
352
|
+
chosen: decision.chosen,
|
|
353
|
+
args: null,
|
|
354
|
+
env: null,
|
|
355
|
+
outcome: { ok: false, reason: "no_route", tried: [], soonestCooldownExpiry: null, attempts: 0 },
|
|
356
|
+
tried: [],
|
|
357
|
+
ok: false,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// The breaker the failover engine drives. Default: the on-disk health.mjs.
|
|
362
|
+
const breaker = opts.breaker || defaultBreaker(opts);
|
|
363
|
+
|
|
364
|
+
// Auth-profile rotation context (additive; absent ⇒ single-key behavior).
|
|
365
|
+
// `profiles` is a loadAuthProfiles result; `deps` is the ledger I/O context.
|
|
366
|
+
const authCtx = opts.authProfiles
|
|
367
|
+
? { profiles: opts.authProfiles, now: opts.now ? opts.now() : undefined, deps: opts.authProfileDeps }
|
|
368
|
+
: null;
|
|
369
|
+
|
|
370
|
+
// Capture the argv/env actually built for the LAST attempt (for the return +
|
|
371
|
+
// the daemon's ledger row). Updated inside the attempt fn.
|
|
372
|
+
let lastArgs = null;
|
|
373
|
+
let lastEnv = null;
|
|
374
|
+
let chosenCandidate = null;
|
|
375
|
+
|
|
376
|
+
const outcome = await stepChain(candidates, {
|
|
377
|
+
now: opts.now,
|
|
378
|
+
breaker,
|
|
379
|
+
allowContentReroute: opts.allowContentReroute === true,
|
|
380
|
+
onAttempt: opts.onAttempt,
|
|
381
|
+
attempt: async (candidate, ctx) => {
|
|
382
|
+
const modelFlag = modelFlagForRef(catalog, candidate.ref, decision.spawnArgs?.modelFlag);
|
|
383
|
+
const args = buildSpawnArgs(decision.spawnArgs, {
|
|
384
|
+
modelFlag,
|
|
385
|
+
extraArgs: opts.extraArgs,
|
|
386
|
+
outputFormat: opts.outputFormat,
|
|
387
|
+
});
|
|
388
|
+
// Re-derive the per-attempt clock for the auth context so a rotated pick
|
|
389
|
+
// sees the current time (the ctx.now from stepChain is the attempt time).
|
|
390
|
+
const aCtx = authCtx ? { ...authCtx, now: ctx.now } : null;
|
|
391
|
+
const retarget = envForCandidate(catalog, candidate.ref, candidate.harness, env, decision, aCtx);
|
|
392
|
+
// The fingerprint of the credential this attempt used (pooled rotation only);
|
|
393
|
+
// strip it from the retarget set so it can never leak into the child env.
|
|
394
|
+
const credFingerprint = retarget.__credFingerprint || null;
|
|
395
|
+
if (retarget.__credFingerprint !== undefined) delete retarget.__credFingerprint;
|
|
396
|
+
const childEnv = buildChildEnv(env, retarget, { keepAnthropicApiKey: opts.keepAnthropicApiKey });
|
|
397
|
+
lastArgs = args;
|
|
398
|
+
lastEnv = childEnv;
|
|
399
|
+
|
|
400
|
+
let res;
|
|
401
|
+
try {
|
|
402
|
+
res = await spawnImpl(candidate, {
|
|
403
|
+
args,
|
|
404
|
+
env: childEnv,
|
|
405
|
+
decision,
|
|
406
|
+
candidate,
|
|
407
|
+
attemptIndex: ctx.attemptIndex,
|
|
408
|
+
});
|
|
409
|
+
} catch (err) {
|
|
410
|
+
return { ok: false, reason: "unknown", meta: { thrown: String((err && err.message) || err) } };
|
|
411
|
+
}
|
|
412
|
+
res = res || { ok: false, reason: "unknown" };
|
|
413
|
+
if (res.ok) {
|
|
414
|
+
chosenCandidate = candidate;
|
|
415
|
+
return { ok: true, value: res.result, meta: res.meta };
|
|
416
|
+
}
|
|
417
|
+
// On a credential-class failure, cool THAT credential in the pool (not the
|
|
418
|
+
// whole provider — the breaker, driven by stepChain, owns backend cooldown).
|
|
419
|
+
// The next attempt's nextCredential() then rotates to a sibling key.
|
|
420
|
+
const reason = res.reason || "unknown";
|
|
421
|
+
if (authCtx && credFingerprint && CRED_COOL_REASONS.has(reason)) {
|
|
422
|
+
try {
|
|
423
|
+
authMarkCooling(refProvider(candidate.ref), { fingerprint: credFingerprint }, reason, null, {
|
|
424
|
+
now: ctx.now,
|
|
425
|
+
deps: authCtx.deps,
|
|
426
|
+
});
|
|
427
|
+
} catch { /* fail-open: a cooldown we can't persist degrades, never blocks */ }
|
|
428
|
+
}
|
|
429
|
+
return { ok: false, reason, meta: res.meta };
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// On success, stepChain.chosen is the winning candidate; reflect it.
|
|
434
|
+
if (outcome.ok && outcome.chosen) chosenCandidate = outcome.chosen;
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
decision,
|
|
438
|
+
chosen: chosenCandidate
|
|
439
|
+
? { ...decision.chosen, ...candidateChosenView(catalog, chosenCandidate) }
|
|
440
|
+
: decision.chosen,
|
|
441
|
+
args: lastArgs,
|
|
442
|
+
env: lastEnv,
|
|
443
|
+
outcome,
|
|
444
|
+
tried: outcome.tried || [],
|
|
445
|
+
ok: outcome.ok === true,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** A `chosen`-shaped view of a winning failover candidate (for the return). */
|
|
450
|
+
function candidateChosenView(catalog, candidate) {
|
|
451
|
+
const row = catalog ? lookupModel(catalog, candidate.ref) : null;
|
|
452
|
+
if (!row) return { harness: candidate.harness, transport: candidate.transport };
|
|
453
|
+
return {
|
|
454
|
+
provider: row.provider,
|
|
455
|
+
model: row.id,
|
|
456
|
+
harness: candidate.harness,
|
|
457
|
+
transport: candidate.transport,
|
|
458
|
+
catalogRow: row,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function resolveCatalogFromOpts(opts, decision) {
|
|
463
|
+
if (opts && opts.catalog) {
|
|
464
|
+
return opts.catalog.byRef ? opts.catalog : opts.catalog.catalog || opts.catalog;
|
|
465
|
+
}
|
|
466
|
+
// The decision's chosen catalogRow can seed a minimal lookup for the chosen ref,
|
|
467
|
+
// but failover targets need the full catalog; without it, modelFlag falls back
|
|
468
|
+
// to the ref's model segment and retarget env is skipped (Anthropic-safe).
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* The default breaker: a thin adapter over the on-disk health.mjs API, shaped
|
|
474
|
+
* the way failover.mjs expects (isOpen/open/close). Injectable for tests.
|
|
475
|
+
*/
|
|
476
|
+
function defaultBreaker(opts) {
|
|
477
|
+
const deps = opts.breakerDeps || {};
|
|
478
|
+
return {
|
|
479
|
+
isOpen: (key, now) => healthIsOpen(key, now, deps),
|
|
480
|
+
open: (key, reason, ms, now) => healthOpen(key, reason, ms, now, deps),
|
|
481
|
+
close: (key, now) => healthClose(key, now, deps),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export const _internals = {
|
|
486
|
+
buildChildEnv,
|
|
487
|
+
buildSpawnArgs,
|
|
488
|
+
envForCandidate,
|
|
489
|
+
modelFlagForRef,
|
|
490
|
+
breakerKeysForRef,
|
|
491
|
+
isCredentialKey,
|
|
492
|
+
refProvider,
|
|
493
|
+
CRED_COOL_REASONS,
|
|
494
|
+
RETARGET_KEYS,
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
export default { spawnRouted, buildChildEnv, buildSpawnArgs };
|